measuretool.java
来自「world wind java sdk 源码」· Java 代码 · 共 1,437 行 · 第 1/4 页
JAVA
1,437 行
/*
Copyright (C) 2001, 2008 United States Government
as represented by the Administrator of the
National Aeronautics and Space Administration.
All Rights Reserved.
*/
package gov.nasa.worldwind.util.measure;
import gov.nasa.worldwind.*;
import gov.nasa.worldwind.avlist.*;
import gov.nasa.worldwind.geom.*;
import gov.nasa.worldwind.globes.Globe;
import gov.nasa.worldwind.layers.RenderableLayer;
import gov.nasa.worldwind.render.*;
import gov.nasa.worldwind.util.Logging;
import java.awt.*;
import java.awt.geom.*;
import java.util.*;
import java.util.List;
/**
* A utility class to interactively draw shapes and measure distance and area across the terrain. When armed,
* the class monitors mouse events to allow the definition of a measure shape that can be one of {@link #SHAPE_LINE},
* {@link #SHAPE_PATH}, {@link #SHAPE_POLYGON}, {@link #SHAPE_CIRCLE}, {@link #SHAPE_ELLIPSE}, {@link #SHAPE_SQUARE}
* or {@link #SHAPE_QUAD}.
*
* <p>In order to allow user interaction with the measuring shape, a controller must be set by calling
* {@link #setController(MeasureToolController)} with a new instance of a <code>MeasureToolController</code>.</p>
*
* <p>The interaction sequence for drawing a shape and measuring is as follows:
* <ul>
* <li>Set the measure shape.</li>
* <li>Arm the <code>MeasureTool</code> object by calling its {@link #setArmed(boolean)} method with an argument
* of true.</li>
* <li>Click on the terrain to add points.</li>
* <li>Disarm the <code>MeasureTool</code> object by calling its {@link #setArmed(boolean)} method with an argument
* of false. </li>
* <li>Read the measured length or area by calling the <code>MeasureTool</code> {@link #getLength()} or
* {@link #getArea()} method. Note that the length and area can be queried at any time during or after the process.</li>
* </ul>
* </p>
* <p>While entering points or after the measure tool has been disarmed, dragging the control points allow to
* change the initial points positions and alter the measure shape.</p>
*
* <p> While the <code>MeasureTool</code> is armed, pressing and immediately releasing mouse button one while also
* pressing the control key (Ctl) removes the last point entered. </p>
*
* <p>Arming and disarming the <code>MeasureTool</code> does not change the contents or attributes of the measure
* tool's layer. Note that the measure tool will NOT disarm itself after the second point of a line or a regular shape
* has been entered - the MeasureToolController has that responsability.</p>
*
* <p><b>Setting the measure shape from the application</b></p>
*
* <p>The application can set the measure shape to an arbitrary list of positions using
* {@link #setPositions(java.util.ArrayList)}. If the provided list contains two positions, the measure shape will
* be set to {@link #SHAPE_LINE}. If more then two positions are provided, the measure shape will be set to
* {@link #SHAPE_PATH} if the last position differs from the first (open path), or {@link #SHAPE_POLYGON} if the
* path is closed.</p>
*
* <p>The application can also set the measure shape to a predefined regular shape by calling
* {@link #setMeasureShape(String, Position, double, double, Angle)}, providing a shape type (one of
* {@link #SHAPE_CIRCLE}, {@link #SHAPE_ELLIPSE}, {@link #SHAPE_SQUARE} or {@link #SHAPE_QUAD}), a center position,
* a wdth, a height (in meters) and a heading angle.</p>
*
* <p>Finally, the application can use an existing <code>Polyline</code> or <code>SurfaceShape</code> by using
* {@link #setMeasureShape(Polyline)} or {@link #setMeasureShape(SurfaceShape)}. The surface shape can be one of
* <code>SurfacePolygon</code>, <code>SurfaceQuad</code>, <code>SurfaceSquare</code>, <code>SurfaceEllipse</code> or
* <code>SurfaceCircle</code>.
*
* <p><b>Measuring</b></p>
*
* <p>The application can read the measured length or area by calling the <code>MeasureTool</code> {@link #getLength()}
* or {@link #getArea()} method. These methods will return -1 when no value is available.</p>
*
* <p>Regular shapes are defined by a center position, a width a height and a heading angle. Those attributes can be
* accessed by calling the {@link #getCenterPosition()}, {@link #getWidth()}, {@link #getHeight()} and
* {@link #getOrientation()} methods.</p>
*
* <p><b>Events</b></p>
*
* <p>The <code>MeasureTool</code> will send events on several occasions: when the position list has changed -
* {@link #EVENT_POSITION_ADD}, {@link #EVENT_POSITION_REMOVE} or {@link #EVENT_POSITION_REPLACE}, when metrics
* has changed {@link #EVENT_METRIC_CHANGED} or when the tool is armed or disarmed {@link #EVENT_ARMED}.</p>
*
* <p>Events will also be fired at the start and end of a rubber band operation during shape creation:
* {@link #EVENT_RUBBERBAND_START} and {@link #EVENT_RUBBERBAND_STOP}.</p>
*
* <p>See {@link gov.nasa.worldwind.examples.MeasureToolPanel} for some events usage.</p>
*
* <p>Several instances of this class can be used simultaneously. However, each instance should be disposed of after
* usage by calling the {@link #dispose()} method.</p>
*
*
* @author Patrick Murris
* @version $Id: MeasureTool.java 10590 2009-04-28 17:12:31Z patrickmurris $
* @see MeasureToolController
*/
public class MeasureTool extends AVListImpl implements Disposable
{
public static final String SHAPE_LINE = "MeasureTool.ShapeLine";
public static final String SHAPE_PATH = "MeasureTool.ShapePath";
public static final String SHAPE_POLYGON = "MeasureTool.ShapePolygon";
public static final String SHAPE_CIRCLE = "MeasureTool.ShapeCircle";
public static final String SHAPE_ELLIPSE = "MeasureTool.ShapeEllipse";
public static final String SHAPE_QUAD = "MeasureTool.ShapeQuad";
public static final String SHAPE_SQUARE = "MeasureTool.ShapeSquare";
public static final String EVENT_POSITION_ADD = "MeasureTool.AddPosition";
public static final String EVENT_POSITION_REMOVE = "MeasureTool.RemovePosition";
public static final String EVENT_POSITION_REPLACE = "MeasureTool.ReplacePosition";
public static final String EVENT_METRIC_CHANGED = "MeasureTool.MetricChanged";
public static final String EVENT_ARMED = "MeasureTool.Armed";
public static final String EVENT_RUBBERBAND_START = "MeasureTool.RubberBandStart";
public static final String EVENT_RUBBERBAND_STOP = "MeasureTool.RubberBandStop";
protected final WorldWindow wwd;
protected MeasureToolController controller;
protected ArrayList<Position> positions = new ArrayList<Position>();
protected ArrayList<Renderable> controlPoints = new ArrayList<Renderable>();
protected RenderableLayer applicationLayer;
protected CustomRenderableLayer layer;
protected CustomRenderableLayer controlPointsLayer;
protected CustomRenderableLayer shapeLayer;
protected Polyline line;
protected SurfaceShape surfaceShape;
protected ScreenAnnotation annotation;
protected Color lineColor = Color.YELLOW;
protected Color fillColor = new Color(.6f, .6f, .4f, .5f);
protected double lineWidth = 2;
protected String pathType = AVKey.GREAT_CIRCLE;
protected AnnotationAttributes controlPointsAttributes;
protected AnnotationAttributes annotationAttributes;
protected String measureShape = SHAPE_LINE;
protected boolean followTerrain = false;
protected boolean showControlPoints = true;
protected boolean showAnnotation = true;
// Rectangle enclosed regular shapes attributes
protected Rectangle2D.Double shapeRectangle = null;
protected Position shapeCenterPosition = null;
protected Angle shapeOrientation = null;
protected int shapeIntervals = 64;
protected class CustomRenderableLayer extends RenderableLayer implements PreRenderable, Renderable
{
public void render(DrawContext dc)
{
if (dc.isPickingMode() && !this.isPickEnabled())
return;
if (!this.isEnabled())
return;
super.render(dc);
}
}
/**
* Construct a new measure tool drawing events from the specified <code>WorldWindow</code>.
*
* @param wwd the <code>WorldWindow</code> to draw events from.
*/
public MeasureTool(final WorldWindow wwd)
{
this(wwd, null);
}
/**
* Construct a new measure tool drawing events from the specified <code>WorldWindow</code> and using the given
* <code>RenderableLayer</code>.
*
* @param wwd the <code>WorldWindow</code> to draw events from.
* @param applicationLayer the <code>RenderableLayer</code> to use - can be null.
*/
public MeasureTool(final WorldWindow wwd, RenderableLayer applicationLayer)
{
if (wwd == null)
{
String msg = Logging.getMessage("nullValue.WorldWindow");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
this.wwd = wwd;
this.applicationLayer = applicationLayer; // can be null
// Set up layers
this.layer = createCustomRenderableLayer();
this.shapeLayer = createCustomRenderableLayer();
this.controlPointsLayer = createCustomRenderableLayer();
this.shapeLayer.setPickEnabled(false);
this.layer.setName("Measure Tool");
this.layer.addRenderable(this.shapeLayer); // add shape layer to render layer
this.layer.addRenderable(this.controlPointsLayer); // add control points layer to render layer
this.controlPointsLayer.setEnabled(this.showControlPoints);
if (this.applicationLayer != null)
this.applicationLayer.addRenderable(this.layer); // add render layer to the application provided layer
else
this.wwd.getModel().getLayers().add(this.layer); // add render layer to the globe model
// Init control points rendering attributes
this.controlPointsAttributes = new AnnotationAttributes();
// Define an 8x8 square centered on the screen point
this.controlPointsAttributes.setFrameShape(FrameFactory.SHAPE_RECTANGLE);
this.controlPointsAttributes.setLeader(FrameFactory.LEADER_NONE);
this.controlPointsAttributes.setAdjustWidthToText(Annotation.SIZE_FIXED);
this.controlPointsAttributes.setSize(new Dimension(8, 8));
this.controlPointsAttributes.setDrawOffset(new Point(0, -4));
this.controlPointsAttributes.setInsets(new Insets(0, 0, 0, 0));
this.controlPointsAttributes.setBorderWidth(0);
this.controlPointsAttributes.setCornerRadius(0);
this.controlPointsAttributes.setBackgroundColor(Color.BLUE); // Normal color
this.controlPointsAttributes.setTextColor(Color.GREEN); // Highlighted color
this.controlPointsAttributes.setHighlightScale(1.2);
this.controlPointsAttributes.setDistanceMaxScale(1); // No distance scaling
this.controlPointsAttributes.setDistanceMinScale(1);
this.controlPointsAttributes.setDistanceMinOpacity(1);
// Annotation attributes
this.annotationAttributes = new AnnotationAttributes();
this.annotationAttributes.setFrameShape(FrameFactory.SHAPE_NONE);
this.annotationAttributes.setInsets(new Insets(0, 0, 0, 0));
this.annotationAttributes.setDrawOffset(new Point(0, 10));
this.annotationAttributes.setTextAlign(MultiLineTextRenderer.ALIGN_CENTER);
this.annotationAttributes.setEffect(MultiLineTextRenderer.EFFECT_OUTLINE);
this.annotationAttributes.setFont(Font.decode("Arial-Bold-14"));
this.annotationAttributes.setTextColor(Color.WHITE);
this.annotationAttributes.setBackgroundColor(Color.BLACK);
this.annotationAttributes.setSize(new Dimension(220, 0));
this.annotation = new ScreenAnnotation("", new Point(0, 0), this.annotationAttributes);
this.annotation.getAttributes().setVisible(false);
this.annotation.getAttributes().setDrawOffset(null); // use defaults
this.shapeLayer.addRenderable(this.annotation);
}
public WorldWindow getWwd()
{
return this.wwd;
}
/**
* @return Instance of the custom renderable layer to use of our internal layers
*/
protected CustomRenderableLayer createCustomRenderableLayer()
{
return new CustomRenderableLayer();
}
/**
* Set the controller object for this measure tool - can be null.
*
* @param controller the controller object for this measure tool.
*/
public void setController(MeasureToolController controller)
{
if (this.controller != null)
{
this.wwd.getInputHandler().removeMouseListener(this.controller);
this.wwd.getInputHandler().removeMouseMotionListener(this.controller);
this.wwd.removePositionListener(this.controller);
this.wwd.removeSelectListener(this.controller);
this.wwd.removeRenderingListener(this.controller);
this.controller = null;
}
if (controller != null)
{
this.controller = controller;
this.controller.setMeasureTool(this);
this.wwd.getInputHandler().addMouseListener(this.controller);
this.wwd.getInputHandler().addMouseMotionListener(this.controller);
this.wwd.addPositionListener(this.controller);
this.wwd.addSelectListener(this.controller);
this.wwd.addRenderingListener(this.controller);
}
}
/**
* Get the <code>MeasureToolController</code> for this measure tool.
*
* @return the <code>MeasureToolController</code> for this measure tool.
*/
public MeasureToolController getController()
{
return this.controller;
}
/**
* Arms and disarms the measure tool controller. When armed, the controller monitors user input and builds the
* shape in response to user actions. When disarmed, the controller ignores all user input.
*
* @param state true to arm the controller, false to disarm it.
*/
public void setArmed(boolean state)
{
if (this.controller != null)
this.controller.setArmed(state);
}
/**
* Identifies whether the measure tool controller is armed.
*
* @return true if armed, false if not armed.
*/
public boolean isArmed()
{
return this.controller != null && this.controller.isArmed();
}
/**
* Returns the measure tool layer.
*
* @return the layer containing the measure shape and control points.
*/
public RenderableLayer getLayer()
{
return this.layer;
}
/**
* Returns the polyline currently used to display lines and path.
*
* @return the polyline currently used to display lines and path.
*/
public Polyline getLine()
{
return this.line;
}
/**
* Returns the surface shape currently used to display polygons.
*
* @return the surface shape currently used to display polygons.
*/
public SurfaceShape getSurfaceShape()
{
return this.surfaceShape;
}
/**
* Get the list of positions that define the current measure shape.
*
* @return the list of positions that define the current measure shape.
*/
public ArrayList<? extends Position> getPositions()
{
return this.positions;
}
/**
* Set the measure shape to an arbitrary list of positions using {@link #setPositions(java.util.ArrayList)}.
* If the provided list contains two positions, the measure shape will be set to {@link #SHAPE_LINE}. If more
* then two positions are provided, the measure shape will be set to {@link #SHAPE_PATH} if the last position
* differs from the first (open path), or {@link #SHAPE_POLYGON} if the path is closed.
*
* @param newPositions the shape position list.
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?