// ********************************************************************** // // <copyright> // // BBN Technologies // 10 Moulton Street // Cambridge, MA 02138 // (617) 873-8000 // // Copyright (C) BBNT Solutions LLC. All rights reserved. // // </copyright> // ********************************************************************** package com.jwetherell.openmap.common; public abstract class ProjMath { /** * North pole latitude in radians. */ public static final transient float NORTH_POLE_F = MoreMath.HALF_PI; /** * South pole latitude in radians. */ public static final transient float SOUTH_POLE_F = -NORTH_POLE_F; /** * North pole latitude in radians. */ public static final transient double NORTH_POLE_D = MoreMath.HALF_PI_D; /** * North pole latitude in degrees. */ public static final transient double NORTH_POLE_DEG_D = 90d; /** * South pole latitude in radians. */ public static final transient double SOUTH_POLE_D = -NORTH_POLE_D; /** * South pole latitude in degrees. */ public static final transient double SOUTH_POLE_DEG_D = -NORTH_POLE_DEG_D; /** * Dateline longitude in radians. */ public static final transient float DATELINE_F = (float) Math.PI; /** * Dateline longitude in radians. */ public static final transient double DATELINE_D = Math.PI; /** * Dateline longitude in degrees. */ public static final transient double DATELINE_DEG_D = 180d; /** * Longitude range in radians. */ public static final transient float LON_RANGE_F = MoreMath.TWO_PI; /** * Longitude range in radians. */ public static final transient double LON_RANGE_D = MoreMath.TWO_PI_D; /** * Longitude range in degrees. */ public static final transient double LON_RANGE_DEG_D = 360d; public static final double DEGREES_TO_MILS = 17.77777777777777777778; /** * rounds the quantity away from 0. * * @param x * in value * @return double * @see #qint(double) */ public static final double roundAdjust(double x) { return qint_old(x); } /** * Rounds the quantity away from 0. * * @param x * value * @return double */ public static final double qint(double x) { return qint_new(x); } private static final double qint_old(double x) { return (((int) x) < 0) ? (x - 0.5) : (x + 0.5); } private static final double qint_new(double x) { // -1 or +1 away from zero return (x <= 0.0) ? (x - 1.0) : (x + 1.0); } /** * Calculate the shortest arc distance between two lons. * * @param lon1 * radians * @param lon2 * radians * @return float distance */ public static final float lonDistance(float lon1, float lon2) { return (float) Math.min(Math.abs(lon1 - lon2), ((lon1 < 0) ? lon1 + Math.PI : Math.PI - lon1) + ((lon2 < 0) ? lon2 + Math.PI : Math.PI - lon2)); } /** * Convert between decimal degrees and scoords. * * @param deg * degrees * @return long scoords * */ public static final long DEG_TO_SC(double deg) { return (long) (deg * 3600000); } /** * Convert between decimal degrees and scoords. * * @param sc * scoords * @return double decimal degrees */ public static final double SC_TO_DEG(int sc) { return ((sc) / (60.0 * 60.0 * 1000.0)); } /** * Convert radians to mils. * * @param rad * radians * @return double mils */ public static final double radToMils(double rad) { double degrees = Math.toDegrees(rad); degrees = (degrees < 0) ? 360 + degrees : degrees; return degrees * DEGREES_TO_MILS; } /** * Convert radians to degrees. * * @param rad * radians * @return double decimal degrees */ public static final double radToDeg(double rad) { return Math.toDegrees(rad); } /** * Convert radians to degrees. * * @param rad * radians * @return float decimal degrees */ public static final float radToDeg(float rad) { return (float) Math.toDegrees(rad); } /** * Convert degrees to radians. * * @param deg * degrees * @return double radians */ public static final double degToRad(double deg) { return Math.toRadians(deg); } /** * Convert degrees to radians. * * @param deg * degrees * @return float radians */ public static final float degToRad(float deg) { return (float) Math.toRadians(deg); } /** * Generate a hashCode value for a lat/lon pair. * * @param lat * latitude * @param lon * longitude * @return int hashcode * */ public static final int hashLatLon(float lat, float lon) { if (lat == -0f) lat = 0f;// handle negative zero (anything else?) if (lon == -0f) lon = 0f; int tmp = Float.floatToIntBits(lat); int hash = (tmp << 5) | (tmp >> 27);// rotate the lat bits return hash ^ Float.floatToIntBits(lon);// XOR with lon } /** * Converts an array of decimal degrees float lat/lons to float radians in * place. * * @param degs * float[] lat/lons in decimal degrees * @return float[] lat/lons in radians */ public static final float[] arrayDegToRad(float[] degs) { for (int i = 0; i < degs.length; i++) { degs[i] = degToRad(degs[i]); } return degs; } /** * Converts an array of radian float lat/lons to decimal degrees in place. * * @param rads * float[] lat/lons in radians * @return float[] lat/lons in decimal degrees */ public static final float[] arrayRadToDeg(float[] rads) { for (int i = 0; i < rads.length; i++) { rads[i] = radToDeg(rads[i]); } return rads; } /** * Converts an array of decimal degrees double lat/lons to double radians in * place. * * @param degs * double[] lat/lons in decimal degrees * @return double[] lat/lons in radians */ public static final double[] arrayDegToRad(double[] degs) { for (int i = 0; i < degs.length; i++) { degs[i] = degToRad(degs[i]); } return degs; } /** * Converts an array of radian double lat/lons to decimal degrees in place. * * @param rads * double[] lat/lons in radians * @return double[] lat/lons in decimal degrees */ public static final double[] arrayRadToDeg(double[] rads) { for (int i = 0; i < rads.length; i++) { rads[i] = radToDeg(rads[i]); } return rads; } /** * Normalizes radian latitude. Normalizes latitude if at or exceeds epsilon * distance from a pole. * * @param lat * float latitude in radians * @param epsilon * epsilon (>= 0) radians distance from pole * @return float latitude (-PI/2 <= phi <= PI/2) * @see Proj#normalizeLatitude(float) * @see com.bbn.openmap.LatLonPoint#normalizeLatitude(float) */ public static final float normalizeLatitude(float lat, float epsilon) { if (lat > NORTH_POLE_F - epsilon) { return NORTH_POLE_F - epsilon; } else if (lat < SOUTH_POLE_F + epsilon) { return SOUTH_POLE_F + epsilon; } return lat; } /** * Normalizes radian latitude. Normalizes latitude if at or exceeds epsilon * distance from a pole. * * @param lat * double latitude in radians * @param epsilon * epsilon (>= 0) radians distance from pole * @return double latitude (-PI/2 <= phi <= PI/2) * @see Proj#normalizeLatitude(float) */ public static final double normalizeLatitude(double lat, double epsilon) { if (lat > NORTH_POLE_D - epsilon) { return NORTH_POLE_D - epsilon; } else if (lat < SOUTH_POLE_D + epsilon) { return SOUTH_POLE_D + epsilon; } return lat; } /** * Sets radian longitude to something sane. * * @param lon * float longitude in radians * @return float longitude (-PI <= lambda < PI) */ public static final float wrapLongitude(float lon) { if ((lon < -DATELINE_F) || (lon > DATELINE_F)) { lon += DATELINE_F; lon %= LON_RANGE_F; lon += (lon < 0) ? DATELINE_F : -DATELINE_F; } return lon; } /** * Sets radian longitude to something sane. * * @param lon * double longitude in radians * @return double longitude (-PI <= lambda < PI) * @see #wrapLongitude(float) */ public static final double wrapLongitude(double lon) { if ((lon < -DATELINE_D) || (lon > DATELINE_D)) { lon += DATELINE_D; lon %= LON_RANGE_D; lon += (lon < 0) ? DATELINE_D : -DATELINE_D; } return lon; } /** * Sets degree longitude to something sane. * * @param lon * double longitude in degrees * @return double longitude (-180 <= lambda < 180) */ public static final double wrapLongitudeDeg(double lon) { if ((lon < -DATELINE_DEG_D) || (lon > DATELINE_DEG_D)) { lon += DATELINE_DEG_D; lon %= LON_RANGE_DEG_D; lon += (lon < 0) ? DATELINE_DEG_D : -DATELINE_DEG_D; } return lon; } /** * Converts units (km, nm, miles, etc) to decimal degrees for a spherical * planet. This does not check for arc distances > 1/2 planet * circumference, which are better represented as (2pi - calculated arc). * * @param u * units float value * @param uCircumference * units circumference of planet * @return float decimal degrees */ public static final float sphericalUnitsToDeg(float u, float uCircumference) { return 360f * (u / uCircumference); } /** * Converts units (km, nm, miles, etc) to arc radians for a spherical * planet. This does not check for arc distances > 1/2 planet * circumference, which are better represented as (2pi - calculated arc). * * @param u * units float value * @param uCircumference * units circumference of planet * @return float arc radians */ public static final float sphericalUnitsToRad(float u, float uCircumference) { return MoreMath.TWO_PI * (u / uCircumference); } /** * Calculate the geocentric latitude given a geographic latitude. According * to John Synder: <br> * "The geographic or geodetic latitude is the angle which a line * perpendicular to the surface of the ellipsoid at the given point makes * with the plane of the equator. ...The geocentric latitude is the angle * made by a line to the center of the ellipsoid with the equatorial plane". * ( <i>Map Projections --A Working Manual </i>, p 13) * <p> * Translated from Ken Anderson's lisp code <i>Freeing the Essence of * Computation </i> * * @param lat * float geographic latitude in radians * @param flat * float flatening factor * @return float geocentric latitude in radians * @see #geographic_latitude */ public static final float geocentricLatitude(float lat, float flat) { float f = 1.0f - flat; return (float) Math.atan((f * f) * (float) Math.tan(lat)); } /** * Calculate the geographic latitude given a geocentric latitude. Translated * from Ken Anderson's lisp code <i>Freeing the Essence of Computation </i> * * @param lat * float geocentric latitude in radians * @param flat * float flatening factor * @return float geographic latitude in radians * @see #geocentric_latitude */ public static final float geographicLatitude(float lat, float flat) { float f = 1.0f - flat; return (float) Math.atan((float) Math.tan(lat) / (f * f)); } /** * Generic test for seeing if an left longitude value and a right longitude * value seem to constitute crossing the dateline. * * @param leftLon * the leftmost longitude, in decimal degrees. Expected to * represent the location of the left side of a map window. * @param rightLon * the rightmost longitude, in decimal degrees. Expected to * represent the location of the right side of a map window. * @param projScale * the projection scale, considered if the two values are very * close to each other and leftLon less than rightLon. * @return true if it seems like these two longitude values represent a * dateline crossing. */ public static boolean isCrossingDateline(double leftLon, double rightLon, float projScale) { // if the left longitude is greater than the right, we're obviously // crossing the dateline. If they are approximately equal, we could be // showing the whole earth, but only if the scale is significantly // large. If the scale is small, we could be really zoomed in. return ((leftLon > rightLon) || (MoreMath.approximately_equal(leftLon, rightLon, .001f) && projScale > 1000000f)); } }