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, farmer's 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
- Create a
GraphicsOverlay
and add it to theMapView
. - Use
PolygonBuilder
to create two polygons. - Add the overlapping polygons to the graphics overlay.
- Perform spatial relationships between the polygons by using the appropriate operation:
GeometryEngine.unionOf(geometry1, geometry2)
- This method returns the two geometries united together as one geometry.GeometryEngine.difference(geometry1, geometry2)
- This method returns any part of Geometry2 that does not intersect Geometry1.GeometryEngine.symmetricDifference(geometry1, geometry2)
- This method returns any part of Geometry1 or Geometry2 which do not intersect.GeometryEngine.intersection(geometry1, geometry2)
- This method returns the intersection of Geometry1 and Geometry2.
- Use the geometry that is returned from the method call to create a new
Graphic
and add it to the graphics overlay for it to be displayed.
Relevant API
- Geometry
- GeometryEngine
- GeometryEngine.difference
- GeometryEngine.intersection
- GeometryEngine.symmetricDifference
- GeometryEngine.unionOf
- Graphic
- GraphicsOverlay
Tags
analysis, combine, difference, geometry, intersection, merge, polygon, union
Sample Code
// [WriteFile Name=SpatialOperations, Category=Geometry]
// [Legal]
// Copyright 2018 Esri.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// [Legal]
import QtQuick 2.6
import QtQuick.Controls 2.2
import Esri.ArcGISRuntime 100.15
Rectangle {
id: rootRectangle
clip: true
width: 800
height: 600
property var geometryOperations: ["None", "Union", "Difference", "Symmetric difference", "Intersection"]
property var geometry1
property var geometry2
MapView {
id: mapView
anchors.fill: parent
Component.onCompleted: {
// Set the focus on MapView to initially enable keyboard navigation
forceActiveFocus();
}
Map {
id: map
Basemap {
initStyle: Enums.BasemapStyleArcGISTopographic
}
ViewpointCenter {
Point {
x: -13453
y: 6710127
spatialReference: spatialRef
}
targetScale: 30000
}
onLoadStatusChanged: {
if (loadStatus === Enums.LoadStatusLoaded)
addPolygons();
}
}
GraphicsOverlay {
id: inputOverlay
}
GraphicsOverlay {
id: outputOverlay
}
}
// Display a ComboBox with options for each operation
ComboBox {
id: comboBox
anchors {
left: parent.left
top: parent.top
margins: 10
}
property int modelWidth: 0
width: modelWidth + leftPadding + rightPadding + indicator.width
model: geometryOperations
onCurrentIndexChanged: applyGeometryOperation(currentIndex);
Component.onCompleted : {
for (let i = 0; i < model.length; ++i) {
metrics.text = model[i];
modelWidth = Math.max(modelWidth, metrics.width);
}
}
TextMetrics {
id: metrics
font: comboBox.font
}
}
SpatialReference {
id: spatialRef
wkid: 3857
}
function addPolygons() {
if (!inputOverlay)
return;
// create blue polygon
const polygonBuilder1 = ArcGISRuntimeEnvironment.createObject("PolygonBuilder", { spatialReference: spatialRef });
polygonBuilder1.addPointXY(-13960, 6709400);
polygonBuilder1.addPointXY(-14660, 6710000);
polygonBuilder1.addPointXY(-13760, 6710730);
polygonBuilder1.addPointXY(-13300, 6710500);
polygonBuilder1.addPointXY(-13160, 6710100);
const fillSymbol1 = ArcGISRuntimeEnvironment.createObject("SimpleFillSymbol");
fillSymbol1.style = Enums.SimpleFillSymbolStyleSolid;
fillSymbol1.color = "blue";
geometry1 = polygonBuilder1.geometry;
const graphic1 = ArcGISRuntimeEnvironment.createObject("Graphic");
graphic1.geometry = geometry1;
graphic1.symbol = fillSymbol1;
inputOverlay.graphics.append(graphic1);
// create green polygon
// outer ring
const outerRing = ArcGISRuntimeEnvironment.createObject("Part", { spatialReference: spatialRef });
outerRing.addPointXY(-13060, 6711030);
outerRing.addPointXY(-12160, 6710730);
outerRing.addPointXY(-13160, 6709700);
outerRing.addPointXY(-14560, 6710730);
outerRing.addPointXY(-13060, 6711030);
// inner ring
const innerRing = ArcGISRuntimeEnvironment.createObject("Part", { spatialReference: spatialRef });
innerRing.addPointXY(-13060, 6710910);
innerRing.addPointXY(-14160, 6710630);
innerRing.addPointXY(-13160, 6709900);
innerRing.addPointXY(-12450, 6710660);
innerRing.addPointXY(-13060, 6710910);
const polygonBuilder2 = ArcGISRuntimeEnvironment.createObject("PolygonBuilder", { spatialReference: spatialRef });
polygonBuilder2.parts.addPart(outerRing);
polygonBuilder2.parts.addPart(innerRing);
geometry2 = polygonBuilder2.geometry;
const fillSymbol2 = ArcGISRuntimeEnvironment.createObject("SimpleFillSymbol");
fillSymbol2.style = Enums.SimpleFillSymbolStyleSolid;
fillSymbol2.color = "green";
const graphic2 = ArcGISRuntimeEnvironment.createObject("Graphic");
graphic2.geometry = geometry2;
graphic2.symbol = fillSymbol2;
inputOverlay.graphics.append(graphic2);
}
function applyGeometryOperation(index) {
if (map.loadStatus !== Enums.LoadStatusLoaded)
return;
// Perform geometry calculations
let resultPolygon;
switch (index) {
case 1:
resultPolygon = GeometryEngine.unionOf(geometry1, geometry2);
break;
case 2:
resultPolygon = GeometryEngine.difference(geometry1, geometry2);
break;
case 3:
resultPolygon = GeometryEngine.symmetricDifference(geometry1, geometry2);
break;
case 4:
resultPolygon = GeometryEngine.intersection(geometry1, geometry2);
break;
case 0:
default:
break;
}
// Clear previous results
outputOverlay.graphics.clear();
if (!resultPolygon)
return;
// Add the resulting polygon as a Graphic
const fillSymbol = ArcGISRuntimeEnvironment.createObject("SimpleFillSymbol");
fillSymbol.style = Enums.SimpleFillSymbolStyleSolid;
fillSymbol.color = "red";
const graphic = ArcGISRuntimeEnvironment.createObject("Graphic");
graphic.geometry = resultPolygon;
graphic.symbol = fillSymbol;
outputOverlay.graphics.append(graphic);
}
}