Offline geocoding

Download Sample Viewer

Description

This application uses an local locator to convert a location on the map to an address, and vice-versa.

Code snippet


    // create the local Locator
    Locator locator = Locator.createLocalLocator(
        "c:/data/locators/MyLocator.loc");
    LocatorFindParameters parameters = new LocatorFindParameters(addressCombo.getSelectedItem().toString());
    parameters.setMaxLocations(3);
    parameters.setOutSR(map.getSpatialReference());

    // run the locator task asynchronously
    locator.find(params, new CallbackListener<List<LocatorGeocodeResult>>()() {
        @Override
        public void onError(Throwable e) {
            // handle the error
        }
        @Override
        public void onCallback(List<LocatorGeocodeResult> results) {
            // display/use/store results
        }
    });
  

Sample Code

/* Copyright 2014 Esri

All rights reserved under the copyright laws of the United States
and applicable international laws, treaties, and conventions.

You may freely redistribute and use this sample code, with or
without modification, provided you include the original copyright
notice and use restrictions.

See the use restrictions.*/
package com.esri.client.samples.search;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.IOException;
import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;

import com.esri.client.local.ArcGISLocalTiledLayer;
import com.esri.core.geometry.Envelope;
import com.esri.core.geometry.GeometryEngine;
import com.esri.core.geometry.Point;
import com.esri.core.geometry.SpatialReference;
import com.esri.core.map.Graphic;
import com.esri.core.symbol.MarkerSymbol;
import com.esri.core.symbol.PictureMarkerSymbol;
import com.esri.core.symbol.SimpleMarkerSymbol;
import com.esri.core.symbol.SimpleMarkerSymbol.Style;
import com.esri.core.symbol.Symbol;
import com.esri.core.tasks.geocode.Locator;
import com.esri.core.tasks.geocode.LocatorFindParameters;
import com.esri.core.tasks.geocode.LocatorGeocodeResult;
import com.esri.core.tasks.geocode.LocatorReverseGeocodeResult;
import com.esri.map.GraphicsLayer;
import com.esri.map.JMap;
import com.esri.map.LayerInitializeCompleteEvent;
import com.esri.map.LayerInitializeCompleteListener;
import com.esri.map.LayerList;
import com.esri.map.MapOverlay;
import com.esri.runtime.ArcGISRuntime;

/***
 * This application shows how to perform geocoding and reverse geocoding using 
 * a locator created with local data (.loc file on disk). The geocoding takes 
 * a single-line address or place name and returns a coordinate point result 
 * which can be added to the map as a graphic.
 * <p>
 * To use the sample, choose a single-line address from the combo box or enter 
 * text in this editable combo box, then click the button to see the top result 
 * displayed on the map as a pushpin graphic. Click anywhere on the map to see
 * the lat-lon coordinates and the address found by reverse geocoding the point, 
 * displayed in a popup.
 */
public class LocalLocatorApp {

  private JComponent contentPane;
  private JMap map;
  private JButton findButton;
  private JButton clearGraphicsButton;
  private GraphicsLayer addressGraphics;
  private static Locator locator;
  private MarkerSymbol symPoint;

  // UI settings
  private static final Color BG_COLOR = new Color(0, 0, 0, 150);
  private static final Color DARK_GRAY = new Color(50, 50, 50);
  private static final Color TRANS_COLOR = new Color(0, 0, 0, 0);
  // formatting
  private static final DecimalFormat df = new DecimalFormat("#.####");
  private static final String FSP = System.getProperty("file.separator");
  
  static {
    initLocator();
  }

