Polygons as feature layer

659
2
04-02-2023 11:41 PM
RostislavBabáček
New Contributor

Greetings, 

I'm working on a project where we want to show natural zones on the surface of the sea. The base maps are loaded from a TPK file. Then the zones are displayed on the water surface (Polygons that are loaded from geojson file). Next, we have a data layer (TPK file) containing information, e.g., the depth, that we would like to display over the zones (polygons).

You can see the desired result in the attached image

- Orange polygon (selectable) is nature zone - geojson file

- Data as "07, 02, Wamperener Riff, YBY, .." - TPK File that should be on the top of the polygon

Unfortunately, I am not able to achieve this. It should also be mentioned that the natural zones (polygons) can be selected by clicking.



SDK version: iOS 100.15.0

Data files:
- Map background: TPK file
- Zones on the water surface: geojson files
- Data with depth etc..: TPK file

Current implementation:
- Map background: base map (TPK)
- Zones on the water surface: GraphicsOverlay (Geojson)
- Missing: Data displayed over polygons


From the documentation, I learned how to stack layers on top of each other. In order to display the data over polygons, I need the data layer to be on top of the polygon layer.

I can display the natural zones (geojsons) as a graphics layer. I am also able to add the depth data (tpk file) as a data layer (operationalLayers). But unfortunately, I am not able to achieve to display the depth data over the zone polygons.

I have tried adding the geojson files as an operational layer. I have also tried creating a graphical layer from the TPK file, but unfortunately, both without success.

Is it possible to achieve the desired result using iOS SDK version 100.15.0?

Thank you very much for any help.

0 Kudos
2 Replies
Nicholas-Furness
Esri Regular Contributor

Hi Rostislav,

It sounds like you are parsing the GeoJSON into polygons that you are adding to a Graphics Overlay.

Unfortunately, Graphics Overlays will always display on top of operational layers by design. However, you can achieve the behavior you're looking for by using a FeatureCollectionTable and FeatureCollectionLayer.

You can use the AGSGraphics that you are currently creating as input to featureCollectionTableWithGeoElements:fields:, since AGSGraphics are AGSGeoElements. However, there is one additional step you will need to take and that is to provide a set of AGSFields to define the shape of the FeatureCollectionTable. While a Graphics Overlay has no schema and the Graphics can each have any collection of Attributes, a FeatureCollectionTable must have a schema defined. So if your polygon graphics have attributes, create a set of Fields that match those attributes and pass that in to create the featureCollectionTable. This also means that all the graphics must have the same shape type (Polygon in your case) and the same attributes.

You can then create a Feature Collection Layer. Here is some sample code that should help. It creates a single large polygon AGSGraphic at 0,0 with two attributes ("ID" and "Created"), and then generates a FeatureCollectionTable. In your code you would of course add many AGSGraphics with polygons generated from the GeoJSON:

 

class ViewController: UIViewController {
    
    @IBOutlet weak var mapView: AGSMapView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        
        // Provide an API Key to use this sample standalone
        // AGSArcGISRuntimeEnvironment.apiKey = "<YOUR API KEY>"
        
        let map = AGSMap(basemapStyle: .arcGISTopographic)
        mapView.map = map
        
        addFeatureCollectionLayer(to: map)
        
        mapView.touchDelegate = self
    }
}

extension ViewController {
    func addFeatureCollectionLayer(to map: AGSMap) {
        let geoElement = AGSGraphic(
            geometry: AGSPolygon(points: [
                AGSPointMakeWGS84(0, -10),
                AGSPointMakeWGS84(-10, 0),
                AGSPointMakeWGS84(0, 10),
                AGSPointMakeWGS84(10, 0)
            ]),
            symbol: nil,
            attributes: [
                "ID":"Shape 1",
                "Created": Date()
            ]
        )
        
        let fields = [
            AGSField(fieldType: .text, name: "ID", alias: "Identifier", length: 255, domain: nil, editable: true, allowNull: false),
            AGSField(fieldType: .date, name: "Created", alias: "Created", length: 0, domain: nil, editable: true, allowNull: false)
        ]
        
        let layer = createFeatureCollectionLayerFromGeoElements(geoElements: [geoElement], fields: fields)
        
        map.operationalLayers.add(layer)
    }
    
    func createFeatureCollectionLayerFromGeoElements(geoElements: [AGSGeoElement], fields: [AGSField]) -> AGSFeatureCollectionLayer {
        let table = AGSFeatureCollectionTable(geoElements: geoElements, fields: fields)
        table.renderer = AGSSimpleRenderer(symbol: AGSSimpleFillSymbol(style: .solid, color: .orange, outline: nil))
        table.displayName = "My custom GeoJSON table"
        let collection = AGSFeatureCollection(featureCollectionTables: [table])
        let layer = AGSFeatureCollectionLayer(featureCollection: collection)
        layer.name = "My custom GeoJSON layer"
        
        return layer
    }
}

extension ViewController: AGSGeoViewTouchDelegate {
    func geoView(_ geoView: AGSGeoView, didTapAtScreenPoint screenPoint: CGPoint, mapPoint: AGSPoint) {
        geoView.identifyLayers(atScreenPoint: screenPoint, tolerance: 12, returnPopupsOnly: false) { [weak self] results, error in
            guard let self else { return }
            
            if let error = error {
                print("Error identifying: \(error.localizedDescription)")
                return
            }
            
            guard let results = results else { return }

            for result in results {
                self.printIdentifyResults(result: result)
            }
        }
    }
    
    func printIdentifyResults(result: AGSIdentifyLayerResult) {
        print(result.layerContent.name)
        for geoElement in result.geoElements {
            print(geoElement)
            print(geoElement.attributes)
        }
        for subResult in result.sublayerResults {
            printIdentifyResults(result: subResult)
        }
    }
}

 

 

Note that I do have some sample code for parsing GeoJSON geomety into Runtime AGSGeometry and even GeoJSON Features into AGSGeoElements available here, in case that helps, though it sounds like you're already doing that part OK: https://gist.github.com/nixta/8a5637a288ce22501aa086e6b5933adf

Hope this helps.

RostislavBabáček
New Contributor

Dear Nicholas,

Thank you for your quick and very precise answer. I will try it and let you know if it's working for our case.

Have a nice day! 😊

0 Kudos