Package _org.geoforge.amd.wwwx.examples.measuretool

Source Code of _org.geoforge.amd.wwwx.examples.measuretool.GfrMeasureTool

/*
*  Copyright (C) 2011-2013 GeoForge Project
*
*  This program is free software; you can redistribute it and/or
*  modify it under the terms of the GNU Lesser General Public License
*  as published by the Free Software Foundation; either version 2
*  of the License, or (at your option) any later version.
*
*  This program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU Lesser General Public License for more details.
*
*  You should have received a copy of the GNU Lesser General Public License
*  along with this program; if not, write to the Free Software
*  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

package _org.geoforge.amd.wwwx.examples.measuretool;

import gov.nasa.worldwind.Disposable;
import gov.nasa.worldwind.WorldWindow;
import gov.nasa.worldwind.avlist.AVKey;
import gov.nasa.worldwind.avlist.AVListImpl;
import gov.nasa.worldwind.geom.Angle;
import gov.nasa.worldwind.geom.LatLon;
import gov.nasa.worldwind.geom.Position;
import gov.nasa.worldwind.geom.Vec4;
import gov.nasa.worldwind.globes.Globe;
import gov.nasa.worldwind.layers.RenderableLayer;
import gov.nasa.worldwind.render.AnnotationAttributes;
import gov.nasa.worldwind.render.BasicShapeAttributes;
import gov.nasa.worldwind.render.DrawContext;
import gov.nasa.worldwind.render.Material;
import gov.nasa.worldwind.render.Polyline;
import gov.nasa.worldwind.render.PreRenderable;
import gov.nasa.worldwind.render.Renderable;
import gov.nasa.worldwind.render.ScreenAnnotation;
import gov.nasa.worldwind.render.ShapeAttributes;
import gov.nasa.worldwind.render.SurfaceShape;
import gov.nasa.worldwind.util.Logging;
import gov.nasa.worldwind.util.UnitsFormat;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Insets;
import java.awt.Point;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.List;

/**
*
* @author Amadeus.Sowerby
*
* email: Amadeus.Sowerby_AT_gmail.com ... please remove "_AT_" from the above
* string to get the right email address
*/
final public class GfrMeasureTool extends AVListImpl implements Disposable
{

   public static final String SHAPE_LINE = "GfrMeasureTool.ShapeLine";

   public static final String SHAPE_PATH = "GfrMeasureTool.ShapePath";

   public static final String EVENT_POSITION_ADD = "GfrMeasureTool.AddPosition";

   public static final String EVENT_POSITION_REMOVE = "GfrMeasureTool.RemovePosition";

   public static final String EVENT_POSITION_REPLACE = "GfrMeasureTool.ReplacePosition";

   public static final String EVENT_METRIC_CHANGED = "GfrMeasureTool.MetricChanged";

   public static final String EVENT_ARMED = "GfrMeasureTool.Armed";

   public static final String EVENT_RUBBERBAND_START = "GfrMeasureTool.RubberBandStart";

   public static final String EVENT_RUBBERBAND_STOP = "GfrMeasureTool.RubberBandStop";

   public static final String ANGLE_LABEL = "GfrMeasureTool.AngleLabel";

   public static final String AREA_LABEL = "GfrMeasureTool.AreaLabel";

   public static final String LENGTH_LABEL = "GfrMeasureTool.LengthLabel";

   public static final String PERIMETER_LABEL = "GfrMeasureTool.PerimeterLabel";

   public static final String RADIUS_LABEL = "GfrMeasureTool.RadiusLabel";

   public static final String HEIGHT_LABEL = "GfrMeasureTool.HeightLabel";

   public static final String WIDTH_LABEL = "GfrMeasureTool.WidthLabel";

   public static final String HEADING_LABEL = "GfrMeasureTool.HeadingLabel";

   public static final String CENTER_LATITUDE_LABEL = "GfrMeasureTool.CenterLatitudeLabel";

   public static final String CENTER_LONGITUDE_LABEL = "GfrMeasureTool.CenterLongitudeLabel";

   public static final String LATITUDE_LABEL = "GfrMeasureTool.LatitudeLabel";

   public static final String LONGITUDE_LABEL = "GfrMeasureTool.LongitudeLabel";

   public static final String ACCUMULATED_LABEL = "GfrMeasureTool.AccumulatedLabel";