  // ------------------------------------------------------------------------
  // Core functionality
  // ------------------------------------------------------------------------
  /**
   * Creates the local locator from .loc file on disk.
   */
  private static void initLocator() {
    locator = Locator.createLocalLocator(getPathSampleData() +
        "disconnected" + FSP + "locators" + FSP + "SanFrancisco" + FSP + "SanFranciscoLocator.loc");
  }
  /**
   * Performs geocoding: finding a location (x,y coordinate) for a given address.
   *
   * @param address text to find
   * @return the highest scoring result
   */
  private LocatorGeocodeResult geocode(String text) {

    LocatorFindParameters findParams = new LocatorFindParameters(text);
    findParams.setOutSR(map.getSpatialReference());
    findParams.setMaxLocations(1); // we just keep one result to display to user
    findParams.setLocation(map.getExtent().getCenter(), map.getSpatialReference());
    findParams.setDistance(2000);

    LocatorGeocodeResult highestScoreResult = null;
    try {
      List<LocatorGeocodeResult> results = locator.find(findParams);
      if (results != null && results.size() > 0)
        highestScoreResult = results.get(0);
      else {
        JOptionPane.showMessageDialog(map.getParent(), "No results found!");
      }
    } catch (Exception ex) {
      JOptionPane.showMessageDialog(map.getParent(), wrap(ex.getMessage()));
      ex.printStackTrace();
    }
    return highestScoreResult;
  }

  /**
   * Display the geocoding result location
   *
   * @param result location
   */
  private void displayGeocodeResult(LocatorGeocodeResult result) {

    if (result != null) {
      // create and populate attribute map
      Map<String, Object> attributes = new HashMap<>();
      for (Entry<String, String> entry : result.getAttributes().entrySet()) {
        attributes.put(entry.getKey(), entry.getValue());
      }

      // create a graphic at this location
      Graphic addressGraphic = new Graphic(result.getLocation(), symPoint, attributes);
      addressGraphics.addGraphic(addressGraphic);

      // centre the map at this location
      Envelope extent = map.getExtent();
      extent.centerAt(result.getLocation());
      map.zoomTo(extent);
    }
  }

  /**
   * Performs reverse geocoding using a point location (x, y coordinate).
   * 
   * @param location the point location to reverse geocode
   * @return the address string found
   */
  private String reverseGeocode(Point location) {
    Point wgs84location = (Point) GeometryEngine.project(location, map.getSpatialReference(),
        SpatialReference.create(4326));

    // Init the results
    String address = "There are no addresses here!";
    LocatorReverseGeocodeResult result = null;

    try {
      result = locator.reverseGeocode(wgs84location, 50);

      if (result != null) {
        Map<String, String> addressFields = result.getAddressFields();
        address = addressFields.values().toString();
      }
    } catch (Exception e) {
      // Code here gets executed when an address is not found.
    }

    return address;
  }

