Nearest vertices

Download Sample Viewer

Description

The purpose of this application is to find all vertices in the given distance between two geometries (a polyline and a point) and to measure the shortest distance between the two geometries. To use the application, click on the polyline toolbar button, draw a polyline on the map using mouse clicks, then click on the point toolbar button and draw a point. When you click 'Solve', the polyline's vertices will be sorted from the nearest to the farthest, and the shortest distance from the point to the polyline will be shown using a magenta line, with the distance displayed in a text field. To finds all vertices in the given distance sorted from the closest to the farthest, use GeometryEngine.getNearestVertices(). A maximum number of vertices to return and the search radius can be specified. The shortest distance between two geometries can be measured using: 1. GeometryEngine.distance() - this returns the distance in the map units 2. To measure the length in geodesic units, such as miles, useGeometryEngine.getNearestCoordinate(). That returns the nearest coordinate from the polyline to the point. A line can then be drawn from the nearest coordinate to the point and the GeometryEngine.geodesicLength() can be used to measure the length in miles.

Code snippet


    // polyline geometry
    Geometry polyline = graphicsLayer.getGraphic(polylineId).getGeometry();
    // point geometry
    Point point = (Point)graphicsLayer.getGraphic(pointId).getGeometry();

    // gets the nearest vertices between the polyline geometry and the point
    rNearestVertices = GeometryEngine.getNearestVertices(
        polyline, point, maxRadius, maxVertices);

    // use GeometryEngine.getNearestCoordinate to get vertices sorted from the closest to the farthest
    Proximity2DResult rNearestPointOnPolyline = GeometryEngine.getNearestCoordinate(
        polyline, point, false);
    nearestPointOnPolyline = rNearestPointOnPolyline.getCoordinate();
  

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.geometry;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.text.DecimalFormat;

import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.JToolBar;
import javax.swing.SwingUtilities;
import javax.swing.border.LineBorder;

import com.esri.core.geometry.Envelope;
import com.esri.core.geometry.Geometry;
import com.esri.core.geometry.GeometryEngine;
import com.esri.core.geometry.LinearUnit;
import com.esri.core.geometry.Point;
import com.esri.core.geometry.Polyline;
import com.esri.core.geometry.Unit;

import com.esri.core.geometry.Proximity2DResult;
import com.esri.core.map.Graphic;
import com.esri.core.symbol.CompositeSymbol;
import com.esri.core.symbol.SimpleLineSymbol;
import com.esri.core.symbol.SimpleMarkerSymbol;
import com.esri.core.symbol.TextSymbol;
import com.esri.core.symbol.SimpleMarkerSymbol.Style;
import com.esri.map.ArcGISTiledMapServiceLayer;
import com.esri.map.GraphicsLayer;
import com.esri.map.JMap;

import com.esri.toolkit.overlays.DrawingCompleteEvent;
import com.esri.toolkit.overlays.DrawingOverlay;
import com.esri.toolkit.overlays.DrawingCompleteListener;
import com.esri.toolkit.overlays.DrawingOverlay.DrawingMode;

/**
 * The purpose of this application is to find all vertices in the given distance between
 * two geometries (a polyline and a point) and to measure the shortest distance between the 
 * two geometries.
 * <p>
 * To use the application, click on the polyline toolbar button, draw a polyline on the 
 * map using mouse clicks, then click on the point toolbar button and draw a point. 
 * When you click 'Solve', the polyline's vertices will be sorted from the nearest to 
 * the farthest, and the shortest distance from the point to the polyline will be shown 
 * using a magenta line, with the distance displayed in a text field.
 * <p>
 * To finds all vertices in the given distance sorted from the closest to the farthest, 
 * use GeometryEngine.getNearestVertices(). A maximum number of vertices to return and 
 * the search radius can be specified.
 * <p>
 * The shortest distance between two geometries can be measured using: <br />
 * 1. GeometryEngine.distance() - this returns the distance in the map units <br />
 * 2. To measure the length in geodesic units, such as miles, use
 * GeometryEngine.getNearestCoordinate(). That returns the nearest coordinate from the 
 * polyline to the point. A line can then be drawn from the nearest coordinate to the 
 * point and the GeometryEngine.geodesicLength() can be used to measure the length 
 * in miles.
 */
public class NearestVertices {

