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

src.gov.nasa.worldwind.geom.Position Maven / Gradle / Ivy

Go to download

World Wind is a collection of components that interactively display 3D geographic information within Java applications or applets.

There is a newer version: 2.0.0-986
Show 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.geom;

import gov.nasa.worldwind.util.*;

import java.util.*;

/**
 * @author tag
 * @version $Id: Position.java 1171 2013-02-11 21:45:02Z dcollins $
 */
public class Position extends LatLon
{
    public static final Position ZERO = new Position(Angle.ZERO, Angle.ZERO, 0d);

    public final double elevation;

    public static Position fromRadians(double latitude, double longitude, double elevation)
    {
        return new Position(Angle.fromRadians(latitude), Angle.fromRadians(longitude), elevation);
    }

    public static Position fromDegrees(double latitude, double longitude, double elevation)
    {
        return new Position(Angle.fromDegrees(latitude), Angle.fromDegrees(longitude), elevation);
    }

    public static Position fromDegrees(double latitude, double longitude)
    {
        return new Position(Angle.fromDegrees(latitude), Angle.fromDegrees(longitude), 0);
    }

    public Position(Angle latitude, Angle longitude, double elevation)
    {
        super(latitude, longitude);
        this.elevation = elevation;
    }

    public Position(LatLon latLon, double elevation)
    {
        super(latLon);
        this.elevation = elevation;
    }

    // A class that makes it easier to pass around position lists.
    public static class PositionList
    {
        public List list;

        public PositionList(List list)
        {
            this.list = list;
        }
    }

    /**
     * Obtains the elevation of this position
     *
     * @return this position's elevation
     */
    public double getElevation()
    {
        return this.elevation;
    }

    /**
     * Obtains the elevation of this position
     *
     * @return this position's elevation
     */
    public double getAltitude()
    {
        return this.elevation;
    }

    public Position add(Position that)
    {
        Angle lat = Angle.normalizedLatitude(this.latitude.add(that.latitude));
        Angle lon = Angle.normalizedLongitude(this.longitude.add(that.longitude));

        return new Position(lat, lon, this.elevation + that.elevation);
    }

    public Position subtract(Position that)
    {
        Angle lat = Angle.normalizedLatitude(this.latitude.subtract(that.latitude));
        Angle lon = Angle.normalizedLongitude(this.longitude.subtract(that.longitude));

        return new Position(lat, lon, this.elevation - that.elevation);
    }