  // ------------------------------------------------------------------------
  // Static methods
  // ------------------------------------------------------------------------
  /**
   * Starting point of this application.
   * 
   * @param args arguments to this application.
   */
  public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
      @Override
      public void run() {
        try {
          // instance of this application
          LocalLocatorApp application = new LocalLocatorApp();

          // create the UI, including the map, for the application.
          JFrame appWindow = application.createWindow();
          appWindow.add(application.createUI());
          appWindow.setVisible(true);
        } catch (Exception e) {
          e.printStackTrace();
        }
      }
    });
  }

  // ------------------------------------------------------------------------
  // Public methods
  // ------------------------------------------------------------------------
  /**
   * Default constructor
   */
  public LocalLocatorApp() {}

  /**
   * Creates and displays the UI for this application.
   */
  public JComponent createUI() {
    contentPane = createContentPane();
    // address input panel
    JPanel addressPanel = createGeocodePanel();
    contentPane.add(addressPanel);
    // map
    map = createMap();
    contentPane.add(map);
    return contentPane;
  }

  // ------------------------------------------------------------------------
  // Private methods
  // ------------------------------------------------------------------------
  /**
   * Creates the map, a graphics layer to show the locations found, 
   * and adds a custom overlay to perform reverse geocoding on mouse clicks.
   * 
   * @return a map.
   */
  private JMap createMap() {

    final JMap jMap = new JMap();

    // add local tiled layer to the map
    final ArcGISLocalTiledLayer sanFranciscoLayer =
        new ArcGISLocalTiledLayer(getPathSampleData() + "tpks" + FSP + "SanFrancisco.tpk");

    LayerList layers = jMap.getLayers();
    layers.add(sanFranciscoLayer);
    
    // display error message if failed to start
    sanFranciscoLayer
        .addLayerInitializeCompleteListener(new LayerInitializeCompleteListener() {

          @Override
          public void layerInitializeComplete(LayerInitializeCompleteEvent e) {
            if (e.getID() == LayerInitializeCompleteEvent.LOCALLAYERCREATE_ERROR) {
              String errMsg = "Failed to initialize due to "
                  + sanFranciscoLayer.getInitializationError();
              JOptionPane.showMessageDialog(jMap, wrap(errMsg), "",
                  JOptionPane.ERROR_MESSAGE);
            }

          }
        });

    jMap.setExtent(new Envelope(-122.520, 37.8365, -122.3023 , 37.6985));

    symPoint = createGeocodeSymbol();
    
    addressGraphics = new GraphicsLayer();
    layers.add(addressGraphics);

    jMap.addMapOverlay(new ReverseGeocodeOverlay(addressGraphics, symPoint));
    return jMap;
  }
  
  /**
   * Creates the panel to display addresses and capture user input
   */
  private JPanel createGeocodePanel() {
    JPanel addressPanel = new JPanel();
    addressPanel.setBounds(10, 10, 500, 90);
    addressPanel.setLayout(new BorderLayout());
    addressPanel.setBackground(TRANS_COLOR);

    JPanel panel = new JPanel();
    panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
    panel.setBackground(BG_COLOR);
    panel.setBorder(BorderFactory.createEmptyBorder(5, 10, 5, 10));
    JLabel topText = new JLabel("Enter or choose an address to geocode; " +
        "click on the map to reverse-geocode:");
    topText.setForeground(Color.WHITE);
    panel.add(topText);

    // editable combo box for address input
    String[] addresses = { "1455 Market St, San Francisco, CA 94103", 
        "2011 Mission St, San Francisco CA 94110",
        "820 Bryant St, San Francisco CA 94103"};
    final JComboBox<String> addressInput = new JComboBox<>(addresses);
    addressInput.setAlignmentX(Component.LEFT_ALIGNMENT);
    addressInput.setEditable(true);
    addressInput.setSelectedIndex(0);
    panel.add(Box.createRigidArea(new Dimension(0, 5)));
    panel.add(addressInput);
    panel.add(Box.createRigidArea(new Dimension(0, 5)));

    // button panel
    JPanel buttonPanel = new JPanel();
    buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS));

    findButton = new JButton("Locate address on map");
    findButton.addActionListener(new ActionListener() {

      @Override
      public void actionPerformed(ActionEvent e) {
        String addressToGeocode = (String) addressInput.getSelectedItem();
        LocatorGeocodeResult result = geocode(addressToGeocode);
        displayGeocodeResult(result);
      }
    });

    clearGraphicsButton = new JButton("Clear address graphics");
    clearGraphicsButton.addActionListener(new ActionListener() {

      @Override
      public void actionPerformed(ActionEvent arg0) {
        addressGraphics.removeAll();
      }
    });

    buttonPanel.add(findButton);
    buttonPanel.add(Box.createRigidArea(new Dimension(5, 0)));
    buttonPanel.add(clearGraphicsButton);
    buttonPanel.setBackground(TRANS_COLOR);
    buttonPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
    panel.add(buttonPanel);
    addressPanel.add(panel, BorderLayout.CENTER);

    return addressPanel;
  }

  /**
   * Map overlay to perform reverse geocoding when the user clicks 
   * on the map, using the clicked point.
   */
  private class ReverseGeocodeOverlay extends MapOverlay {
    
    private static final long serialVersionUID = 1L;
    GraphicsLayer gLayer;
    Symbol symbol;
    JPopupMenu popup = new JPopupMenu();
    private JLabel addressLabel;
    private JLabel latLonLabel;

    ReverseGeocodeOverlay(GraphicsLayer graphicsLayer, Symbol symbol) {
      this.gLayer = graphicsLayer;
      this.symbol = symbol;
      initPopup();
    }

    private void initPopup() {
      LineBorder outsideBorder = new LineBorder(Color.LIGHT_GRAY, 2);
      EmptyBorder insideBorder = new EmptyBorder(5, 10, 5, 10);
      popup.setBorder(BorderFactory.createCompoundBorder(outsideBorder, insideBorder));
      popup.setBackground(DARK_GRAY);
      latLonLabel = new JLabel("lat lon label"); // dummy text
      latLonLabel.setForeground(Color.WHITE);
      addressLabel = new JLabel("address label"); // dummy text
      addressLabel.setForeground(Color.WHITE);
      popup.add(latLonLabel);
      popup.add(addressLabel);
    }

    @Override
    public void onMouseClicked(MouseEvent event) {
      super.onMouseClicked(event);
      // get clicked point, convert to map point
      Point location = getMap().toMapPoint(event.getX(), event.getY());
      // reverse geocode the clicked point
      String address = reverseGeocode(location);

      Point wgs84location = (Point) GeometryEngine.project(
          location, map.getSpatialReference(), SpatialReference.create(4326));
      latLonLabel.setText("Coordinates: [" + 
          df.format(wgs84location.getY()) + ", " + df.format(wgs84location.getX()) + "]");
      addressLabel.setText("Address: "+ address);
      // display coordinates and address in a popup
      popup.show(event.getComponent(),
          event.getX(), event.getY());

      if (event.getClickCount() == 1) {
        // show point graphic on map
        Graphic pointGraphic = new Graphic(location, symbol);
        gLayer.addGraphic(pointGraphic);
      }
    }
  }

  /**
   * Creates a picture marker symbol.
   * 
   * @return a picture marker symbol.
   */
  private MarkerSymbol createGeocodeSymbol() {
    try {
      symPoint = new PictureMarkerSymbol(
          ImageIO.read(LocalLocatorApp.class.getResource("resources/RedShinyPin.png")));
      ((PictureMarkerSymbol)symPoint).setSize(40, 40);
      ((PictureMarkerSymbol)symPoint).setOffsetY(20.0f);
    } catch (IOException e) {
      e.printStackTrace();
      symPoint = new SimpleMarkerSymbol(Color.RED, 18, Style.X);
    }
    return symPoint;
  }

  /**
   * Creates a window.
   * 
   * @return a window.
   */
  private JFrame createWindow() {
    JFrame window = new JFrame("Local Locator Application");
    window.setBounds(100, 100, 1000, 700);
    window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    window.getContentPane().setLayout(new BorderLayout(0, 0));
    window.addWindowListener(new WindowAdapter() {
      @Override
      public void windowClosing(WindowEvent windowEvent) {
        super.windowClosing(windowEvent);
        if (map != null) map.dispose();
        if (locator != null) locator.dispose();
      }
    });
    return window;
  }

  /**
   * Creates a content pane.
   * 
   * @return a content pane.
   */
  private static JLayeredPane createContentPane() {
    JLayeredPane contentPane = new JLayeredPane();
    contentPane.setBounds(100, 100, 1000, 700);
    contentPane.setLayout(new BorderLayout());
    contentPane.setVisible(true);
    return contentPane;
  }

  private static String getPathSampleData() {
    String dataPath = null;
    String javaPath = ArcGISRuntime.getInstallDirectory();
    if (javaPath != null) {
      if (!(javaPath.endsWith("/") || javaPath.endsWith("\\"))){
        javaPath += FSP;
      }
      dataPath = javaPath + "sdk" + FSP + "samples" + FSP + "data" + FSP;
    }
    File dataFile = new File(dataPath);
    if (!dataFile.exists()) { 
      dataPath = ".." + FSP + "data" + FSP;
    }
    return dataPath;
  }
  
  private String wrap(String str) {
    // create a HTML string that wraps text when longer
    return "<html><p style='width:200px;'>" + str + "</html>";
  }
}
Feedback on this topic?