Hi,
I am using "Sketch on the map" documentation to select the region on the map. The map already contains some SymbolMarkers. Now I need to identify all the Symbol Markers which are in the selected region/ Geometry. Can anyone help me to achieve this?
PS: I have attached a sample reference image for my use case.
Solved! Go to Solution.
Hi.
The identify calls available on AGSMapView are focused on interaction so revolve around tapping on the screen.
To search using an arbitrary geometry, you will have to call Query on the feature tables that you're interested in. This assumes that the "symbol markers" you're referring to represent features in a feature layer.
Take a look a this conceptual doc: hosted-feature-layers
Also take a look at this sample, which references a feature table (in this case, an AGSServiceFeatureTable), sets up an AGSQueryParameters, and then calls queryFeatures using the parameters, which is returned a set of features.
You would do the same, except that instead of setting a whereClause on the parameters, you would set the geometry to the geometry from the sketch editor, and spatialRelationship to .within.
If the "symbol markers" are graphics in a graphics overlay, then you'll have to iterate over each graphic in the graphics overlay and for its geometry use the AGSGeometryEngine.geometry(graphicGeom, within: sketchGeom) to narrow it down.
This code could help. It shows both GraphicsOverlay and Feature Layer approaches. I haven't actually run it, but it compiles 🙂
extension AGSGraphicsOverlay {
func getGraphics(within searchPolygon: AGSPolygon) -> [AGSGraphic] {
let graphicsWithinGeometry = (graphics as? [AGSGraphic])?.filter { graphic in
guard let graphicGeometry = graphic.geometry else { return false }
return AGSGeometryEngine.geometry(graphicGeometry, within: searchPolygon)
}
return graphicsWithinGeometry ?? []
}
}
extension Array where Element == AGSGraphicsOverlay {
func getGraphics(within searchPolygon: AGSPolygon) -> [AGSGraphicsOverlay: [AGSGraphic]] {
var results = [AGSGraphicsOverlay: [AGSGraphic]]()
for overlay in self {
results[overlay] = overlay.getGraphics(within: searchPolygon)
}
return results
}
}
extension AGSMap {
func queryFeatures(within searchPolygon: AGSPolygon, completion: @escaping ([AGSFeatureLayer: Result<AGSFeatureQueryResult, Error>]) -> Void) {
// Create a store for all the results coming back from each feature layer
var results = [AGSFeatureLayer: Result<AGSFeatureQueryResult, Error>]()
// Create a Dispatch Group to coordinate the async Query calls against all the feature layers.
let coordinator = DispatchGroup()
// Query each feature layer in parallel, collecting all the query results in the `results` variable.
for layer in operationalLayers.compactMap({ return $0 as? AGSFeatureLayer }) {
guard let table = layer.featureTable else { continue }
// Find all features in the feature layer that are within the search polygon
let params = AGSQueryParameters()
params.whereClause = "1=1"
params.spatialRelationship = .within
params.geometry = searchPolygon
coordinator.enter()
table.queryFeatures(with: params) { [layer] result, error in
defer { coordinator.leave() }
if let error = error {
results[layer] = Result.failure(error)
} else if let result = result {
results[layer] = Result.success(result)
} else {
assertionFailure("No result OR error - that shouldn't happen")
}
}
}
// Once all the queries have completed, execute this code…
coordinator.notify(queue: .main) {
completion(results)
}
}
}
You could then call that with something like this…
if let searchGeom = sketchEditor.geometry as? AGSPolygon {
map.queryFeatures(within: searchGeom) { results in
for (layer, queryResponse) in results {
print("Got result for \(layer.name)")
switch queryResponse {
case .success(let queryResult):
print("Found \(queryResult.featureEnumerator().allObjects.count) features")
case .failure(let error):
print("There was an error querying the layer: \(error.localizedDescription)")
}
}
}
if let results = (mapView.graphicsOverlays as? [AGSGraphicsOverlay])?.getGraphics(within: searchGeom) {
for (overlay, graphics) in results {
print("Found \(graphics.count) graphics in overlay \(overlay)")
}
}
}
Again, I haven't had a chance to run this code, but it should give you an idea of how to do this.
Hi.
The identify calls available on AGSMapView are focused on interaction so revolve around tapping on the screen.
To search using an arbitrary geometry, you will have to call Query on the feature tables that you're interested in. This assumes that the "symbol markers" you're referring to represent features in a feature layer.
Take a look a this conceptual doc: hosted-feature-layers
Also take a look at this sample, which references a feature table (in this case, an AGSServiceFeatureTable), sets up an AGSQueryParameters, and then calls queryFeatures using the parameters, which is returned a set of features.
You would do the same, except that instead of setting a whereClause on the parameters, you would set the geometry to the geometry from the sketch editor, and spatialRelationship to .within.
If the "symbol markers" are graphics in a graphics overlay, then you'll have to iterate over each graphic in the graphics overlay and for its geometry use the AGSGeometryEngine.geometry(graphicGeom, within: sketchGeom) to narrow it down.
This code could help. It shows both GraphicsOverlay and Feature Layer approaches. I haven't actually run it, but it compiles 🙂
extension AGSGraphicsOverlay {
func getGraphics(within searchPolygon: AGSPolygon) -> [AGSGraphic] {
let graphicsWithinGeometry = (graphics as? [AGSGraphic])?.filter { graphic in
guard let graphicGeometry = graphic.geometry else { return false }
return AGSGeometryEngine.geometry(graphicGeometry, within: searchPolygon)
}
return graphicsWithinGeometry ?? []
}
}
extension Array where Element == AGSGraphicsOverlay {
func getGraphics(within searchPolygon: AGSPolygon) -> [AGSGraphicsOverlay: [AGSGraphic]] {
var results = [AGSGraphicsOverlay: [AGSGraphic]]()
for overlay in self {
results[overlay] = overlay.getGraphics(within: searchPolygon)
}
return results
}
}
extension AGSMap {
func queryFeatures(within searchPolygon: AGSPolygon, completion: @escaping ([AGSFeatureLayer: Result<AGSFeatureQueryResult, Error>]) -> Void) {
// Create a store for all the results coming back from each feature layer
var results = [AGSFeatureLayer: Result<AGSFeatureQueryResult, Error>]()
// Create a Dispatch Group to coordinate the async Query calls against all the feature layers.
let coordinator = DispatchGroup()
// Query each feature layer in parallel, collecting all the query results in the `results` variable.
for layer in operationalLayers.compactMap({ return $0 as? AGSFeatureLayer }) {
guard let table = layer.featureTable else { continue }
// Find all features in the feature layer that are within the search polygon
let params = AGSQueryParameters()
params.whereClause = "1=1"
params.spatialRelationship = .within
params.geometry = searchPolygon
coordinator.enter()
table.queryFeatures(with: params) { [layer] result, error in
defer { coordinator.leave() }
if let error = error {
results[layer] = Result.failure(error)
} else if let result = result {
results[layer] = Result.success(result)
} else {
assertionFailure("No result OR error - that shouldn't happen")
}
}
}
// Once all the queries have completed, execute this code…
coordinator.notify(queue: .main) {
completion(results)
}
}
}
You could then call that with something like this…
if let searchGeom = sketchEditor.geometry as? AGSPolygon {
map.queryFeatures(within: searchGeom) { results in
for (layer, queryResponse) in results {
print("Got result for \(layer.name)")
switch queryResponse {
case .success(let queryResult):
print("Found \(queryResult.featureEnumerator().allObjects.count) features")
case .failure(let error):
print("There was an error querying the layer: \(error.localizedDescription)")
}
}
}
if let results = (mapView.graphicsOverlays as? [AGSGraphicsOverlay])?.getGraphics(within: searchGeom) {
for (overlay, graphics) in results {
print("Found \(graphics.count) graphics in overlay \(overlay)")
}
}
}
Again, I haven't had a chance to run this code, but it should give you an idea of how to do this.
Thanks, Nicholas. It is working as expected.