Convex hull list

View on GitHubSample viewer app

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

Image of convex hull list

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

Click the 'Create Convex Hull' button to create convex hull(s) from the polygon graphics. If the 'Union' checkbox is checked, the resulting output will be one polygon being the convex hull for the two input polygons. If the 'Union' checkbox is un-checked, the resulting output will have two convex hull polygons - one for each of the two input polygons. Click the 'Clear' button to start over.

How it works

  1. Create an ArcGISMap and display it in a MapView.
  2. Create two input polygon graphics and add them to a GraphicsOverlay.
  3. Call GeometryEngine.convexHull(inputGeometries, boolean), specifying a list of geometries for which to generate the convex hull. Set the boolean 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 as graphics for display on the map.

Relevant API

  • GeometryEngine
  • Graphic
  • GraphicsOverlay

Tags

analysis, geometry, outline, perimeter, union

Sample Code

ConvexHullListSample.java
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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
/*
 * 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.
 */

package com.esri.samples.convex_hull_list;

import java.util.Arrays;
import java.util.List;

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.stage.Stage;
import javafx.scene.control.Button;

import com.esri.arcgisruntime.ArcGISRuntimeEnvironment;
import com.esri.arcgisruntime.geometry.Geometry;
import com.esri.arcgisruntime.geometry.GeometryEngine;
import com.esri.arcgisruntime.geometry.Point;
import com.esri.arcgisruntime.geometry.PointCollection;
import com.esri.arcgisruntime.geometry.Polygon;
import com.esri.arcgisruntime.geometry.SpatialReferences;
import com.esri.arcgisruntime.mapping.ArcGISMap;
import com.esri.arcgisruntime.mapping.BasemapStyle;
import com.esri.arcgisruntime.mapping.Viewpoint;
import com.esri.arcgisruntime.mapping.view.Graphic;
import com.esri.arcgisruntime.mapping.view.GraphicsOverlay;
import com.esri.arcgisruntime.mapping.view.MapView;
import com.esri.arcgisruntime.symbology.SimpleFillSymbol;
import com.esri.arcgisruntime.symbology.SimpleLineSymbol;


public class ConvexHullListSample extends Application {

  private MapView mapView;

