ArcGIS Runtime SDK for macOS

Identify features

Maps and scenes often combine multiple sources of information such as feature layers, image layers, and graphics. Labels and legends don't always provide enough information to work out what the map or scene is displaying. Use the identify methods to quickly answer the question 'what is this item here?', allowing your users to easily explore and learn about the map or scene content by tapping or clicking on them. Information returned can be shown in pop-ups or other UI components in your app.

You can identify the visible items at a specific point on screen:

  • Within all the layers in the map or scene, or only within a specific layer
  • Within all the graphics overlays in the view or only within a specific graphics overlay
  • Returning only the topmost item or all the items at that location
  • Returning the feature, graphic, or other item at that location, or by returning pop-ups for pop-up-enabled layers

Identify methods are asynchronous, so that the UI thread of your application is not blocked waiting for results. This is especially important when working with data which resides on a server, as results do not return immediately. The results of an identify take into account:

  • Symbology—tapping on a large marker symbol used to draw a point, or a wide line symbol used to draw a polyline or outline of a polygon will include those items in the results; it's not just the geometry that is used.
  • Visibility—the visibility of a graphics overlay or layer, and of an individual feature or graphic is checked, and results will only include the visible items. The opacity of a layer or graphic is ignored.
  • Visible extent—only items within the currently visible map or scene extent are included in identify results.

Identify is supported on feature layers, map image layers, Web Map Service (WMS) layers, graphics overlays, scene layers, and also on tiled layers that are based on map services that support identify.

Note:

If time-based filtering is being used (a time extent has been set on the displaying map or scene view), identify will only return features that are within the time extent set on the geo view.

The sections below show you how to identify different items in the map or scene in different ways, but these approaches can be combined to provide general identify functionality if required.

Identify features in a feature layer

The steps below show you how to identify the features within a specific feature layer in the map or scene. Later in this topic, these steps are adapted to identify only the topmost feature, identify against multiple layers, different types of layers, and to identify graphics.

  1. Listen to a tap or click event on the map or scene view, and get the point representing the center of the tap or click.

    func geoView(_ geoView: AGSGeoView, didTapAtScreenPoint screenPoint: CGPoint, mapPoint: AGSPoint) {
        // code to identify using the tap location in screen units
        self.screenPoint = screenPoint
        // ... identify features at the screenPoint
    }

  2. Call the required identify method, passing in the screen point from the previous step.
    1. Choose either to identify against a specific layer, or to identify against all layers.
    2. Specify a tolerance for the search radius of the identify operation. A tolerance of 0 identifies only items at the single pixel at the screen point. However, typically this level of precision is hard to achieve, so you can supply a tolerance around the screen point. A suitable tolerance for a tap operation on a touch screen should be equivalent to the size of the tip of a finger, and a smaller tolerance should be considered for mouse pointers.
    3. Specify whether to include pop-up information on the geo-elements returned. (If no pop-ups are defined for a layer or graphics overlay, pop-ups will not be included in the results regardless of this parameter value).
    4. Optionally, specify the maximum number of results per-layer to return. This may be especially useful if identifying on service layers, as you can limit the amount of information returned to the client (the maximum results will also be limited by the server).

    // identify a layer using MapView, passing in the layer, the tap point, tolerance, types to return, and max results
     self.mapView.identifyLayer(self.parksFeatureLayer, screenPoint: self.screenPoint, tolerance: 12, returnPopupsOnly: false, maximumResults: 10) { (identifyLayerResult: AGSIdentifyLayerResult) -> Void in
        if let error = identifyLayerResult.error {
            print(error)
            return
        }
        //process the identifyLayerResult
        //...
    }

  3. Results consist of layer content information about the layer the results are from, and a list of AGSGeoElement. Each AGSGeoElement represents a feature identified at the given screen point. Iterate the results, access the geometry and attributes of the identified features, and use them as required. In the example below, each identified feature is selected.

    // get the layer identified and cast it to FeatureLayer
    if let theLayer = identifyLayerResult.layerContent as? AGSFeatureLayer {
    
        // iterate each identified GeoElement in the results
        for geoElement in identifyLayerResult.geoElements
        {
            // select this feature in the feature layer
            if let feature = geoElement as? AGSFeature {theLayer.select(feature)}
        }
    }

    Tip:

    Features are loadable, but when using identify methods, they are always returned already loaded.

