//********************************************************************** // //<copyright> // //BBN Technologies //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: GeoArray.java,v $ //$Revision: 1.2 $ //$Date: 2007/02/13 20:02:10 $ //$Author: dietrick $ // //********************************************************************** package com.bbn.openmap.geo; /** * A GeoArray is a interface that represents a set of Geo information. Rather * than keeping a set of Geo[] around and managing the memory for all of those * objects, the GeoArray provides an object that just holds onto the coordinates * of those points. * * @author dietrick */ public interface GeoArray { /** * Get a Geo represented by the index i. * * @param i * @return Geo at index i */ Geo get(int i); /** * Load the values for Geo at index i into ret. * * @param i * @param ret * @return ret filled in with values at index i */ Geo get(int i, Geo ret); /** * Get the number of Geo points represented by this array. * * @return number of geo points */ int getSize(); /** * Convert the GeoArray to an array of Geos. * * @return Geo array from values */ Geo[] toPointArray(); /** * Convert the GeoArray to an array of decimal degree values, alternating * lat, lon, lat, lon. * * @return lat,lon array representing decimal degrees of points. */ double[] toLLDegrees(); /** * Convert the GeoArray to an array of radian values, alternating lat, lon, * lat, lon. * * @return lat, lon array representing radians for points. */ double[] toLLRadians(); /** * @param index the index of the Geo in the GeoArray to compare. * @param comp the Geo to compare to the indexed value. * @return true of x, y, and z of the Geos match. */ boolean equals(int index, Geo comp); /** * Returns the perpendicular distance to the closest point on the edge of the * polygon. * * @param geo the point to test against the poly * @param closestPoint will be filled with location of poly edge point closest to geo. * @return the distance in radians */ double distance(Geo geo, Geo closestPoint); /** * Compute the area of the GeoArray polygon on the surface of a unit sphere * given an enumeration of its point. For a non unit sphere, multiply this by * the radius of sphere squared. * * @return area value. */ double area(); /** * Ensure that the Geo array starts and ends with the same values. Will * replace the current coord array with one three floats longer if needed. */ void closeArray(); /** * Modify, if needed, the Geo array with the duplicates removed. */ void removeDups(); /** * A Mutable GeoArray is one where the points can be modified. * * @author dietrick */ public static interface Mutable extends GeoArray { /** * Set the values for the provided index to the values represented by g. * * @param i * @param g */ void set(int i, Geo g); /** * Set the values for the provided index to the values x, y, z, which are * vector Geo values, *not* lat, lon and height. * * @param i * @param x * @param y * @param z */ void set(int i, double x, double y, double z); /** * Set the values for the provided index to the latitude, longitude. * * @param i * @param lat * @param lon * @param isDegrees true if lat/lon in decimal degrees. */ void set(int i, double lat, double lon, boolean isDegrees); } /** * An abstract parent implementation class of GeoArray that handles common * methods. * * @author dietrick */ public static abstract class Adapter implements GeoArray { /** * Convert the GeoArray to an array of Geos. * * @return Geo[] */ public Geo[] toPointArray() { int size = getSize(); Geo[] geos = new Geo[size]; for (int i = 0; i < size; i++) { geos[i] = get(i, new Geo()); } return geos; } /** * Convert the GeoArray to an array of decimal degree values, alternating * lat, lon, lat, lon. * * @return lat/lon double[] of decimal degrees */ public double[] toLLDegrees() { int size = getSize(); double[] coords = new double[size * 2]; Geo storage = new Geo(); for (int i = 0; i < size; i++) { get(i, storage); int loc = i * 2; coords[loc] = storage.getLatitude(); coords[loc + 1] = storage.getLongitude(); } return coords; } /** * Convert the GeoArray to an array of radian values, alternating lat, * lon, lat, lon. * * @return lat/lon double[] of radians */ public double[] toLLRadians() { int size = getSize(); double[] coords = new double[size * 2]; Geo storage = new Geo(); for (int i = 0; i < size; i++) { get(i, storage); int loc = i * 2; coords[loc] = storage.getLatitudeRadians(); coords[loc + 1] = storage.getLongitudeRadians(); } return coords; } /** * Computes the area of a polygon on the surface of a unit sphere. For a * non unit sphere, multiply this by the radius of sphere squared. The * value might be negative based on the counter-clockwise order of the * coordinates, but the absolute value is valid. This method will test for * closed polygon coordinates and handle that situation. */ public double area() { int count = 0; double area = 0; Geo v0 = get(0, new Geo()); Geo v1 = get(1, new Geo()); Geo p0 = new Geo(v0); Geo p1 = new Geo(v1); Geo p2 = get(getSize() - 1, new Geo()); // Having the first and last points the same messes up the // algorithm. // So skip the last point if it equals the first. boolean closed = p0.equals(p2); int size = getSize() - (closed ? 1 : 0); for (int i = 2; i < size; i++) { count++; get(i, p2); area += Geo.angle(p0, p1, p2); p0.initialize(p1); p1.initialize(p2); } count++; p2.initialize(v0); area += Geo.angle(p0, p1, p2); p0.initialize(p1); p1.initialize(p2); count++; p2.initialize(v1); area += Geo.angle(p0, p1, p2); return area - ((count - 2) * Math.PI); } /** * Returns the perpendicular distance to the closest point on the edge of * the polygon. * * @param pnt the point to test against the poly * @param closestPoint if not null, will be set with the location of the * point on the poly closest to pnt, you can read this object after * this method call to get coordinates. * @return the distance in radians, or Double.POSITIVE_INFINITY if * something weird happens. */ public double distance(Geo pnt, Geo closestPoint) { double ret = java.lang.Double.POSITIVE_INFINITY; double testDist = ret; int size = getSize(); Geo p0 = get(0, new Geo()); Geo p1 = new Geo(); Geo intersect = new Geo(); for (int i = 1; i < size; i++) { get(i, p1); // Don't want to do distance test if end points are the same. if (p0.equals(p1)) { continue; } // The test needs to check two things - the distance between pnt and // the great circle line between p1 and p2. It should also check to // make sure that the perpendicular line intersects that great // circle line between p0 and p1. We'll calculate the distance // first, and then if the distance is the shortest seen so far, // we'll test the distance between that intersection point and make // sure that it's less than the distance between the two points. testDist = Intersection.pointCircleDistance(p0, p1, pnt); System.out.println("testing " + p0 + ", " + p1 + ", getting distance of " + testDist); if (testDist < ret) { // Find the point where the perpendicular line intersects the // great circle intersect = p0.intersect(p1, pnt, intersect); System.out.println("candidate received, gc intersected at " + intersect); if (Intersection.isOnSegment(p0, p1, intersect)) { // Shortest distance, and between points ret = testDist; if (closestPoint != null) { closestPoint.initialize(intersect); } } } // Move to next point in array. p0.initialize(p1); } return ret; } } /** * An implementation of GeoArray and GeoArray.Mutable that contains * float-precision values. Holds the coordinates in a float array of x, y, z, * x, y, z values. * * @author dietrick */ public static class Float extends Adapter implements Mutable { private float[] coords; public Float(Geo[] geos) { coords = new float[geos.length * 3]; for (int i = 0; i < geos.length; i++) { int loc = i * 3; Geo geo = geos[i]; coords[loc] = (float) geo.x(); coords[loc + 1] = (float) geo.y(); coords[loc + 2] = (float) geo.z(); } } public Float(GeoArray ga) { int size = ga.getSize(); coords = new float[size * 3]; Geo geo = new Geo(); for (int i = 0; i < size; i++) { int loc = i * 3; ga.get(i, geo); coords[loc] = (float) geo.x(); coords[loc + 1] = (float) geo.y(); coords[loc + 2] = (float) geo.z(); } } protected Float(float[] coords) { this.coords = coords; } public static Float createFromLatLonDegrees(float[] latlondeg) { int numCoordSets = latlondeg.length / 2; float[] coords = new float[numCoordSets * 3]; Geo geo = new Geo(); for (int i = 0; i < numCoordSets; i++) { geo.initialize(latlondeg[i * 2], latlondeg[i * 2 + 1]); int loc = i * 3; coords[loc] = (float) geo.x(); coords[loc + 1] = (float) geo.y(); coords[loc + 2] = (float) geo.z(); } return new Float(coords); } public static Float createFromLatLonDegrees(double[] latlondeg) { int numCoordSets = latlondeg.length / 2; float[] coords = new float[numCoordSets * 3]; Geo geo = new Geo(); for (int i = 0; i < numCoordSets; i++) { geo.initialize(latlondeg[i * 2], latlondeg[i * 2 + 1]); int loc = i * 3; coords[loc] = (float) geo.x(); coords[loc + 1] = (float) geo.y(); coords[loc + 2] = (float) geo.z(); } return new Float(coords); } public static Float createFromLatLonRadians(float[] latlonrad) { int numCoordSets = latlonrad.length / 2; float[] coords = new float[numCoordSets * 3]; Geo geo = new Geo(); for (int i = 0; i < numCoordSets; i++) { geo.initializeRadians(latlonrad[i * 2], latlonrad[i * 2 + 1]); int loc = i * 3; coords[loc] = (float) geo.x(); coords[loc + 1] = (float) geo.y(); coords[loc + 2] = (float) geo.z(); } return new Float(coords); } public static Float createFromLatLonRadians(double[] latlonrad) { int numCoordSets = latlonrad.length / 2; float[] coords = new float[numCoordSets * 3]; Geo geo = new Geo(); for (int i = 0; i < numCoordSets; i++) { geo.initializeRadians(latlonrad[i * 2], latlonrad[i * 2 + 1]); int loc = i * 3; coords[loc] = (float) geo.x(); coords[loc + 1] = (float) geo.y(); coords[loc + 2] = (float) geo.z(); } return new Float(coords); } public static Float createFromGeoCoords(float[] xyz) { return new Float(xyz); } public int getSize() { if (coords != null) { return coords.length / 3; } return 0; } public void set(int i, double x, double y, double z) { int loc = i * 3; coords[loc] = (float) x; coords[loc + 1] = (float) y; coords[loc + 2] = (float) z; } public void set(int i, Geo g) { set(i, g.x(), g.y(), g.z()); } public void set(int i, double lat, double lon, boolean isDegrees) { set(i, new Geo(lat, lon, isDegrees)); } public Geo get(int i) { return get(i, new Geo()); } public Geo get(int i, Geo ret) { int loc = i * 3; double x = coords[loc]; double y = coords[loc + 1]; double z = coords[loc + 2]; ret.initialize(x, y, z); return ret; } public boolean equals(int index, Geo comp) { int loc = index * 3; double x = coords[loc]; double y = coords[loc + 1]; double z = coords[loc + 2]; return x == comp.x() && y == comp.y() && z == comp.z(); } /** * Ensure that the Geo array starts and ends with the same values. Will * replace the current coord array with one three floats longer if needed. */ public void closeArray() { int l = coords.length; int i = l - 3; if (coords[0] != coords[i] || coords[1] != coords[i + 1] || coords[2] != coords[i + 2]) { float[] newCoords = new float[l + 3]; System.arraycopy(coords, 0, newCoords, 0, l); newCoords[l] = coords[0]; newCoords[l + 1] = coords[1]; newCoords[l + 2] = coords[2]; coords = newCoords; } } /** * Modify, if needed, the Geo array with the duplicates removed. */ public void removeDups() { Geo[] ga = toPointArray(); Geo[] r = new Geo[ga.length]; int p = 0; for (int i = 0; i < ga.length; i++) { if (p == 0 || !(r[p - 1].equals(ga[i]))) { r[p] = ga[i]; p++; } } if (p != ga.length) { coords = new float[p * 3]; for (int i = 0; i < p; i++) { int loc = i * 3; Geo geo = r[i]; coords[loc] = (float) geo.x(); coords[loc + 1] = (float) geo.y(); coords[loc + 2] = (float) geo.z(); } } } } /** * An implementation of GeoArray and GeoArray.Mutable that contains * double-precision values. Holds the coordinates in a double array of x, y, * z, x, y, z values. * * @author dietrick */ public static class Double extends Adapter implements Mutable { private double[] coords; public Double(Geo[] geos) { coords = new double[geos.length * 3]; for (int i = 0; i < geos.length; i++) { int loc = i * 3; Geo geo = geos[i]; coords[loc] = geo.x(); coords[loc + 1] = geo.y(); coords[loc + 2] = geo.z(); } } public Double(GeoArray ga) { int size = ga.getSize(); coords = new double[size * 3]; Geo geo = new Geo(); for (int i = 0; i < size; i++) { int loc = i * 3; ga.get(i, geo); coords[loc] = geo.x(); coords[loc + 1] = geo.y(); coords[loc + 2] = geo.z(); } } protected Double(double[] coords) { this.coords = coords; } public static Double createFromLatLonDegrees(double[] latlondeg) { int numCoordSets = latlondeg.length / 2; double[] coords = new double[numCoordSets * 3]; Geo geo = new Geo(); for (int i = 0; i < numCoordSets; i++) { geo.initialize(latlondeg[i * 2], latlondeg[i * 2 + 1]); int loc = i * 3; coords[loc] = geo.x(); coords[loc + 1] = geo.y(); coords[loc + 2] = geo.z(); } return new Double(coords); } public static Double createFromLatLonRadians(double[] latlonrad) { int numCoordSets = latlonrad.length / 2; double[] coords = new double[numCoordSets * 3]; Geo geo = new Geo(); for (int i = 0; i < numCoordSets; i++) { geo.initializeRadians(latlonrad[i * 2], latlonrad[i * 2 + 1]); int loc = i * 3; coords[loc] = geo.x(); coords[loc + 1] = geo.y(); coords[loc + 2] = geo.z(); } return new Double(coords); } public static Double createFromGeoCoords(double[] xyz) { return new Double(xyz); } public int getSize() { if (coords != null) { return coords.length / 3; } return 0; } public void set(int i, double x, double y, double z) { int loc = i * 3; coords[loc] = x; coords[loc + 1] = y; coords[loc + 2] = z; } public void set(int i, Geo g) { set(i, g.x(), g.y(), g.z()); } public void set(int i, double lat, double lon, boolean isDegrees) { set(i, new Geo(lat, lon, isDegrees)); } public Geo get(int i) { return get(i, new Geo()); } public Geo get(int i, Geo ret) { if (ret == null) { ret = new Geo(); } int loc = i * 3; double x = coords[loc]; double y = coords[loc + 1]; double z = coords[loc + 2]; ret.initialize(x, y, z); return ret; } public boolean equals(int index, Geo comp) { int loc = index * 3; double x = coords[loc]; double y = coords[loc + 1]; double z = coords[loc + 2]; return x == comp.x() && y == comp.y() && z == comp.z(); } /** * Ensure that the Geo array starts and ends with the same values. Will * replace the current coord array with one three double longer if needed. */ public void closeArray() { int l = coords.length; int i = l - 3; if (coords[0] != coords[i] || coords[1] != coords[i + 1] || coords[2] != coords[i + 2]) { double[] newCoords = new double[l + 3]; System.arraycopy(coords, 0, newCoords, 0, l); newCoords[l] = coords[0]; newCoords[l + 1] = coords[1]; newCoords[l + 2] = coords[2]; coords = newCoords; } } /** * Modify, if needed, the Geo array with the duplicates removed. */ public void removeDups() { Geo[] ga = toPointArray(); Geo[] r = new Geo[ga.length]; int p = 0; for (int i = 0; i < ga.length; i++) { if (p == 0 || !(r[p - 1].equals(ga[i]))) { r[p] = ga[i]; p++; } } if (p != ga.length) { coords = new double[p * 3]; for (int i = 0; i < p; i++) { int loc = i * 3; Geo geo = r[i]; coords[loc] = geo.x(); coords[loc + 1] = geo.y(); coords[loc + 2] = geo.z(); } } } } }