Use a geoprocessing service and a set of features to identify statistically significant hot spots and cold spots.

Use case
This tool identifies statistically significant spatial clusters of high values (hot spots) and low values (cold spots). For example, a hotspot analysis based on the frequency of 911 calls within a set region.
How to use the sample
Select a date range (between 1998-01-01 and 1998-05-31) from the dialog and tap on Analyze. The results will be shown on the map upon successful completion of the GeoprocessingJob.
How it works
- Create a
GeoprocessingTaskwith the URL set to the endpoint of a geoprocessing service. - Create a query string with the date range as an input of
GeoprocessingParameters. - Use the
GeoprocessingTaskto create aGeoprocessingJobwith theGeoprocessingParametersinstance. - Start the
GeoprocessingJoband wait for it to complete and return aGeoprocessingResult. - Get the resulting
ArcGISMapImageLayerusingGeoprocessingResult.getMapImageLayer. - Add the layer to the map’s operational layers.
Relevant API
- GeoprocessingJob
- GeoprocessingParameters
- GeoprocessingResult
- GeoprocessingTask
Tags
analysis, density, geoprocessing, hot spots, hotspots
Sample Code
// [WriteFile Name=AnalyzeHotspots, Category=Analysis]// [Legal]// Copyright 2017 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 "AnalyzeHotspots.h"
// ArcGIS Maps SDK headers#include "ArcGISMapImageLayer.h"#include "Envelope.h"#include "Error.h"#include "GeoprocessingJob.h"#include "GeoprocessingResult.h"#include "GeoprocessingString.h"#include "GeoprocessingTask.h"#include "GeoprocessingTypes.h"#include "LayerListModel.h"#include "Map.h"#include "MapQuickView.h"#include "MapTypes.h"#include "TaskTypes.h"
// Qt headers#include <QFuture>
using namespace Esri::ArcGISRuntime;
AnalyzeHotspots::AnalyzeHotspots(QQuickItem* parent /* = nullptr */): QQuickItem(parent){}
AnalyzeHotspots::~AnalyzeHotspots() = default;
void AnalyzeHotspots::init(){ qmlRegisterType<MapQuickView>("Esri.Samples", 1, 0, "MapView"); qmlRegisterType<AnalyzeHotspots>("Esri.Samples", 1, 0, "AnalyzeHotspotsSample");}
void AnalyzeHotspots::componentComplete(){ QQuickItem::componentComplete();
// find QML MapView component m_mapView = findChild<MapQuickView*>("mapView");
// Create a map using the topographic basemap m_map = new Map(BasemapStyle::ArcGISTopographic, this);
// Set map to map view m_mapView->setMap(m_map);
// Create the Geoprocessing Task m_hotspotTask = new GeoprocessingTask(QUrl("https://sampleserver6.arcgisonline.com/arcgis/rest/services/911CallsHotspot/GPServer/911%20Calls%20Hotspot"), this);
// Connect to the GP Task's errorOccurred signal connect(m_hotspotTask, &GeoprocessingTask::errorOccurred, this, [this](const Error& error) { emit displayErrorDialog("Geoprocessing Task failed", error.message()); });}
void AnalyzeHotspots::executeTaskWithDates(const QString& fromDate, const QString& toDate){ // Create the GP Parameters GeoprocessingParameters hotspotParameters = createParameters(fromDate, toDate);
// Create the GP Job and connect to the status signals GeoprocessingJob* job = m_hotspotTask->createJob(hotspotParameters); connect(job, &GeoprocessingJob::statusChanged, this, [this, job](JobStatus jobStatus) { switch (jobStatus) { case JobStatus::Failed: emit displayErrorDialog("Geoprocessing Task failed", !job->error().isEmpty() ? job->error().additionalMessage() : "Unknown error."); m_jobInProgress = false; m_jobStatus = "Job failed"; break; case JobStatus::Started: m_jobInProgress = true; m_jobStatus = "Job in progress..."; break; case JobStatus::Paused: m_jobInProgress = false; m_jobStatus = "Job paused..."; break; case JobStatus::Succeeded: m_jobInProgress = false; m_jobStatus = "Job succeeded"; // handle the results processResults(job->result()); break; default: break; }
// emit signals emit jobInProgressChanged(); emit statusChanged(); });
// Start the job job->start(); m_jobInProgress = true; emit jobInProgressChanged(); m_jobStatus = "Job in progress..."; emit statusChanged();}
GeoprocessingParameters AnalyzeHotspots::createParameters(const QString& fromDate, const QString& toDate){ // Create the GeoprocessingParameters and set the execution type GeoprocessingParameters hotspotParameters = GeoprocessingParameters(GeoprocessingExecutionType::AsynchronousSubmit);
// create the query string QString queryString("(\"DATE\" > date '%1 00:00:00' AND \"DATE\" < date '%2 00:00:00')"); queryString = queryString.arg(fromDate, toDate);
// Add query that contains the date range and the days of the week that are used in analysis QMap<QString, GeoprocessingParameter*> inputs; inputs["Query"] = new GeoprocessingString(queryString, this); hotspotParameters.setInputs(inputs);
// return the GeoprocessingParameters return hotspotParameters;}
void AnalyzeHotspots::processResults(GeoprocessingResult* result){ // Clear the map's operational layers m_mapView->map()->operationalLayers()->clear();
// Extract the layer from the result and add to the map m_layer = result->mapImageLayer(); connect(m_layer, &ArcGISMapImageLayer::doneLoading, this, [this](const Error& error) { if (error.isEmpty()) { m_mapView->setViewpointGeometryAsync(m_layer->fullExtent()); } }); m_mapView->map()->operationalLayers()->append(m_layer);}// [WriteFile Name=AnalyzeHotspots, Category=Analysis]// [Legal]// Copyright 2017 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 ANALYZEHOTSPOTS_H#define ANALYZEHOTSPOTS_H
// ArcGIS Maps SDK headers#include "GeoprocessingParameters.h"
// Qt headers#include <QQuickItem>
namespace Esri::ArcGISRuntime{ class Map; class MapQuickView; class GeoprocessingTask; class GeoprocessingResult; class ArcGISMapImageLayer;}
class AnalyzeHotspots : public QQuickItem{ Q_OBJECT
Q_PROPERTY(bool jobInProgress MEMBER m_jobInProgress NOTIFY jobInProgressChanged) Q_PROPERTY(QString statusText MEMBER m_jobStatus NOTIFY statusChanged)
public: explicit AnalyzeHotspots(QQuickItem* parent = nullptr); ~AnalyzeHotspots() override;
void componentComplete() override; static void init();
Q_INVOKABLE void executeTaskWithDates(const QString& fromDate, const QString& toDate);
signals: void jobInProgressChanged(); void statusChanged(); void displayErrorDialog(const QString& titleText, const QString& detailedText);
private: Esri::ArcGISRuntime::Map* m_map = nullptr; Esri::ArcGISRuntime::MapQuickView* m_mapView = nullptr; Esri::ArcGISRuntime::GeoprocessingTask* m_hotspotTask = nullptr; Esri::ArcGISRuntime::ArcGISMapImageLayer* m_layer = nullptr; bool m_jobInProgress = false; QString m_jobStatus = QStringLiteral("Not started.");
private: Esri::ArcGISRuntime::GeoprocessingParameters createParameters(const QString& fromDate, const QString& toDate); void processResults(Esri::ArcGISRuntime::GeoprocessingResult* result);};
#endif // ANALYZEHOTSPOTS_H// [WriteFile Name=AnalyzeHotspots, Category=Analysis]// [Legal]// Copyright 2017 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.Controls// import Qt.labs.calendar // Calendar is not supported in Qt 6.2import QtQuick.Layoutsimport Esri.Samples
AnalyzeHotspotsSample { id: rootRectangle clip: true width: 800 height: 600
property date fromThisDate : Date.fromLocaleDateString(Qt.locale(), "98/01/01", "yy/MM/dd") property date toThisDate : Date.fromLocaleDateString(Qt.locale(), "98/01/31", "yy/MM/dd")
// Declare the MapView MapView { anchors.fill: parent objectName: "mapView"
Component.onCompleted: { // Set the focus on MapView to initially enable keyboard navigation forceActiveFocus(); } }
onDisplayErrorDialog: { messageDialog.title = "Error"; messageDialog.text = titleText; messageDialog.detailedText = detailedText; messageDialog.open(); }
// Create the settings rectangle to set dates and execute the task Rectangle { anchors { margins: -10 fill: settingsColumn } color: "lightgrey" radius: 5 border.color: "black" opacity: 0.75 }
Column { id: settingsColumn anchors { right: parent.right top: parent.top margins: 20 } spacing: 5
Text { anchors.horizontalCenter: parent.horizontalCenter text: "Select date range for analysis\n(d MMM yyyy)" font.pixelSize: 14 }
Text { text: "From" font.pixelSize: 12 }
TextField { id: fromDate width: parent.width text: "1 Jan 1998" onTextChanged: { fromThisDate = Date.fromLocaleString(Qt.locale(), text, "d MMM yyyy") validateDates(); } selectByMouse: true }
Text { text: "To" font.pixelSize: 12 }
TextField { id: toDate width: parent.width text: "31 Jan 1998" onTextChanged: { toThisDate = Date.fromLocaleString(Qt.locale(), text, "d MMM yyyy") validateDates(); } selectByMouse: true }
Button { width: parent.width anchors.horizontalCenter: parent.horizontalCenter text: "Run analysis" enabled: !jobInProgress && validateDates(fromThisDate, toThisDate)
onClicked: { const fromString = fromThisDate.toLocaleString(Qt.locale(), "yyyy-MM-dd"); const toString = toThisDate.toLocaleString(Qt.locale(), "yyyy-MM-dd"); // Run the task executeTaskWithDates(fromString, toString); } }
Row { spacing: 15 visible: jobInProgress
BusyIndicator { anchors.verticalCenter: parent.verticalCenter width: 24 height: width }
Text { anchors.verticalCenter: parent.verticalCenter text: statusText font.pixelSize: 14 } } }
Dialog { id: messageDialog modal: true x: Math.round(parent.width - width) / 2 y: Math.round(parent.height - height) / 2 standardButtons: Dialog.Ok title: "Error" property alias text : textLabel.text property alias detailedText : detailsLabel.text ColumnLayout { Text { id: textLabel } Text { id: detailsLabel } } }
// function to validate the date ranges provided function validateDates() { // check if each date is within the period if (fromThisDate > toThisDate) { return false; }
// check that there is at least one day in between the from and to date const oneDay = 86400000; if ((toThisDate - fromThisDate) < oneDay) { return false; }
return true; }}// [Legal]// Copyright 2017 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 "AnalyzeHotspots.h"
// ArcGIS Maps SDK headers#include "ArcGISRuntimeEnvironment.h"
// Qt headers#include <QCommandLineParser>#include <QDir>#include <QGuiApplication>#include <QQmlEngine>#include <QQuickView>
// Platform specific headers#ifdef Q_OS_WIN#include <Windows.h>#endif
#define STRINGIZE(x) #x#define QUOTE(x) STRINGIZE(x)
int main(int argc, char *argv[]){ Esri::ArcGISRuntime::ArcGISRuntimeEnvironment::setUseLegacyAuthentication(false); QGuiApplication app(argc, argv); app.setApplicationName(QString("Analyze Hotspots"));
// 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 AnalyzeHotspots::init();
// Initialize application view QQuickView view; view.setResizeMode(QQuickView::SizeRootObjectToView);
// Add the import Path view.engine()->addImportPath(QDir(QCoreApplication::applicationDirPath()).filePath("qml"));
QString arcGISRuntimeImportPath = QUOTE(ARCGIS_RUNTIME_IMPORT_PATH);
#if defined(LINUX_PLATFORM_REPLACEMENT) // on some linux platforms the string 'linux' is replaced with 1 // fix the replacement paths which were created QString replaceString = QUOTE(LINUX_PLATFORM_REPLACEMENT); arcGISRuntimeImportPath = arcGISRuntimeImportPath.replace(replaceString, "linux", Qt::CaseSensitive); #endif
// Add the Runtime and Extras path view.engine()->addImportPath(arcGISRuntimeImportPath);
// Set the source view.setSource(QUrl("qrc:/Samples/Analysis/AnalyzeHotspots/AnalyzeHotspots.qml"));
view.show();
return app.exec();}