All Downloads are FREE. Search and download functionalities are using the official Maven repository.

gov.nasa.worldwind.render.SurfaceQuad Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2012 United States Government as represented by the Administrator of the
 * National Aeronautics and Space Administration.
 * All Rights Reserved.
 */
package gov.nasa.worldwind.render;

import gov.nasa.worldwind.*;
import gov.nasa.worldwind.avlist.AVKey;
import gov.nasa.worldwind.geom.*;
import gov.nasa.worldwind.globes.*;
import gov.nasa.worldwind.ogc.kml.KMLConstants;
import gov.nasa.worldwind.ogc.kml.impl.KMLExportUtil;
import gov.nasa.worldwind.util.*;

import javax.xml.stream.*;
import java.io.*;
import java.util.*;

/**
 * @author dcollins
 * @version $Id: SurfaceQuad.java 2406 2014-10-29 23:39:29Z dcollins $
 */
public class SurfaceQuad extends AbstractSurfaceShape implements Exportable
{
    protected LatLon center = LatLon.ZERO;
    protected double width;
    protected double height;
    protected Angle heading = Angle.ZERO;

    /**
     * Constructs a new surface quad with the default attributes, default center location, default dimensions, and
     * default heading.
     */
    public SurfaceQuad()
    {
    }

    /**
     * Constructs a new surface quad with the specified normal (as opposed to highlight) attributes, default center
     * location, default dimensions, and default heading. Modifying the attribute reference after calling this
     * constructor causes this shape's appearance to change accordingly.
     *
     * @param normalAttrs the normal attributes. May be null, in which case default attributes are used.
     */
    public SurfaceQuad(ShapeAttributes normalAttrs)
    {
        super(normalAttrs);
    }

