Overview

You will learn: how to find an address or place using the ArcGIS World Geocoding Service.

With the ArcGIS Runtime SDK for Java you can easily find places and addresses all around the world. The process of matching locations on the map to an address is referred to as geocoding. Your user can specify a location of interest or street address and see it on the map.

Locating addresses is a complex topic. Refer to Search for places (geocoding) if you would like to read more about the process and options.

This lab is a bit complex because you will define a user interface to handle user interaction. This demonstrates one of many possible methods for requesting user input. The method used here is a JavaFX TextField.

Before you begin

Reuse the starter project

If you have not done the starter project lab, be sure to review it first so you are familiar with running the app.

In a new or empty folder, make a copy of the Create a starter app solution.

Steps

Update the project

  1. Open the settings.gradle file in an editor of your choice and change the rootProject.name value to search-and-geocode.

Imports and variable declarations

  1. Open the App.java file from src/main/java/com/arcgis/developers/labs in the editor. Merge the app's current imports with the following imports. We will make use of all of these in this lab.

     import java.util.Arrays;
     import java.util.List;
     import java.util.concurrent.ExecutionException;
    
     import javafx.scene.control.Alert;
     import javafx.scene.control.TextField;
     import javafx.geometry.Insets;
     import javafx.geometry.Pos;
    
     import com.esri.arcgisruntime.concurrent.ListenableFuture;
     import com.esri.arcgisruntime.mapping.view.Graphic;
     import com.esri.arcgisruntime.mapping.view.GraphicsOverlay;
     import com.esri.arcgisruntime.symbology.SimpleMarkerSymbol;
     import com.esri.arcgisruntime.symbology.TextSymbol;
     import com.esri.arcgisruntime.tasks.geocode.GeocodeParameters;
     import com.esri.arcgisruntime.tasks.geocode.GeocodeResult;
     import com.esri.arcgisruntime.tasks.geocode.LocatorTask;
    
  2. Setup fields for application.

    /* ** ADD ** */
    private TextField searchBox;
    
    private GeocodeParameters geocodeParameters;
    private LocatorTask locatorTask;
    private GraphicsOverlay graphicsOverlay;
    /* ** ADD ** */
    private MapView mapView;
    

Add graphics overlay to a map view

  1. Add a new private method setupGraphicsOverlay that creates a graphics overlay and attaches it to the map view to register the graphics to the map. This will be used later to show user the location they entered.

    private void setupGraphicsOverlay() {
      if (mapView != null) {
        graphicsOverlay = new GraphicsOverlay();
        mapView.getGraphicsOverlays().add(graphicsOverlay);
      }
    }
    
  2. Make a call to the mehtod just created.

    setupMap();
    
    /* ** ADD ** */
    setupGraphicsOverlay();
    

Prompt the user for input

  1. Add a private method setupTextField that defines a UI element to ask the user for input. In this application we use a TextField to get user input. Give it an explicit width and some prompt text to indicate it is used for searching addresses.

    private void setupTextField() {
      searchBox = new TextField();
      searchBox.setMaxWidth(400);
      searchBox.setPromptText("Search for an address");
    }
    
  2. A geocode query will need to be triggered when the user presses the Enter key in the search box, set an event handler on the TextField. For now the geocodeQuery method is commented out, that will be defined later.

    searchBox.setPromptText("Search for an address");
    /* ** ADD ** */
    searchBox.setOnAction(event -> {
      String query = searchBox.getText();
      if (!"".equals(query)) {
        //geocodeQuery(query);
      }
    });
    
  3. Make a call to the mehtod just created.

    setupGraphicsOverlay();
    /* ** ADD ** */
    setupTextField();
    
  4. Add the search box to the top of the map using the StackPane's children after the map view. To place it in the top left corner, set its alignment and margin. Run the app to check that the search box appears in the correct location and is displaying its prompt text.

    stackPane.getChildren().add(mapView);
    /* *** ADD *** */
    stackPane.getChildren().add(searchBox);
    StackPane.setAlignment(searchBox, Pos.TOP_LEFT);
    StackPane.setMargin(searchBox, new Insets(10, 0, 0, 10));
    /* *** ADD *** */
    