  @Override
  public void start(Stage stage) {

    try {
      // create stack pane and application scene
      StackPane stackPane = new StackPane();
      Scene scene = new Scene(stackPane);
      scene.getStylesheets().add(getClass().getResource("/convex_hull_list/style.css").toExternalForm());

      // set title, size, and add scene to stage
      stage.setTitle("Convex Hull List Sample");
      stage.setWidth(800);
      stage.setHeight(700);
      stage.setScene(scene);
      stage.show();

      // authentication with an API key or named user is required to access basemaps and other location services
      String yourAPIKey = System.getProperty("apiKey");
      ArcGISRuntimeEnvironment.setApiKey(yourAPIKey);

      // create a map with the topographic basemap style
      ArcGISMap map = new ArcGISMap(BasemapStyle.ARCGIS_TOPOGRAPHIC);

      // create a map view and set the map to it
      mapView = new MapView();
      mapView.setMap(map);

      // set a viewpoint on the map view
      mapView.setViewpoint(new Viewpoint(30.0, 0.0, 147914381));

      // create a graphics overlay for the two input polygons
      GraphicsOverlay polygonGraphicsOverlay = new GraphicsOverlay();
      // create a graphics overlay for displaying convex hull polygon
      GraphicsOverlay convexHullGraphicsOverlay = new GraphicsOverlay();

      // add the graphics overlays to the map view
      mapView.getGraphicsOverlays().add(convexHullGraphicsOverlay);
      mapView.getGraphicsOverlays().add(polygonGraphicsOverlay);

      // create a simple line symbol for the outline of the two input polygon graphics
      SimpleLineSymbol polygonOutline = new SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, Color.BLUE, 3);
      // create a simple fill symbol for the two input polygon graphics
      SimpleFillSymbol polygonFill = new SimpleFillSymbol(SimpleFillSymbol.Style.SOLID,
        Color.web("blue", 0.3), polygonOutline);

      // create a point collection that represents polygon1. Use the same spatial reference as the underlying base map.
      PointCollection pointCollection1 = new PointCollection(SpatialReferences.getWebMercator());

      // add polygon1 boundary map points to the point collection
      pointCollection1.add(new Point(-4983189.15470412, 8679428.55774286));
      pointCollection1.add(new Point(-5222621.66664186, 5147799.00666126));
      pointCollection1.add(new Point(-13483043.3284937, 4728792.11077023));
      pointCollection1.add(new Point(-13273539.8805482, 2244679.79941622));
      pointCollection1.add(new Point(-5372266.98660294, 2035176.3514707));
      pointCollection1.add(new Point(-5432125.11458738, -4100281.76693377));
      pointCollection1.add(new Point(-2469147.7793579, -4160139.89491821));
      pointCollection1.add(new Point(-1900495.56350578, 2035176.3514707));
      pointCollection1.add(new Point(2768438.41928007, 1975318.22348627));
      pointCollection1.add(new Point(2409289.65137346, 5477018.71057565));
      pointCollection1.add(new Point(-2409289.65137346, 5387231.518599));
      pointCollection1.add(new Point(-2469147.7793579, 8709357.62173508));

      // create a polygon geometry from the point collection
      Polygon polygon1 = new Polygon(pointCollection1);
      // create the graphic for polygon1
      Graphic polygonGraphic1 = new Graphic(polygon1, polygonFill);
      // add the polygon1 graphic to the graphics overlay collection
      polygonGraphicsOverlay.getGraphics().add(polygonGraphic1);

      // create a point collection that represents polygon2. Use the same spatial reference as the underlying base map.
      PointCollection pointCollection2 = new PointCollection(SpatialReferences.getWebMercator());

      // add polygon2 boundary map points to the point collection
      pointCollection2.add(new Point(5993520.19456882, -1063938.49607736));
      pointCollection2.add(new Point(3085421.63862418, -1383120.04490055));
      pointCollection2.add(new Point(3794713.96934239, -2979027.78901651));
      pointCollection2.add(new Point(6880135.60796657, -4078430.90162972));
      pointCollection2.add(new Point(7092923.30718203, -2837169.32287287));
      pointCollection2.add(new Point(8617901.81822617, -2092412.37561875));
      pointCollection2.add(new Point(6986529.4575743, 354646.16535905));
      pointCollection2.add(new Point(5319692.48038653, 1205796.96222089));

      // create a polygon geometry from the point collection
      Polygon polygon2 = new Polygon(pointCollection2);
      // create the graphic for polygon2
      Graphic polygonGraphic2 = new Graphic(polygon2, polygonFill);
      // add the polygon2 graphic to the graphics overlay collection
      polygonGraphicsOverlay.getGraphics().add(polygonGraphic2);

      // create a button to create and show the convex hull
      var convexHullButton = new Button("Create Convex Hull");
      convexHullButton.setMaxWidth(Double.MAX_VALUE);

      // create a button to clear the convex hull results
      var clearButton = new Button("Clear");
      clearButton.setMaxWidth(Double.MAX_VALUE);

      // create a check box for toggling union option on or off
      var checkBox = new CheckBox("Union");

      convexHullButton.setOnAction(e -> {
        // reset the convex hull graphics overlay
        convexHullGraphicsOverlay.getGraphics().clear();

        // add the geometries of the two polygon graphics to a list of geometries
        List<Geometry> allPolygonGeometries = Arrays.asList(polygonGraphic1.getGeometry(), polygonGraphic2.getGeometry());

        // retrieve the returned result from the convex hull operation
        // if unioned, one geometry is returned, otherwise one convex hull geometry is returned per input geometry
        List<Geometry> convexHullGeometries = GeometryEngine.convexHull(allPolygonGeometries, checkBox.isSelected());

        // loop through the returned geometries.
        for (Geometry geometry : convexHullGeometries) {

          // create a simple line symbol for the outline of the convex hull graphic(s)
          SimpleLineSymbol convexHullLine = new SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, Color.RED, 5);

          // create the graphic for the convex hull(s)
          Graphic convexHullGraphic = new Graphic(geometry, convexHullLine);

          // add the convex hull(s) graphic to the convex hull graphics overlay
          convexHullGraphicsOverlay.getGraphics().add(convexHullGraphic);
        }

        // enable clear button after convex hull button has been pressed
        clearButton.setDisable(false);
      });

      // disable clear button from starting application (when nothing to clear)
      clearButton.setDisable(true);

      clearButton.setOnAction(e -> {
        // clear convex hull graphics from map view
        convexHullGraphicsOverlay.getGraphics().clear();
        // once cleared disable clear button
        clearButton.setDisable(true);
        // enable convex hull button after clearing
        convexHullButton.setDisable(false);
        // reset checkbox
        checkBox.setSelected(false);
      });

      // create a control panel
      var controlsVBox = new VBox(6);
      controlsVBox.setBackground(new Background(new BackgroundFill(Paint.valueOf("rgba(0, 0, 0, 0.3)"),
              CornerRadii.EMPTY, Insets.EMPTY)));
      controlsVBox.setPadding(new Insets(10));
      controlsVBox.setMaxSize(260, 110);
      controlsVBox.getStyleClass().add("panel-region");
      controlsVBox.getChildren().addAll(convexHullButton, clearButton, checkBox);

      // add the map view to the stack pane
      stackPane.getChildren().addAll(mapView, controlsVBox);
      stackPane.setAlignment(controlsVBox, Pos.TOP_RIGHT);
      stackPane.setMargin(controlsVBox, new Insets(10, 10, 0, 10));

    } catch (Exception e) {
      // on any error, display the stack trace
      e.printStackTrace();
    }
  }


  /**
   * Stops and releases all resources used in application
   */
  @Override
  public void stop() {
    // release resources when the application closes
    if (mapView != null) {
      mapView.dispose();
    }
  }

  /**
   * Opens and runs application.
   *
   * @param args arguments passed to this application
   */
  public static void main(String[] args) {

    Application.launch(args);
  }
}

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