    /**
     * Constructs a new surface quad with the default attributes, the specified center location and dimensions (in
     * meters).
     *
     * @param center the quad's center location.
     * @param width  the quad's width, in meters.
     * @param height the quad's height, in meters.
     *
     * @throws IllegalArgumentException if the center is null, or if the width or height are negative.
     */
    public SurfaceQuad(LatLon center, double width, double height)
    {
        if (center == null)
        {
            String message = Logging.getMessage("nullValue.CenterIsNull");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        if (width < 0)
        {
            String message = Logging.getMessage("Geom.WidthIsNegative", width);
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        if (height < 0)
        {
            String message = Logging.getMessage("Geom.HeightIsNegative", height);
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        this.center = center;
        this.width = width;
        this.height = height;
    }

    /**
     * Constructs a new surface quad with the default attributes, the specified center location, dimensions (in meters),
     * and heading clockwise from North.
     *
     * @param center  the quad's center location.
     * @param width   the quad's width, in meters.
     * @param height  the quad's height, in meters.
     * @param heading the quad's heading, clockwise from North.
     *
     * @throws IllegalArgumentException if the center or heading are null, or if the width or height are negative.
     */
    public SurfaceQuad(LatLon center, double width, double height, Angle heading)
    {
        this(center, width, height);

        if (heading == null)
        {
            String message = Logging.getMessage("nullValue.HeadingIsNull");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        this.heading = heading;
    }

    /**
     * Constructs a new surface quad with the specified normal (as opposed to highlight) attributes, the specified
     * center location and dimensions (in meters). Modifying the attribute reference after calling this constructor
     * causes this shape's appearance to change accordingly.
     *
     * @param normalAttrs the normal attributes. May be null, in which case default attributes are used.
     * @param center      the quad's center location.
     * @param width       the quad's width, in meters.
     * @param height      the quad's height, in meters.
     *
     * @throws IllegalArgumentException if the center is null, or if the width or height are negative.
     */
    public SurfaceQuad(ShapeAttributes normalAttrs, LatLon center, double width, double height)
    {
        super(normalAttrs);

        if (center == null)
        {
            String message = Logging.getMessage("nullValue.CenterIsNull");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        if (width < 0)
        {
            String message = Logging.getMessage("Geom.WidthIsNegative", width);
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        if (height < 0)
        {
            String message = Logging.getMessage("Geom.HeightIsNegative", height);
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        this.center = center;
        this.width = width;
        this.height = height;
    }

    /**
     * Creates a shallow copy of the specified source shape.
     *
     * @param source the shape to copy.
     */
    public SurfaceQuad(SurfaceQuad source)
    {
        super(source);

        this.center = source.center;
        this.width = source.width;
        this.height = source.height;
        this.heading = source.heading;
    }

    /**
     * Constructs a new surface quad with the specified normal (as opposed to highlight) attributes, the specified
     * center location and dimensions (in meters). Modifying the attribute reference after calling this constructor
     * causes this shape's appearance to change accordingly.
     *
     * @param normalAttrs the normal attributes. May be null, in which case default attributes are used.
     * @param center      the quad's center location.
     * @param width       the quad's width, in meters.
     * @param height      the quad's height, in meters.
     * @param heading     the quad's heading, clockwise from North.
     *
     * @throws IllegalArgumentException if the center or heading are null, or if the width or height are negative.
     */
    public SurfaceQuad(ShapeAttributes normalAttrs, LatLon center, double width, double height, Angle heading)
    {
        this(normalAttrs, center, width, height);

        if (heading == null)
        {
            String message = Logging.getMessage("nullValue.HeadingIsNull");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        this.heading = heading;
    }

    public LatLon getCenter()
    {
        return this.center;
    }

    public void setCenter(LatLon center)
    {
        if (center == null)
        {
            String message = Logging.getMessage("nullValue.CenterIsNull");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        this.center = center;
        this.onShapeChanged();
    }

    public double getWidth()
    {
        return this.width;
    }

    public double getHeight()
    {
        return this.height;
    }

    public void setWidth(double width)
    {
        if (width < 0)
        {
            String message = Logging.getMessage("Geom.WidthIsNegative", width);
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        this.width = width;
        this.onShapeChanged();
    }

    public void setHeight(double height)
    {
        if (height < 0)
        {
            String message = Logging.getMessage("Geom.HeightIsNegative", height);
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        this.height = height;
        this.onShapeChanged();
    }

    public void setSize(double width, double height)
    {
        if (width < 0)
        {
            String message = Logging.getMessage("Geom.WidthIsNegative", width);
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        if (height < 0)
        {
            String message = Logging.getMessage("Geom.HeightIsNegative", height);
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        this.width = width;
        this.height = height;
        this.onShapeChanged();
    }

    public Angle getHeading()
    {
        return this.heading;
    }

    public void setHeading(Angle heading)
    {
        if (heading == null)
        {
            String message = Logging.getMessage("nullValue.HeadingIsNull");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        this.heading = heading;
        this.onShapeChanged();
    }

    /**
     * {@inheritDoc}
     * 

* Overridden to include the globe's state key in the returned state key. * * @see gov.nasa.worldwind.globes.Globe#getStateKey(DrawContext) */ @Override public Object getStateKey(DrawContext dc) { // Store a copy of the active attributes to insulate the key from changes made to the shape's active attributes. return new SurfaceShapeStateKey(this.getUniqueId(), this.lastModifiedTime, this.getActiveAttributes().copy(), dc.getGlobe().getStateKey(dc)); } public Position getReferencePosition() { return new Position(this.center, 0); } protected void doMoveTo(Position oldReferencePosition, Position newReferencePosition) { Angle heading = LatLon.greatCircleAzimuth(oldReferencePosition, this.center); Angle pathLength = LatLon.greatCircleDistance(oldReferencePosition, this.center); this.setCenter(LatLon.greatCircleEndPosition(newReferencePosition, heading, pathLength)); } protected void doMoveTo(Globe globe, Position oldReferencePosition, Position newReferencePosition) { List locations = new ArrayList(1); locations.add(this.getCenter()); List newLocations = LatLon.computeShiftedLocations(globe, oldReferencePosition, newReferencePosition, locations); this.setCenter(newLocations.get(0)); } public Iterable getLocations(Globe globe) { if (globe == null) { String message = Logging.getMessage("nullValue.GlobeIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } if (this.width == 0 && this.height == 0) return null; double hw = this.width / 2.0; double hh = this.height / 2.0; double globeRadius = globe.getRadiusAt(this.center.getLatitude(), this.center.getLongitude()); double distance = Math.sqrt(hw * hw + hh * hh); double pathLength = distance / globeRadius; double[] cornerAngles = new double[] { Math.atan2(-hh, -hw), Math.atan2(-hh, hw), Math.atan2(hh, hw), Math.atan2(hh, -hw), Math.atan2(-hh, -hw), }; LatLon[] locations = new LatLon[cornerAngles.length]; for (int i = 0; i < cornerAngles.length; i++) { double azimuth = (Math.PI / 2.0) - (cornerAngles[i] - this.heading.radians); locations[i] = LatLon.greatCircleEndPosition(this.center, azimuth, pathLength); } return java.util.Arrays.asList(locations); } protected List> createGeometry(Globe globe, double edgeIntervalsPerDegree) { Iterable originalLocations = this.getLocations(globe); if (originalLocations == null) return null; ArrayList drawLocations = new ArrayList(); this.generateIntermediateLocations(originalLocations, edgeIntervalsPerDegree, false, drawLocations); ArrayList> geom = new ArrayList>(); geom.add(drawLocations); return geom; } //**************************************************************// //******************** Restorable State ***********************// //**************************************************************// protected void doGetRestorableState(RestorableSupport rs, RestorableSupport.StateObject context) { super.doGetRestorableState(rs, context); rs.addStateValueAsLatLon(context, "center", this.getCenter()); rs.addStateValueAsDouble(context, "width", this.getWidth()); rs.addStateValueAsDouble(context, "height", this.getHeight()); rs.addStateValueAsDouble(context, "headingDegrees", this.getHeading().degrees); } protected void doRestoreState(RestorableSupport rs, RestorableSupport.StateObject context) { super.doRestoreState(rs, context); LatLon ll = rs.getStateValueAsLatLon(context, "center"); if (ll != null) this.setCenter(ll); Double d = rs.getStateValueAsDouble(context, "width"); if (d != null) this.setWidth(d); d = rs.getStateValueAsDouble(context, "height"); if (d != null) this.setHeight(d); d = rs.getStateValueAsDouble(context, "headingDegrees"); if (d != null) this.setHeading(Angle.fromDegrees(d)); } protected void legacyRestoreState(RestorableSupport rs, RestorableSupport.StateObject context) { super.legacyRestoreState(rs, context); // Previous versions of SurfaceQuad used half-width and half-height properties. We are now using standard // width and height, so these restored values must be converted. Double width = rs.getStateValueAsDouble(context, "halfWidth"); Double height = rs.getStateValueAsDouble(context, "halfHeight"); if (width != null && height != null) this.setSize(2 * width, 2 * height); // This property has not changed since the previos version, but it's shown here for reference. //LatLon center = rs.getStateValueAsLatLon(context, "center"); //if (center != null) // this.setCenter(center); Double od = rs.getStateValueAsDouble(context, "orientationDegrees"); if (od != null) this.setHeading(Angle.fromDegrees(od)); } /** * Export the polygon to KML as a {@code } element. The {@code output} object will receive the data. This * object must be one of: java.io.Writer java.io.OutputStream javax.xml.stream.XMLStreamWriter * * @param output Object to receive the generated KML. * * @throws javax.xml.stream.XMLStreamException If an exception occurs while writing the KML * @throws java.io.IOException if an exception occurs while exporting the data. * @see #export(String, Object) */ protected void exportAsKML(Object output) throws IOException, XMLStreamException { XMLStreamWriter xmlWriter = null; XMLOutputFactory factory = XMLOutputFactory.newInstance(); boolean closeWriterWhenFinished = true; if (output instanceof XMLStreamWriter) { xmlWriter = (XMLStreamWriter) output; closeWriterWhenFinished = false; } else if (output instanceof Writer) { xmlWriter = factory.createXMLStreamWriter((Writer) output); } else if (output instanceof OutputStream) { xmlWriter = factory.createXMLStreamWriter((OutputStream) output); } if (xmlWriter == null) { String message = Logging.getMessage("Export.UnsupportedOutputObject"); Logging.logger().warning(message); throw new IllegalArgumentException(message); } xmlWriter.writeStartElement("Placemark"); String property = getStringValue(AVKey.DISPLAY_NAME); if (property != null) { xmlWriter.writeStartElement("name"); xmlWriter.writeCharacters(property); xmlWriter.writeEndElement(); } xmlWriter.writeStartElement("visibility"); xmlWriter.writeCharacters(KMLExportUtil.kmlBoolean(this.isVisible())); xmlWriter.writeEndElement(); String shortDescription = (String) getValue(AVKey.SHORT_DESCRIPTION); if (shortDescription != null) { xmlWriter.writeStartElement("Snippet"); xmlWriter.writeCharacters(shortDescription); xmlWriter.writeEndElement(); } String description = (String) getValue(AVKey.BALLOON_TEXT); if (description != null) { xmlWriter.writeStartElement("description"); xmlWriter.writeCharacters(description); xmlWriter.writeEndElement(); } // KML does not allow separate attributes for cap and side, so just use the side attributes. final ShapeAttributes normalAttributes = getAttributes(); final ShapeAttributes highlightAttributes = getHighlightAttributes(); // Write style map if (normalAttributes != null || highlightAttributes != null) { xmlWriter.writeStartElement("StyleMap"); KMLExportUtil.exportAttributesAsKML(xmlWriter, KMLConstants.NORMAL, normalAttributes); KMLExportUtil.exportAttributesAsKML(xmlWriter, KMLConstants.HIGHLIGHT, highlightAttributes); xmlWriter.writeEndElement(); // StyleMap } // Write geometry xmlWriter.writeStartElement("Polygon"); xmlWriter.writeStartElement("extrude"); xmlWriter.writeCharacters("0"); xmlWriter.writeEndElement(); xmlWriter.writeStartElement("altitudeMode"); xmlWriter.writeCharacters("clampToGround"); xmlWriter.writeEndElement(); String globeName = Configuration.getStringValue(AVKey.GLOBE_CLASS_NAME, "gov.nasa.worldwind.globes.Earth"); Globe globe = (Globe) WorldWind.createComponent(globeName); // Outer boundary Iterable outerBoundary = this.getLocations(globe); if (outerBoundary != null) { xmlWriter.writeStartElement("outerBoundaryIs"); KMLExportUtil.exportBoundaryAsLinearRing(xmlWriter, outerBoundary, null); xmlWriter.writeEndElement(); // outerBoundaryIs } xmlWriter.writeEndElement(); // Polygon xmlWriter.writeEndElement(); // Placemark xmlWriter.flush(); if (closeWriterWhenFinished) xmlWriter.close(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy