# Spatial operations

Find the union, intersection, or difference of two geometries.

## Use case

The different spatial operations (union, difference, symmetric difference, and intersection) can be used for a variety of spatial analyses. For example, government authorities may use the intersect operation to determine whether a proposed road cuts through a restricted piece of land such as a nature reserve or a private property.

When these operations are chained together, they become even more powerful. An analysis of food deserts within an urban area might begin by union-ing service areas of grocery stores, farmers markets, and food co-ops. Taking the difference between this single geometry of all services areas and that of a polygon delineating a neighborhood would reveal the areas within that neighborhood where access to healthy, whole foods may not exist.

## How to use the sample

The sample provides an option to select a spatial operation. When an operation is selected, the resulting geometry is shown in red.

## How it works

1. Create an `AGSGraphicsOverlay` and add it to the `AGSMapView`.
2. Create each polygon `AGSGeometry` using `AGSPolygonBuilder`.
3. Add the overlapping polygons to the graphics overlay.
4. Perform spatial relationships between the polygons by using the appropriate operation:
• `class AGSGeometryEngine.union(ofGeometry1:geometry2:)` - This method returns the two geometries united together as one geometry.
• `class AGSGeometryEngine.difference(ofGeometry1:geometry2:)` - This method returns the difference of Geometry1 from Geometry2.
• `class AGSGeometryEngine.symmetricDifference(ofGeometry1:geometry2:)` - This method returns any part of Geometry1 or Geometry2 which do not intersect.
• `class AGSGeometryEngine.intersection(ofGeometry1:geometry2:)` - This method returns the intersection of Geometry1 and Geometry2.
5. Use the geometry that is returned from the method call to create a new `AGSGraphic` and add it to the graphics overlay for it to be displayed.

## Relevant API

• AGSGeometry
• AGSGeometryEngine
• AGSGraphic
• AGSGraphicsOverlay
• class AGSGeometryEngine.difference(ofGeometry1:geometry2:)
• class AGSGeometryEngine.intersection(ofGeometry1:geometry2:)
• class AGSGeometryEngine.symmetricDifference(ofGeometry1:geometry2:)
• class AGSGeometryEngine.union(ofGeometry1:geometry2:)

## Tags

analysis, combine, difference, geometry, intersection, merge, polygon, union

## Sample Code

SpatialOperationsViewController.swift
Use dark colors for code blocksCopy

``````// Copyright 2017 Esri.
//
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//
// Unless required by applicable law or agreed to in writing, software
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and

import UIKit
import ArcGIS

class SpatialOperationsViewController: UIViewController {
// MARK: Storyboard view and properties

/// The map view managed by the view controller.
@IBOutlet var mapView: AGSMapView! {
didSet {
// Initialize map with basemap.
mapView.map = AGSMap(basemapStyle: .arcGISTopographic)
// Add the graphics overlay with two polygon graphics and the result graphic to map view.
// Set the map view's viewpoint.
let center = AGSPoint(x: -13453, y: 6710127, spatialReference: .webMercator())
mapView.setViewpointCenter(center, scale: 30000, completion: nil)
}
}

/// The resulting graphic for the spatial operation.
private var resultGraphic: AGSGraphic!

private let polygon1: AGSGeometry = {
// Create the polygon 1.
let polygon = AGSPolygonBuilder(spatialReference: .webMercator())
return polygon.toGeometry()
}()

private let polygon2: AGSGeometry = {
// The outer ring of polygon 2.
let outerRing = AGSMutablePart(spatialReference: .webMercator())

// The inner ring of polygon 2.
let innerRing = AGSMutablePart(spatialReference: .webMercator())

// Create polygon 2.
let polygon = AGSPolygonBuilder(spatialReference: .webMercator())
return polygon.toGeometry()
}()

/// An enum of spatial operations.
private enum SpatialOperation: CaseIterable {
case none, union, difference, symmetricDifference, intersection
/// Human readable label strings for each spatial operation.
var label: String {
switch self {
case .none: return "None"
case .union: return "Union"
case .difference: return "Difference"
case .symmetricDifference: return "Symmetric Difference"
case .intersection: return "Intersection"
}
}
}
/// The selected operation.
private var selectedOperation = SpatialOperation.none

// MARK: Methods

func makeGraphicsOverlay() -> AGSGraphicsOverlay {
// A black line symbol for borders of the graphics.
let lineSymbol = AGSSimpleLineSymbol(style: .solid, color: .black, width: 1)
// The blue fill symbol of polygon 1.
let fillSymbol1 = AGSSimpleFillSymbol(style: .solid, color: .blue, outline: lineSymbol)
// The graphic of polygon 1.
let polygon1Graphic = AGSGraphic(geometry: polygon1, symbol: fillSymbol1)
// The green fill symbol of polygon 2.
let fillSymbol2 = AGSSimpleFillSymbol(style: .solid, color: .green, outline: lineSymbol)
// The graphic of polygon 2.
let polygon2Graphic = AGSGraphic(geometry: polygon2, symbol: fillSymbol2)

// Using red fill symbol with black border for result graphic.
let symbol = AGSSimpleFillSymbol(style: .solid, color: .red, outline: lineSymbol)
let graphic = AGSGraphic(geometry: nil, symbol: symbol)
resultGraphic = graphic

// An overlay to display polygon graphics.
let graphicsOverlay = AGSGraphicsOverlay()

// Add graphics to graphics overlay.
return graphicsOverlay
}

private func performOperation(_ operation: SpatialOperation) {
let resultGeometry: AGSGeometry?
switch operation {
case .none:
resultGeometry = nil
case .union:
resultGeometry = AGSGeometryEngine.union(ofGeometry1: polygon1, geometry2: polygon2)!
case .difference:
resultGeometry = AGSGeometryEngine.difference(ofGeometry1: polygon1, geometry2: polygon2)!
case .symmetricDifference:
resultGeometry = AGSGeometryEngine.symmetricDifference(ofGeometry1: polygon1, geometry2: polygon2)!
case .intersection:
resultGeometry = AGSGeometryEngine.intersection(ofGeometry1: polygon1, geometry2: polygon2)!
}
// Update the geometry.
resultGraphic.geometry = resultGeometry
}

@IBAction func chooseOperationBarButtonTapped(_ sender: UIBarButtonItem) {
let selectedIndex = SpatialOperation.allCases.firstIndex(of: selectedOperation)

let controller = OptionsTableViewController(labels: SpatialOperation.allCases.map { \$0.label }, selectedIndex: selectedIndex) { [weak self] newIndex in
guard let self = self else { return }
let newOperation = SpatialOperation.allCases[newIndex]
self.selectedOperation = newOperation
// Perform the new spatial operation.
self.performOperation(newOperation)
}

// Configure the options controller as a popover.
controller.modalPresentationStyle = .popover
controller.presentationController?.delegate = self
controller.preferredContentSize = CGSize(width: 300, height: CGFloat(SpatialOperation.allCases.count) * 44)
controller.popoverPresentationController?.barButtonItem = sender

// Show the popover.
present(controller, animated: true)
}

// MARK: UIViewController

// Add the source code button item to the right of navigation bar.
(navigationItem.rightBarButtonItem as? SourceCodeBarButtonItem)?.filenames = ["SpatialOperationsViewController", "OptionsTableViewController"]
}
}