diff --git a/src/main/java/com/esri/samples/na/closest_facility_static/ClosestFacilityStatic.png b/src/main/java/com/esri/samples/na/closest_facility_static/ClosestFacilityStatic.png new file mode 100644 index 0000000000..0309afc3e0 Binary files /dev/null and b/src/main/java/com/esri/samples/na/closest_facility_static/ClosestFacilityStatic.png differ diff --git a/src/main/java/com/esri/samples/na/closest_facility_static/ClosestFacilityStaticSample.java b/src/main/java/com/esri/samples/na/closest_facility_static/ClosestFacilityStaticSample.java new file mode 100644 index 0000000000..94c3fd4f97 --- /dev/null +++ b/src/main/java/com/esri/samples/na/closest_facility_static/ClosestFacilityStaticSample.java @@ -0,0 +1,275 @@ +/* + * 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.closest_facility_static; + +import java.util.ArrayList; +import java.util.Arrays; +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.image.Image; +import javafx.scene.layout.StackPane; +import javafx.stage.Stage; + +import com.esri.arcgisruntime.concurrent.ListenableFuture; +import com.esri.arcgisruntime.data.Feature; +import com.esri.arcgisruntime.data.FeatureQueryResult; +import com.esri.arcgisruntime.data.FeatureTable; +import com.esri.arcgisruntime.data.QueryParameters; +import com.esri.arcgisruntime.data.ServiceFeatureTable; +import com.esri.arcgisruntime.geometry.Envelope; +import com.esri.arcgisruntime.geometry.GeometryEngine; +import com.esri.arcgisruntime.geometry.Point; +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.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.SimpleLineSymbol; +import com.esri.arcgisruntime.symbology.SimpleRenderer; +import com.esri.arcgisruntime.tasks.networkanalysis.ClosestFacilityParameters; +import com.esri.arcgisruntime.tasks.networkanalysis.ClosestFacilityResult; +import com.esri.arcgisruntime.tasks.networkanalysis.ClosestFacilityRoute; +import com.esri.arcgisruntime.tasks.networkanalysis.ClosestFacilityTask; +import com.esri.arcgisruntime.tasks.networkanalysis.Facility; +import com.esri.arcgisruntime.tasks.networkanalysis.Incident; + +public class ClosestFacilityStaticSample extends Application { + + private MapView mapView; + + @Override + public void start(Stage stage) throws Exception { + + try { + // create stack pane and application scene + StackPane stackPane = new StackPane(); + Scene scene = new Scene(stackPane); + + // set title, size, and add scene to stage + stage.setTitle("Closest Facility (Static) Sample"); + stage.setWidth(800); + stage.setHeight(700); + stage.setScene(scene); + stage.show(); + + // create buttons + Button solveRoutesButton = new Button("Solve Routes"); + solveRoutesButton.setMaxWidth(150); + solveRoutesButton.setDisable(true); + + // create a progress indicator + ProgressIndicator progressIndicator = new ProgressIndicator(); + progressIndicator.setVisible(true); + + // create a ArcGISMap with a Basemap instance with an Imagery base layer + ArcGISMap map = new ArcGISMap(Basemap.createStreetsWithReliefVector()); + + // set the map to be displayed in this view + mapView = new MapView(); + mapView.setMap(map); + + // create a graphics overlay and add it to the map + GraphicsOverlay graphicsOverlay = new GraphicsOverlay(); + mapView.getGraphicsOverlays().add(graphicsOverlay); + + // create Symbols for displaying facilities + PictureMarkerSymbol facilitySymbol = new PictureMarkerSymbol(new Image("https://static.arcgis.com/images/Symbols/SafetyHealth/FireStation.png", 30, 30, true, false)); + PictureMarkerSymbol incidentSymbol = new PictureMarkerSymbol(new Image("https://static.arcgis.com/images/Symbols/SafetyHealth/esriCrimeMarker_56_Gradient.png", 30, 30, true, false)); + + // create a line symbol to mark the route + SimpleLineSymbol simpleLineSymbol = new SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, 0x4D0000FF, 5.0f); + + // create a closest facility task from a network analysis service + ClosestFacilityTask closestFacilityTask = new ClosestFacilityTask("https://sampleserver6.arcgisonline.com/arcgis/rest/services/NetworkAnalysis/SanDiego/NAServer/ClosestFacility"); + + // create a table for facilities using the feature service + FeatureTable facilitiesFeatureTable = new ServiceFeatureTable("https://services2.arcgis.com/ZQgQTuoyBrtmoGdP/ArcGIS/rest/services/San_Diego_Facilities/FeatureServer/0"); + // create a feature layer from the table, apply facilities icon + FeatureLayer facilitiesFeatureLayer = new FeatureLayer(facilitiesFeatureTable); + facilitiesFeatureLayer.setRenderer(new SimpleRenderer(facilitySymbol)); + + // create a table for incidents using the feature service + FeatureTable incidentsFeatureTable = new ServiceFeatureTable("https://services2.arcgis.com/ZQgQTuoyBrtmoGdP/ArcGIS/rest/services/San_Diego_Incidents/FeatureServer/0"); + // create a feature layer from the table, apply incident icon + FeatureLayer incidentsFeatureLayer = new FeatureLayer(incidentsFeatureTable); + incidentsFeatureLayer.setRenderer(new SimpleRenderer(incidentSymbol)); + + // add the layers to the map + map.getOperationalLayers().addAll(Arrays.asList(facilitiesFeatureLayer, incidentsFeatureLayer)); + + // create the list to store the facilities + ArrayList facilities = new ArrayList<>(); + + // create the list to store the incidents + ArrayList incidents = new ArrayList<>(); + + // wait for the feature layers to load to retrieve the facilities and incidents + facilitiesFeatureLayer.addDoneLoadingListener(() -> incidentsFeatureLayer.addDoneLoadingListener(() -> { + if (facilitiesFeatureLayer.getLoadStatus() == LoadStatus.LOADED && incidentsFeatureLayer.getLoadStatus() == LoadStatus.LOADED) { + + // hide the progress indicator + progressIndicator.setVisible(false); + + // zoom to the extent of the combined feature layers + Envelope fullFeatureLayerExtent = GeometryEngine.combineExtents(facilitiesFeatureLayer.getFullExtent(), incidentsFeatureLayer.getFullExtent()); + mapView.setViewpointGeometryAsync(fullFeatureLayerExtent, 90); + + // create query parameters to select all features + QueryParameters queryParameters = new QueryParameters(); + queryParameters.setWhereClause("1=1"); + + // retrieve a list of all facilities + ListenableFuture result = facilitiesFeatureTable.queryFeaturesAsync(queryParameters); + result.addDoneListener(() -> { + try { + FeatureQueryResult facilitiesResult = result.get(); + + // add the found facilities to the list + for (Feature facilityFeature : facilitiesResult) { + // since we know our feature layer only contains point features, we can cast them as Point in order to create a Facility + facilities.add(new Facility((Point) facilityFeature.getGeometry())); + } + + } catch (InterruptedException | ExecutionException e) { + new Alert(Alert.AlertType.ERROR, "Error retrieving list of facilities.").show(); + } + }); + + // retrieve a list of all incidents + ListenableFuture incidentsQueryResult = incidentsFeatureTable.queryFeaturesAsync(queryParameters); + incidentsQueryResult.addDoneListener(() -> { + try { + FeatureQueryResult incidentsResult = incidentsQueryResult.get(); + + // add the found incidents to the list + for (Feature incidentFeature : incidentsResult) { + // since we know our feature layer only contains point features, we can cast them as Point in order to create an Incident + incidents.add(new Incident((Point) incidentFeature.getGeometry())); + } + + } catch (InterruptedException | ExecutionException e) { + new Alert(Alert.AlertType.ERROR, "Error retrieving list of incidents.").show(); + } + }); + + // enable the 'solve routes' button + solveRoutesButton.setDisable(false); + + // resolve button press + solveRoutesButton.setOnAction(e -> { + + // disable the 'solve routes' button and show the progress indicator + solveRoutesButton.setDisable(true); + progressIndicator.setVisible(true); + + // start the routing task + closestFacilityTask.loadAsync(); + closestFacilityTask.addDoneLoadingListener(() -> { + if (closestFacilityTask.getLoadStatus() == LoadStatus.LOADED) { + try { + // create default parameters for the task and add facilities and incidents to parameters + ClosestFacilityParameters closestFacilityParameters = closestFacilityTask.createDefaultParametersAsync().get(); + closestFacilityParameters.setFacilities(facilities); + closestFacilityParameters.setIncidents(incidents); + + // solve closest facilities + try { + // use the task to solve for the closest facility + ListenableFuture closestFacilityTaskResult = closestFacilityTask.solveClosestFacilityAsync(closestFacilityParameters); + closestFacilityTaskResult.addDoneListener(() -> { + try { + ClosestFacilityResult closestFacilityResult = closestFacilityTaskResult.get(); + + // find the closest facility for each incident + for (int incidentIndex = 0; incidentIndex < incidents.size(); incidentIndex++) { + + // get the index of the closest facility to incident + Integer closestFacilityIndex = closestFacilityResult.getRankedFacilityIndexes(incidentIndex).get(0); + + // get the route to the closest facility + ClosestFacilityRoute closestFacilityRoute = closestFacilityResult.getRoute(closestFacilityIndex, incidentIndex); + + // display the route on the graphics overlay + graphicsOverlay.getGraphics().add(new Graphic(closestFacilityRoute.getRouteGeometry(), simpleLineSymbol)); + + // hide the progress indicator and enable the reset button + progressIndicator.setVisible(false); + } + + } catch (ExecutionException | InterruptedException ex) { + new Alert(Alert.AlertType.ERROR, "Error getting the closest facility task result.").show(); + } + }); + + } catch (Exception ex) { + new Alert(Alert.AlertType.ERROR, "Error solving the closest facility task.").show(); + } + + } catch (InterruptedException | ExecutionException ex) { + new Alert(Alert.AlertType.ERROR, "Error getting default route parameters.").show(); + } + + } else { + new Alert(Alert.AlertType.ERROR, "Error loading route task.").show(); + } + }); + }); + } + }) + ); + + // add the map view, control panel and progress indicator to the stack pane + stackPane.getChildren().addAll(mapView, solveRoutesButton, progressIndicator); + StackPane.setAlignment(solveRoutesButton, Pos.TOP_LEFT); + StackPane.setMargin(solveRoutesButton, new Insets(10, 0, 0, 10)); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * 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); + } +} diff --git a/src/main/java/com/esri/samples/na/closest_facility_static/README.md b/src/main/java/com/esri/samples/na/closest_facility_static/README.md new file mode 100644 index 0000000000..36468d135e --- /dev/null +++ b/src/main/java/com/esri/samples/na/closest_facility_static/README.md @@ -0,0 +1,61 @@ +

Closest Facility (Static)

+ +

Find routes from several locations to the respective closest facility.

+ +

+ +

Use case

+ +

Quickly and accurately determining the most efficient route between a location and a facility is a frequently encountered task (e.g. emergency services).

+ +

How to use the sample

+ +

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.

+ +

How it works

+ +

To display a ClosestFacilityRoute between several incidents and facilities:

+ +
    +
  1. Create a ClosestFacilityTask using a Url from an online service.
  2. +
  3. Get the default set of ClosestFacilityParameters from the task: closestFacilityTask.createDefaultParametersAsync().get().
  4. +
  5. Build a list of all Facilities and Incidents: +
      +
    • Create a FeatureTable using ServiceFeatureTable(Uri).
    • +
    • Query the FeatureTable for all Features using .queryFeaturesAsync(queryParameters).
    • +
    • Iterate over the result and add each Feature to a List, instantiating the feature as a Facility or Incident.
    • +
    +
  6. +
  7. Add a list of all facilities to the task parameters: closestFacilityParameters.setFacilities(facilitiesList).
  8. +
  9. Add a list of all incidents to the task parameters: closestFacilityParameters.setIncidents(incidentsList).
  10. +
  11. Get ClosestFacilityResult from solving the task with the provided parameters: closestFacilityTask.solveClosestFacilityAsync(closestFacilityParameters).
  12. +
  13. Find the closest facility for each incident by iterating over the previously created incidentsList: +
      +
    • Get index list of closet facilities to the incident, closestFacilityResult.getRankedFacilityIndexes(indexOfIncident).get(0).
    • +
    • Find closest facility route, closestFacilityResult.getRoute(closestFacilityIndex, indexOfIncident).
    • +
    +
  14. +
  15. Display the route: +
      +
    • create a Graphic from route geometry, with new Graphic(closestFacilityRoute.getRouteGeometry()).
    • +
    • add graphic to GraphicsOverlay and set it to the mapview.
    • +
    +
  16. +
+ +

Relevant API

+ +
    +
  • ClosestFacilityParameters
  • +
  • ClosestFacilityResult
  • +
  • ClosestFacilityRoute
  • +
  • ClosestFacilityTask
  • +
  • Facility
  • +
  • Graphic
  • +
  • GraphicsOverlay
  • +
  • Incident
  • +
+ +

Tags

+ +

facility, incident, network analysis, route, search

diff --git a/src/main/java/com/esri/samples/scene/terrain_exaggeration/TerrainExaggerationController.java b/src/main/java/com/esri/samples/scene/terrain_exaggeration/TerrainExaggerationController.java index 64e03fb2cc..a24f745398 100644 --- a/src/main/java/com/esri/samples/scene/terrain_exaggeration/TerrainExaggerationController.java +++ b/src/main/java/com/esri/samples/scene/terrain_exaggeration/TerrainExaggerationController.java @@ -1,12 +1,12 @@ /* * Copyright 2017 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 @@ -31,6 +31,7 @@ public class TerrainExaggerationController { @FXML private SceneView sceneView; @FXML private Slider exaggerationSlider; + private Surface surface; public void initialize() { @@ -44,18 +45,12 @@ public void initialize() { sceneView.setArcGISScene(scene); // add base surface for elevation data - Surface surface = new Surface(); + surface = new Surface(); final String elevationImageService = - "http://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer"; + "http://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer"; surface.getElevationSources().add(new ArcGISTiledElevationSource(elevationImageService)); scene.setBaseSurface(surface); - // set exaggeration of surface to the value the user selected - exaggerationSlider.valueChangingProperty().addListener(o -> { - if (!exaggerationSlider.isValueChanging()) { - surface.setElevationExaggeration((float) exaggerationSlider.getValue()); - } - }); // add a camera and initial camera position Point initialLocation = new Point(-119.94891542688772, 46.75792111605992, 0, sceneView.getSpatialReference()); Camera camera = new Camera(initialLocation, 15000.0, 40.0, 60.0, 0.0); @@ -66,6 +61,14 @@ public void initialize() { e.printStackTrace(); } } + + /** + * Sets the elevation exaggeration to the float chosen via the JavaFX slider. + */ + @FXML + private void changeElevationExaggeration() { + surface.setElevationExaggeration((float) exaggerationSlider.getValue()); + } /** * Disposes application resources. diff --git a/src/main/resources/fxml/terrain_exaggeration.fxml b/src/main/resources/fxml/terrain_exaggeration.fxml index 3f60e2373a..578c7d3b7d 100644 --- a/src/main/resources/fxml/terrain_exaggeration.fxml +++ b/src/main/resources/fxml/terrain_exaggeration.fxml @@ -48,6 +48,6 @@