自从arcgis for android支持三维后,让app端增加了不少色彩,也值得去研究。好了,我们来看一下,这里是关于对三维中视域的位置设置。其中关于视域的位置参数设置有几个值需要我们去了解的。如下图所示,为测试的一个demo,首先heading为绕z轴旋转参数,而pitch位绕z轴翻滚参数,也就是说,当pitch越来越大时,那么这个浅绿色的投影将会离过这个锥形顶点z轴越来越远。而我们依此移动的方向作为x轴,那么Vertical Angle就是在x轴上,向两边扩大,伸展的参数。而Horizontal Angle则为x-z平面上垂直方向,即做y轴方向,向两边扩大、伸展的参数。至于,Minimum Distance、Maximum Distance两个参数没有深入去了解。
好了,我们来看一下代码,如下所示。
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
// initial values
private static final int mInitHeading = 0;
private static final int mInitPitch = 60;
private static final int mInitHorizontalAngle = 75;
private static final int mInitVerticalAngle = 90;
private static final int mInitMinDistance = 0;
private static final int mInitMaxDistance = 1500;
private SceneView mSceneView;
private LocationViewshed mViewshed;
private int mMinDistance;
private int mMaxDistance;
private SeekBar mHeadingSeekBar;
private SeekBar mPitchSeekBar;
private SeekBar mHorizontalAngleSeekBar;
private SeekBar mVerticalAngleSeekBar;
private SeekBar mMinDistanceSeekBar;
private SeekBar mMaxDistanceSeekBar;
private TextView mCurrHeading;
private TextView mCurrPitch;
private TextView mCurrHorizontalAngle;
private TextView mCurrVerticalAngle;
private TextView mCurrMinDistance;
private TextView mCurrMaxDistance;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// set initial values
mMinDistance = mInitMinDistance;
mMaxDistance = mInitMaxDistance;
// 创建场景,并将影像底图服务作为底图
mSceneView = findViewById(R.id.sceneView);
ArcGISScene scene = new ArcGISScene();
scene.setBasemap(Basemap.createImagery());
mSceneView.setScene(scene);
// 添加平面作为高程值
Surface surface = new Surface();
final String localElevationImageService = getString(
com.esri.arcgisruntime.sample.viewshedlocation.R.string.elevation_service);
surface.getElevationSources().add(new ArcGISTiledElevationSource(localElevationImageService));
scene.setBaseSurface(surface);
// 添加一个三维图层
final String buildings = getString(com.esri.arcgisruntime.sample.viewshedlocation.R.string.buildings_layer);
ArcGISSceneLayer sceneLayer = new ArcGISSceneLayer(buildings);
scene.getOperationalLayers().add(sceneLayer);
// 创建视域位置
Point location = new Point(-4.50, 48.4, 100.0);
Viewshed.setFrustumOutlineColor(Color.BLUE);
mViewshed = new LocationViewshed(location, mInitHeading, mInitPitch, mInitHorizontalAngle, mInitVerticalAngle,
mInitMinDistance, mInitMaxDistance);
mViewshed.setFrustumOutlineVisible(true);
// 添加一个相机和设置相机的轨迹位置
Camera camera = new Camera(location, 20000000, 0, 55, 0);
OrbitLocationCameraController orbitCamera = new OrbitLocationCameraController(location, 5000);
mSceneView.setCameraController(orbitCamera);
mSceneView.setViewpointCamera(camera);
//在三维场景图层中,添加一个分析图层,图中的淡绿色部分
AnalysisOverlay analysisOverlay = new AnalysisOverlay();
analysisOverlay.getAnalyses().add(mViewshed);
mSceneView.getAnalysisOverlays().add(analysisOverlay);
handleUiElements();
}
/**
处理拖拽seak bar进度条来调整视域的位置
*/
private void handleUiElements() {
mSceneView.setOnTouchListener(new DefaultSceneViewOnTouchListener(mSceneView) {
@Override public boolean onDoubleTouchDrag(MotionEvent motionEvent) {
// convert from screen point to location point
android.graphics.Point screenPoint = new android.graphics.Point(Math.round(motionEvent.getX()),
Math.round(motionEvent.getY()));
ListenableFuture<Point> locationPointFuture = mSceneView.screenToLocationAsync(screenPoint);
locationPointFuture.addDoneListener(() -> {
try {
Point locationPoint = locationPointFuture.get();
// add 50 meters to location point and set to viewshed
mViewshed.setLocation(new Point(locationPoint.getX(), locationPoint.getY(), locationPoint.getZ() + 50));
} catch (InterruptedException | ExecutionException e) {
String error = "Error converting screen point to location point: " + e.getMessage();
Log.e(TAG, error);
Toast.makeText(MainActivity.this, error, Toast.LENGTH_LONG).show();
}
});
// ignore default double touch drag gesture
return true;
}
});
// get views from layout
mCurrHeading = findViewById(R.id.curr_heading);
mCurrPitch = findViewById(R.id.curr_pitch);
mCurrHorizontalAngle = findViewById(R.id.curr_horizontal_angle);
mCurrVerticalAngle = findViewById(R.id.curr_vertical_angle);
mCurrMinDistance = findViewById(R.id.curr_minimum_distance);
mCurrMaxDistance = findViewById(R.id.curr_maximum_distance);
// heading range 0 - 360
mHeadingSeekBar = findViewById(R.id.heading_seek_bar);
mHeadingSeekBar.setMax(360);
setHeading(mInitHeading);
mHeadingSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
setHeading(seekBar.getProgress());
}
@Override public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override public void onStopTrackingTouch(SeekBar seekBar) {
}
});
// set arbitrary max to 180 to avoid nonsensical pitch values
mPitchSeekBar = findViewById(R.id.pitch_seek_bar);
mPitchSeekBar.setMax(180);
setPitch(mInitPitch);
mPitchSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
setPitch(seekBar.getProgress());
}
@Override public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override public void onStopTrackingTouch(SeekBar seekBar) {
}
});
// horizontal angle range 1 - 120
mHorizontalAngleSeekBar = findViewById(R.id.horizontal_angle_seekbar);
mHorizontalAngleSeekBar.setMax(120);
setHorizontalAngle(mInitHorizontalAngle);
mHorizontalAngleSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
int horizontalAngle = mHorizontalAngleSeekBar.getProgress();
if (horizontalAngle > 0) { // horizontal angle must be > 0
setHorizontalAngle(horizontalAngle);
}
}
@Override public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override public void onStopTrackingTouch(SeekBar seekBar) {
}
});
// vertical angle range 1 - 120
mVerticalAngleSeekBar = findViewById(R.id.vertical_angle_seekbar);
mVerticalAngleSeekBar.setMax(120);
setVerticalAngle(mInitVerticalAngle);
mVerticalAngleSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
int verticalAngle = mVerticalAngleSeekBar.getProgress();
if (verticalAngle > 0) { // vertical angle must be > 0
setVerticalAngle(verticalAngle);
}
}
@Override public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override public void onStopTrackingTouch(SeekBar seekBar) {
}
});
// set to 1000 below the arbitrary max
mMinDistanceSeekBar = findViewById(R.id.min_distance_seekbar);
mMinDistanceSeekBar.setMax(8999);
setMinDistance(mInitMinDistance);
mMinDistanceSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
mMinDistance = seekBar.getProgress();
if (mMaxDistance - mMinDistance < 1000) {
mMaxDistance = mMinDistance + 1000;
setMaxDistance(mMaxDistance);
}
setMinDistance(mMinDistance);
}
@Override public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override public void onStopTrackingTouch(SeekBar seekBar) {
}
});
// set arbitrary max to 9999 to allow a maximum of 4 digits
mMaxDistanceSeekBar = findViewById(R.id.max_distance_seekbar);
mMaxDistanceSeekBar.setMax(9999);
setMaxDistance(mInitMaxDistance);
mMaxDistanceSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
mMaxDistance = seekBar.getProgress();
if (mMaxDistance - mMinDistance < 1000) {
if (mMaxDistance > 1000) {
mMinDistance = mMaxDistance - 1000;
} else {
mMinDistance = 0;
}
setMinDistance(mMinDistance);
}
setMaxDistance(mMaxDistance);
}
@Override public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override public void onStopTrackingTouch(SeekBar seekBar) {
}
});
}
/**
* Set viewshed heading, seek bar progress, and current heading text view.
*
* @param heading in degrees
*/
private void setHeading(int heading) {
mViewshed.setHeading(heading);
mHeadingSeekBar.setProgress(heading);
mCurrHeading.setText(Integer.toString(heading));
}
/**
* Set viewshed pitch, seek bar progress, and current pitch text view.
*
* @param pitch in degrees
*/
private void setPitch(int pitch) {
mViewshed.setPitch(pitch);
mPitchSeekBar.setProgress(pitch);
mCurrPitch.setText(Integer.toString(pitch));
}
/**
* Set viewshed horizontal angle, seek bar progress, and current horizontal angle text view.
*
* @param horizontalAngle in degrees, > 0 and <= 120
*/
private void setHorizontalAngle(int horizontalAngle) {
if (horizontalAngle > 0 && horizontalAngle <= 120) {
mViewshed.setHorizontalAngle(horizontalAngle);
mHorizontalAngleSeekBar.setProgress(horizontalAngle);
mCurrHorizontalAngle.setText(Integer.toString(horizontalAngle));
} else {
Log.e(TAG, "Horizontal angle must be greater than 0 and less than or equal to 120.");
}
}