添加图层

从概念上讲,地图由一系列图层组成。

地图显示在AGSMapView组件中,可以将其添加到应用程序的用户界面中。因此,地图视图像一个空画布一样绘制一个或多个地图图层。

图层以自下而上的顺序显示,以便随后的图层在以前的图层上呈现。每个地图图层都会引用存储在地图服务,平铺服务等中的数据,而不是实际存储地理数据。此API可以支持多种图层类型:平铺图层,动态图层,要素图层,图形图层以及许多其他图层,WMS图层和Bing图层。

图层和服务的配置也可以在ArcGIS Online中创建并通过其唯一的ItemId访问的网络地图中保存。在这里探索更多关于网络地图。

地图上添加图层

1). 实例化图层。如果该图层依赖于Web服务,请提供该服务的URL。如果Web服务受到保护,请提供访问该服务所需的凭据。

let url = NSURL(string: "http://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer")
let layer = AGSTiledMapServiceLayer(URL: url)

2). 使用addMapLayer:withName:方法将图层添加到地图视图。默认情况下,该图层添加到任何现有图层的顶部。*图层按照自下而上的顺序绘制。*这意味着一个图层可能会掩盖其下的其他图层的内容。如果要将图层插入特定位置,可以使用insertMapLayer:withName:atIndex:方法,其中索引0将在底部插入新图层。 在添加或插入图层时提供唯一的名称。 如果您想稍后检索图层,地图会使用它来跟踪图层。

完整代码

 func simpleInit() {
        
   self.mapView.layerDelegate = self
   self.mapView.touchDelegate = self
   
   let urlString = "http://services.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Light_Gray_Base/MapServer"
   let tiledLayer = AGSTiledMapServiceLayer(url:URL.init(string: urlString))
   self.mapView.addMapLayer(tiledLayer, withName: kBasemapLayerName)
}

func addLayer(){
   
   let url = URL.init(string: "http://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer")
   let layer =  AGSTiledMapServiceLayer.init(url: url!)
   self.mapView.addMapLayer(layer, withName: "Streets")
}    

确认地图已经添加

层在后台连接到Web服务。添加图层时不会阻塞线程,因此不能根据执行addMapLayer就认为图层已经添加上了。

通过AGSLayerDelegate可以知道图层加载成功或失败。

extension ViewController : AGSLayerDelegate{
    
    func layerDidLoad(_ layer: AGSLayer!) {
        if layer.name == "Streets"  {
            print("did load streets layer")
        }
    }
    
    func layer(_ layer: AGSLayer!, didFailToLoadWithError error: Error!) {
        if layer.name == "Streets"  {
            print("load streets layer failed")
        }
    }
}

地图的空间参考

您添加到地图的第一层被认为是底图图层。它规定了地图的初始范围以及地图的整体空间参考。随后添加到地图的任何平铺图层也必须位于此空间参考中;否则,他们的内容将不会显示。

另一方面,动态图层可以在任何空间参考中,因为它们会在必要时自动将其内容重新投影到地图的空间参考中。

使用Web Map

网络地图基本上描述了要在地图上显示的内容。 Web地图的核心是包含需要在地图中显示的图层的信息,其顺序,可见性和呈现细节。网络地图还可以包含其他信息,如书签,笔记,幻灯片,任务,小工具等。

您可以使用ArcGIS Explorer Online或内置的ArcGIS.com Viewer等应用程序在www.ArcGIS.com上以交互方式创建共享Web地图。

然后,您可以使用各种Web和移动客户端查看Web地图,包括使用API​​构建的自定义应用程序。

该API仅支持Web地图中的信息子集,例如图层信息,弹出窗口定义和书签。网络地图中的所有其他信息都会被忽略。图层信息用于构建图层并将其添加到地图视图。弹出式定义用于使用AGSPopupsContainerViewController显示和编辑功能细节。书签可用作AGSEnvelope对象,可用于将地图缩放到特定区域。

如果您有兴趣访问存储在Web地图中的信息,则可以从AGSWebMap对象的json属性中获取Web地图的原始JSON数据。

要查看网络地图的内容,您需要执行以下操作:

  • Load the web map
  • Set the delegate
  • Open into a map view

加载地图

首先再网页端创建web map, 点击“详细信息”, 点开后查看链接http://username.maps.arcgis.com/home/item.html?id=a675fb1ed44f436dbxxx中的id。

但是,如果网络地图是私人的或只与少数人共享,则还需要提供有效的凭据以访问网络地图。 以下是一个例子:

let credential = AGSCredential(user: "<user>", password: "<password>")
credential.authType = .Token
let webmap = AGSWebMap(itemId: "e229d715f7ca4fa980308549fb288165", credential: credential

如果Web地图托管在www.arcgis.com之外,则在企业级ArcGIS Portal上,您需要提供可访问Web地图数据的共享端点的URL。 以下是一个例子:

let endpoint = NSURL(string: "http://<my_arcgis_portal>/sharing")
let webmap = AGSWebMap(itemId: "e229d715f7ca4fa980308549fb288165", sharingEndPoint: endpoint, credential: credential)

当你实例化一个webmap时,你需要确保它的内存在它有机会执行之前不会被释放。要做到这一点,如果您使用的是手动保留版本,则需要保留该网站地图,或者如果您使用的是ARC,请创建一个强引用。有关如何管理对象内存的更多信息,请参阅Apple内存管理编程指南

extension ViewController : AGSWebMapDelegate{

    
    @IBAction func buttonClick(_ sender: Any) {
        
        print("button click")
        //addLayer();
        
        initWebMap()
        
        self.webmap?.open(into: self.mapView!)
        
    }
    func initWebMap() {
        
        if self.webmap == nil {
            
            let credential = AGSCredential(user: "name", password: "xx")
            credential!.authType = .token
            self.webmap = AGSWebMap(itemId: "79356c3d96d9439ca7a31d953f8828ac", credential: credential)
            self.webmap?.delegate = self
        }
    }
    
    func webMapDidLoad(_ webMap: AGSWebMap!) {
        
        print("webmap didload")
    }
}

按钮点击后稍等片刻就可以看到自己配置的地图内容了。

Navigating the map

map view 包含了用于定义和更改地图范围的选项以及修改范围时的用户体验。值得注意的是底图图层base layer(添加到地图的第一层)定义了以下地图属性:

  • Initial extent
  • Full extent
  • Spatial reference

虽然可以修改初始范围,但不能明确更改空间参考。 本主题讨论使用地图范围的开发人员和最终用户解决方案。

Setting the map extent 设置地图的显示范围

要设置地图范围,请在地图视图中使用zoomToEnvelope:animated:方法,方法是传入代表要放大(zoom)的区域的evelope geometry。

override func viewDidLoad() {
 super.viewDidLoad()
        
 //add a layer to the map
 let tiledLayer = AGSTiledMapServiceLayer(URL: NSURL(string:  "http://server.arcgisonline.com/ArcGIS/rest/services/ESRI_StreetMap_World_2D/MapServer"))
 self.mapView.addMapLayer(tiledLayer, withName: "Tiled Layer")
        
 //zoom to an area
 let envelope = AGSEnvelope(xmin: -124.83145667, ymin: 30.49849464, xmax: -113.91375495,  ymax: 44.69150688,  spatialReference: mapView.spatialReference) 
 self.mapView.zoomToEnvelope(envelope, animated: true)
}

或者,也可以使用zoomToGeometry:withPadding:animated:方法缩放到地图上的任意几何图形(例如国家或河流的形状)。

获取地图范围

您可以使用地图视图的visibleArea属性来获取地图的范围。

let mapExtent = self.mapView.visibleArea()

visibleArea属性返回一个多边形,该多边形包含地图上四个角的每个角的顶点,从左上角开始,顺时针方向前进。

如果地图不包含任何旋转,则visibleArea多边形表示的区域与多边形的封闭信封完全重合。 因此,您可以使用visibleArea多边形或其envelope作为地图范围。

但是,如果地图已旋转,则visibleArea多边形和多边形的包络(envelope)将不同。 包络表示一个比多边形稍大的区域,它完全包含多边形。 在这种情况下,使用visibleArea多边形来获取地图范围。

跟踪平移和缩放

地图视图为范围更改提供两个通知,AGSMapViewDidEndPanningNotificationAGSMapViewDidEndZoomingNotification。这些通知分别在地图平移或缩放后进行广播。

以下代码是如何在用户平移或缩放地图时侦听通知的示例。它会显示一条alert,显示从地图视图的信封属性中检索到的新地图范围:

func mapViewDidLoad(mapView: AGSMapView!) {
 //register for pan notifications
 NSNotificationCenter.defaultCenter().addObserver(self, selector: "respondToEnvChange:", name: AGSMapViewDidEndPanningNotification, object: nil)
        
 //register for zoom notifications
 NSNotificationCenter.defaultCenter().addObserver(self, selector: "respondToEnvChange:", name: AGSMapViewDidEndZoomingNotification, object: nil)
        
 ...
}
    
 // The method that should be called when the notification arises
 func respondToEnvChange(notification:NSNotification) {
        
  //create the string containing the new map extent NSString*
  let theString = "xmin = \(mapView.visibleAreaEnvelope.xmin),\nymin = \(mapView.visibleAreaEnvelope.ymin),\nxmax = \(mapView.visibleAreaEnvelope.xmax),\nymax = \(mapView.visibleAreaEnvelope.ymax)"
        
  //display the new map extent in a simple alert
  let alertView = UIAlertView(title: "Finished Panning", message: theString, delegate: nil, cancelButtonTitle: "OK")
  alertView.show()
}

你应该注意到这些通知在处理器闲置时被广播;因此,用户完成执行操作和通知提出时间之间可能会有一些延迟。

另外,通知在主线上发布。你不应该通过执行昂贵的任务来阻止这个线程;否则,您的应用程序对用户显得很迟钝。

更改地图范围

如前所述,您可以在地图视图上使用zoomToEnvelope:动画方法来设置地图视图的范围。 您还可以使用地图视图的centerAtPoint将给定地点的地图居centerAtPoint:animated方法:

let newPoint = AGSPoint(x: -93.032201, y: 49.636213, spatialReference: self.mapView.spatialReference)
self.mapView.centerAtPoint(newPoint, animated: true)

用户手势

用户可以在运行时执行几个手势来更改地图视图的范围。 所有这些都会触发MapDidEndPanningMapDidEndZooming通知。

User Action Map Action Notification raised
Pinch-in Zoom out on map AGSMapViewDidEndZoomingNotification
Pinch-out Zoom in on map AGSMapViewDidEndZoomingNotification
Double tap Zoom in on map AGSMapViewDidEndZoomingNotification
Two finger tap Zoom out on map AGSMapViewDidEndZoomingNotification
Swipe (any direction) Pan map in direction of swipe AGSMapViewDidEndPanningNotification
Two finger twist= (see the following note) Rotate the map

注意: 启用AGSMapView AGSMapView上的allowRotationByPinching属性以启用双指扭曲手势。

Enable wrap around 启用环绕

世界是圆的。它不会在日期行结束。然而,地球的大多数平面表示仅延伸至东经180度。这使得难以可视化跨越日期线的区域或穿越太平洋的路线。

利用环绕支持,可以将日期线以外的世界地图无缝地展现出来。 东半球和西半球互相缠绕形成一张连续的地图,给人的印象是地图是无止境的。 平移地图与旋转地球相似。

开启方法:

self.mapView.enableWrapAround()

Constraints 限制条件

启用环绕的限制条件:

  • 地图的全部信封必须覆盖整个世界。
  • 地图的空间参考必须是WGS 84(WKID = 4326)或Web墨卡托(WKID = 102113,102100或3857)。这意味着地图上的所有平铺图层都必须属于这些空间参照之一。另一方面,动态图层可以在任何空间参考中,因为它们能够重新投影其数据。
  • 动态图层必须基于来自ArcGIS Server 10.0或更高版本的地图服务。这是因为早期版本的REST API不支持用于空间参考的知名文本(WKT)值,这是使动态地图服务支持环绕所需的。
  • 如果这些需求中的任何一个不满足并且您尝试启用环绕,则AGSMapView上的wrapAroundStatus属性指示不支持环绕。
self.mapView.enableWrapAround()
if self.mapView.wrapAroundStatus() == .Unsupported  {
 println("Wrap around is not supported")
}

Normalizing geometries 规范几何

为了更好地理解环绕,将地图可视化为由帧组成。 位于-180经度和+180经度之间的地图部分为0帧。即使未启用环绕,此帧也可以定期显示。 在东边与这个框架相邻的是框架1,其假设在+180和+540经度之间延伸。 在西边邻近第0帧的是第-1帧,假设在-180和-540经度之间延伸。

根据所显示的帧,地图报告的经度值可能是真实的(在-180和+180之间)或假设的(超出-180或+180)。 以下是可能包含假设坐标的几何示例:

  • A map’s envelope
  • Touch coordinates reported by a map’s delegates
  • Geometries digitized by a sketch layer

当几何图形包含假设坐标时,在使用它们执行空间查询或将其存储在地理数据库中之前对其进行规格化。 这种标准化过程将几何图形的坐标从其他框架转换为框架0.如果几何图形包含假设值并且需要标准化或非标准化,则难以在运行时以编程方式进行检查。 启用绕回时总是使几何图形标准化更安全。

您可以使用AGSGeometryEngine上的normalizeCentralMeridianOfGeometry:方法规格化几何。

let geo:AGSGeometry = ...
        
if self.mapView.wrapAroundStatus() == .Enabled {
 geo = AGSGeometryEngine.defaultGeometryEngine().normalizeCentralMeridianOfGeometry(geo)
}

创建离线地图

您可以为用户提供离线地图,以便在他们的网络连接较差或不存在时提高生产率。要创建离线地图,您可以创建地理数据库和平铺缓存,然后根据这些主题构建图层。

您可以添加以下功能:

  • Viewing basemaps(也称为平铺图层tiled layers,由平铺缓存创建)
  • Editing operational data 编辑操作数据(也称为特征数据和矢量数据)。这包括查询和编辑要素的位置,形状,属性,相关表格和附件。
  • 如果您的数据是基于服务的,则在再次联机时同步编辑(上传编辑和/或仅下拉其他人编辑的更新功能)。
  • 快速搜索位置(地理编码和反向地理编码)和路线。 有关详细信息,请参阅定位器任务和路由任务。

[略]