Skip to content

Commit 561b5cb

Browse files
authored
Closest facility static (#346)
1 parent a0ecdbc commit 561b5cb

File tree

3 files changed

+336
-0
lines changed

3 files changed

+336
-0
lines changed
324 KB
Loading
Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
/*
2+
* Copyright 2019 Esri.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
5+
* use this file except in compliance with the License. You may obtain a copy of
6+
* the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations under
14+
* the License.
15+
*/
16+
17+
package com.esri.samples.na.closest_facility_static;
18+
19+
import java.util.ArrayList;
20+
import java.util.Arrays;
21+
import java.util.concurrent.ExecutionException;
22+
23+
import javafx.application.Application;
24+
import javafx.geometry.Insets;
25+
import javafx.geometry.Pos;
26+
import javafx.scene.Scene;
27+
import javafx.scene.control.Alert;
28+
import javafx.scene.control.Button;
29+
import javafx.scene.control.ProgressIndicator;
30+
import javafx.scene.image.Image;
31+
import javafx.scene.layout.StackPane;
32+
import javafx.stage.Stage;
33+
34+
import com.esri.arcgisruntime.concurrent.ListenableFuture;
35+
import com.esri.arcgisruntime.data.Feature;
36+
import com.esri.arcgisruntime.data.FeatureQueryResult;
37+
import com.esri.arcgisruntime.data.FeatureTable;
38+
import com.esri.arcgisruntime.data.QueryParameters;
39+
import com.esri.arcgisruntime.data.ServiceFeatureTable;
40+
import com.esri.arcgisruntime.geometry.Envelope;
41+
import com.esri.arcgisruntime.geometry.GeometryEngine;
42+
import com.esri.arcgisruntime.geometry.Point;
43+
import com.esri.arcgisruntime.layers.FeatureLayer;
44+
import com.esri.arcgisruntime.loadable.LoadStatus;
45+
import com.esri.arcgisruntime.mapping.ArcGISMap;
46+
import com.esri.arcgisruntime.mapping.Basemap;
47+
import com.esri.arcgisruntime.mapping.view.Graphic;
48+
import com.esri.arcgisruntime.mapping.view.GraphicsOverlay;
49+
import com.esri.arcgisruntime.mapping.view.MapView;
50+
import com.esri.arcgisruntime.symbology.PictureMarkerSymbol;
51+
import com.esri.arcgisruntime.symbology.SimpleLineSymbol;
52+
import com.esri.arcgisruntime.symbology.SimpleRenderer;
53+
import com.esri.arcgisruntime.tasks.networkanalysis.ClosestFacilityParameters;
54+
import com.esri.arcgisruntime.tasks.networkanalysis.ClosestFacilityResult;
55+
import com.esri.arcgisruntime.tasks.networkanalysis.ClosestFacilityRoute;
56+
import com.esri.arcgisruntime.tasks.networkanalysis.ClosestFacilityTask;
57+
import com.esri.arcgisruntime.tasks.networkanalysis.Facility;
58+
import com.esri.arcgisruntime.tasks.networkanalysis.Incident;
59+
60+
public class ClosestFacilityStaticSample extends Application {
61+
62+
private MapView mapView;
63+
64+
@Override
65+
public void start(Stage stage) throws Exception {
66+
67+
try {
68+
// create stack pane and application scene
69+
StackPane stackPane = new StackPane();
70+
Scene scene = new Scene(stackPane);
71+
72+
// set title, size, and add scene to stage
73+
stage.setTitle("Closest Facility (Static) Sample");
74+
stage.setWidth(800);
75+
stage.setHeight(700);
76+
stage.setScene(scene);
77+
stage.show();
78+
79+
// create buttons
80+
Button solveRoutesButton = new Button("Solve Routes");
81+
solveRoutesButton.setMaxWidth(150);
82+
solveRoutesButton.setDisable(true);
83+
84+
// create a progress indicator
85+
ProgressIndicator progressIndicator = new ProgressIndicator();
86+
progressIndicator.setVisible(true);
87+
88+
// create a ArcGISMap with a Basemap instance with an Imagery base layer
89+
ArcGISMap map = new ArcGISMap(Basemap.createStreetsWithReliefVector());
90+
91+
// set the map to be displayed in this view
92+
mapView = new MapView();
93+
mapView.setMap(map);
94+
95+
// create a graphics overlay and add it to the map
96+
GraphicsOverlay graphicsOverlay = new GraphicsOverlay();
97+
mapView.getGraphicsOverlays().add(graphicsOverlay);
98+
99+
// create Symbols for displaying facilities
100+
PictureMarkerSymbol facilitySymbol = new PictureMarkerSymbol(new Image("https://static.arcgis.com/images/Symbols/SafetyHealth/FireStation.png", 30, 30, true, false));
101+
PictureMarkerSymbol incidentSymbol = new PictureMarkerSymbol(new Image("https://static.arcgis.com/images/Symbols/SafetyHealth/esriCrimeMarker_56_Gradient.png", 30, 30, true, false));
102+
103+
// create a line symbol to mark the route
104+
SimpleLineSymbol simpleLineSymbol = new SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, 0x4D0000FF, 5.0f);
105+
106+
// create a closest facility task from a network analysis service
107+
ClosestFacilityTask closestFacilityTask = new ClosestFacilityTask("https://sampleserver6.arcgisonline.com/arcgis/rest/services/NetworkAnalysis/SanDiego/NAServer/ClosestFacility");
108+
109+
// create a table for facilities using the feature service
110+
FeatureTable facilitiesFeatureTable = new ServiceFeatureTable("https://services2.arcgis.com/ZQgQTuoyBrtmoGdP/ArcGIS/rest/services/San_Diego_Facilities/FeatureServer/0");
111+
// create a feature layer from the table, apply facilities icon
112+
FeatureLayer facilitiesFeatureLayer = new FeatureLayer(facilitiesFeatureTable);
113+
facilitiesFeatureLayer.setRenderer(new SimpleRenderer(facilitySymbol));
114+
115+
// create a table for incidents using the feature service
116+
FeatureTable incidentsFeatureTable = new ServiceFeatureTable("https://services2.arcgis.com/ZQgQTuoyBrtmoGdP/ArcGIS/rest/services/San_Diego_Incidents/FeatureServer/0");
117+
// create a feature layer from the table, apply incident icon
118+
FeatureLayer incidentsFeatureLayer = new FeatureLayer(incidentsFeatureTable);
119+
incidentsFeatureLayer.setRenderer(new SimpleRenderer(incidentSymbol));
120+
121+
// add the layers to the map
122+
map.getOperationalLayers().addAll(Arrays.asList(facilitiesFeatureLayer, incidentsFeatureLayer));
123+
124+
// create the list to store the facilities
125+
ArrayList<Facility> facilities = new ArrayList<>();
126+
127+
// create the list to store the incidents
128+
ArrayList<Incident> incidents = new ArrayList<>();
129+
130+
// wait for the feature layers to load to retrieve the facilities and incidents
131+
facilitiesFeatureLayer.addDoneLoadingListener(() -> incidentsFeatureLayer.addDoneLoadingListener(() -> {
132+
if (facilitiesFeatureLayer.getLoadStatus() == LoadStatus.LOADED && incidentsFeatureLayer.getLoadStatus() == LoadStatus.LOADED) {
133+
134+
// hide the progress indicator
135+
progressIndicator.setVisible(false);
136+
137+
// zoom to the extent of the combined feature layers
138+
Envelope fullFeatureLayerExtent = GeometryEngine.combineExtents(facilitiesFeatureLayer.getFullExtent(), incidentsFeatureLayer.getFullExtent());
139+
mapView.setViewpointGeometryAsync(fullFeatureLayerExtent, 90);
140+
141+
// create query parameters to select all features
142+
QueryParameters queryParameters = new QueryParameters();
143+
queryParameters.setWhereClause("1=1");
144+
145+
// retrieve a list of all facilities
146+
ListenableFuture<FeatureQueryResult> result = facilitiesFeatureTable.queryFeaturesAsync(queryParameters);
147+
result.addDoneListener(() -> {
148+
try {
149+
FeatureQueryResult facilitiesResult = result.get();
150+
151+
// add the found facilities to the list
152+
for (Feature facilityFeature : facilitiesResult) {
153+
// since we know our feature layer only contains point features, we can cast them as Point in order to create a Facility
154+
facilities.add(new Facility((Point) facilityFeature.getGeometry()));
155+
}
156+
157+
} catch (InterruptedException | ExecutionException e) {
158+
new Alert(Alert.AlertType.ERROR, "Error retrieving list of facilities.").show();
159+
}
160+
});
161+
162+
// retrieve a list of all incidents
163+
ListenableFuture<FeatureQueryResult> incidentsQueryResult = incidentsFeatureTable.queryFeaturesAsync(queryParameters);
164+
incidentsQueryResult.addDoneListener(() -> {
165+
try {
166+
FeatureQueryResult incidentsResult = incidentsQueryResult.get();
167+
168+
// add the found incidents to the list
169+
for (Feature incidentFeature : incidentsResult) {
170+
// since we know our feature layer only contains point features, we can cast them as Point in order to create an Incident
171+
incidents.add(new Incident((Point) incidentFeature.getGeometry()));
172+
}
173+
174+
} catch (InterruptedException | ExecutionException e) {
175+
new Alert(Alert.AlertType.ERROR, "Error retrieving list of incidents.").show();
176+
}
177+
});
178+
179+
// enable the 'solve routes' button
180+
solveRoutesButton.setDisable(false);
181+
182+
// resolve button press
183+
solveRoutesButton.setOnAction(e -> {
184+
185+
// disable the 'solve routes' button and show the progress indicator
186+
solveRoutesButton.setDisable(true);
187+
progressIndicator.setVisible(true);
188+
189+
// start the routing task
190+
closestFacilityTask.loadAsync();
191+
closestFacilityTask.addDoneLoadingListener(() -> {
192+
if (closestFacilityTask.getLoadStatus() == LoadStatus.LOADED) {
193+
try {
194+
// create default parameters for the task and add facilities and incidents to parameters
195+
ClosestFacilityParameters closestFacilityParameters = closestFacilityTask.createDefaultParametersAsync().get();
196+
closestFacilityParameters.setFacilities(facilities);
197+
closestFacilityParameters.setIncidents(incidents);
198+
199+
// solve closest facilities
200+
try {
201+
// use the task to solve for the closest facility
202+
ListenableFuture<ClosestFacilityResult> closestFacilityTaskResult = closestFacilityTask.solveClosestFacilityAsync(closestFacilityParameters);
203+
closestFacilityTaskResult.addDoneListener(() -> {
204+
try {
205+
ClosestFacilityResult closestFacilityResult = closestFacilityTaskResult.get();
206+
207+
// find the closest facility for each incident
208+
for (int incidentIndex = 0; incidentIndex < incidents.size(); incidentIndex++) {
209+
210+
// get the index of the closest facility to incident
211+
Integer closestFacilityIndex = closestFacilityResult.getRankedFacilityIndexes(incidentIndex).get(0);
212+
213+
// get the route to the closest facility
214+
ClosestFacilityRoute closestFacilityRoute = closestFacilityResult.getRoute(closestFacilityIndex, incidentIndex);
215+
216+
// display the route on the graphics overlay
217+
graphicsOverlay.getGraphics().add(new Graphic(closestFacilityRoute.getRouteGeometry(), simpleLineSymbol));
218+
219+
// hide the progress indicator and enable the reset button
220+
progressIndicator.setVisible(false);
221+
}
222+
223+
} catch (ExecutionException | InterruptedException ex) {
224+
new Alert(Alert.AlertType.ERROR, "Error getting the closest facility task result.").show();
225+
}
226+
});
227+
228+
} catch (Exception ex) {
229+
new Alert(Alert.AlertType.ERROR, "Error solving the closest facility task.").show();
230+
}
231+
232+
} catch (InterruptedException | ExecutionException ex) {
233+
new Alert(Alert.AlertType.ERROR, "Error getting default route parameters.").show();
234+
}
235+
236+
} else {
237+
new Alert(Alert.AlertType.ERROR, "Error loading route task.").show();
238+
}
239+
});
240+
});
241+
}
242+
})
243+
);
244+
245+
// add the map view, control panel and progress indicator to the stack pane
246+
stackPane.getChildren().addAll(mapView, solveRoutesButton, progressIndicator);
247+
StackPane.setAlignment(solveRoutesButton, Pos.TOP_LEFT);
248+
StackPane.setMargin(solveRoutesButton, new Insets(10, 0, 0, 10));
249+
250+
} catch (Exception e) {
251+
e.printStackTrace();
252+
}
253+
}
254+
255+
/**
256+
* Stops and releases all resources used in application.
257+
*/
258+
@Override
259+
public void stop() {
260+
261+
if (mapView != null) {
262+
mapView.dispose();
263+
}
264+
}
265+
266+
/**
267+
* Opens and runs application.
268+
*
269+
* @param args arguments passed to this application
270+
*/
271+
public static void main(String[] args) {
272+
273+
Application.launch(args);
274+
}
275+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<h1>Closest Facility (Static)</h1>
2+
3+
<p>Find routes from several locations to the respective closest facility.</p>
4+
5+
<p><img src="ClosestFacilityStatic.png"/></p>
6+
7+
<h2>Use case</h2>
8+
9+
<p>Quickly and accurately determining the most efficient route between a location and a facility is a frequently encountered task (e.g. emergency services).</p>
10+
11+
<h2>How to use the sample</h2>
12+
13+
<p>Click the 'Solve Routes' button to determine and display the route from each incident (fire) to the nearest facility (fire station). Click the reset button to remove the views.</p>
14+
15+
<h2>How it works</h2>
16+
17+
<p>To display a <code>ClosestFacilityRoute</code> between several incidents and facilities:</p>
18+
19+
<ol>
20+
<li>Create a <code>ClosestFacilityTask</code> using a Url from an online service.</li>
21+
<li>Get the default set of <code>ClosestFacilityParameters</code> from the task: <code>closestFacilityTask.createDefaultParametersAsync().get()</code>.</li>
22+
<li>Build a list of all Facilities and Incidents:
23+
<ul>
24+
<li>Create a <code>FeatureTable</code> using <code>ServiceFeatureTable(Uri)</code>.</li>
25+
<li>Query the <code>FeatureTable</code> for all <code>Feature</code>s using <code>.queryFeaturesAsync(queryParameters)</code>.</li>
26+
<li>Iterate over the result and add each <code>Feature</code> to a <code>List</code>, instantiating the feature as a <code>Facility</code> or <code>Incident</code>.</li>
27+
</ul>
28+
</li>
29+
<li>Add a list of all facilities to the task parameters: <code>closestFacilityParameters.setFacilities(facilitiesList)</code>.</li>
30+
<li>Add a list of all incidents to the task parameters: <code>closestFacilityParameters.setIncidents(incidentsList)</code>.</li>
31+
<li>Get <code>ClosestFacilityResult</code> from solving the task with the provided parameters: <code>closestFacilityTask.solveClosestFacilityAsync(closestFacilityParameters)</code>.</li>
32+
<li>Find the closest facility for each incident by iterating over the previously created <code>incidentsList</code>:
33+
<ul>
34+
<li>Get index list of closet facilities to the incident, <code>closestFacilityResult.getRankedFacilityIndexes(indexOfIncident).get(0)</code>.</li>
35+
<li>Find closest facility route, <code>closestFacilityResult.getRoute(closestFacilityIndex, indexOfIncident)</code>.</li>
36+
</ul>
37+
</li>
38+
<li>Display the route:
39+
<ul>
40+
<li>create a <code>Graphic</code> from route geometry, with <code>new Graphic(closestFacilityRoute.getRouteGeometry())</code>.</li>
41+
<li>add graphic to <code>GraphicsOverlay</code> and set it to the mapview.</li>
42+
</ul>
43+
</li>
44+
</ol>
45+
46+
<h2>Relevant API</h2>
47+
48+
<ul>
49+
<li>ClosestFacilityParameters</li>
50+
<li>ClosestFacilityResult</li>
51+
<li>ClosestFacilityRoute</li>
52+
<li>ClosestFacilityTask</li>
53+
<li>Facility</li>
54+
<li>Graphic</li>
55+
<li>GraphicsOverlay</li>
56+
<li>Incident</li>
57+
</ul>
58+
59+
<h2>Tags</h2>
60+
61+
<p>facility, incident, network analysis, route, search</p>

0 commit comments

Comments
 (0)