/* * * * Copyright 1990-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 only, as published by the Free Software Foundation. * * 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 * General Public License version 2 for more details (a copy is * included at /legal/license.txt). * * You should have received a copy of the GNU General Public License * version 2 along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa * Clara, CA 95054 or visit www.sun.com if you need additional * information or have any questions. */ package javax.microedition.location; import com.sun.j2me.location.*; /** * This class is defined by the JSR-179 specification * <em>Location API for J2ME for J2ME™.</em> */ // JAVADOC COMMENT ELIDED public class Coordinates { // JAVADOC COMMENT ELIDED public static final int DD_MM_SS = 1; // JAVADOC COMMENT ELIDED public static final int DD_MM = 2; // JAVADOC COMMENT ELIDED private double latitude; // JAVADOC COMMENT ELIDED private double longitude; // JAVADOC COMMENT ELIDED private float altitude; // JAVADOC COMMENT ELIDED static final double EARTH_RADIUS = 6378137D; // JAVADOC COMMENT ELIDED static final double FLATTENING = 298.257223563D; // JAVADOC COMMENT ELIDED static final double DEG2RAD = 0.01745329252D; // JAVADOC COMMENT ELIDED private float azimuth; // JAVADOC COMMENT ELIDED private float distance; // JAVADOC COMMENT ELIDED public Coordinates(double latitude, double longitude, float altitude) { setLatitude(latitude); setLongitude(longitude); this.altitude = altitude; } // JAVADOC COMMENT ELIDED public double getLatitude() { return latitude; } // JAVADOC COMMENT ELIDED public double getLongitude() { return longitude; } // JAVADOC COMMENT ELIDED public float getAltitude() { return altitude; } // JAVADOC COMMENT ELIDED public void setAltitude(float altitude) { this.altitude = altitude; } // JAVADOC COMMENT ELIDED public void setLatitude(double latitude) { Util.checkRange(latitude, -90, 90, "Latitude out of range [-90.0, 90]: "); this.latitude = latitude; } // JAVADOC COMMENT ELIDED public void setLongitude(double longitude) { Util.checkRange(longitude, -180, 180, "Longitude out of range [-180.0, 180): "); if (longitude == 180D) { throw new IllegalArgumentException( "Longitude out of range [-180.0, 180): " + longitude); } this.longitude = longitude; } // JAVADOC COMMENT ELIDED public static double convert(String coordinate) { if (coordinate == null) { throw new NullPointerException("Null string specified"); } // tokenize the coordianates to 2 or 3 elements if (coordinate.startsWith("0") && (!coordinate.startsWith("0:"))) { throw new IllegalArgumentException( "A coordinate cannot start with a 0 with two digits"); } double[] coordinates = new double[] { Double.NaN, Double.NaN, 0 }; int next = -1; int current = 0; do { if (current > 2) { throw new IllegalArgumentException( "Invalid coordinate format"); } int position = next + 1; next = coordinate.indexOf(':', position); String currentText; if (next > -1) { currentText = coordinate.substring(position, next); try { coordinates[current] = Double.parseDouble(currentText); } catch (NumberFormatException e) { throw new IllegalArgumentException( "Invalid coordinate format: " + e.getMessage()); } // only the last coordinate may be a fracture if ((long)coordinates[current] != coordinates[current]) { throw new IllegalArgumentException( "Only the last coordinate may be a fracture: " + coordinate); } } else { currentText = coordinate.substring(position, coordinate.length()); try { coordinates[current] = Double.parseDouble(currentText); } catch (NumberFormatException e) { throw new IllegalArgumentException( "Invalid coordinate format: " + e.getMessage()); } } if (currentText.startsWith("+")) { throw new IllegalArgumentException( "Coordinate should not use 'plus' sign :" + currentText); } if (current > 0) { int pos = currentText.indexOf('.'); if (pos > -1) { if (pos != 2 || currentText.length() < 4) { throw new IllegalArgumentException( "Invalid coordinate format"); } if (current != 2) { if (currentText.length() - pos > 6) { throw new IllegalArgumentException( "Invalid coordinate format"); } } else { if (currentText.length() - pos > 4) { throw new IllegalArgumentException( "Invalid coordinate format"); } } } else { if (currentText.length() != 2) { throw new IllegalArgumentException( "Invalid coordinate format"); } } } if (currentText.endsWith(".")) { throw new IllegalArgumentException( "Invalid coordinate format"); } current++; } while (next > -1); // special case for 180 when the degrees is -180 and the // minutes, seconds and decimal fractions are 0 if (coordinates[0] != -180D) { Util.checkRange(coordinates[0], -179, 179, "Degrees out of range [-179.0, 179]: "); Util.checkRange(coordinates[1], 0, 60, "Minutes out of range [0, 59]: "); Util.checkRange(coordinates[2], 0, 60, "Seconds out of range [0, 59]: "); if (coordinates[1] == 60D) { throw new IllegalArgumentException( "Minutes out of range [0, 59]: 60"); } if (coordinates[2] == 60D) { throw new IllegalArgumentException( "Seconds out of range [0, 59]: 60"); } if (Double.isNaN(coordinates[1])) { throw new IllegalArgumentException( "Invalid coordinate format"); } } else { if (coordinates[1] != 0D || coordinates[2] != 0D) { throw new IllegalArgumentException( "Invalid coordinate format"); } } // convert the integer array to a numeric representation: double value = coordinates[0]; if (!coordinate.startsWith("-")) { value += coordinates[1] / 60 + coordinates[2] / 3600; } else { value -= coordinates[1] / 60 + coordinates[2] / 3600; } return value; } // JAVADOC COMMENT ELIDED public static String convert(double coordinate, int outputType) { if (coordinate == 180D || Double.isNaN(coordinate)) { throw new IllegalArgumentException("Coordinate out of range"); } Util.checkRange(coordinate, -180, 180, "Coordinate out of range [-180.0, 180): "); StringBuffer buffer = new StringBuffer(); if (coordinate < 0) { buffer.append("-"); } coordinate = Math.abs(coordinate); int deg = (int)coordinate; buffer.append(deg); buffer.append(":"); double dMin = (coordinate - deg) * 60D; if (outputType == DD_MM_SS) { int min1 = (int)dMin; if (min1 == 60) min1 = 59; if (min1 < 10) { buffer.append("0"); } buffer.append(min1); buffer.append(":"); double dSec = (dMin - min1) * 60D; double sec1 = (double)(int)Math.floor(1000D * dSec + 0.5D) / 1000D; if (sec1 >= 60) sec1 = 59.999; if (sec1 < 10) { buffer.append("0"); } buffer.append(sec1); } else { if (outputType != DD_MM) { throw new IllegalArgumentException( "outputType must be either DD_MM or DD_MM_SS, " + "instead we got: " + outputType); } double min2 = (double)(int)Math.floor(100000D * dMin + 0.5D) / 100000D; if (min2 >= 60) min2 = 59.99999; if (min2 < 10) { buffer.append("0"); } buffer.append(min2); } return buffer.toString(); } // JAVADOC COMMENT ELIDED public float azimuthTo(Coordinates to) { if (to == null) { throw new NullPointerException("Null coordinates specified"); } computeAzimuthAndDistance(latitude, longitude, to.latitude, to.longitude); return azimuth; } // JAVADOC COMMENT ELIDED public float distance(Coordinates to) { if (to == null) { throw new NullPointerException("Null coordinates specified"); } computeAzimuthAndDistance(latitude, longitude, to.latitude, to.longitude); return distance; } // JAVADOC COMMENT ELIDED private void computeAzimuthAndDistance(double lat1, double long1, double lat2, double long2) { if ((lat1 == lat2) && (long1 == long2)) { azimuth = 0; distance = 0; return; } double c = 0.0; double d = 0.0; double e = 0.0; double y = 0.0; double sa = 0.0; double sx = 0.0; double sy = 0.0; double cx = 0.0; double cy = 0.0; double cz = 0.0; double c2a = 0.0; double f = 1.0D / FLATTENING; // Flattening factor // Initial values double eps = 0.5E-13; // Tolerence double glon1 = long1 * DEG2RAD; double glat1 = lat1 * DEG2RAD; double glon2 = long2 * DEG2RAD; double glat2 = lat2 * DEG2RAD; double r = 1.0D - f; double tu1 = r * Math.sin(glat1) / Math.cos(glat1); double tu2 = r * Math.sin(glat2) / Math.cos(glat2); double cu1 = 1 / Math.sqrt(1 + tu1 * tu1); double su1 = cu1 * tu1; double cu2 = 1 / Math.sqrt(1 + tu2 * tu2); double s = cu1 * cu2; double baz = s * tu2; double faz = baz * tu1; double x = glon2 - glon1; // Iterate do { sx = Math.sin(x); cx = Math.cos(x); tu1 = cu2 * sx; tu2 = baz - su1 * cu2 * cx; sy = Math.sqrt(tu1 * tu1 + tu2 * tu2); cy = s * cx + faz; y = LocationMath.atan2(sy, cy); sa = s * sx / sy; c2a = -sa * sa + 1; cz = faz + faz; if (c2a > 0) { cz = -cz / c2a + cy; } e = cz * cz * 2 - 1; c = ((-3 * c2a + 4) * f + 4) * c2a * f / 16; d = x; x = ((e * cy * c + cz) * sy * c + y) * sa; x = (1 - c) * x * f + glon2 - glon1; } while (Math.abs(d - x) > eps); // Finish up faz = LocationMath.atan2(tu1, tu2); azimuth = (float)(faz / DEG2RAD); if (azimuth < 0) { azimuth += 360; } if (lat1 == 90D) { azimuth = 180F; } else if (lat1 == -90D) { azimuth = 0.0F; } x = Math.sqrt((1 / r / r - 1) * c2a + 1) + 1; x = (x - 2) / x; c = 1 - x; c = (x * x / 4 + 1) / c; d = (0.375 * x * x - 1) * x; x = e * cy; s = 1 - 2 * e; distance = (float)(((((sy * sy * 4 - 3) * s * cz * d / 6 - x) * d / 4 + cz) * sy * d + y) * c * EARTH_RADIUS * r); } }