Identify features in all feature layers

When you do not know which specific layer to identify on, you can identify items in any layer in the map or scene. Change the first workflow above so that you do not specify the layer to identify. This time, the results are returned as a list of AGSIdentifyLayerResult instead of a single result.

self.mapView.identifyLayers(atScreenPoint: self.screenPoint, tolerance: 12, returnPopupsOnly: false, maximumResultsPerLayer: 10) { (identifyLayerResults: [AGSIdentifyLayerResult]?, error: Error?) in

    //check for errors and ensure identifyLayerResults is not nil
    if let error = error {
        print(error)
        return
    }
    guard let identifyLayerResults = identifyLayerResults else { return }
         // get the identify results for all the layers
    for identifyLayerResult in identifyLayerResults {
             // get the layer identified and cast it to FeatureLayer
        if let theLayer = identifyLayerResult.layerContent as? AGSFeatureLayer {
                         // iterate each identified GeoElement in the results
            for geoElement in identifyLayerResult.geoElements
            {
                // select this feature in the feature layer
                if let feature = geoElement as? AGSFeature {theLayer.select(feature)}
            }
        }
    }
}

Layers that do not support identify or do not have any results based on the inputs are not included in the returned list.

Identify topmost item only

To identify only the topmost item at the screen point (or the topmost item per layer, if identifying against all layers) change the first workflow above by removing the maximum number of results parameter. AGSGeoElements are still returned as a list, but the list will have only zero or one items.

self.mapView.identifyLayers(atScreenPoint: self.screenPoint, tolerance: 12, returnPopupsOnly: false) { (identifyLayerResults: [AGSIdentifyLayerResult]?, error: Error?) in
         //check for errors and ensure identifyLayerResults is not nil
    if let error = error {
        print(error)
        return
    }
    guard let identifyLayerResults = identifyLayerResults else { return }

    // for each identify result for each layer
    for identifyLayerResult in identifyLayerResults {
                 // get the layer identified and cast it to FeatureLayer
        if let theLayer = identifyLayerResult.layerContent as? AGSFeatureLayer {
                         // if there was a result
            if identifyLayerResult.geoElements.count > 0 {
                             // get element zero
                if let topMostElement = identifyLayerResult.geoElements[0] as? AGSFeature {
                    // select this feature in the feature layer
                    if let feature = topMostElement as? AGSFeature {theLayer.select(feature)}
                }
            }
        }
    }
}

Identify on map image layers

Identifying against map image layers and tiled map layers follows the same workflow as shown for feature layers. The difference when identifying against map image layers are:

  • Results are returned as AGSFeatures; unlike other features however, they will not have a reference to a AGSFeatureTable.
  • Map image layers may have one or more sublayers—identify results from map image layers reflect this structure, and return results for each sublayer separately. (Note that if you have specified a maximum number of results to return, this value applies per sublayer.)

Demonstrates how to get all identify results that will be identified at the screenPoint given for all layers attached to MapView.

self.mapView.identifyLayers(atScreenPoint: self.screenPoint, tolerance: 12, returnPopupsOnly: false, maximumResultsPerLayer: 10) { (identifyLayerResults: [AGSIdentifyLayerResult]?, error: Error?) in
         //check for errors and ensure identifyLayerResults is not nil
    if let error = error {
        print(error)
        return
    }
    guard let identifyLayerResults = identifyLayerResults else { return }
         // iterate the identify layer results
}

