osm-focus/src/dk/network42/osmfocus/GeoMath.java

121 lines
4.1 KiB
Java

package dk.network42.osmfocus;
public class GeoMath {
// Convenience constants
private static final double _180_PI = 180.0/Math.PI;
private static final double _PI_2 = Math.PI/2.0;
private static final double _PI_4 = Math.PI/4.0;
private static final double _PI_180 = Math.PI/180.0;
private static final double _PI_360 = Math.PI/360.0;
// Maximum latitude due to Mercator projection.
public static final double MAX_LAT = _180_PI * Math.atan(Math.sinh(Math.PI));
// See http://en.wikipedia.org/wiki/Earth_ellipsoid
public static final double EARTH_POLAR_RADIUS = 6356752;
public static final double EARTH_EQUATORIAL_RADIUS = 6378137;
// Reference Earth radius in meters (mean between equatorial and polar WGS84 radii).
public static final double EARTH_RADIUS = (EARTH_EQUATORIAL_RADIUS+EARTH_POLAR_RADIUS)/2;
// Approximate conversions between 'on the ground' meter measure to angle [degrees]
// One equator
public static double convertMetersToGeoDistanceEq(double meters) {
return _180_PI*meters/EARTH_EQUATORIAL_RADIUS;
}
// On parallel at a given latitude
public static double convertMetersToGeoDistancePar(double meters, double lat) {
return _180_PI*meters/EARTH_EQUATORIAL_RADIUS/Math.cos(lat*_PI_180);
}
// On median
public static double convertMetersToGeoDistanceMed(double meters) {
return _180_PI*meters/EARTH_POLAR_RADIUS;
}
// Mercator projection. Truncated to MAX_LAT if lat is outside +-MAX_LAT.
// http://en.wikipedia.org/wiki/Mercator_projection#Mathematics_of_the_projection
public static double latToMercator(double lat) {
lat = Math.min(MAX_LAT, lat);
lat = Math.max(-MAX_LAT, lat);
return _180_PI * Math.log(Math.tan(lat*_PI_360 + _PI_4));
}
// Inverse mercator projection
public static double mercatorToLat(double merc) {
return _180_PI * (2.0 * Math.atan(Math.exp(merc*_PI_180)) - _PI_2);
}
public static double getMercatorScale(double lat) {
return latToMercator(lat)/lat;
}
// Closest point on finite line (l1 to l2) from point (p)
// Algorithm, see http://paulbourke.net/geometry/pointlineplane/
public static double[] nearestPoint(double px, double py, double l1x, double l1y, double l2x, double l2y) {
double x, y;
double dx = l2x - l1x;
double dy = l2y - l1y;
if (dx == 0.0 && dy == 0.0) {
x = l1x;
y = l1y;
} else {
double u;
u = ((px-l1x)*dx + (py-l1y)*dy) / (dx*dx+dy*dy);
if (u <= 0.0) {
x = l1x;
y = l1y;
} else if (u >= 1.0) {
x = l2x;
y = l2y;
} else {
x = l1x + u*dx;
y = l1y + u*dy;
}
}
return new double []{x, y};
}
// Distance from point (p) to finite line (l1 to l2)
public static double getLineDistance(double px, double py, double l1x, double l1y, double l2x, double l2y) {
double x, y;
double dx = l2x - l1x;
double dy = l2y - l1y;
if (dx == 0.0 && dy == 0.0) {
x = l1x;
y = l1y;
} else {
double u;
u = ((px-l1x)*dx + (py-l1y)*dy) / (dx*dx+dy*dy);
if (u <= 0.0) {
x = l1x;
y = l1y;
} else if (u >= 1.0) {
x = l2x;
y = l2y;
} else {
x = l1x + u*dx;
y = l1y + u*dy;
}
}
return Math.hypot(px-x, py-y);
}
// Shorten line with 'by' from (l1x,l1y)
public static double[] shortenLine(double l1x, double l1y, double l2x, double l2y, double by) {
double dx = l2x - l1x;
double dy = l2y - l1y;
if ((dx == 0.0 && dy == 0.0) || (by <= 0)) {
return new double []{l1x, l1y};
}
double len = Math.hypot(dx, dy);
if (len <= by) {
return new double []{l2x, l2y};
}
double x, y, u;
u = by/len;
x = l1x + u*dx;
y = l1y + u*dy;
return new double []{x, y};
}
}