(一)显示地图
地图指定地理数据是如何组织的,以及如何将其传递给应用程序用户。地图的每个实例(ArcGISMap)表示可以为用户显示的单个地图。要显示地图,必须将其分配给地图视图(MapView)。在MVC架构中,地图表示模型层,地图视图表示视图层。地图和地图视图一起工作,使屏幕上的地理数据可视化。
用户可以使用地图视图的内置导航工具与地图及地图各分层进行交互。通过配置地图的外观和感觉以及向这些用户交互添加业务逻辑,您有许多方法来增强这种体验。
1. 打开地图
您可以通过从网络地图或移动地图包创建地图来打开地图。
1.1 从web地图创建地图对象
Web Map可以使用其URL访问,也可以使用门户项ID直接从门户访问。这里描述了这两种方法以及访问地图(如果地图或其数据不是公共的)所需的身份验证过程。
1.1.1 使用web地图的URL访问
您可以从映射的URL字符串中构造map对象,如下所示:
ArcGISMap map = new ArcGISMap("URL/to/webmap")
映射构造函数支持以下三种URL格式:
- 地图查看器URL,例如 https://www. ARCGIS.COM/HOME/WebMAP/VIEWR.HTML?WebMap=69FDCD8E40734 712AAEC34 194D4B988C。当使用ARCGISS.MAP查看器创建或查看网络地图时,在浏览器的地址栏中提供URL。
- 项数据详情URL,例如 https://www.arcgis.com/home/item.html?id=69fdcd8e40734712aaec34194d4b988c.当你查看ArcGIS.com中"我的内容"中显示的项目的详细信息页时,这个URL会在地址栏中找到。
- 项数据URL,如https://www.arcgis.com/sharing/rest/content/items/69fdcd8e40734712aaec34194d4b988c/data?.这是表示要显示映射的信息的JSON字符串的地址。
如果地图不是公共的,API的身份验证质疑处理程序会自动请求用户提供他的凭据。
1.1.2 使用web地图门户项
可以使用地图的门户项ID构造map对象。您可以在上面描述的三个URL中的任何一个中找到这个32个字符ID。例如,下面是由69fdd8e40734712aec34194d4b988c项ID标识的地图。
1) 通过向门户提供URL字符串来创建门户对象。在下面的示例中,公共地图可从ArcGIS在线访问(www.arcgis.com)。
//construct the portal from the URL of the portal
Portal portal = new Portal("http://www.arcgis.com");
2)通过为映射提供门户和项ID字符串来构造门户项对象。
//construct a portal item from the portal and item ID string
PortalItem mapPortalItem = new PortalItem(portal, "e229d715f7ca4fa980308549fb288165");
3)将门户项目传递给map构造函数。
//construct a map from the portal item
ArcGISMap map = new ArcGISMap(mapPortalItem);
完成此代码后,门户、门户项目或地图均未加载,当你将map对象设置到mapView或是很明确的加载他们时,这些对象才会被加载。有关详细信息,请参见 loadable pattern。在加载地图之前,你可以重新设置地图的属性值。
1.1.3 身份验证
如果地图或其覆盖层不是公共的,则需要提供访问它们的凭据。在这些情况下,你可以依赖API的身份验证质疑处理程序提示用户输入他们的凭据,或者你可以提供自己的登录功能,并将凭据传递给门户的 setCredentials 方法,如Access the ArcGIS platform中所述。
1.2 从移动地图包创建地图对象
移动地图包是在ArcGIS Pro中创建的文件对象(.mmpk)。它是地图、地图覆盖层、数据、网络和定位器的传输机制。移动地图包可以通过电子邮件或特定于平台的传输机制旁置到设备上,也可以从门户下载到设备。
Note:
街景地图还提供随时使用和定期更新的移动地图包,其中包括全球各地的街道地图,定位器和网络数据集。
有关详细信息,请参阅Add StreetMap Premium data.
每个移动地图包可以包含一个或多个地图,这些地图可以显示在地图视图中。有关更多信息,请参见creating mobile map packages.
1)获取移动地图包的文件路径。
// The mobile map package has been placed in the apps documents folder
File mmpkFile = new File(Environment.getExternalStorageDirectory(), dataPath + "CaliforniaNevada.mmpk");
String mmpkPath = mmpkFile.getAbsolutePath();
2)使用文件路径构造移动地图包对象。
final MobileMapPackage mobileMapPackage = new MobileMapPackage("path/to/file/.mmpk");
3)在地图本身可以被访问之前,移动地图包必须加载到应用程序中。一旦加载了移动地图包,它的任何地图都可以被分配给要显示的地图视图。
mobileMapPackage.addDoneLoadingListener(() -> {
if (mobileMapPackage.getLoadStatus() == LoadStatus.LOADED) {
System.out.println("Number of maps = " + mobileMapPackage.getMaps().size());
// In this case the first map in the array is obtained
ArcGISMap mobileMap = mobileMapPackage.getMaps().get(0);
} else {
// If loading failed, deal with failure depending on the cause...
}
});
mobileMapPackage.loadAsync();
完成此代码后,地图不会被加载。可以通过在地图加载之前设置它们的值来覆盖任何地图的属性。有关更多信息,请参见 loadable pattern。
2. 在MapView中显示地图
若要显示地图,需将其分配给地图视图。每个MapView可以显示地图的不同可见区域,可以不同的方式配置,并且可以设计为对用户手势作出不同的响应。将地图分配到地图视图,如下所示:
mapView.setMap(map);
将地图指定给MapView会初始化地图的加载周期。当周期完成时,MapView会在地图的图层上启动地图的加载周期,以便绘制它们。有关加载资源(加载周期)的更多信息,请参见 Loadable pattern of asynchronous resources. 你可以使用MapView的LayerViewStateChangedListener监视各个图层的加载。
在将地图分配给地图视图之前,你可以将地图加载到应用程序中。如果你想要确认地图的内容,或者想访问地图中的任何内容或属性而不显示它,这是非常有用的。例如,此代码显式加载地图,并在MapView显示地图之前检查地图是否包含书签。
map.addDoneLoadingListener(new Runnable() {
@Override
public void run() {
if (map.getLoadStatus() == LoadStatus.LOADED) {
// Once map is loaded, can check its properties and content
if (map.getBookmarks().size() > 0) {
// For example, show UI and allow user to choose a map bookmark...
}
} else {
// If loading failed, deal with failure depending on the cause...
dealWithLoadFailure();
}
}
});
map.loadAsync();
2.1 监控地图图层加载
将地图分配给MapView或显式加载地图触发加载地图的基本图层和操作层。每当一个层的状态发生变化时,就会触发MapView的LayerViewStateChangedListener。检查该层的视图状态,以确定一个层是否是活动的、加载的、不可见的、超出比例的,或者在该层的加载过程中是否发生了错误。
mapView.addLayerViewStateChangedListener(new LayerViewStateChangedListener() {
@Override
public void layerViewStateChanged(LayerViewStateChangedEvent layerViewStateChangedEvent) {
// Each layer may have more than one layer view state.
StringBuilder layerStatuses = new StringBuilder();
for (LayerViewStatus status : layerViewStateChangedEvent.getLayerViewStatus()) {
if (layerStatuses.length() > 0) {
layerStatuses.append(",");
}
layerStatuses.append(status.name());
}
showMessage(String.format("Layer '%s' status=%s", layerViewStateChangedEvent.getLayer().getName(),
layerStatuses.toString()));
}
});
3. 监控地图绘制
如果用户浏览或缩放地图,或者应用程序中的业务逻辑更改地图的可见区域,则需要绘制新的地图内容。在此地图绘制阶段完成之前,将有一个较短的时间延迟。你的应用程序可能需要知道绘图是完整的。例如,你可能希望你的应用程序只在绘图完成后才能对地图进行快照或截图。
使用MapView的 DrawStatusChangedListener 来检测地图绘制是否正在进行或已经完成。例如,如果绘图仍在进行中,此代码将显示进度条。
例如,此代码在绘图过程中显示进度条。
mMapView.addDrawStatusChangedListener(new DrawStatusChangedListener() {
@Override
public void drawStatusChanged(DrawStatusChangedEvent drawStatusChangedEvent) {
if(drawStatusChangedEvent.getDrawStatus() == DrawStatus.IN_PROGRESS){
progressBar.setVisibility(View.VISIBLE);
Log.d("drawStatusChanged", "spinner visible");
}else if (drawStatusChangedEvent.getDrawStatus() == DrawStatus.COMPLETED){
progressBar.setVisibility(View.INVISIBLE);
}
}
});
4. liulan地图
当地图首次加载到地图视图中时,它将显示在地图初始视图定义的地理位置。然后,用户可以使用内置在地图视图中的一系列手势来平移或缩放地图。你可以访问MapView的 getVisibleArea 方法返回的一个多边形来访问地图的可见区域。可见区域返回为多边形,而不是信封,因为地图可以旋转,地图的每个角落都可能包含唯一的x-y坐标。
Polygon polygon = mapView.getVisibleArea();
作为开发人员,你可以通过调用MapView的许多setViewpoint方法之一,以编程方式在应用程序中设置地图的可见区域。有许多选项可供选择,以使你能够:
- 将地图旋转到指定的角度。
- 将地图缩放到指定的比例。
- 缩放或平移地图,以使给定的几何图形适合于地图视图的可见区域。
- 将地图缩放或平移到指定位置。
- 缩放或平移地图到一个特定的视图。可以使用以下方法定义视图点:
中心与规模中心
比例和旋转
纬度、经度和比例尺
目标范围
目标范围和旋转
例如,下面的代码将视图点设置为特定的纬度、经度和比例尺,动画持续2秒。每个方法都返回一个布尔值,以便你可以检测动画是否被用户中断。
Viewpoint viewpoint = new Viewpoint(27.3805833, 33.6321389, 6E3);
final ListenableFuture<Boolean> viewpointSetFuture = mapView.setViewpointAsync(viewpoint, 2);
viewpointSetFuture.addDoneListener(new Runnable() {
@Override
public void run() {
try {
boolean completed = viewpointSetFuture.get();
if (completed) showMessage("Animation completed successfully");
} catch (InterruptedException e) {
showMessage("Animation interrupted");
} catch (ExecutionException e) {
// Deal with exception during animation...
}
}
});
5. 配置地图视图
您可以通过显示设备的位置、旋转地图显示或使世界地图无缝,从而增强用户的地图体验。
5.1 显示设备的位置(显示用户的当前位置)
许多设备通过Wi-Fi、蜂窝网络或全球定位系统(Gps)提供有关其位置的信息。设备的操作系统将其位置传递给MapView,以便在地图上显示和跟踪设备。地图视图总是试图得到最准确的位置,但报告的位置可能是一个基于信号强度、卫星位置和其他因素的最佳近似值。默认情况下,MapView以蓝色圆形符号和半透明蓝色圆圈显示该符号周围的位置,以表示精度范围。
地图上的位置显示、使用的符号、动画和自动控制行为都由 LocationDisplay 实例管理。每个 MapView 都有自己的位置显示实例。
开始显示设备的位置如下:
LocationDisplay locationDisplay = mapView.getLocationDisplay();
locationDisplay.addDataSourceStatusChangedListener(new LocationDisplay.DataSourceStatusChangedListener() {
@Override
public void onStatusChanged(LocationDisplay.DataSourceStatusChangedEvent dataSourceStatusChangedEvent) {
if (dataSourceStatusChangedEvent.getSource().getLocationDataSource().getError() == null) {
showMessage("Location Display Started=" + dataSourceStatusChangedEvent.isStarted());
} else {
// Deal with problems starting the LocationDisplay...
}
}
});
locationDisplay.startAsync();
Note:
使用 LocationDisplay 的应用程序必须被授予位置权限,如Google文档中所描述的请求应用程序权限。
有关显示用户位置的详细信息和其他选项,如显示轨道或更改符号,请参阅 Show device location。还请参阅API引用中的LocationDisplay。
5.2 启用环绕
地球的大多数平面图延伸到东经和西经180度,因此很难想象跨越180度经线的区域。正如我们所知,世界是球形的,不会在180度经度结束。在地图视图中启用环绕显示一个二维地图,无缝地延伸到180度子午线。有关更多信息,请参见 wrap around maps。
// wraparound is enabled if layers within map support it
mapView.setWrapAroundMode(WrapAroundMode.ENABLE_WHEN_SUPPORTED);
Note:
如果您的地图已启用环绕模式,并且您正在处理地图位置、地图范围,或者正在用地图编辑或绘制几何图形,则必须将几何图形规范化。
Note:
有些地图不支持环绕;支持环绕的地图在显示时将自动启用环绕模式。
5.3 旋转地图
你可能不希望你的地图在设备上显示为南北方向。通过设置视点旋转,可以在地图视图中重新定位地图显示方向。正旋转值逆时针旋转地图,负旋转值沿顺时针方向旋转地图。
final ListenableFuture<Boolean> viewpointSetFuture = mapView.setViewpointRotationAsync(90);
viewpointSetFuture.addDoneListener(new Runnable() {
@Override
public void run() {
try {
boolean completed = viewpointSetFuture.get();
if (completed)
showMessage("Rotation completed successfully");
} catch (InterruptedException e) {
showMessage("Rotation interrupted");
} catch (ExecutionException e) {
// Deal with exception during animation...
}
}
});
5.4 增强用户对地图的交互体验
你可以通过实现你自己的地图触摸监听接口,在用户与地图交互时提供额外的功能。监听接口的每个方法将接收到有关用户与屏幕交互位置的触摸事件信息。只需在方法中添加代码即可使用这些坐标。
最简单的方法是使用现有的DefaultMapViewOnTouchListener。
1) 创建一个类,继承 DefaultMapViewOnTouchListener.
class MapSingleTapListener extends DefaultMapViewOnTouchListener {
}
2)重写与要更改行为的手势对应的方法。在本例中,通过重写onSingleTapConfined,将单个点击手势更改为在地图坐标中显示被点击的位置。
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
Point mapPoint = mapView.screenToLocation(new android.graphics.Point((int)e.getX(), (int)e.getY()));
showMessage(String.format("User tapped on the map at (%.3f,%.3f)", mapPoint.getX(), mapPoint.getY()));
return true;
}
3)为MapView 设置新的监听。
mapView.setOnTouchListener(new MapSingleTapListener(context, mapView));
Note:
或者,您可以直接实现MapView.OnTouchListener接口,并自己处理所有手势。
6. 拍一张地图的快照
使用map视图的exportImageAsync方法获取地图可见区域的快照或屏幕截图。使用应用程序中的图像,或将其存储在要共享、打印或包含在其他文档中的文件中。
下面的代码演示如何将从exportImageAsync方法返回的Bitmap对象保存到设备本地存储的Pictures目录中。请注意,此代码将需要 WRITE_EXTERNAL_STORAGE 的权限。
final ListenableFuture<Bitmap> exportImageFuture = mapView.exportImageAsync();
exportImageFuture.addDoneListener(new Runnable() {
@Override
public void run() {
try {
// Get the resulting Bitmap from the future
Bitmap bitmap = exportImageFuture.get();
if (bitmap != null) {
// Create a File to write the Bitmap into
File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "map.png");
FileOutputStream fileOutputStream;
try {
// Write the Bitmap into the file and close the file stream.
fileOutputStream = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, fileOutputStream);
fileOutputStream.close();
} catch (IOException e) {
// Deal with exception writing file...
}
}
} catch (InterruptedException | ExecutionException e) {
// Deal with exception during export...
}
}
});
Note:
如果要将此图像存储为门户项目的缩略图,请注意缩略图的大小限制为1MB。在将图像保存为缩略图之前检查图像大小,并在必要时调整其大小。