Search for an address

  1. Add a private method createLocatorTaskWithParameters that creates a locator task with geocode parameters.

    private void createLocatorTaskWithParameters() {
      locatorTask = new LocatorTask("http://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer");
    
      geocodeParameters = new GeocodeParameters();
      geocodeParameters.getResultAttributeNames().add("*"); // return all attributes
      geocodeParameters.setMaxResults(1); // get closest match
      geocodeParameters.setOutputSpatialReference(mapView.getSpatialReference());
    }
    
  2. Make a call to the mehtod just created.

    setupTextField();
    /* ** ADD ** */
    createLocatorTaskWithParameters();
    
  3. Add a new private method called geocodeQuery to perform the geocode operation.

    private void geocodeQuery(String query) {
      ListenableFuture<List<GeocodeResult>> geocode = locatorTask.geocodeAsync(query, geocodeParameters);
    }
    
  4. Wait for locator task to be done before fetching results.

     ListenableFuture<List<GeocodeResult>> geocode = locatorTask.geocodeAsync(query, geocodeParameters);
    
     /* ** ADD ** */
     geocode.addDoneListener(() -> {
       try {
         List<GeocodeResult> results = geocode.get();
       } catch (InterruptedException | ExecutionException e) {
         Alert alert = new Alert(Alert.AlertType.ERROR, "Error getting result.");
         alert.show();
       }
     });
    
  5. Check that at least one result has been returned. We will implement the displayResult method later.

    List<GeocodeResult> results = geocode.get();
    /* ** ADD ** */
    if (results.size() > 0) {
      GeocodeResult result = results.get(0);
      //displayResult(result);
    } else {
      Alert alert = new Alert(Alert.AlertType.INFORMATION, "No results found.");
      alert.show();
    }
    
  6. Uncomment geocodeQuery from setupTextField method.

    if (!"".equals(query)) {
      geocodeQuery(query);
    }
    

Display the result

  1. Add private method displayResult that adds a text and marker graphic at the result location. The map view is then centered on that location.

    private void displayResult(GeocodeResult geocodeResult) {
      graphicsOverlay.getGraphics().clear();
    
      String label = geocodeResult.getLabel();
      TextSymbol textSymbol = new TextSymbol(18, label, 0xFF000000, TextSymbol.HorizontalAlignment.CENTER, TextSymbol.VerticalAlignment.BOTTOM);
      Graphic textGraphic = new Graphic(geocodeResult.getDisplayLocation(), textSymbol);
      SimpleMarkerSymbol markerSymbol = new SimpleMarkerSymbol(SimpleMarkerSymbol.Style.SQUARE, 0xFFFF0000, 12.0f);
      Graphic markerGraphic = new Graphic(geocodeResult.getDisplayLocation(), geocodeResult.getAttributes(), markerSymbol);
      graphicsOverlay.getGraphics().addAll(Arrays.asList(markerGraphic, textGraphic));
    
      mapView.setViewpointCenterAsync(geocodeResult.getDisplayLocation());
    }
    

Congratulations, you're done!

Your map should load. The search box should display on the top border of the map with the prompt text Find an address. When an address is entered and the Enter key is pressed, the map should re-center on a red square with a text label displaying the geocoded address.

Challenge

Explore graphics

Try different graphics rendering for the displayResult method - change the text and graphic size and color.

Multiple results

Try setting setMaxResults to a number larger than one and show the user the possible matches. Can you come up with a UI that allows the user to select a possible match?

Geocoding parameters

Can you discover other geocoding parameters available to the locator task? For example, limit the search area to the visible extent of the map view.

Reverse geocode

The LocatorTask class has a reverseGeocodeAsync method that returns an interpolated address (string) for a location (map point). Experiment with code that uses a click on the map to perform a reverse geocode.