Find service areas for multiple facilities

View inMAUIWPFUWPWinUIView on GitHubSample viewer app

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

Image of find service areas for multiple facilities

Use case

A service area is a region which can be accessed from a facility as limited by one or more factors, such as travel time, distance, or cost. When analyzing the service area of multiple facilities, this workflow can be used to identify gaps in service area coverage, or significant overlaps, helping to optimize the distribution of facilities. For example, a city's health service may identify areas of a city that can be served effectively from particular hospitals, and with this optimize distribution of staff and resources.

How to use the sample

Click 'find service area' to calculate and display the service area of each facility on the map. The polygons displayed around each facility represents the service area; in red is the area that is within 3 minutes away from the hospital by car. Light orange is the area that is within 5 minutes away from the hospital by car.

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.ReturnPolygons = true to return polygons of all service areas.
  4. Add facilities of the ServiceAreaParameters. For this, use a set of QueryParameters to select features from a ServiceFeatureTable: serviceAreaParameters.SetFacilities(_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.GetResultPolygons(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.xaml.csFindServiceAreasForMultipleFacilities.xaml.csFindServiceAreasForMultipleFacilities.xaml
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
// Copyright 2019 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.

using Esri.ArcGISRuntime.Data;
using Esri.ArcGISRuntime.Geometry;
using Esri.ArcGISRuntime.Mapping;
using Esri.ArcGISRuntime.Symbology;
using Esri.ArcGISRuntime.Tasks.NetworkAnalysis;
using Esri.ArcGISRuntime.UI;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;

namespace ArcGIS.WPF.Samples.FindServiceAreasForMultipleFacilities
{
    [ArcGIS.Samples.Shared.Attributes.Sample(
        name: "Find service areas for multiple facilities",
        category: "Network analysis",
        description: "Find the service areas of several facilities from a feature service.",
        instructions: "Click 'find service area' to calculate and display the service area of each facility on the map. The polygons displayed around each facility represents the service area; in red is the area that is within 3 minutes away from the hospital by car. Light orange is the area that is within 5 minutes away from the hospital by car.",
        tags: new[] { "facilities", "feature service", "impedance", "network analysis", "service area", "travel time" })]
    [ArcGIS.Samples.Shared.Attributes.OfflineData()]
    public partial class FindServiceAreasForMultipleFacilities
    {
        // URLs to resources used by the sample.
        private const string NetworkAnalysisUrl = "https://sampleserver6.arcgisonline.com/arcgis/rest/services/NetworkAnalysis/SanDiego/NAServer/ServiceArea";
        private const string FacilitiesFeatureUrl = "https://services2.arcgis.com/ZQgQTuoyBrtmoGdP/ArcGIS/rest/services/San_Diego_Facilities/FeatureServer/0";
        private const string IconUrl = "https://static.arcgis.com/images/Symbols/SafetyHealth/Hospital.png";

        // The table that contains the facilities.
        private ServiceFeatureTable _facilitiesTable;

        // The task for performing the service area analysis.
        private ServiceAreaTask _serviceAreaTask;

        // The graphics overlay for displaying the resulting polygons.
        private GraphicsOverlay _resultOverlay;

        // Symbology for the resulting service areas.
        private List<SimpleFillSymbol> _fillSymbols;

        public FindServiceAreasForMultipleFacilities()
        {
            InitializeComponent();
            _ = Initialize();
        }

        private async Task Initialize()
        {
            try
            {
                // Create the map and show it in the view.
                Map newMap = new Map(BasemapStyle.ArcGISLightGray);
                MyMapView.Map = newMap;

                // Create the table containing the facilities.
                _facilitiesTable = new ServiceFeatureTable(new Uri(FacilitiesFeatureUrl));

                // Create the layer for rendering the facilities table.
                FeatureLayer facilitiesLayer = new FeatureLayer(_facilitiesTable);

                // Create a simple renderer that will display an image for each facility.
                facilitiesLayer.Renderer = new SimpleRenderer(new PictureMarkerSymbol(new Uri(IconUrl)));

                // Add the layer to the map.
                newMap.OperationalLayers.Add(facilitiesLayer);

                // Create the graphics overlay for displaying the result polygons.
                _resultOverlay = new GraphicsOverlay();

                // Add the result overlay to the view.
                MyMapView.GraphicsOverlays.Add(_resultOverlay);

                // Create a list of fill symbols for rendering the results.
                _fillSymbols = new List<SimpleFillSymbol>();
                _fillSymbols.Add(new SimpleFillSymbol(SimpleFillSymbolStyle.Solid, Color.FromArgb(0x66, 0xFF, 0xA5, 0x00), null));
                _fillSymbols.Add(new SimpleFillSymbol(SimpleFillSymbolStyle.Solid, Color.FromArgb(0x66, 0xFF, 0x00, 0x00), null));

                // Wait for the table to load and zoom to its extent.
                await _facilitiesTable.LoadAsync();
                await MyMapView.SetViewpointGeometryAsync(_facilitiesTable.Extent, 50);

                // Enable the button now that the sample is ready.
                ServiceAreaButton.IsEnabled = true;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(ex);
                ShowMessage("Error", "Error starting the sample.");
            }
        }

        private async Task FindServiceAreas()
        {
            try
            {
                // Create the service area task.
                _serviceAreaTask = await ServiceAreaTask.CreateAsync(new Uri(NetworkAnalysisUrl));

                // Create the default parameters for the service.
                ServiceAreaParameters serviceAreaParameters = await _serviceAreaTask.CreateDefaultParametersAsync();

                // Configure the service area parameters.
                serviceAreaParameters.PolygonDetail = ServiceAreaPolygonDetail.High;
                serviceAreaParameters.ReturnPolygons = true;
                serviceAreaParameters.DefaultImpedanceCutoffs.Clear();
                serviceAreaParameters.DefaultImpedanceCutoffs.Add(0);
                serviceAreaParameters.DefaultImpedanceCutoffs.Add(3);
                serviceAreaParameters.DefaultImpedanceCutoffs.Add(5);

                // A query that finds all of the relevant facilities from the facilities feature service.
                QueryParameters facilityQueryParameters = new QueryParameters();
                facilityQueryParameters.WhereClause = "1=1";

                // Provide the feature service and the query as parameters to the service area task.
                serviceAreaParameters.SetFacilities(_facilitiesTable, facilityQueryParameters);

                // Perform the service area analysis.
                ServiceAreaResult result = await _serviceAreaTask.SolveServiceAreaAsync(serviceAreaParameters);

                // Count the features in the facilities layer.
                long facilityCount = await _facilitiesTable.QueryFeatureCountAsync(facilityQueryParameters);

                // Get the service area for each facility.
                for (int facilityIndex = 0; facilityIndex < facilityCount; facilityIndex++)
                {
                    // Get each area polygon from the result for that facility.
                    List<ServiceAreaPolygon> areaPolygons = result.GetResultPolygons(facilityIndex).ToList();

                    // Add each service area polygon to the graphics overlay.
                    for (int polygonIndex = 0; polygonIndex < areaPolygons.Count; polygonIndex++)
                    {
                        // Get the polygon from the result.
                        Polygon resultingPolygon = areaPolygons[polygonIndex].Geometry;

                        // Choose a symbol for the polygon.
                        SimpleFillSymbol selectedSymbol = _fillSymbols[polygonIndex % _fillSymbols.Count];

                        // Create and add the graphic.
                        _resultOverlay.Graphics.Add(new Graphic(resultingPolygon, selectedSymbol));
                    }
                }

                // Zoom to the extent of the results.
                await MyMapView.SetViewpointGeometryAsync(_resultOverlay.Extent, 50);
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(ex);
                ShowMessage("Error", "Couldn't complete service area analysis.");
            }
        }

        private async void FindServiceArea_Clicked(object sender, RoutedEventArgs e)
        {
            try
            {
                // Clear the old results.
                _resultOverlay?.Graphics.Clear();

                // Update the UI.
                ServiceAreaButton.IsEnabled = false;
                ProgressView.Visibility = Visibility.Visible;

                // Do the service area work.
                await FindServiceAreas();

                // Update the UI.
                ProgressView.Visibility = Visibility.Collapsed;
                ServiceAreaButton.IsEnabled = true;
            }
            catch (Exception exception)
            {
                System.Diagnostics.Debug.WriteLine(exception);
                ShowMessage("Error", "Couldn't complete the service area analysis.");
            }
        }

        private void ShowMessage(string title, string detail) => MessageBox.Show(detail, title);
    }
}

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