Find service areas for multiple facilities

View inAndroidFormsUWPWPFWinUIiOSView on GitHub

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

Tap '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.

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.

Relevant API

  • ServiceAreaParameters
  • ServiceAreaPolygon
  • ServiceAreaResult
  • ServiceAreaTask

Tags

facilities, feature service, impedance, network analysis, service area, travel time

Sample Code

FindServiceAreasForMultipleFacilities.cs
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
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
// 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 Android.App;
using Android.OS;
using Android.Views;
using Android.Widget;
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 Esri.ArcGISRuntime.UI.Controls;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace ArcGISRuntimeXamarin.Samples.FindServiceAreasForMultipleFacilities
{
    [Activity(ConfigurationChanges = Android.Content.PM.ConfigChanges.Orientation | Android.Content.PM.ConfigChanges.ScreenSize)]
    [ArcGISRuntime.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: "Tap '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" })]
    [ArcGISRuntime.Samples.Shared.Attributes.OfflineData()]
    public class FindServiceAreasForMultipleFacilities : Activity
    {
        // Hold references to the UI controls.
        private MapView _myMapView;
        private ProgressBar _activityIndicator;
        private Button _findServiceAreasButton;

        // 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;

        protected override void OnCreate(Bundle bundle)
        {
            base.OnCreate(bundle);

            Title = "Find service areas for multiple facilities";

            CreateLayout();
            Initialize();
        }

        private async void 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, System.Drawing.Color.FromArgb(0x66, 0xFF, 0xA5, 0x00), null));
                _fillSymbols.Add(new SimpleFillSymbol(SimpleFillSymbolStyle.Solid, System.Drawing.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.
                _findServiceAreasButton.Enabled = 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, EventArgs e)
        {
            try
            {
                // Clear the old results.
                _resultOverlay?.Graphics.Clear();

                // Update the UI.
                _findServiceAreasButton.Enabled = false;
                _activityIndicator.Visibility = ViewStates.Visible;

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

                // Update the UI.
                _activityIndicator.Visibility = ViewStates.Gone;
                _findServiceAreasButton.Enabled = 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) => new AlertDialog.Builder(this).SetTitle(title).SetMessage(detail).Show();

        private void CreateLayout()
        {
            // Create a new vertical layout for the app.
            var layout = new LinearLayout(this) {Orientation = Orientation.Vertical};

            // Create the activity spinner.
            _activityIndicator = new ProgressBar(this);
            _activityIndicator.Indeterminate = true;
            _activityIndicator.Visibility = ViewStates.Gone;

            // Create the service areas button.
            _findServiceAreasButton = new Button(this);
            _findServiceAreasButton.Text = "Find service areas";
            _findServiceAreasButton.Enabled = false;
            _findServiceAreasButton.Click += FindServiceArea_Clicked;

            // Add the button and the spinner.
            layout.AddView(_findServiceAreasButton);
            layout.AddView(_activityIndicator);

            // Add the map view to the layout.
            _myMapView = new MapView(this);
            layout.AddView(_myMapView);

            // Show the layout in the app.
            SetContentView(layout);
        }
    }
}

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