//********************************************************************** // //<copyright> // //BBN Technologies, a Verizon Company //10 Moulton Street //Cambridge, MA 02138 //(617) 873-8000 // //Copyright (C) BBNT Solutions LLC. All rights reserved. // //</copyright> //********************************************************************** // //$Source: ///cvs/darwars/ambush/aar/src/com/bbn/ambush/mission/MissionHandler.java,v //$ //$RCSfile: LatLonPoint.java,v $ //$Revision: 1.2 $ //$Date: 2007/12/03 23:39:31 $ //$Author: dietrick $ // //********************************************************************** package com.bbn.openmap.proj.coords; import java.awt.geom.Point2D; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.Serializable; import com.bbn.openmap.MoreMath; import com.bbn.openmap.proj.GreatCircle; import com.bbn.openmap.proj.ProjMath; /** * A Point2D representation of LatLonPoints, used integrate with the * Projections. These LatLonPoints wrap their internal decimal degree values so * the latitude are between -90 and 90 and the longitudes are between -180 and * 180. Radian values are precalculated and held within the object. * <P> * * The LatLonPoint is an abstract class and can't be instantiated directly. You * need to create a Float or Double version of a LatLonPoint, much like the * Point2D object. * <P> * * @author dietrick */ public abstract class LatLonPoint extends Point2D implements Cloneable, Serializable { /** * */ private static final long serialVersionUID = 4416029542303298672L; public final static double NORTH_POLE = 90.0; public final static double SOUTH_POLE = -NORTH_POLE; public final static double DATELINE = 180.0; public final static double LON_RANGE = 360.0; protected LatLonPoint() { } /** * Factory method that will create a LatLonPoint.Float from a Point2D * object. If pt2D is already a LatLonPoint.Float object, it is simply * returned. * * @param pt2D * @return a LatLonPoint.Float object. */ public static LatLonPoint getFloat(Point2D pt2D) { if (pt2D instanceof Float) { return (Float) pt2D; } else { return new Float(pt2D); } } /** * Factory method that will create a LatLonPoint.Double from a Point2D * object. If pt2D is already a LatLonPoint.Double object, it is simply * returned. * * @param pt2D * @return a LatLonPoint.Double object. */ public static LatLonPoint getDouble(Point2D pt2D) { if (pt2D instanceof Double) { return (Double) pt2D; } else { return new Double(pt2D); } } /** * Set the latitude, longitude for this point. * * @param lat decimal degree latitude * @param lon decimal degree longitude. */ public abstract void setLatLon(double lat, double lon); /** * Set the latitude, longitude for this point, with the option of noting * whether the values are in degrees or radians. * * @param lat latitude * @param lon longitude. * @param isRadians true of values are radians. */ public abstract void setLatLon(double lat, double lon, boolean isRadians); /** * @return decimal degree longitude as a float. */ public abstract float getLongitude(); /** * @return decimal degree latitude as a float. */ public abstract float getLatitude(); /** * @return radian longitude value. */ public abstract double getRadLon(); /** * @return radian latitude value. */ public abstract double getRadLat(); /** * Set decimal degree latitude. */ public abstract void setLatitude(double lat); /** * Set decimal degree longitude. */ public abstract void setLongitude(double lon); /** * The Float version of a LatLonPoint, where coordinates are held to float * precision. */ public static class Float extends LatLonPoint { /** * */ private static final long serialVersionUID = -2447464428275551182L; protected float lat; protected float lon; protected transient float radLat; protected transient float radLon; /** * Default constructor, values set to 0, 0. */ public Float() { } /** * @param lat decimal degree latitude. * @param lon decimal degree longitude. */ public Float(float lat, float lon) { setLatLon(lat, lon, false); } /** * @param lat latitude * @param lon longitude * @param isRadian true if values are radians, false if decimal degrees. */ public Float(float lat, float lon, boolean isRadian) { setLatLon(lat, lon, isRadian); } /** * Create Float version from another LatLonPoint. * * @param llp */ public Float(LatLonPoint llp) { setLatLon(llp.getLatitude(), llp.getLongitude(), false); } /** * Create Float version from Point2D object, where the x, y values are * expected to be decimal degrees. * * @param pt2D */ public Float(Point2D pt2D) { setLatLon(pt2D.getY(), pt2D.getX(), false); } /** * Point2D method, inheriting signature!! * * @param x longitude value in decimal degrees. * @param y latitude value in decimal degrees. */ public void setLocation(float x, float y) { setLatLon(y, x, false); } /** * Point2D method, inheriting signature!! * * @param x longitude value in decimal degrees. * @param y latitude value in decimal degrees. */ public void setLocation(double x, double y) { setLatLon((float) y, (float) x, false); } /** * Set lat/lon values. * * @param lat decimal degree latitude. * @param lon decimal degree longitude. */ public void setLatLon(float lat, float lon) { setLatLon(lat, lon, false); } /** * Set lat/lon values. * * @param lat decimal degree latitude. * @param lon decimal degree longitude. */ public void setLatLon(double lat, double lon) { setLatLon((float) lat, (float) lon, false); } /** * Set lat/lon values. * * @param lat latitude. * @param lon longitude. * @param isRadians true if values are radians. */ public void setLatLon(double lat, double lon, boolean isRadians) { if (isRadians) { radLat = (float) lat; radLon = (float) lon; this.lat = (float) ProjMath.radToDeg(lat); this.lon = (float) ProjMath.radToDeg(lon); } else { this.lat = (float) Double.normalizeLatitude(lat); this.lon = (float) Double.wrapLongitude(lon); radLat = (float) ProjMath.degToRad(lat); radLon = (float) ProjMath.degToRad(lon); } } /** * Set lat/lon values. * * @param lat latitude. * @param lon longitude. * @param isRadians true if values are radians. */ public void setLatLon(float lat, float lon, boolean isRadians) { if (isRadians) { radLat = lat; radLon = lon; this.lat = ProjMath.radToDeg(lat); this.lon = ProjMath.radToDeg(lon); } else { this.lat = normalizeLatitude(lat); this.lon = wrapLongitude(lon); radLat = ProjMath.degToRad(lat); radLon = ProjMath.degToRad(lon); } } /** * Point2D method. * * @return decimal degree longitude. * * @see java.awt.geom.Point2D#getX() */ public double getX() { return (double) lon; } /** * Point2D method * * @return decimal degree latitude. */ public double getY() { return (double) lat; } /** * @return decimal degree longitude. */ public float getLongitude() { return lon; } /** * @return decimal degree latitude. */ public float getLatitude() { return lat; } /** * @return radian longitude. */ public double getRadLon() { return (double) radLon; } /** * @return radian latitude. */ public double getRadLat() { return (double) radLat; } /** * Set latitude. * * @param lat latitude in decimal degrees */ public void setLatitude(float lat) { this.lat = normalizeLatitude(lat); radLat = ProjMath.degToRad(lat); } /** * Set latitude. * * @param lat latitude in decimal degrees */ public void setLatitude(double lat) { setLatitude((float) lat); } /** * Set longitude. * * @param lon longitude in decimal degrees */ public void setLongitude(float lon) { this.lon = wrapLongitude(lon); radLon = ProjMath.degToRad(lon); } /** * Set longitude. * * @param lon longitude in decimal degrees */ public void setLongitude(double lon) { setLongitude((float) lon); } /** * Find a LatLonPoint a distance and direction away from this point, * based on the spherical earth model. * * @param dist distance, in radians. * @param az radians of azimuth (direction) east of north (-PI <= Az * < PI) * @return LatLonPoint result */ public LatLonPoint getPoint(float dist, float az) { return GreatCircle.sphericalBetween(radLat, radLon, dist, az); } /** * Write object. * * @param s DataOutputStream */ public void write(DataOutputStream s) throws IOException { // Write my information s.writeFloat(lat); s.writeFloat(lon); } /** * Read object. Assumes that the floats read off the stream will be in * decimal degrees. Latitude read off the stream first, then longitude. * * @param s DataInputStream */ public void read(DataInputStream s) throws IOException { setLatLon(s.readFloat(), s.readFloat(), false); } /** * Read object. Latitude read off the stream first, then longitude. * * @param s DataInputStream * @param inRadians if true, the floats read off stream will be * considered to be radians. Otherwise, they will be considered * to be decimal degrees. */ public void read(DataInputStream s, boolean inRadians) throws IOException { setLatLon(s.readFloat(), s.readFloat(), inRadians); } /** * Calculate the <code>radlat_</code> and <code>radlon_</code> instance * variables upon deserialization. Also, check <code>lat_</code> and * <code>lon_</code> for safety; someone may have tampered with the * stream. * * @param stream Stream to read <code>lat_</code> and <code>lon_</code> * from. */ private void readObject(java.io.ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); lat = normalizeLatitude(lat); lon = wrapLongitude(lon); radLat = ProjMath.degToRad(lat); radLon = ProjMath.degToRad(lon); } public String toString() { return "LatLonPoint.Float[lat=" + lat + ",lon=" + lon + "]"; } } /** * Double precision version of LatLonPoint. * * @author dietrick */ public static class Double extends LatLonPoint { /** * */ private static final long serialVersionUID = -7463055211717523471L; protected double lat; protected double lon; protected transient double radLat; protected transient double radLon; /** * Default constructor, values set to 0, 0. */ public Double() { } /** * Set the latitude, longitude for this point in decimal degrees. * * @param lat latitude * @param lon longitude. */ public Double(double lat, double lon) { setLatLon(lat, lon, false); } /** * Set the latitude, longitude for this point, with the option of noting * whether the values are in degrees or radians. * * @param lat latitude * @param lon longitude. * @param isRadian true of values are radians. */ public Double(double lat, double lon, boolean isRadian) { setLatLon(lat, lon, isRadian); } /** * Create Double version from another LatLonPoint. * * @param llp */ public Double(LatLonPoint llp) { setLatLon(llp.getY(), llp.getX(), false); } /** * Create Double version from Point2D object, where the x, y values are * expected to be decimal degrees. * * @param pt2D */ public Double(Point2D pt2D) { setLatLon(pt2D.getY(), pt2D.getX(), false); } /** * Point2D method, inheriting signature!! * * @param x longitude value in decimal degrees. * @param y latitude value in decimal degrees. */ public void setLocation(double x, double y) { setLatLon(y, x, false); } /** * Set latitude and longitude. * * @param lat latitude in decimal degrees. * @param lon longitude in decimal degrees. */ public void setLatLon(double lat, double lon) { setLatLon(lat, lon, false); } /** * Set latitude and longitude. * * @param lat latitude. * @param lon longitude. * @param isRadians true if lat/lon values are radians. */ public void setLatLon(double lat, double lon, boolean isRadians) { if (isRadians) { radLat = lat; radLon = lon; this.lat = ProjMath.radToDeg(lat); this.lon = ProjMath.radToDeg(lon); } else { this.lat = normalizeLatitude(lat); this.lon = wrapLongitude(lon); radLat = ProjMath.degToRad(lat); radLon = ProjMath.degToRad(lon); } } /** * @return longitude in decimal degrees. */ public double getX() { return lon; } /** * @return latitude in decimal degrees. */ public double getY() { return lat; } /** * @return float latitude in decimal degrees. */ public float getLatitude() { return (float) lat; } /** * @return float longitude in decimal degrees. */ public float getLongitude() { return (float) lon; } /** * @return radian longitude. */ public double getRadLon() { return radLon; } /** * @return radian latitude. */ public double getRadLat() { return radLat; } /** * Set latitude. * * @param lat latitude in decimal degrees */ public void setLatitude(double lat) { this.lat = normalizeLatitude(lat); radLat = ProjMath.degToRad(lat); } /** * Set longitude. * * @param lon longitude in decimal degrees */ public void setLongitude(double lon) { this.lon = wrapLongitude(lon); radLon = ProjMath.degToRad(lon); } /** * Find a LatLonPoint a distance and direction away from this point, * based on the spherical earth model. * * @param dist distance, in radians. * @param az radians of azimuth (direction) east of north (-PI <= Az * < PI) * @return LatLonPoint result */ public LatLonPoint getPoint(double dist, double az) { return GreatCircle.sphericalBetween(radLat, radLon, dist, az); } /** * Write object. * * @param s DataOutputStream */ public void write(DataOutputStream s) throws IOException { // Write my information s.writeDouble(lat); s.writeDouble(lon); } /** * Read object. Assumes that the floats read off the stream will be in * decimal degrees. Latitude read off the stream first, then longitude. * * @param s DataInputStream */ public void read(DataInputStream s) throws IOException { setLatLon(s.readDouble(), s.readDouble(), false); } /** * Read object. Latitude read off the stream first, then longitude. * * @param s DataInputStream * @param inRadians if true, the floats read off stream will be * considered to be radians. Otherwise, they will be considered * to be decimal degrees. */ public void read(DataInputStream s, boolean inRadians) throws IOException { setLatLon(s.readDouble(), s.readDouble(), inRadians); } /** * Calculate the <code>radLat</code> and <code>radLon</code> instance * variables upon deserialization. Also, check <code>lat</code> and * <code>lon</code> for safety; someone may have tampered with the * stream. * * @param stream Stream to read <code>lat</code> and <code>lon</code> * from. */ private void readObject(java.io.ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); lat = normalizeLatitude(lat); lon = wrapLongitude(lon); radLat = ProjMath.degToRad(lat); radLon = ProjMath.degToRad(lon); } public String toString() { return "LatLonPoint.Double[lat=" + lat + ",lon=" + lon + "]"; } } /** * Set location values from another lat/lon point. * * @param llp */ public void setLatLon(LatLonPoint llp) { setLatLon(llp.getY(), llp.getX(), false); } /** * Ensure latitude is between the poles. * * @param lat * @return latitude greater than or equal to -90 and less than or equal to * 90. */ public final static float normalizeLatitude(float lat) { return (float) normalizeLatitude((double) lat); } /** * Sets latitude to something sane. * * @param lat latitude in decimal degrees * @return float normalized latitude in decimal degrees (−90° ≤ * φ ≤ 90°) */ public final static double normalizeLatitude(double lat) { if (lat > NORTH_POLE) { lat = NORTH_POLE; } if (lat < SOUTH_POLE) { lat = SOUTH_POLE; } return lat; } /** * Ensure the longitude is between the date line. * * @param lon * @return longitude that is smaller than or equal to 180 and greater than * or equal to -180 */ public final static float wrapLongitude(float lon) { return (float) wrapLongitude((double) lon); } /** * Sets longitude to something sane. * * @param lon longitude in decimal degrees * @return float wrapped longitude in decimal degrees (−180° ≤ * λ ≤ 180°) */ public final static double wrapLongitude(double lon) { if ((lon < -DATELINE) || (lon > DATELINE)) { // System.out.print("LatLonPoint: wrapping longitude " // + // lon); lon += DATELINE; lon = lon % LON_RANGE; lon = (lon < 0) ? DATELINE + lon : -DATELINE + lon; // Debug.output(" to " + lon); } return lon; } /** * Check if latitude is bogus. Latitude is invalid if lat > 90° or if * lat < −90°. * * @param lat latitude in decimal degrees * @return boolean true if latitude is invalid */ public static boolean isInvalidLatitude(double lat) { return ((lat > NORTH_POLE) || (lat < SOUTH_POLE)); } /** * Check if longitude is bogus. Longitude is invalid if lon > 180° or * if lon < −180°. * * @param lon longitude in decimal degrees * @return boolean true if longitude is invalid */ public static boolean isInvalidLongitude(double lon) { return ((lon < -DATELINE) || (lon > DATELINE)); } /** * Determines whether two LatLonPoints are equal. * * @param obj Object * @return Whether the two points are equal up to a tolerance of 10 <sup>-5 * </sup> degrees in latitude and longitude. */ public boolean equals(Object obj) { if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final LatLonPoint pt = (LatLonPoint) obj; return (MoreMath.approximately_equal(getY(), pt.getY()) && MoreMath.approximately_equal(getX(), pt.getX())); } /** * Find the distance to another LatLonPoint, based on a earth spherical * model. * * @param toPoint LatLonPoint * @return distance, in radians. You can use an com.bbn.openmap.proj.Length * to convert the radians to other units. */ public double distance(LatLonPoint toPoint) { return GreatCircle.sphericalDistance(getRadLat(), getRadLon(), toPoint.getRadLat(), toPoint.getRadLon()); } /** * Find the azimuth to another point, based on the spherical earth model. * * @param toPoint LatLonPoint * @return the azimuth `Az' east of north from this point bearing toward the * one provided as an argument.(-PI <= Az <= PI). * */ public double azimuth(LatLonPoint toPoint) { return GreatCircle.sphericalAzimuth(getRadLat(), getRadLon(), toPoint.getRadLat(), toPoint.getRadLon()); } /** * Get a new LatLonPoint a distance and azimuth from another point, based on * the spherical earth model. * * @param distance radians * @param azimuth radians * @return LatLonPoint that is distance and azimuth away from this one. */ public LatLonPoint getPoint(double distance, double azimuth) { return GreatCircle.sphericalBetween(getRadLat(), getRadLon(), distance, azimuth); } }