Create a convex hull for a given set of points. The convex hull is a polygon with shortest perimeter that encloses a set of points. As a visual analogy, consider a set of points as nails in a board. The convex hull of the points would be like a rubber band stretched around the outermost nails.

Use case
A convex hull can be useful in collision detection. For example, when charting the position of two yacht fleets (with each vessel represented by a point), if their convex hulls have been precomputed, it is efficient to first check if their convex hulls intersect before computing their proximity point-by-point.
How to use the sample
Tap on the map to add points. Click “Convex hull” button to generate the convex hull of those points. Click the “Reset” button to start over.
How it works
- Create an input geometry such as a
Multipointobject. - Use
GeometryEngine::convexHull(inputGeometry)to create a newGeometryobject representing the convex hull of the input points. The returned geometry will either be aPoint,Polyline, orPolygonbased on the number of input points.
Relevant API
- Geometry
- GeometryEngine
Tags
convex hull, geometry, spatial analysis
Sample Code
// [WriteFile Name=ConvexHull, Category=Geometry]// [Legal]// Copyright 2020 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]
#ifdef PCH_BUILD#include "pch.hpp"#endif // PCH_BUILD
// sample headers#include "ConvexHull.h"
// ArcGIS Maps SDK headers#include "Error.h"#include "Geometry.h"#include "GeometryEngine.h"#include "GeometryTypes.h"#include "Graphic.h"#include "GraphicListModel.h"#include "GraphicsOverlay.h"#include "GraphicsOverlayListModel.h"#include "Map.h"#include "MapQuickView.h"#include "MapTypes.h"#include "MultipointBuilder.h"#include "Point.h"#include "PointCollection.h"#include "SimpleFillSymbol.h"#include "SimpleLineSymbol.h"#include "SimpleMarkerSymbol.h"#include "SpatialReference.h"#include "SymbolTypes.h"
using namespace Esri::ArcGISRuntime;
ConvexHull::ConvexHull(QObject* parent /* = nullptr */): QObject(parent), m_map(new Map(BasemapStyle::ArcGISTopographic, this)){ setupGraphics();}
ConvexHull::~ConvexHull() = default;
void ConvexHull::init(){ // Register the map view for QML qmlRegisterType<MapQuickView>("Esri.Samples", 1, 0, "MapView"); qmlRegisterType<ConvexHull>("Esri.Samples", 1, 0, "ConvexHullSample");}
MapQuickView* ConvexHull::mapView() const{ return m_mapView;}
void ConvexHull::displayConvexHull(){ if (m_inputsGraphic->geometry().isEmpty()) return;
// normalizing the geometry before performing geometric operations const Geometry normalizedPoints = GeometryEngine::normalizeCentralMeridian(m_inputsGraphic->geometry()); const Geometry convexHull = GeometryEngine::convexHull(normalizedPoints);
// change the symbol based on the returned geometry type if (convexHull.geometryType() == GeometryType::Point) { m_convexHullGraphic->setSymbol(m_markerSymbol); } else if (convexHull.geometryType() == GeometryType::Polyline) { m_convexHullGraphic->setSymbol(m_lineSymbol); } else if (convexHull.geometryType() == GeometryType::Polygon) { m_convexHullGraphic->setSymbol(m_fillSymbol); } else { qWarning("Not a valid geometry."); }
m_convexHullGraphic->setGeometry(convexHull);}
void ConvexHull::clearGraphics(){ if (m_multipointBuilder) m_multipointBuilder->points()->removeAll(); if (m_inputsGraphic) m_inputsGraphic->setGeometry(Geometry()); if (m_convexHullGraphic) m_convexHullGraphic->setGeometry(Geometry());}
void ConvexHull::setupGraphics(){ // graphics overlay to show clicked points and convex hull m_graphicsOverlay = new GraphicsOverlay(this);
// create a graphic to show clicked points m_markerSymbol = new SimpleMarkerSymbol(SimpleMarkerSymbolStyle::Circle, Qt::red, 10, this); m_inputsGraphic = new Graphic(this); m_inputsGraphic->setSymbol(m_markerSymbol); m_graphicsOverlay->graphics()->append(m_inputsGraphic);
// create a graphic to display the convex hull m_convexHullGraphic = new Graphic(this); m_graphicsOverlay->graphics()->append(m_convexHullGraphic);
// create a graphic to show the convex hull m_lineSymbol = new SimpleLineSymbol(SimpleLineSymbolStyle::Solid, Qt::blue, 3, this); m_fillSymbol = new SimpleFillSymbol(SimpleFillSymbolStyle::Null, Qt::transparent, m_lineSymbol, this);}
void ConvexHull::getInputs(){ // show clicked points on MapView connect(m_mapView, &MapQuickView::mouseClicked, this, [this](QMouseEvent& e) { e.accept();
const Point clickedPoint = m_mapView->screenToLocation(e.position().x(), e.position().y()); m_multipointBuilder->points()->addPoint(clickedPoint); m_inputsGraphic->setGeometry(m_multipointBuilder->toGeometry()); });}
// Set the view (created in QML)void ConvexHull::setMapView(MapQuickView* mapView){ if (!mapView || mapView == m_mapView) return;
m_mapView = mapView; m_mapView->setMap(m_map);
// wait for map to load before creating multipoint builder connect(m_map, &Map::doneLoading, this, [this](const Error& e){ if (!e.isEmpty()) { qDebug() << e.message() << e.additionalMessage(); return; }
if (m_map->loadStatus() == LoadStatus::FailedToLoad) { qWarning( "Failed to load map."); return; }
m_multipointBuilder = new MultipointBuilder(m_map->spatialReference(), this); });
getInputs();
m_mapView->graphicsOverlays()->append(m_graphicsOverlay); emit mapViewChanged();}// [WriteFile Name=ConvexHull, Category=Geometry]// [Legal]// Copyright 2020 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]
#ifndef CONVEXHULL_H#define CONVEXHULL_H
// Qt headers#include <QList>#include <QObject>
namespace Esri::ArcGISRuntime{class Graphic;class GraphicsOverlay;class Map;class MapQuickView;class MultipointBuilder;class SimpleFillSymbol;class SimpleLineSymbol;class SimpleMarkerSymbol;}
Q_MOC_INCLUDE("MapQuickView.h")
class ConvexHull : public QObject{ Q_OBJECT
Q_PROPERTY(Esri::ArcGISRuntime::MapQuickView* mapView READ mapView WRITE setMapView NOTIFY mapViewChanged)
public: explicit ConvexHull(QObject* parent = nullptr); ~ConvexHull();
static void init();
Q_INVOKABLE void displayConvexHull(); Q_INVOKABLE void clearGraphics();
signals: void mapViewChanged();
private: Esri::ArcGISRuntime::MapQuickView* mapView() const; void setMapView(Esri::ArcGISRuntime::MapQuickView* mapView);
void setupGraphics(); void getInputs();
Esri::ArcGISRuntime::Map* m_map = nullptr; Esri::ArcGISRuntime::MapQuickView* m_mapView = nullptr;
Esri::ArcGISRuntime::Graphic* m_inputsGraphic = nullptr; Esri::ArcGISRuntime::Graphic* m_convexHullGraphic = nullptr; Esri::ArcGISRuntime::GraphicsOverlay* m_graphicsOverlay = nullptr; Esri::ArcGISRuntime::SimpleFillSymbol* m_fillSymbol = nullptr; Esri::ArcGISRuntime::SimpleLineSymbol* m_lineSymbol = nullptr; Esri::ArcGISRuntime::SimpleMarkerSymbol* m_markerSymbol = nullptr; Esri::ArcGISRuntime::MultipointBuilder* m_multipointBuilder = nullptr;};
#endif // CONVEXHULL_H// [WriteFile Name=ConvexHull, Category=Geometry]// [Legal]// Copyright 2020 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 QtQuickimport QtQuick.Controlsimport Esri.Samplesimport QtQuick.Layouts
Item {
// add a mapView component MapView { id: view anchors.fill: parent
Component.onCompleted: { // Set the focus on MapView to initially enable keyboard navigation forceActiveFocus(); }
RowLayout { anchors { left: parent.left top: parent.top }
Button { Layout.fillWidth: true Layout.fillHeight: true text: "Convex hull" onClicked: { model.displayConvexHull(); } }
Button { Layout.fillWidth: true Layout.fillHeight: true text: "Reset" onClicked: { model.clearGraphics(); } } } }
// Declare the C++ instance which creates the map etc. and supply the view ConvexHullSample { id: model mapView: view }}// [Legal]// Copyright 2020 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]
// sample headers#include "ConvexHull.h"
// ArcGIS Maps SDK headers#include "ArcGISRuntimeEnvironment.h"
// Qt headers#include <QDir>#include <QGuiApplication>#include <QQmlApplicationEngine>
// Platform specific headers#ifdef Q_OS_WIN#include <Windows.h>#endif
int main(int argc, char *argv[]){ Esri::ArcGISRuntime::ArcGISRuntimeEnvironment::setUseLegacyAuthentication(false); QGuiApplication app(argc, argv); app.setApplicationName(QString("ConvexHull"));
// Use of ArcGIS location services, such as basemap styles, geocoding, and routing services, // requires an access token. For more information see // https://links.esri.com/arcgis-runtime-security-auth.
// The following methods grant an access token:
// 1. User authentication: Grants a temporary access token associated with a user's ArcGIS account. // To generate a token, a user logs in to the app with an ArcGIS account that is part of an // organization in ArcGIS Online or ArcGIS Enterprise.
// 2. API key authentication: Get a long-lived access token that gives your application access to // ArcGIS location services. Go to the tutorial at https://links.esri.com/create-an-api-key. // Copy the API Key access token.
const QString accessToken = QString("");
if (accessToken.isEmpty()) { qWarning() << "Use of ArcGIS location services, such as the basemap styles service, requires" << "you to authenticate with an ArcGIS account or set the API Key property."; } else { Esri::ArcGISRuntime::ArcGISRuntimeEnvironment::setApiKey(accessToken); }
// Initialize the sample ConvexHull::init();
// Initialize application view QQmlApplicationEngine engine; // Add the import Path engine.addImportPath(QDir(QCoreApplication::applicationDirPath()).filePath("qml"));
#ifdef ARCGIS_RUNTIME_IMPORT_PATH_2 engine.addImportPath(ARCGIS_RUNTIME_IMPORT_PATH_2);#endif
// Set the source engine.load(QUrl("qrc:/Samples/Geometry/ConvexHull/main.qml"));
return app.exec();}// Copyright 2020 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.
import QtQuick.Controlsimport Esri.Samples
ApplicationWindow { visible: true width: 800 height: 600
ConvexHull { anchors.fill: parent }}