Find service areas for multiple facilities

View on GitHubSample viewer app

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

screenshot

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

  1. Create a new ServiceAreaTask from a network service.
  2. Create default ServiceAreaParameters from the service area task.
  3. Set the parameters ServiceAreaParameters.setReturnPolygons(true) to return polygons of all service areas.
  4. Define QueryParameters that retrieve all Facility items from a ServiceFeatureTable. Add the facilities to the service area parameters using the query parameters, serviceAreaParameters.SetFacilitiesWithFeatureTable(facilitiesTable, queryParameters).
  5. Get the ServiceAreaResult by solving the service area task using the parameters.
  6. For each facility, get any ServiceAreaPolygons that were returned, serviceAreaResult.resultPolygons(facilityIndex).
  7. Display the service area polygons as Graphics in a GraphicsOverlay on the MapView.

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

FindServiceAreasForMultipleFacilities.cppFindServiceAreasForMultipleFacilities.cppFindServiceAreasForMultipleFacilities.hFindServiceAreasForMultipleFacilities.qml
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
// [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();
}

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