The following code processes identify results for feature layers and map image layers. A further iteration is required to iterate through the sublayer results for map image layers.

// iterate through each layer's identify result
for identifyLayerResult in identifyLayerResults {
         //process the geoElements for the layer if there are any (e.g. for feature layers)
    for geoElement in identifyLayerResult.geoElements {
        // ... process attributes/geometries)
    }
         //process the sublayer results for each layer (e.g. for map image layers)
    for subLayerResult in identifyLayerResult.sublayerResults {
        //for each geoelement in each sublayer result
        for geoElement in subLayerResult.geoElements {
            // ... process attributes/geometries
        }
    }
}

Return pop-ups as results

Some types of layer support pop-ups. You can use an identify method to return pop-ups for map content at a screen point.

// You can choose to return popups only, instead of both geoelements and popups.
// If the maximum results not defined then top most popup is returned
self.mapView.identifyLayers(atScreenPoint: self.screenPoint, tolerance: 12, returnPopupsOnly: true) { (identifyLayerResults: [AGSIdentifyLayerResult]?, error: Error?) in
         //check for errors and ensure identifyLayerResults is not nil
    if let error = error {
        print(error)
        return
    }
    guard let identifyLayerResults = identifyLayerResults else { return }

    // show the first popup for the first (top) layer.
    if identifyLayerResults.count > 0 {
        let identifyLayerResult = identifyLayerResults[0]
                 // does the result have popups
        if identifyLayerResult.popups.count > 0 {
            let popup = identifyLayerResult.popups[0]
            //display the popup
        }
        // if more than one layer has pop-ups, or more than one element in a layer has popups
        // you should add controls to the pop-up content to iterate through multiple pop-ups.
    }
}

Identify features in a WMS layer

WMS layers differ from other layers as they do not support returning individual attributes or geometry for a feature. WMS services perform identification on the server and return HTML documents describing identified features. You can access the returned HTML document string through the "HTML" entry in the feature's attributes. This HTML string is suitable for display in a web view.

It is impossible to get the geometry for an identified (or any other) WMS feature. An identified WMS feature's geometry will always be null. Consequently, WMS layers do not support feature selection/highlight.

// perform an identify on a WMSLayer
self.mapView.identifyLayer(self.wmsLayer, screenPoint: self.screenPoint, tolerance: 12, returnPopupsOnly: false) { (identifyLayerResult: AGSIdentifyLayerResult) -> Void in
         //check for errors and ensure identifyLayerResult is not nil
    if let error = identifyLayerResult.error {
        print(error)
        return
    }
         // if the identify layer result returns a geoelement
    if identifyLayerResult.geoElements.count > 0 {
                 // get the first element as a AGSWMSFeature
        if let wmsFeature = identifyLayerResult.geoElements[0] as? AGSWMSFeature {
            let htmlString = wmsFeature.attributes["HTML"] as? String
            // display the HTML from the AGSWMSFeature
        }
    }
}

Identify graphics

Graphics are identified using methods different than those used for layers. You can choose to return graphics in a specific graphic overlay or for all graphics overlays; you can also limit results to only the topmost graphic in each graphics overlay.

self.mapView.identifyGraphicsOverlays(atScreenPoint: self.screenPoint, tolerance: 12, returnPopupsOnly: false, maximumResultsPerOverlay: 10) { (identifyGraphicsOverlayResults: [AGSIdentifyGraphicsOverlayResult]?, error: Error?)  in
         //check for errors and ensure identifyGraphicsOverlayResult is not nil
    if let error = error {
        print(error)
        return
    }
    guard let identifyGraphicsOverlayResults = identifyGraphicsOverlayResults else { return }
         //iterate the results for each graphics overlays
    for identifyGraphicsOverlayResult in identifyGraphicsOverlayResults {
        // get the graphics for each graphic overlay result
        for graphic in identifyGraphicsOverlayResult.graphics {
            //select each graphic
            graphic.isSelected = true
        }
    }
}