  // symbology
  private final static SimpleLineSymbol SYM_LINE   = new SimpleLineSymbol(Color.RED, 2);
  private final static SimpleMarkerSymbol SYM_POINT_BLUE = new SimpleMarkerSymbol(new Color(0, 0, 200, 200), 20, Style.CIRCLE);
  private final static SimpleMarkerSymbol SYM_POINT_GREEN = new SimpleMarkerSymbol(new Color(0, 200, 0), 20, Style.CIRCLE);

  // resources
  private final String URL_USA_TOPO = 
      "http://services.arcgisonline.com/ArcGIS/rest/services/ESRI_StreetMap_World_2D/MapServer";

  // map and layers
  private JMap map;
  private GraphicsLayer graphicsLayer;

  // UI components
  private JButton polylineButton;
  private JTextField resultBox;
  private JTextField txtRadius;
  private JTextField txtVertices;

  // to store graphics IDs
  private int polylineId;
  private int pointId;

  // result format
  private final static DecimalFormat resultFormat = new DecimalFormat("##.##");

  // Constructor
  public NearestVertices() { }
  
  // ------------------------------------------------------------------------
  // Core functionality
  // ------------------------------------------------------------------------
  /**
   * Calculates and shows the nearest vertices between the point and polyline, in 
   * the given distance around the point, sorted from the closest to the farthest. 
   * Also calculates and shows the shortest distance between the two geometries.
   */
  private void getVerticesAndDistance() {
    Proximity2DResult[] rNearestVertices = null;
    Point nearestPointOnPolyline = null;

    try {
      double shortestDistance = 0;
      int maxRadius = Integer.parseInt(txtRadius.getText().trim());
      int maxVertices = Integer.parseInt(txtVertices.getText().trim());
      // polyline geometry
      Geometry polyline = graphicsLayer.getGraphic(polylineId).getGeometry();
      // point geometry
      Point point = (Point)graphicsLayer.getGraphic(pointId).getGeometry();

      // gets the nearest vertices between the polyline geometry and the point
      rNearestVertices = GeometryEngine.getNearestVertices(
          polyline, point, maxRadius, maxVertices);

      // use GeometryEngine.getNearestCoordinate to get vertices sorted from the closest to the farthest.
      Proximity2DResult rNearestPointOnPolyline = GeometryEngine.getNearestCoordinate(
          polyline, point, false);
      nearestPointOnPolyline = rNearestPointOnPolyline.getCoordinate();

      // use GeometryEngine.geodesicLength() to get distance in linear unit.
      shortestDistance = getGeodesicDistance(point, nearestPointOnPolyline);
      resultBox.setText(
          "Shortest distance: " +
              NearestVertices.resultFormat.format(shortestDistance) + " miles.");

      // create graphic for the vertices on the polyline.
      if (rNearestVertices != null) {
        CompositeSymbol compositeSymbol = new CompositeSymbol();
        for (int i=0; i<rNearestVertices.length;i++) {
          compositeSymbol.add(SYM_POINT_BLUE);
          compositeSymbol.add(new TextSymbol(12, String.valueOf(i+1), Color.WHITE));
          graphicsLayer.addGraphic(new Graphic(rNearestVertices[i].getCoordinate(),
              compositeSymbol));
          compositeSymbol.removeAll();
        }
      }

      // create a graphic between current point and nearest point on the polyline.
      Polyline line = new Polyline();
      line.startPath(point);
      line.lineTo(nearestPointOnPolyline);
      SimpleLineSymbol lineSymbol = new SimpleLineSymbol(Color.MAGENTA, 2);
      Graphic lineGraphic = new Graphic(line,lineSymbol,0);
      graphicsLayer.addGraphic(lineGraphic);

    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  /**
   * Returns the geodesic length between two points.
   * @param p1 point 1.
   * @param p2 point 2.
   * @return the geodesic length, in miles, between two points.
   */
  public double getGeodesicDistance(Point p1, Point p2) {

    Polyline line = new Polyline();
    line.startPath(p1);
    line.lineTo(p2);

    double distance = GeometryEngine.geodesicLength(
        line,
        map.getSpatialReference(),
        (LinearUnit) Unit.create(LinearUnit.Code.MILE_STATUTE));

    return distance;
  }

  // ------------------------------------------------------------------------
  // 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
          NearestVertices distanceApp = new NearestVertices();

          // create the UI, including the map, for the application.
          JFrame appWindow = distanceApp.createWindow();
          appWindow.add(distanceApp.createUI());
          appWindow.setVisible(true);
        } catch (Exception e) {
          // on any error, display the stack trace.
          e.printStackTrace();
        }
      }
    });
  }

  // ------------------------------------------------------------------------
  // Public methods
  // ------------------------------------------------------------------------
  /**
   * Creates and displays the UI, including the map, for this application.
   *
   * @return the UI component
   */
  public JComponent createUI() {

    // for map and panel
    JComponent layeredPane = createLayeredPane();

    // description
    JTextArea description = createDescription();
    // maximum search radius and vertices number to returns
    JPanel controlPanel = createControlPanel();
    // result
    resultBox = createResultBox();

    // description, user pane and result UI component
    final JPanel userPanel = new JPanel();
    BoxLayout boxLayout = new BoxLayout(userPanel, BoxLayout.Y_AXIS);
    userPanel.setLayout(boxLayout);
    userPanel.setLocation(10, 10);
    userPanel.setSize(320, 230);
    userPanel.setBorder(new LineBorder(Color.BLACK, 2, false));
    userPanel.add(description);
    userPanel.add(controlPanel);
    userPanel.add(resultBox);

    // map
    map = createMap();

    // add the panel and map to the layered pane
    layeredPane.add(userPanel);
    layeredPane.add(map);

    // create drawing overlay on which graphics will be drawn, add to map, and activate
    final DrawingOverlay drawingOverlay = new DrawingOverlay();
    map.addMapOverlay(drawingOverlay);
    drawingOverlay.setActive(true);

    // add a listener to add the drawn graphic into our graphics layer
    drawingOverlay.addDrawingCompleteListener(new DrawingCompleteListener() {

      @Override
      public void drawingCompleted(DrawingCompleteEvent arg0) {

        // get the graphic drawn
        Graphic inputGraphic = (Graphic) drawingOverlay.getAndClearFeature();

        inputGraphic.getGeometry();
        if (inputGraphic.getGeometry() instanceof Point) 
          pointId = graphicsLayer.addGraphic(inputGraphic);
        else polylineId = graphicsLayer.addGraphic(inputGraphic);
      }

    });

    // toolbar
    final JToolBar toolBar = createToolBar(drawingOverlay);

    // group toolbar and layered pane in a panel
    JPanel panel = new JPanel();
    panel.setLayout(new BorderLayout());
    panel.add(toolBar, BorderLayout.NORTH);
    panel.add(layeredPane, BorderLayout.CENTER);

    return panel;
  }

  // ------------------------------------------------------------------------
  // Private methods
  // ------------------------------------------------------------------------
  /**
   * Create a toolbar with our drawing tool buttons. Clicking a button will change the drawing mode and symbology (and
   * attributes, optionally) to that specified in the setUp method.
   * 
   * @param drawingOverlay - the drawing overlay associated with the toolbar
   * @return toolBar - the toolBar
   */
  private JToolBar createToolBar(final DrawingOverlay drawingOverlay) {

    JToolBar toolBar = new JToolBar();
    toolBar.setLayout(new FlowLayout(FlowLayout.CENTER));

    // remove graphics
    final JButton resetButton = new JButton("Reset");
    resetButton.setFocusPainted(false);
    resetButton.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        graphicsLayer.removeAll();
        polylineButton.setEnabled(true);
        drawingOverlay.setActive(false);
        resultBox.setText("");
      }
    });

    // solve 
    final JButton solveButton = new JButton("Solve");
    solveButton.setFocusPainted(false);
    solveButton.setEnabled(false);
    solveButton.addActionListener(new ActionListener() {

      @Override
      public void actionPerformed(ActionEvent e) {
        resetButton.setEnabled(true);
        solveButton.setEnabled(false);
        getVerticesAndDistance();
      }
    });

    // point
    final JButton pointButton = new JButton(new ImageIcon(getClass().getResource(
        "/com/esri/client/toolkit/images/EditingPointTool16.png")));
    pointButton.setFocusable(false);
    pointButton.setToolTipText("Point tool");
    pointButton.setEnabled(false);

    pointButton.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        drawingOverlay.setUp(DrawingMode.POINT, SYM_POINT_GREEN, null);
        drawingOverlay.setActive(true);
        pointButton.setEnabled(false);
        solveButton.setEnabled(true);
      }
    });

    // polyline
    polylineButton = new JButton(new ImageIcon(getClass().getResource(
        "/com/esri/client/toolkit/images/EditingLineTool16.png")));
    polylineButton.setFocusable(false);
    polylineButton.setToolTipText("Polyline tool");
    polylineButton.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        drawingOverlay.setUp(DrawingMode.POLYLINE, SYM_LINE, null);
        drawingOverlay.setActive(true);
        pointButton.setEnabled(true);
        polylineButton.setEnabled(false);
      }
    });

    // add the buttons 
    toolBar.add(polylineButton);
    toolBar.add(pointButton);
    toolBar.add(solveButton);
    toolBar.add(resetButton);

    return toolBar;
  }

  /**
   * Creates the application window, disposes the map on window closing.
   * 
   * @return a window.
   */
  private JFrame createWindow() {
    JFrame window = new JFrame("Geometry Engine 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);
        map.dispose();
      }
    });
    return window;
  }

  /**
   * Creates a content pane.
   * 
   * @return a content pane.
   */
  private static JLayeredPane createLayeredPane() {
    JLayeredPane contentPane = new JLayeredPane();
    contentPane.setLayout(new BorderLayout(0, 0));
    contentPane.setVisible(true);
    return contentPane;
  }

  /**
   * Creates a map, containing a tiled layer and a graphics layer.
   * 
   * @return a map.
   */
  private JMap createMap() {
    final JMap jMap = new JMap();

    // base Layer -  focus on U.S extent by default
    final ArcGISTiledMapServiceLayer baseLayer = new ArcGISTiledMapServiceLayer(URL_USA_TOPO);
    jMap.setExtent(new Envelope(
        -141.12213323593141, 3.34210829734802, -61.229555110931415, 64.05499892234802));
    jMap.getLayers().add(baseLayer);

    // graphics Layer - to draw the geometries
    graphicsLayer = new GraphicsLayer();
    jMap.getLayers().add(graphicsLayer);

    return jMap;
  }

  /**
   * Creates a description for this application.
   * 
   * @return description
   */
  private JTextArea createDescription() {
    JTextArea description = new JTextArea(
        "This application finds all vertices in the given distance between a polyline and " +
        "a point and measures the shortest distance between them.\n" +
        "- Use the toolbar to draw a polyline; double-click to end the polyline;\n" +
        "- Then draw a point using the toolbar;\n" +
        "- Press the 'Solve' button to show the vertices sorted from the closest to the furthest.");
    description.setFont(new Font("Verdana", Font.PLAIN, 11));
    description.setForeground(Color.WHITE);
    description.setEditable(false);
    description.setLineWrap(true);
    description.setWrapStyleWord(true);
    description.setBackground(Color.DARK_GRAY);
    description.setBorder(BorderFactory.createEmptyBorder(5,10,5,5));
    return description;
  }

  /**
   * Creates a result text component for this application.
   * 
   * @return text component to display result text.
   */
  private JTextField createResultBox() {
    JTextField result = new JTextField("Shortest distance result will be displayed here.");
    result.setEditable(false);
    result.setBackground(Color.WHITE);
    result.setBorder(BorderFactory.createEmptyBorder(5,10,5,5));
    return result;
  }

  private JPanel createControlPanel() {
    JPanel controlPanel = new JPanel();
    controlPanel.setPreferredSize(new Dimension(200, 60));
    controlPanel.setLayout(new BoxLayout(controlPanel, BoxLayout.Y_AXIS));

    // maximum search radius panel
    JPanel panelRadius = new JPanel();
    panelRadius.setLayout(new BoxLayout(panelRadius, BoxLayout.X_AXIS));
    panelRadius.setBorder(BorderFactory.createEmptyBorder(5,10,5,5));
    panelRadius.add(new JLabel("Search radius: "));
    txtRadius = new JTextField("20", 10);
    txtRadius.setMinimumSize(new Dimension(30, 20));
    txtRadius.setMaximumSize(new Dimension(30, 20));
    panelRadius.add(txtRadius);
    panelRadius.add(new JLabel(" map units"));

    // maximum vertices to return
    JPanel panelVertices = new JPanel();
    panelVertices.setLayout(new BoxLayout(panelVertices, BoxLayout.X_AXIS));
    panelVertices.add(new JLabel("Maximum vertices to return: "));
    txtVertices = new JTextField("10", 10);
    txtVertices.setMinimumSize(new Dimension(30, 20));
    txtVertices.setMaximumSize(new Dimension(30, 20));
    panelVertices.add(txtVertices);
    panelVertices.setBorder(BorderFactory.createEmptyBorder(5,10,5,5));

    // add the components to the the JPanel
    controlPanel.add(panelRadius);
    controlPanel.add(panelVertices);

    return controlPanel;
  }
}
Feedback on this topic?