   public static final String MAJOR_AXIS_LABEL = "GfrMeasureTool.MajorAxisLabel";

   public static final String MINOR_AXIS_LABEL = "GfrMeasureTool.MinorAxisLabel";

   public static final String CONTROL_TYPE_LOCATION_INDEX = "GfrMeasureTool.ControlTypeLocationIndex";

   public static final String CONTROL_TYPE_REGULAR_SHAPE = "GfrMeasureTool.ControlTypeRegularShape";


   private static final String CENTER = "Center";

   private static final String NORTH = "North";

   private static final String EAST = "East";

   private static final String SOUTH = "South";

   private static final String WEST = "West";

   private static final String NORTHEAST = "NE";

   private static final String SOUTHEAST = "SE";

   private static final String SOUTHWEST = "SW";

   private static final String NORTHWEST = "NW";


   protected final WorldWindow wwd;

   protected GfrMeasureToolController 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 double lineWidth = 2;

   protected String pathType = AVKey.GREAT_CIRCLE;

   protected AnnotationAttributes controlPointsAttributes;

   //protected AnnotationAttributes controlPointWithLeaderAttributes;

   //protected ShapeAttributes leaderAttributes;

   protected AnnotationAttributes annotationAttributes;

   protected String measureShapeType = SHAPE_LINE;

   protected boolean showAnnotation = true;

   protected UnitsFormat unitsFormat = new UnitsFormat();

   // Rectangle enclosed regular shapes attributes

   protected Angle shapeOrientation = null;

