Find the service areas of several facilities from a feature service.

Use case
A city taxi company may calculate service areas around their vehicle lots to identify gaps in coverage. Alternatively, they may want to ensure overlaps where a high volume of passengers requires redundant facilities, such as near an airport.
How to use the sample
Click ‘Find service areas’ to calculate and display the service area of each facility (hospital) on the map. The polygons displayed around each facility represent the facility’s service area: the red area is within 1 minute travel time from the hospital by car, whilst orange is within 3 minutes by car. All service areas are semi-transparent to show where they overlap.
How it works
- Create a new
ServiceAreaTaskfrom a network service. - Create default
ServiceAreaParametersfrom the service area task. - Set the parameters
ServiceAreaParameters.setReturnPolygons(true)to return polygons of all service areas. - Define
QueryParametersthat retrieve allFacilityitems from aServiceFeatureTable. Add the facilities to the service area parameters using the query parameters,serviceAreaParameters.SetFacilitiesWithFeatureTable(facilitiesTable, queryParameters). - Get the
ServiceAreaResultby solving the service area task using the parameters. - For each facility, get any
ServiceAreaPolygonsthat were returned,serviceAreaResult.resultPolygons(facilityIndex). - Display the service area polygons as
Graphicsin aGraphicsOverlayon theMapView.
Relevant API
- ServiceAreaParameters
- ServiceAreaPolygon
- ServiceAreaResult
- ServiceAreaTask
About the data
This sample uses a street map of San Diego, in combination with a feature service with facilities (used here to represent hospitals). Additionally a street network is used on the server for calculating the service area.
Tags
facilities, feature service, impedance, network analysis, service area, travel time
Sample Code
// [WriteFile Name=FindServiceAreasForMultipleFacilities, Category=Routing]// [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 "FindServiceAreasForMultipleFacilities.h"
// ArcGIS Maps SDK headers#include "Envelope.h"#include "Error.h"#include "ErrorException.h"#include "FeatureLayer.h"#include "Graphic.h"#include "GraphicListModel.h"#include "GraphicsOverlay.h"#include "GraphicsOverlayListModel.h"#include "LayerListModel.h"#include "Map.h"#include "MapQuickView.h"#include "MapTypes.h"#include "NetworkAnalystTypes.h"#include "PictureMarkerSymbol.h"#include "Polygon.h"#include "QueryParameters.h"#include "ServiceAreaFacility.h"#include "ServiceAreaParameters.h"#include "ServiceAreaPolygon.h"#include "ServiceAreaResult.h"#include "ServiceAreaTask.h"#include "ServiceFeatureTable.h"#include "SimpleFillSymbol.h"#include "SimpleRenderer.h"#include "SymbolTypes.h"
// Qt headers#include <QUrl>#include <QUuid>
using namespace Esri::ArcGISRuntime;
namespace{const QUrl url("https://services2.arcgis.com/ZQgQTuoyBrtmoGdP/ArcGIS/rest/services/San_Diego_Facilities/FeatureServer/0");const QUrl imageUrl("https://static.arcgis.com/images/Symbols/SafetyHealth/Hospital.png");const QUrl serviceAreaTaskUrl("https://sampleserver6.arcgisonline.com/arcgis/rest/services/NetworkAnalysis/SanDiego/NAServer/ServiceArea");}
FindServiceAreasForMultipleFacilities::FindServiceAreasForMultipleFacilities(QObject* parent /* = nullptr */): QObject(parent), m_map(new Map(BasemapStyle::ArcGISLightGray, this)), m_serviceAreasOverlay(new GraphicsOverlay(this)){ // create fill symbols for rendering the results m_fillSymbols.append(new SimpleFillSymbol(SimpleFillSymbolStyle::Solid, QColor(255, 166, 0, 66), this)); // translucent orange m_fillSymbols.append(new SimpleFillSymbol(SimpleFillSymbolStyle::Solid, QColor(255, 0, 0, 66), this)); // translucent red
m_facilitiesTable = new ServiceFeatureTable(url, this); m_facilitiesFeatureLayer = new FeatureLayer(m_facilitiesTable, this);
// create a symbol to display the facilities PictureMarkerSymbol* facilitiesSymbol = new PictureMarkerSymbol(imageUrl, this); facilitiesSymbol->setWidth(25); facilitiesSymbol->setHeight(25); m_facilitiesFeatureLayer->setRenderer(new SimpleRenderer(facilitiesSymbol, this));
// add the facilities feature layer to the map m_map->operationalLayers()->append(m_facilitiesFeatureLayer);}
FindServiceAreasForMultipleFacilities::~FindServiceAreasForMultipleFacilities() = default;
void FindServiceAreasForMultipleFacilities::init(){ // Register the map view for QML qmlRegisterType<MapQuickView>("Esri.Samples", 1, 0, "MapView"); qmlRegisterType<FindServiceAreasForMultipleFacilities>("Esri.Samples", 1, 0, "FindServiceAreasForMultipleFacilitiesSample");}
MapQuickView* FindServiceAreasForMultipleFacilities::mapView() const{ return m_mapView;}
bool FindServiceAreasForMultipleFacilities::taskRunning() const{ return m_future.isRunning();}
// Set the view (created in QML)void FindServiceAreasForMultipleFacilities::setMapView(MapQuickView* mapView){ if (!mapView || mapView == m_mapView) return;
m_mapView = mapView;
connect(m_facilitiesFeatureLayer, &FeatureLayer::doneLoading, this, [this](const Error& loadError) { if (!loadError.isEmpty()) { qDebug() << loadError.message() << loadError.additionalMessage(); return; }
// zoom to the full extent of the feature layer int buffer = 100; m_mapView->setViewpointGeometryAsync(m_facilitiesFeatureLayer->fullExtent(), buffer);
});
m_mapView->setMap(m_map);
m_mapView->graphicsOverlays()->append(m_serviceAreasOverlay);
emit mapViewChanged();}
void FindServiceAreasForMultipleFacilities::connectServiceAreaTaskSignals(){ // once service area task is done loading, create default parameters connect(m_serviceAreaTask, &ServiceAreaTask::doneLoading, this, [this](const Error& loadError) { if (!loadError.isEmpty()) { qDebug() << loadError.message() << loadError.additionalMessage(); return; } m_serviceAreaTask->createDefaultParametersAsync().then(this, [this](ServiceAreaParameters serviceAreaParameters) { // once default parameters created, set parameters and solve serviceAreaParameters.setPolygonDetail(ServiceAreaPolygonDetail::High); serviceAreaParameters.setReturnPolygons(true);
QList<double> travelTimes = {1.0, 3.0};
// add service areas of 1 minute and 3 minutes travel time by car serviceAreaParameters.setDefaultImpedanceCutoffs(travelTimes);
// create query parameters used to select all facilities from the feature table QueryParameters queryParameters; queryParameters.setWhereClause("1=1");
// add all facilities to the service area parameters serviceAreaParameters.setFacilitiesWithFeatureTable(m_facilitiesTable, queryParameters);
m_future = m_serviceAreaTask->solveServiceAreaAsync(serviceAreaParameters); m_future.then(this, [this](const ServiceAreaResult& serviceAreaResult) { emit taskRunningChanged();
// iterate through the facilities to get the service area polygons for (int i = 0; i < serviceAreaResult.facilities().size(); ++i) { QList<ServiceAreaPolygon> serviceAreaPolygonList = serviceAreaResult.resultPolygons(i); // create a graphic for each available polygon for (int j = 0; j < serviceAreaPolygonList.size(); ++j) { m_serviceAreasOverlay->graphics()->append(new Graphic(serviceAreaPolygonList[j].geometry(), m_fillSymbols[j], this)); } } }).onFailed([](const ErrorException& e) { qWarning() << e.error().message(); });
if (!m_future.isValid()) qWarning() << "Furure not valid.";
});
emit taskRunningChanged(); });}
void FindServiceAreasForMultipleFacilities::findServiceAreas(){ if (m_serviceAreaTask) return;
m_serviceAreaTask = new ServiceAreaTask(serviceAreaTaskUrl, this); connectServiceAreaTaskSignals(); m_serviceAreaTask->load();}// [WriteFile Name=FindServiceAreasForMultipleFacilities, Category=Routing]// [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 FINDSERVICEAREASFORMULTIPLEFACILITIES_H#define FINDSERVICEAREASFORMULTIPLEFACILITIES_H
// ArcGIS Maps SDK headers#include "ServiceAreaResult.h"
// Qt headers#include <QFuture>#include <QObject>
namespace Esri::ArcGISRuntime{class FeatureLayer;class GraphicsOverlay;class Map;class MapQuickView;class ServiceAreaResult;class ServiceAreaTask;class ServiceFeatureTable;class SimpleFillSymbol;}
Q_MOC_INCLUDE("MapQuickView.h")
class FindServiceAreasForMultipleFacilities : public QObject{ Q_OBJECT
Q_PROPERTY(Esri::ArcGISRuntime::MapQuickView* mapView READ mapView WRITE setMapView NOTIFY mapViewChanged) Q_PROPERTY(bool taskRunning READ taskRunning NOTIFY taskRunningChanged)
public: explicit FindServiceAreasForMultipleFacilities(QObject* parent = nullptr); ~FindServiceAreasForMultipleFacilities();
static void init();
Q_INVOKABLE void findServiceAreas();
signals: void mapViewChanged(); void taskRunningChanged();
private: Esri::ArcGISRuntime::MapQuickView* mapView() const; void setMapView(Esri::ArcGISRuntime::MapQuickView* mapView); bool taskRunning() const; void connectServiceAreaTaskSignals();
Esri::ArcGISRuntime::Map* m_map = nullptr; Esri::ArcGISRuntime::MapQuickView* m_mapView = nullptr; Esri::ArcGISRuntime::FeatureLayer* m_facilitiesFeatureLayer = nullptr; Esri::ArcGISRuntime::GraphicsOverlay* m_serviceAreasOverlay = nullptr; Esri::ArcGISRuntime::ServiceAreaTask* m_serviceAreaTask = nullptr; Esri::ArcGISRuntime::ServiceFeatureTable* m_facilitiesTable = nullptr; QList<Esri::ArcGISRuntime::SimpleFillSymbol*> m_fillSymbols; bool m_taskRunning = false; QFuture<Esri::ArcGISRuntime::ServiceAreaResult> m_future;};
#endif // FINDSERVICEAREASFORMULTIPLEFACILITIES_H// [WriteFile Name=FindServiceAreasForMultipleFacilities, Category=Routing]// [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 QtQuick.Layoutsimport Esri.Samples
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: "Find service areas" onClicked: { model.findServiceAreas(); enabled = false; } } } }
// Declare the C++ instance which creates the map etc. and supply the view FindServiceAreasForMultipleFacilitiesSample { id: model mapView: view }
BusyIndicator { anchors.centerIn: parent visible: true running: model.taskRunning }}// [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 "FindServiceAreasForMultipleFacilities.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("FindServiceAreasForMultipleFacilities"));
// 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 FindServiceAreasForMultipleFacilities::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/Routing/FindServiceAreasForMultipleFacilities/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
FindServiceAreasForMultipleFacilities { anchors.fill: parent }}