Create convex hull around geometries

View on GitHub

Generate convex hull polygon(s) from multiple input geometries.

Image of create convex hull around geometries

Use case

Creating a convex hull allows for analysis to define the polygon with the least possible perimeter that encloses a group of geometric shapes. As a visual analogy, consider a set of nails in a board where the convex hull is a rubber band stretched around the outermost nails.

How to use the sample

Tap on the "Create" button to create convex hull(s) from the polygon graphics. If the "Union" toggle is on, the resulting output will be one polygon being the convex hull for the two input polygons. If the "Union" toggle is off, the resulting output will have two convex hull polygons - one for each of the two input polygons. Tap "Reset" to start over and remove the convex hull(s).

How it works

  1. Create an Map and display it in a MapView.
  2. Create two input polygon graphics and add them to a GraphicsOverlay.
  3. Call GeometryEngine.convexHull(for:shouldMerge:), specifying a list of geometries for which to generate the convex hull. Set the shouldMerge: parameter to true to generate a convex hull for the union of the geometries. Set it to false to create a convex hull for each individual geometry.
  4. Loop through the returned geometries and add them to a Graphics Overlay to display on the map.

Relevant API

  • GeometryEngine
  • GraphicsOverlay

Tags

analysis, geometry, outline, perimeter, union

Sample Code

CreateConvexHullAroundGeometriesView.swift
Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
// Copyright 2023 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
//
//   https://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.

import ArcGIS
import SwiftUI

struct CreateConvexHullAroundGeometriesView: View {
    /// A map with a topographic basemap.
    @State private var map = Map(basemapStyle: .arcGISTopographic)

    /// The graphics overlay for the geometry graphics.
    @State private var geometriesGraphicsOverlay: GraphicsOverlay = {
        let polygonFillSymbol = SimpleFillSymbol(
            style: .forwardDiagonal,
            color: .systemBlue,
            outline: SimpleLineSymbol(style: .solid, color: .blue, width: 2)
        )

        // Create graphics for the example polygons.
        let polygonGraphic1 = Graphic(geometry: .polygon1, symbol: polygonFillSymbol)
        let polygonGraphic2 = Graphic(geometry: .polygon2, symbol: polygonFillSymbol)
        return GraphicsOverlay(graphics: [polygonGraphic1, polygonGraphic2])
    }()

    /// The graphics overlay for the convex hull graphics.
    @State private var convexHullGraphicsOverlay = GraphicsOverlay()

    /// A Boolean indicating whether the button is set to create.
    @State private var createIsOn = true

    /// A Boolean indicating whether the convex hull geometries should union.
    @State private var shouldUnion = false

    var body: some View {
        MapView(map: map, graphicsOverlays: [convexHullGraphicsOverlay, geometriesGraphicsOverlay])
            .toolbar {
                ToolbarItemGroup(placement: .bottomBar) {
                    Toggle(shouldUnion ? "Union Enabled" : "Union Disabled", isOn: $shouldUnion)
                        .disabled(convexHullGraphicsOverlay.graphics.isEmpty)
                        .onChange(of: shouldUnion) { _ in
                            if !createIsOn {
                                convexHullGraphicsOverlay.removeAllGraphics()
                                convexHullGraphicsOverlay.addGraphics(
                                    makeConvexHullGraphics(
                                        for: [.polygon1, .polygon2],
                                        unioned: shouldUnion
                                    )
                                )
                            }
                        }
                    Button(createIsOn ? "Create" : "Reset") {
                        if createIsOn {
                            convexHullGraphicsOverlay.removeAllGraphics()
                            convexHullGraphicsOverlay.addGraphics(
                                makeConvexHullGraphics(
                                    for: [.polygon1, .polygon2],
                                    unioned: shouldUnion
                                )
                            )
                            createIsOn = false
                        } else {
                            convexHullGraphicsOverlay.removeAllGraphics()
                            createIsOn = true
                        }
                    }
                }
            }
    }
}

private extension CreateConvexHullAroundGeometriesView {
    /// Creates convex hulls graphic(s) from passed geometries.
    /// - Parameters:
    ///   - geometries: A `Geometry Array` to create the convex hull geometries from.
    ///   - unioned: A `Bool` indicating whether to union the convex hull geometries.
    /// - Returns: A `Graphics Array` of the created convex hull graphic(s).
    func makeConvexHullGraphics(for geometries: [Geometry], unioned: Bool) -> [Graphic] {
        // Create convex hull geometries.
        let convexHullGeometries = GeometryEngine.convexHull(for: geometries, shouldMerge: unioned)

        // Create fill symbol for the convex hull graphics.
        let convexHullFillSymbol = SimpleFillSymbol(
            style: .noFill,
            color: .systemBlue,
            outline: SimpleLineSymbol(style: .solid, color: .red, width: 4)
        )

        // Create a graphic for each convex hull geometry.
        var graphics: [Graphic] = []
        for geometry in convexHullGeometries {
            let convexHullGraphic = Graphic(geometry: geometry, symbol: convexHullFillSymbol)
            graphics.append(convexHullGraphic)
        }
        return graphics
    }
}

private extension Geometry {
    /// Example polygon 1.
    static let polygon1: ArcGIS.Polygon = Polygon(
        points: [
            Point(x: -4983189.15470412, y: 8679428.55774286),
            Point(x: -5222621.66664186, y: 5147799.00666126),
            Point(x: -13483043.3284937, y: 4728792.11077023),
            Point(x: -13273539.8805482, y: 2244679.79941622),
            Point(x: -5372266.98660294, y: 2035176.3514707),
            Point(x: -5432125.11458738, y: -4100281.76693377),
            Point(x: -2469147.7793579, y: -4160139.89491821),
            Point(x: -1900495.56350578, y: 2035176.3514707),
            Point(x: 2768438.41928007, y: 1975318.22348627),
            Point(x: 2409289.65137346, y: 5477018.71057565),
            Point(x: -2409289.65137346, y: 5387231.518599),
            Point(x: -2469147.7793579, y: 8709357.62173508)
        ],
        spatialReference: .webMercator
    )

    /// Example polygon 2.
    static let polygon2: ArcGIS.Polygon = Polygon(
        points: [
            Point(x: 5993520.19456882, y: -1063938.49607736),
            Point(x: 3085421.63862418, y: -1383120.04490055),
            Point(x: 3794713.96934239, y: -2979027.78901651),
            Point(x: 6880135.60796657, y: -4078430.90162972),
            Point(x: 7092923.30718203, y: -2837169.32287287),
            Point(x: 8617901.81822617, y: -2092412.37561875),
            Point(x: 6986529.4575743, y: 354646.16535905),
            Point(x: 5319692.48038653, y: 1205796.96222089)
        ],
        spatialReference: .webMercator
    )
}

#Preview {
    NavigationStack {
        CreateConvexHullAroundGeometriesView()
    }
}

Your browser is no longer supported. Please upgrade your browser for the best experience. See our browser deprecation post for more details.