    /**
     * Returns the linear interpolation of value1 and value2, treating the geographic
     * locations as simple 2D coordinate pairs, and treating the elevation values as 1D scalars.
     *
     * @param amount the interpolation factor
     * @param value1 the first position.
     * @param value2 the second position.
     *
     * @return the linear interpolation of value1 and value2.
     *
     * @throws IllegalArgumentException if either position is null.
     */
    public static Position interpolate(double amount, Position value1, Position value2)
    {
        if (value1 == null || value2 == null)
        {
            String message = Logging.getMessage("nullValue.PositionIsNull");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        if (amount < 0)
            return value1;
        else if (amount > 1)
            return value2;

        LatLon latLon = LatLon.interpolate(amount, value1, value2);
        // Elevation is independent of geographic interpolation method (i.e. rhumb, great-circle, linear), so we
        // interpolate elevation linearly.
        double elevation = WWMath.mix(amount, value1.getElevation(), value2.getElevation());

        return new Position(latLon, elevation);
    }

    /**
     * Returns the an interpolated location along the great-arc between value1 and value2. The
     * position's elevation components are linearly interpolated as a simple 1D scalar value. The interpolation factor
     * amount defines the weight given to each value, and is clamped to the range [0, 1]. If a
     * is 0 or less, this returns value1. If amount is 1 or more, this returns
     * value2. Otherwise, this returns the position on the great-arc between value1 and
     * value2 with a linearly interpolated elevation component, and corresponding to the specified
     * interpolation factor.
     *
     * @param amount the interpolation factor
     * @param value1 the first position.
     * @param value2 the second position.
     *
     * @return an interpolated position along the great-arc between value1 and value2, with a
     *         linearly interpolated elevation component.
     *
     * @throws IllegalArgumentException if either location is null.
     */
    public static Position interpolateGreatCircle(double amount, Position value1, Position value2)
    {
        if (value1 == null || value2 == null)
        {
            String message = Logging.getMessage("nullValue.PositionIsNull");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        LatLon latLon = LatLon.interpolateGreatCircle(amount, value1, value2);
        // Elevation is independent of geographic interpolation method (i.e. rhumb, great-circle, linear), so we
        // interpolate elevation linearly.
        double elevation = WWMath.mix(amount, value1.getElevation(), value2.getElevation());

        return new Position(latLon, elevation);
    }

    /**
     * Returns the an interpolated location along the rhumb line between value1 and value2.
     * The position's elevation components are linearly interpolated as a simple 1D scalar value. The interpolation
     * factor amount defines the weight given to each value, and is clamped to the range [0, 1]. If
     * a is 0 or less, this returns value1. If amount is 1 or more, this returns
     * value2. Otherwise, this returns the position on the rhumb line between value1 and
     * value2 with a linearly interpolated elevation component, and corresponding to the specified
     * interpolation factor.
     *
     * @param amount the interpolation factor
     * @param value1 the first position.
     * @param value2 the second position.
     *
     * @return an interpolated position along the great-arc between value1 and value2, with a
     *         linearly interpolated elevation component.
     *
     * @throws IllegalArgumentException if either location is null.
     */
    public static Position interpolateRhumb(double amount, Position value1, Position value2)
    {
        if (value1 == null || value2 == null)
        {
            String message = Logging.getMessage("nullValue.PositionIsNull");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        LatLon latLon = LatLon.interpolateRhumb(amount, value1, value2);
        // Elevation is independent of geographic interpolation method (i.e. rhumb, great-circle, linear), so we
        // interpolate elevation linearly.
        double elevation = WWMath.mix(amount, value1.getElevation(), value2.getElevation());

        return new Position(latLon, elevation);
    }

    public static boolean positionsCrossDateLine(Iterable positions)
    {
        if (positions == null)
        {
            String msg = Logging.getMessage("nullValue.PositionsListIsNull");
            Logging.logger().severe(msg);
            throw new IllegalArgumentException(msg);
        }

        Position pos = null;
        for (Position posNext : positions)
        {
            if (pos != null)
            {
                // A segment cross the line if end pos have different longitude signs
                // and are more than 180 degress longitude apart
                if (Math.signum(pos.getLongitude().degrees) != Math.signum(posNext.getLongitude().degrees))
                {
                    double delta = Math.abs(pos.getLongitude().degrees - posNext.getLongitude().degrees);
                    if (delta > 180 && delta < 360)
                        return true;
                }
            }
            pos = posNext;
        }

        return false;
    }

    /**
     * Computes a new set of positions translated from a specified reference position to a new reference position.
     *
     * @param oldPosition the original reference position.
     * @param newPosition the new reference position.
     * @param positions   the positions to translate.
     *
     * @return the translated positions, or null if the positions could not be translated.
     *
     * @throws IllegalArgumentException if any argument is null.
     */
    public static List computeShiftedPositions(Position oldPosition, Position newPosition,
        Iterable positions)
    {
        // TODO: Account for dateline spanning
        if (oldPosition == null || newPosition == null)
        {
            String msg = Logging.getMessage("nullValue.PositionIsNull");
            Logging.logger().severe(msg);
            throw new IllegalArgumentException(msg);
        }

        if (positions == null)
        {
            String msg = Logging.getMessage("nullValue.PositionsListIsNull");
            Logging.logger().severe(msg);
            throw new IllegalArgumentException(msg);
        }

        ArrayList newPositions = new ArrayList();

        double elevDelta = newPosition.getElevation() - oldPosition.getElevation();

        for (Position pos : positions)
        {
            Angle distance = LatLon.greatCircleDistance(oldPosition, pos);
            Angle azimuth = LatLon.greatCircleAzimuth(oldPosition, pos);
            LatLon newLocation = LatLon.greatCircleEndPosition(newPosition, azimuth, distance);
            double newElev = pos.getElevation() + elevDelta;

            newPositions.add(new Position(newLocation, newElev));
        }

        return newPositions;
    }

    @Override
    public boolean equals(Object o)
    {
        if (this == o)
            return true;
        if (o == null || getClass() != o.getClass())
            return false;
        if (!super.equals(o))
            return false;

        Position position = (Position) o;

        //noinspection RedundantIfStatement
        if (Double.compare(position.elevation, elevation) != 0)
            return false;

        return true;
    }

    @Override
    public int hashCode()
    {
        int result = super.hashCode();
        long temp;
        temp = elevation != +0.0d ? Double.doubleToLongBits(elevation) : 0L;
        result = 31 * result + (int) (temp ^ (temp >>> 32));
        return result;
    }

    public String toString()
    {
        return "(" + this.latitude.toString() + ", " + this.longitude.toString() + ", " + this.elevation + ")";
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy