Skip to content

Find service areas for multiple facilities #367

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 43 commits into from
Jul 23, 2019
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
31518ba
create basic class with ui elements, symbols
JonLavi May 23, 2019
14480a6
load in feature table, layer, and zoom to extent
JonLavi May 23, 2019
91fc664
solve service area task and display
JonLavi May 23, 2019
bf7064d
UI improvements
JonLavi May 23, 2019
af94134
imports
JonLavi May 23, 2019
1a731b7
add facilities to task through featuretable
JonLavi May 24, 2019
95db967
formatting
JonLavi May 24, 2019
2f3cf86
refactoring, ui fixes
JonLavi May 24, 2019
f71dba7
comments and formatting
JonLavi May 28, 2019
3822638
rm comment
JonLavi May 28, 2019
1b8ec87
add readme and screenshot
JonLavi May 28, 2019
0e0b60d
readme and comment edits
JonLavi May 28, 2019
8ffede1
rename, remove reset button
JonLavi May 31, 2019
dd26190
rename, reduce service area
JonLavi May 31, 2019
e65f623
update readme to mirror design
JonLavi Jun 6, 2019
5ddcba7
comments
JonLavi Jun 6, 2019
122fa5d
remove unused SimpleFillSymbol
JonLavi Jun 6, 2019
abfa113
graphics changes
JonLavi Jun 6, 2019
fe96b87
change basemap to grey canvas
JonLavi Jun 7, 2019
6267a0b
refactoring and variable naming
JonLavi Jun 10, 2019
7d10db6
refactoring
JonLavi Jun 17, 2019
604a4f8
readme wording
JonLavi Jun 17, 2019
fe3e7b7
comments
JonLavi Jun 17, 2019
6fd4491
screenshot
JonLavi Jun 17, 2019
a5880be
3/5min service areas, yellow/red color
JonLavi Jun 21, 2019
6be0093
comments, bigger facility symbols
JonLavi Jun 21, 2019
4380185
remove unneeded code, comment improvements, loop indices
JonLavi Jun 27, 2019
06900a1
describe both cutoffs in readme
JonLavi Jun 27, 2019
7857d25
service area comment
JonLavi Jun 27, 2019
420b8e2
readme to html
JonLavi Jun 27, 2019
83d9386
smaller service areas, new screenshot
JonLavi Jul 1, 2019
ccf7969
update readme for new service areas
JonLavi Jul 1, 2019
06d5859
update README.md, sample title
JonLavi Jul 4, 2019
20c650c
implement review changes
JonLavi Jul 9, 2019
6e7a1e6
rebase to split dev branch
JonLavi Jul 18, 2019
fc6b5f5
update settings.gradle
JonLavi Jul 18, 2019
7cf8adf
update build.gradle
JonLavi Jul 18, 2019
5ed7802
package and folder names
JonLavi Jul 18, 2019
477e486
Sample title: Area -> Areas
JonLavi Jul 22, 2019
bb7e094
add gradle wrapper
tschie Jul 22, 2019
e6e0215
fix main class name
tschie Jul 22, 2019
b9f3f31
implement review changes
JonLavi Jul 23, 2019
cb4bfc5
update README.md, comments
JonLavi Jul 23, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
/*
* 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.
*/

package com.esri.samples.na.find_service_areas_for_multiple_facilities;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ExecutionException;

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

import com.esri.arcgisruntime.concurrent.ListenableFuture;
import com.esri.arcgisruntime.data.ArcGISFeatureTable;
import com.esri.arcgisruntime.data.QueryParameters;
import com.esri.arcgisruntime.data.ServiceFeatureTable;
import com.esri.arcgisruntime.layers.FeatureLayer;
import com.esri.arcgisruntime.loadable.LoadStatus;
import com.esri.arcgisruntime.mapping.ArcGISMap;
import com.esri.arcgisruntime.mapping.Basemap;
import com.esri.arcgisruntime.mapping.view.DrawStatus;
import com.esri.arcgisruntime.mapping.view.DrawStatusChangedEvent;
import com.esri.arcgisruntime.mapping.view.DrawStatusChangedListener;
import com.esri.arcgisruntime.mapping.view.Graphic;
import com.esri.arcgisruntime.mapping.view.GraphicsOverlay;
import com.esri.arcgisruntime.mapping.view.MapView;
import com.esri.arcgisruntime.symbology.PictureMarkerSymbol;
import com.esri.arcgisruntime.symbology.SimpleFillSymbol;
import com.esri.arcgisruntime.symbology.SimpleRenderer;
import com.esri.arcgisruntime.tasks.networkanalysis.ServiceAreaParameters;
import com.esri.arcgisruntime.tasks.networkanalysis.ServiceAreaPolygon;
import com.esri.arcgisruntime.tasks.networkanalysis.ServiceAreaPolygonDetail;
import com.esri.arcgisruntime.tasks.networkanalysis.ServiceAreaResult;
import com.esri.arcgisruntime.tasks.networkanalysis.ServiceAreaTask;