   /**
    * Construct a new measure tool drawing events from the specified <code>WorldWindow</code>.
    *
    * @param wwd the <code>WorldWindow</code> to draw events from.
    */
   public GfrMeasureTool(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. May be null. If specified, the caller is
    *                         responsible for adding the layer to the model and enabling it.
    */
   public GfrMeasureTool(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(true);
     
      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(AVKey.SHAPE_RECTANGLE);
      this.controlPointsAttributes.setLeader(AVKey.SHAPE_NONE);
      this.controlPointsAttributes.setAdjustWidthToText(AVKey.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);

      // Init control point with leader rendering attributes.
      /*this.controlPointWithLeaderAttributes = new AnnotationAttributes();
      this.controlPointWithLeaderAttributes.setDefaults(this.controlPointsAttributes);
      this.controlPointWithLeaderAttributes.setFrameShape(AVKey.SHAPE_ELLIPSE);
      this.controlPointWithLeaderAttributes.setSize(new Dimension(10, 10));
      this.controlPointWithLeaderAttributes.setDrawOffset(new Point(0, -5));
      this.controlPointWithLeaderAttributes.setBackgroundColor(Color.LIGHT_GRAY);

      this.leaderAttributes = new BasicShapeAttributes();
      this.leaderAttributes.setOutlineMaterial(Material.WHITE);
      this.leaderAttributes.setOutlineOpacity(0.7);
      this.leaderAttributes.setOutlineWidth(3);*/

      // Annotation attributes
      this.annotationAttributes = new AnnotationAttributes();
      this.annotationAttributes.setFrameShape(AVKey.SHAPE_NONE);
      this.annotationAttributes.setInsets(new Insets(0, 0, 0, 0));
      this.annotationAttributes.setDrawOffset(new Point(0, 10));
      this.annotationAttributes.setTextAlign(AVKey.CENTER);
      this.annotationAttributes.setEffect(AVKey.TEXT_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 the {@link UnitsFormat} instance governing the measurement value display units and format.
    *
    * @return the tool's units format instance.
    */
   public UnitsFormat getUnitsFormat()
   {
      return this.unitsFormat;
   }

   /**
    * Set the measure tool's @{link UnitsFormat} instance that governs measurement value display units and format.
    *
    * @param unitsFormat the units format instance.
    *
    * @throws IllegalArgumentException if the units format instance is null.
    */
   public void setUnitsFormat(UnitsFormat unitsFormat)
   {
      if (unitsFormat == null)
      {
         String msg = Logging.getMessage("nullValue.Format");
         Logging.logger().severe(msg);
         throw new IllegalArgumentException(msg);
      }

      this.unitsFormat = unitsFormat;
   }

   /** @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(GfrMeasureToolController 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);
      }
   }

   public void setLabel(String labelName, String label)
   {
      if (labelName != null && labelName.length() > 0)
         this.setValue(labelName, label);
   }

   public String getLabel(String labelName)
   {
      if (labelName == null)
      {
         String msg = Logging.getMessage("nullValue.LabelName");
         Logging.logger().severe(msg);
         throw new IllegalArgumentException(msg);
      }

      String label = this.getStringValue(labelName);

      return label != null ? label : this.unitsFormat.getStringValue(labelName);
   }

   /**
    * Get the <code>GfrMeasureToolController</code> for this measure tool.
    *
    * @return the <code>GfrMeasureToolController</code> for this measure tool.
    */
   public GfrMeasureToolController 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 applilcation layer passed to the constructor.
    *
    * @return the layer containing the measure shape and control points.
    */
   public RenderableLayer getApplicationLayer()
   {
      return applicationLayer;
   }

   /**
    * 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;
   }

 
   /**
    * 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. 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.
    */
   public void setPositions(ArrayList<? extends Position> newPositions)
   {
      if (newPositions == null)
      {
         String msg = Logging.getMessage("nullValue.PositionsListIsNull");
         Logging.logger().severe(msg);
         throw new IllegalArgumentException(msg);
      }

      if (newPositions.size() < 2)
         return;

      this.clear();

      // Setup the proper measure shape
      boolean closedShape = newPositions.get(0).equals(newPositions.get(newPositions.size() - 1));
      /*if (newPositions.size() > 2 && closedShape)
      setMeasureShapeType(SHAPE_POLYGON);
      else*/
      setMeasureShapeType(getPathType(newPositions));

      // Import positions and create control points
      for (int i = 0; i < newPositions.size(); i++)
      {
         Position pos = newPositions.get(i);
         this.positions.add(pos);
         if (i < newPositions.size() - 1 || !closedShape)
            addControlPoint(pos, CONTROL_TYPE_LOCATION_INDEX, this.positions.size() - 1);
      }

      // Update line heading if needed
      if (this.measureShapeType.equals(SHAPE_LINE))
         this.shapeOrientation = LatLon.greatCircleAzimuth(this.positions.get(0), this.positions.get(1));

      // Update screen shapes
      updateMeasureShape();
      this.firePropertyChange(EVENT_POSITION_REPLACE, null, null);
      this.wwd.redraw();
   }

   /**
    * Get the list of control points associated with the current measure shape.
    *
    * @return the list of control points associated with the current measure shape.
    */
   public ArrayList<Renderable> getControlPoints()
   {
      return this.controlPoints;
   }

   /**
    * Get the attributes associated with the control points.
    *
    * @return the attributes associated with the control points.
    */
   public AnnotationAttributes getControlPointsAttributes()
   {
      return this.controlPointsAttributes;
   }

   /**
    * Get the attributes associated with the tool tip annotation.
    *
    * @return the attributes associated with the tool tip annotation.
    */
   public AnnotationAttributes getAnnotationAttributes()
   {
      return this.annotationAttributes;
   }

   public void setLineColor(Color color)
   {
      if (color == null)
      {
         String msg = Logging.getMessage("nullValue.ColorIsNull");
         Logging.logger().severe(msg);
         throw new IllegalArgumentException(msg);
      }
      this.lineColor = color;
      if (this.line != null)
      {
         this.line.setColor(color);
      }
      if (this.surfaceShape != null)
      {
         ShapeAttributes attr = this.surfaceShape.getAttributes();
         if (attr == null)
            attr = new BasicShapeAttributes();
         attr.setOutlineMaterial(new Material(color));
         attr.setOutlineOpacity(color.getAlpha() / 255d);
         this.surfaceShape.setAttributes(attr);
      }
      this.wwd.redraw();
   }

   public Color getLineColor()
   {
      return this.lineColor;
   }
  

   public void setLineWidth(double width)
   {
      this.lineWidth = width;
      if (this.line != null)
         this.line.setLineWidth(width);
      if (this.surfaceShape != null)
      {
         ShapeAttributes attr = this.surfaceShape.getAttributes();
         if (attr == null)
            attr = new BasicShapeAttributes();
         attr.setOutlineWidth(width);
         this.surfaceShape.setAttributes(attr);
      }
      this.wwd.redraw();
   }

   public double getLineWidth()
   {
      return this.lineWidth;
   }

   public String getPathType()
   {
      return this.pathType;
   }

   public void setPathType(String type)
   {
      this.pathType = type;
      if (this.line != null)
         this.line.setPathType(polylinePathTypeFromKey(type));

      if (this.surfaceShape != null)
         this.surfaceShape.setPathType(type);

      this.wwd.redraw();
   }

   @SuppressWarnings(
   {
      "StringEquality"
   })
   protected static int polylinePathTypeFromKey(String type)
   {
      if (type != null && type.equals(AVKey.GREAT_CIRCLE))
      {
         return Polyline.GREAT_CIRCLE;
      }
      else if (type != null && (type.equals(AVKey.RHUMB_LINE) || type.equals(AVKey.LOXODROME)))
      {
         return Polyline.RHUMB_LINE;
      }
      else
      {
         return Polyline.LINEAR;
      }
   }

   protected static String keyFromPolylinePathType(int type)
   {
      if (type == Polyline.GREAT_CIRCLE)
      {
         return AVKey.GREAT_CIRCLE;
      }
      else if (type == Polyline.RHUMB_LINE)
      {
         return AVKey.RHUMB_LINE;
      }
      else
      {
         return AVKey.LINEAR;
      }
   }
 
   public boolean isShowAnnotation()
   {
      return this.showAnnotation;
   }

   public void setShowAnnotation(boolean state)
   {
      this.showAnnotation = state;
   }

   /** Removes all positions from the shape, clear attributes. */
   public void clear()
   {
      while (this.positions.size() > 0 || this.controlPoints.size() > 0)
      {
         this.removeControlPoint();
      }

      this.shapeOrientation = null;
   }

   public boolean isMeasureShape(Object o)
   {
      return o == this.shapeLayer;
   }

   /**
    * Get the measure shape type. 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}.
    *
    * @return the measure shape type.
    */
   public String getMeasureShapeType()
   {
      return this.measureShapeType;
   }

   /**
    * Set the measure shape type. 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}. This will reset the
    * measure tool and clear the current measure shape.
    *
    * @param shape the measure shape type.
    */
   public void setMeasureShapeType(String shape)
   {
      if (shape == null)
      {
         String msg = Logging.getMessage("nullValue.ShapeType");
         Logging.logger().severe(msg);
         throw new IllegalArgumentException(msg);
      }

      if (!this.measureShapeType.equals(shape))
      {
         setArmed(false);
         clear();
         this.measureShapeType = shape;
      }
   }

   /**
    * Set the measure shape to an existing <code>Polyline</code>.
    *
    * @param line a <code>Polyline</code> instance.
    */
   public void setMeasureShape(Polyline line)
   {
      if (line == null)
      {
         String msg = Logging.getMessage("nullValue.Shape");
         Logging.logger().severe(msg);
         throw new IllegalArgumentException(msg);
      }
      setArmed(false);
      this.clear();

      // Clear and replace current shape
      if (this.surfaceShape != null)
      {
         this.shapeLayer.removeRenderable(this.surfaceShape);
         this.surfaceShape = null;
      }

      if (this.line != null)
         this.shapeLayer.removeRenderable(this.line);

      this.line = line;
      this.shapeLayer.addRenderable(line);
      // Grab some of the line attributes
      setPathType(keyFromPolylinePathType(line.getPathType()));
      // Update position list and create control points
      int i = 0;

      for (Position pos : line.getPositions())
      {
         this.positions.add(pos);
         addControlPoint(pos, CONTROL_TYPE_LOCATION_INDEX, i++);
      }

      // Set proper measure shape type
      this.measureShapeType = getPathType(this.positions);
      this.firePropertyChange(EVENT_POSITION_REPLACE, null, null);
      this.wwd.redraw();
   }

   protected String getPathType(List<? extends Position> positions)
   {
      return positions.size() > 2 ? SHAPE_PATH : SHAPE_LINE;
   }

   // *** Metric accessors ***
   public double getLength()
   {
      Globe globe = this.wwd.getModel().getGlobe();

      if (this.line != null)
         return this.line.getLength(globe);

      if (this.surfaceShape != null)
         return this.surfaceShape.getPerimeter(globe);

      return -1;
   }


   public Angle getOrientation()
   {
      return this.shapeOrientation;
   }


   // *** Editing shapes ***
   /** Add a control point to the current measure shape at the cuurrent WorldWindow position. */
   public void addControlPoint()
   {
      Position curPos = this.wwd.getCurrentPosition();
      if (curPos == null)
         return;


      // Line, path or polygons with less then two points


      this.positions.add(curPos);



      addControlPoint(this.positions.get(this.positions.size() - 1), CONTROL_TYPE_LOCATION_INDEX,
              this.positions.size() - 1);


      if (this.measureShapeType.equals(SHAPE_LINE) && this.positions.size() > 1)
      {
         // Two points on a line, update line heading info
         this.shapeOrientation = LatLon.greatCircleAzimuth(this.positions.get(0), this.positions.get(1));
      }


      // Update screen shapes
      updateMeasureShape();
      this.firePropertyChange(EVENT_POSITION_ADD, null, curPos);
      this.wwd.redraw();
   }

   /** Remove the last control point from the current measure shape. */
   public void removeControlPoint()
   {
      Position currentLastPosition = null;

      if (this.positions.isEmpty())
         return;

      if (/*!this.measureShapeType.equals(SHAPE_POLYGON) || */this.positions.size() == 1)
      {
         currentLastPosition = this.positions.get(this.positions.size() - 1);
         this.positions.remove(this.positions.size() - 1);
      }
      else
      {
         // For polygons with more then 2 points, the last position is the same as the first, so remove before it
         currentLastPosition = this.positions.get(this.positions.size() - 2);
         this.positions.remove(this.positions.size() - 2);
      }
      if (this.controlPoints.size() > 0)
         this.controlPoints.remove(this.controlPoints.size() - 1);
      //}
      this.controlPointsLayer.setRenderables(this.controlPoints);
      // Update screen shapes
      updateMeasureShape();
      this.firePropertyChange(EVENT_POSITION_REMOVE, currentLastPosition, null);
      this.wwd.redraw();
   }

   /**
    * Update the current measure shape according to a given control point position.
    *
    * @param point one of the shape control points.
    */
   public void moveControlPoint(ControlPoint point)
   {
      moveControlPoint(point, null); // use the default mode.
   }

   /**
    * Update the current measure shape according to a given control point position and shape edition mode.
    *
    * @param point one of the shape control points.
    * @param mode  the shape edition mode.
    */
   public void moveControlPoint(ControlPoint point, String mode)
   {
      if (point == null)
      {
         String msg = Logging.getMessage("nullValue.PointIsNull");
         Logging.logger().severe(msg);
         throw new IllegalArgumentException(msg);
      }

      if (point.getValue(CONTROL_TYPE_LOCATION_INDEX) != null)
      {
         int positionIndex = (Integer) point.getValue(CONTROL_TYPE_LOCATION_INDEX);
         // Update positions
         Position surfacePosition = computeSurfacePosition(point.getPosition());
         surfacePosition = new Position(point.getPosition(), surfacePosition.getAltitude());
         positions.set(positionIndex, surfacePosition);

         // Update last pos too if polygon and first pos changed
            /*if (measureShapeType.equals(SHAPE_POLYGON) && positions.size() > 2 && positionIndex == 0)
         positions.set(positions.size() - 1, surfacePosition);*/

         // Update heading for simple line
         if (measureShapeType.equals(SHAPE_LINE) && positions.size() > 1)
            shapeOrientation = LatLon.greatCircleAzimuth(positions.get(0), positions.get(1));
      }

      // Update rendered shapes
      updateMeasureShape();
   }

   /**
    * Move the current measure shape along a great circle arc at a given azimuth <code>Angle</code> for a given
    * distance <code>Angle</code>.
    *
    * @param azimuth  the azimuth <code>Angle</code>.
    * @param distance the distance <code>Angle</code>.
    */
   public void moveMeasureShape(Angle azimuth, Angle distance)
   {
      if (distance == null)
      {
         String msg = Logging.getMessage("nullValue.AngleIsNull");
         Logging.logger().severe(msg);
         throw new IllegalArgumentException(msg);
      }
      if (azimuth == null)
      {
         String msg = Logging.getMessage("nullValue.AngleIsNull");
         Logging.logger().severe(msg);
         throw new IllegalArgumentException(msg);
      }

      /*if (this.isRegularShape())//always false
      {
      // Move regular shape center
      if (controlPoints.size() > 0)
      {
      ControlPoint point = this.getControlPoint(CENTER);
      point.setPosition(
      new Position(LatLon.greatCircleEndPosition(point.getPosition(), azimuth, distance), 0));
      moveControlPoint(point);
      }
      }
      else
      {*/
      // Move all positions and control points
      for (int i = 0; i < positions.size(); i++)
      {
         Position newPos = computeSurfacePosition(
                 LatLon.greatCircleEndPosition(positions.get(i), azimuth, distance));
         positions.set(i, newPos);
         if (/*!this.measureShapeType.equals(SHAPE_POLYGON) || */i < positions.size() - 1)
            ((ControlPoint) controlPoints.get(i)).setPosition(new Position(newPos, 0));
      }
      // Update heading for simple line
      if (measureShapeType.equals(SHAPE_LINE) && positions.size() > 1)
         shapeOrientation = LatLon.greatCircleAzimuth(positions.get(0), positions.get(1));
      // Update rendered shapes
      updateMeasureShape();
      //}
   }

   protected Position computeSurfacePosition(LatLon latLon)
   {
      Vec4 surfacePoint = wwd.getSceneController().getTerrain().getSurfacePoint(latLon.getLatitude(),
              latLon.getLongitude());
      if (surfacePoint != null)
         return wwd.getModel().getGlobe().computePositionFromPoint(surfacePoint);
      else
         return new Position(latLon, wwd.getModel().getGlobe().getElevation(latLon.getLatitude(),
                 latLon.getLongitude()));
   }

   public String getShapeInitialControl(Position position)
   {
      /*if (this.measureShapeType.equals(SHAPE_ELLIPSE) || this.measureShapeType.equals(SHAPE_CIRCLE))
      {
      return EAST;
      }
      else if (this.measureShapeType.equals(SHAPE_QUAD) || this.measureShapeType.equals(SHAPE_SQUARE))
      {
      return NORTHEAST;
      }*/

      return null;
   }

   protected Angle getShapeInitialHeading()
   {
      return this.wwd.getView().getHeading();
   }

   

   public ControlPoint getControlPoint(String control)
   {
      for (Renderable cp : this.controlPoints)
      {
         String value = ((ControlPoint) cp).getStringValue(CONTROL_TYPE_REGULAR_SHAPE);
         if (value != null && value.equals(control))
            return (ControlPoint) cp;
      }
     
      return null;
   }
  
   protected ControlPoint getOppositeControl(String control)
   {
      if (this.controlPoints.isEmpty())
         return null;
      else if (this.controlPoints.size() == 1)
         return getControlPoint(CENTER);
      else if (control.equals(NORTH))
         return getControlPoint(SOUTH);
      else if (control.equals(EAST))
         return getControlPoint(WEST);
      else if (control.equals(SOUTH))
         return getControlPoint(NORTH);
      else if (control.equals(WEST))
         return getControlPoint(EAST);
      else if (control.equals(NORTHEAST))
         return getControlPoint(SOUTHWEST);
      else if (control.equals(SOUTHEAST))
         return getControlPoint(NORTHWEST);
      else if (control.equals(SOUTHWEST))
         return getControlPoint(NORTHEAST);
      else if (control.equals(NORTHWEST))
         return getControlPoint(SOUTHEAST);
      return null;
   }

   protected LatLon computeControlPointLocation(String control, Globe globe, Angle heading, LatLon center,
           double width, double height)
   {
      Angle azimuth = this.computeControlPointAzimuth(control, width, height);
      Angle pathLength = this.computeControlPointPathLength(control, width, height, globe.getRadiusAt(center));

      if (control.equals(CENTER))
      {
         return center;
      }
      else if (azimuth != null && pathLength != null)
      {
         azimuth = azimuth.add(heading);
         return LatLon.greatCircleEndPosition(center, azimuth, pathLength);
      }

      return null;
   }

   @SuppressWarnings(
   {
      "SuspiciousNameCombination"
   })
   protected Angle computeControlPointAzimuth(String control, double width, double height)
   {
      Angle azimuth = null;

      if (control.equals(NORTH))
         azimuth = Angle.ZERO;
      else if (control.equals(EAST))
         azimuth = Angle.POS90;
      else if (control.equals(SOUTH))
         azimuth = Angle.POS180;
      else if (control.equals(WEST))
         azimuth = Angle.fromDegrees(270);
      else if (control.equals(NORTHEAST))
         azimuth = Angle.fromRadians(Math.atan2(width, height));
      else if (control.equals(SOUTHEAST))
         azimuth = Angle.fromRadians(Math.atan2(width, -height));
      else if (control.equals(SOUTHWEST))
         azimuth = Angle.fromRadians(Math.atan2(-width, -height));
      else if (control.equals(NORTHWEST))
         azimuth = Angle.fromRadians(Math.atan2(-width, height));

      return azimuth != null ? computeNormalizedHeading(azimuth) : null;
   }
  
   protected Angle computeControlPointPathLength(String control, double width, double height, double globeRadius)
   {
      Angle pathLength = null;

      if (control.equals(NORTH) || control.equals(SOUTH))
      {
         pathLength = Angle.fromRadians((height / 2d) / globeRadius);
      }
      else if (control.equals(EAST) || control.equals(WEST))
      {
         pathLength = Angle.fromRadians((width / 2d) / globeRadius);
      }
      else if (control.equals(NORTHEAST) || control.equals(SOUTHEAST) || control.equals(SOUTHWEST)
              || control.equals(NORTHWEST))
      {
         double diag = Math.sqrt((width * width) / 4d + (height * height) / 4d);
         pathLength = Angle.fromRadians(diag / globeRadius);
      }

      return pathLength;
   }

   protected static Angle computeNormalizedHeading(Angle heading)
   {
      double a = heading.degrees % 360;
      double degrees = a > 360 ? a - 360 : a < 0 ? 360 + a : a;
      return Angle.fromDegrees(degrees);
   }

   protected void updateMeasureShape()
   {
      // Update line
      if (this.measureShapeType.equals(SHAPE_LINE) || this.measureShapeType.equals(SHAPE_PATH))
      {
         if (this.positions.size() > 1 && this.line == null)
         {
            // Init polyline
            this.line = new Polyline();
            this.line.setFollowTerrain(true);
            this.line.setLineWidth(this.getLineWidth());
            this.line.setColor(this.getLineColor());
            this.line.setPathType(polylinePathTypeFromKey(this.getPathType()));

            this.shapeLayer.addRenderable(this.line);
         }
         if (this.positions.size() < 2 && this.line != null)
         {
            // Remove line if less then 2 positions
            this.shapeLayer.removeRenderable(this.line);
            this.line = null;
         }
         // Update current line
         if (this.positions.size() > 1 && this.line != null)
            this.line.setPositions(this.positions);

         if (this.surfaceShape != null)
         {
            // Remove surface shape if necessary
            this.shapeLayer.removeRenderable(this.surfaceShape);
            this.surfaceShape = null;
         }
      }
      // Update polygon

   }

   @Override
   public void dispose()
   {
      this.setController(null);
      if (this.applicationLayer != null)
         this.applicationLayer.removeRenderable(this.layer);
      else
         this.wwd.getModel().getLayers().remove(this.layer);
      this.layer.removeAllRenderables();
      this.shapeLayer.removeAllRenderables();
      this.controlPoints.clear();
//        this.controlPointsLayer.removeAllRenderables(); // TODO: why commented out? Are annotations being disposed?
   }

 
   protected void addControlPoint(Position position, String key, Object value)
   {
      ControlPoint controlPoint = new ControlPoint(new Position(position, 0), this.controlPointsAttributes, this);
      controlPoint.setValue(key, value);
      this.doAddControlPoint(controlPoint);
   }

 
   protected void doAddControlPoint(ControlPoint controlPoint)
   {
      this.controlPoints.add(controlPoint);
      this.controlPointsLayer.setRenderables(this.controlPoints);
   }

   public void updateAnnotation(Position pos)
   {
      if (pos == null)
      {
         this.annotation.getAttributes().setVisible(false);
         return;
      }

      String displayString = this.getDisplayString(pos);

      if (displayString == null)
      {
         this.annotation.getAttributes().setVisible(false);
         return;
      }

      this.annotation.setText(displayString);

      Vec4 screenPoint = this.computeAnnotationPosition(pos);
      if (screenPoint != null)
         this.annotation.setScreenPoint(new Point((int) screenPoint.x, (int) screenPoint.y));

      this.annotation.getAttributes().setVisible(true);
   }

   protected String getDisplayString(Position pos)
   {
      String displayString = null;

      if (pos != null)
      {
         if (this.measureShapeType.equals(SHAPE_LINE) || this.measureShapeType.equals(SHAPE_PATH))
         {
            displayString = this.formatLineMeasurements(pos);
         }
      }

      return displayString;
   }

   protected Vec4 computeAnnotationPosition(Position pos)
   {
      Vec4 surfacePoint = this.wwd.getSceneController().getTerrain().getSurfacePoint(
              pos.getLatitude(), pos.getLongitude());
      if (surfacePoint == null)
      {
         Globe globe = this.wwd.getModel().getGlobe();
         surfacePoint = globe.computePointFromPosition(pos.getLatitude(), pos.getLongitude(),
                 globe.getElevation(pos.getLatitude(), pos.getLongitude()));
      }

      return this.wwd.getView().project(surfacePoint);
   }

   protected String formatLineMeasurements(Position pos)
   {
      // TODO: Compute the heading of individual path segments
      StringBuilder sb = new StringBuilder();

      sb.append(this.unitsFormat.lengthNL(this.getLabel(LENGTH_LABEL), this.getLength()));

      Double accumLength = this.computeAccumulatedLength(pos);
      if (accumLength != null && accumLength >= 1 && !lengthsEssentiallyEqual(this.getLength(), accumLength))
         sb.append(this.unitsFormat.lengthNL(this.getLabel(ACCUMULATED_LABEL), accumLength));

      if (this.getOrientation() != null)
         sb.append(this.unitsFormat.angleNL(this.getLabel(HEADING_LABEL), this.getOrientation()));

      sb.append(this.unitsFormat.angleNL(this.getLabel(LATITUDE_LABEL), pos.getLatitude()));
      sb.append(this.unitsFormat.angleNL(this.getLabel(LONGITUDE_LABEL), pos.getLongitude()));

      return sb.toString();
   }

   protected Double computeAccumulatedLength(LatLon pos)
   {
      if (this.positions.size() <= 2)
         return null;

      double radius = this.wwd.getModel().getGlobe().getRadius();
      double distanceFromStart = 0;
      int segmentIndex = 0;
      LatLon pos1 = this.positions.get(segmentIndex);
      for (int i = 1; i < this.positions.size(); i++)
      {
         LatLon pos2 = this.positions.get(i);
         double segmentLength = LatLon.greatCircleDistance(pos1, pos2).radians * radius;

         // Check whether the position is inside the segment
         double length1 = LatLon.greatCircleDistance(pos1, pos).radians * radius;
         double length2 = LatLon.greatCircleDistance(pos2, pos).radians * radius;
         if (length1 <= segmentLength && length2 <= segmentLength)
         {
            // Compute portion of segment length
            distanceFromStart += length1 / (length1 + length2) * segmentLength;
            break;
         }
         else
            distanceFromStart += segmentLength;
         pos1 = pos2;
      }

      double gcPathLength = this.computePathLength();

      return distanceFromStart < gcPathLength ? this.getLength() * (distanceFromStart / gcPathLength) : null;
   }

   protected double computePathLength()
   {
      double pathLengthRadians = 0;

      LatLon pos1 = null;
      for (LatLon pos2 : this.positions)
      {
         if (pos1 != null)
            pathLengthRadians += LatLon.greatCircleDistance(pos1, pos2).radians;
         pos1 = pos2;
      }

      return pathLengthRadians * this.wwd.getModel().getGlobe().getRadius();
   }

   protected boolean lengthsEssentiallyEqual(Double l1, Double l2)
   {
      return Math.abs(l1 - l2) / l1 < 0.001; // equal to within a milimeter
   }

   protected boolean areLocationsRedundant(LatLon locA, LatLon locB)
   {
      if (locA == null || locB == null)
         return false;

      String aLat = this.unitsFormat.angleNL("", locA.getLatitude());
      String bLat = this.unitsFormat.angleNL("", locB.getLatitude());

      if (!aLat.equals(bLat))
         return false;

      String aLon = this.unitsFormat.angleNL("", locA.getLongitude());
      String bLon = this.unitsFormat.angleNL("", locB.getLongitude());

      return aLon.equals(bLon);
   }

}
TOP

Related Classes of _org.geoforge.amd.wwwx.examples.measuretool.GfrMeasureTool

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.