public class FindServiceAreasForMultipleFacilitiesSample extends Application {

private MapView mapView;

@Override
public void start(Stage stage) {

// create stack pane and application scene
StackPane stackPane = new StackPane();
Scene scene = new Scene(stackPane);
scene.getStylesheets().add(getClass().getResource("/css/style.css").toExternalForm());

// set title, size, and add scene to stage
stage.setTitle("Find Service Area for Facilities Sample");
stage.setWidth(800);
stage.setHeight(700);
stage.setScene(scene);
stage.show();

// create button
Button findServiceAreasButton = new Button("Find Service Areas");
findServiceAreasButton.setMaxWidth(150);
findServiceAreasButton.setDisable(true);

// create a progress indicator
ProgressIndicator progressIndicator = new ProgressIndicator();
progressIndicator.setVisible(false);

// create an ArcGISMap with a streets basemap
ArcGISMap map = new ArcGISMap(Basemap.createLightGrayCanvas());

// set the ArcGISMap to be displayed in the map view
mapView = new MapView();
mapView.setMap(map);

// create a graphics overlay for displaying service areas
GraphicsOverlay serviceAreasGraphicsOverlay = new GraphicsOverlay();

// add the graphics overlay to the map view
mapView.getGraphicsOverlays().add(serviceAreasGraphicsOverlay);

// create fill symbols for rendering the result
ArrayList<SimpleFillSymbol> fillSymbols = new ArrayList<>();
fillSymbols.add(new SimpleFillSymbol(SimpleFillSymbol.Style.SOLID, 0x66FFA500, null));
fillSymbols.add(new SimpleFillSymbol(SimpleFillSymbol.Style.SOLID, 0x66FF0000, null));

// create a feature table of facilities using a FeatureServer
ArcGISFeatureTable facilitiesTable = new ServiceFeatureTable("https://services2.arcgis.com/ZQgQTuoyBrtmoGdP/ArcGIS/rest/services/San_Diego_Facilities/FeatureServer/0");
// create a feature layer from the table
FeatureLayer facilitiesFeatureLayer = new FeatureLayer(facilitiesTable);

// create a symbol used to display the facilities
PictureMarkerSymbol facilitySymbol = new PictureMarkerSymbol("http://static.arcgis.com/images/Symbols/SafetyHealth/Hospital.png");
facilitySymbol.setHeight(25);
facilitySymbol.setWidth(25);
// set the renderer of the facilities feature layer to use the facilities symbol
facilitiesFeatureLayer.setRenderer(new SimpleRenderer(facilitySymbol));

// add the facilities feature layer to the map
map.getOperationalLayers().add(facilitiesFeatureLayer);

// wait for the facilities feature layer to load
facilitiesFeatureLayer.addDoneLoadingListener(() -> {
if (facilitiesFeatureLayer.getLoadStatus() == LoadStatus.LOADED) {

// zoom to the extent of the feature layer
mapView.setViewpointGeometryAsync(facilitiesFeatureLayer.getFullExtent(), 130);

// enable the find service areas button when the draw status is completed for the first time
mapView.addDrawStatusChangedListener(new DrawStatusChangedListener() {
@Override
public void drawStatusChanged(DrawStatusChangedEvent drawStatusChangedEvent) {
if (drawStatusChangedEvent.getDrawStatus() == DrawStatus.COMPLETED) {
// enable the 'find service areas' button
findServiceAreasButton.setDisable(false);
mapView.removeDrawStatusChangedListener(this);
}
}
});

// determine the service areas and display them when the button is clicked
findServiceAreasButton.setOnAction(event -> {

// disable the button
findServiceAreasButton.setDisable(true);

// show the progress indicator
progressIndicator.setVisible(true);

// create a service area task from URL
ServiceAreaTask serviceAreaTask = new ServiceAreaTask("https://sampleserver6.arcgisonline.com/arcgis/rest/services/NetworkAnalysis/SanDiego/NAServer/ServiceArea");
serviceAreaTask.loadAsync();

// create default service area task parameters
ListenableFuture<ServiceAreaParameters> serviceAreaTaskParametersFuture = serviceAreaTask.createDefaultParametersAsync();
serviceAreaTaskParametersFuture.addDoneListener(() -> {
try {
ServiceAreaParameters serviceAreaParameters = serviceAreaTaskParametersFuture.get();
// set the task parameters to have the task return polygons
serviceAreaParameters.setPolygonDetail(ServiceAreaPolygonDetail.HIGH);
serviceAreaParameters.setReturnPolygons(true);
// clear the default service area, and add service areas of 1 minute and 3 minutes travel time by car
serviceAreaParameters.getDefaultImpedanceCutoffs().clear();
serviceAreaParameters.getDefaultImpedanceCutoffs().addAll(Arrays.asList(1.0, 3.0));

// create query parameters used to select all facilities from the feature table
QueryParameters queryParameters = new QueryParameters();
queryParameters.setWhereClause("1=1");

// add all facilities to the service area parameters
serviceAreaParameters.setFacilities(facilitiesTable, queryParameters);

// find the service areas around the facilities using the parameters
ListenableFuture<ServiceAreaResult> serviceAreaResultFuture = serviceAreaTask.solveServiceAreaAsync(serviceAreaParameters);
serviceAreaResultFuture.addDoneListener(() -> {
try {
// get the task results
ServiceAreaResult serviceAreaResult = serviceAreaResultFuture.get();

// display all the service areas that were found to the map view
List<Graphic> serviceAreaGraphics = serviceAreasGraphicsOverlay.getGraphics();

// iterate through all the facilities to get the service area polygons
for (int i = 0; i < serviceAreaResult.getFacilities().size(); i++) {
List<ServiceAreaPolygon> serviceAreaPolygonList = serviceAreaResult.getResultPolygons(i);
// create a graphic for each available polygon, as there may be more than one for each service area
for (int j = 0; j < serviceAreaPolygonList.size(); j++) {
// create and show a graphics for the service area
serviceAreaGraphics.add(new Graphic(serviceAreaPolygonList.get(j).getGeometry(), fillSymbols.get(j % fillSymbols.size())));
}
}

} catch (ExecutionException | InterruptedException e) {
new Alert(Alert.AlertType.ERROR, "Error solving the service area task").show();
}
// hide the progress indicator after the task is complete
progressIndicator.setVisible(false);
});

} catch (ExecutionException | InterruptedException e) {
e.printStackTrace();
}
});
});
}
});

// add the map view, find service area button, and progress indicator to stack pane
stackPane.getChildren().addAll(mapView, findServiceAreasButton, progressIndicator);
StackPane.setAlignment(findServiceAreasButton, Pos.TOP_LEFT);
StackPane.setMargin(findServiceAreasButton, new Insets(10, 0, 0, 10));
}

/**
* Stops and releases all resources used in application.
*/
@Override
public void stop() {

if (mapView != null) {
mapView.dispose();
}
}

/**
* Opens and runs application.
*
* @param args arguments passed to this application
*/
public static void main(String[] args) {

Application.launch(args);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<h1>Find service areas for multiple facilities</h1>

<p>Find the service areas of several facilities from a feature service.</p>

<img src="FindServiceAreasForMultipleFacilities.png"/>

<h2>Use case</h2>

<p>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.</p>

<h2>How to use the sample</h2>

<p>Click 'find service areas' 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 1 minute away from the hospital by car. Light orange is the area that is within 3 minutes away from the hospital by car. All polygons have transparency to them, so that when service areas from multiple facilities overlap, these areas will appear in bolder colors.</p>

<h2>How it works</h2>

<ol>
<li>Create a new <code>ServiceAreaTask</code> from a network service.</li>
<li>Create default <code>ServiceAreaParameters</code> from the service area task.</li>
<li>Set the parameters <code>ServiceAreaParameters.setReturnPolygons(true)</code> to return polygons of all service areas.</li>
<li>Add facilities of the <code>ServiceAreaParameters</code>. For this, use a set of <code>QueryParameters</code> to select features from a <code>ServiceFeatureTable</code>: <code>serviceAreaParameters.SetFacilities(facilitiesTable, queryParameters)</code>.</li>
<li>Get the <code>ServiceAreaResult</code> by solving the service area task using the parameters.</li>
<li>For each facility, get any <code>ServiceAreaPolygons</code> that were returned, <code>serviceAreaResult.getResultPolygons(facilityIndex)</code>.</li>
<li>Display the service area polygons as <code>Graphics</code> in a <code>GraphicsOverlay</code> on the <code>MapView</code>.</li>
</ol>

<h2>About the data</h2>

<p>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.</p>

<h2>Relevant API</h2>

<ul>
<li>ServiceAreaParameters</li>
<li>ServiceAreaPolygon</li>
<li>ServiceAreaResult</li>
<li>ServiceAreaTask</li>
</ul>

<h2 id="tags">Tags</h2>

<p>feature service, impedance, network analysis, travel time</p>