package org.osm2world.core.map_data.creation;
import static java.lang.Double.NaN;
import static java.lang.Math.*;
import org.osm2world.core.math.VectorXZ;
import org.osm2world.core.osm.data.OSMData;
/**
* application of an orthographic projection that is intended to use
* values in meters centered around the coordinate center (0,0).
* It projects coordinates onto a plane touching the globe at the origin.
* This results in sufficient accuracy
* if the data covers only a "small" part of the globe.
*/
public class OrthographicAzimuthalMapProjection extends OriginMapProjection {
private final double GLOBE_RADIUS = 6371000;
private double lat0 = NaN;
private double lon0 = NaN;
@Override
public VectorXZ calcPos(LatLon latlon) {
return calcPos(latlon.lat, latlon.lon);
}
@Override
public void setOrigin(LatLon origin) {
super.setOrigin(origin);
lat0 = toRadians(getOrigin().lat);
lon0 = toRadians(getOrigin().lon);
}
@Override
public void setOrigin(OSMData osmData) {
super.setOrigin(osmData);
lat0 = toRadians(getOrigin().lat);
lon0 = toRadians(getOrigin().lon);
}
@Override
public VectorXZ calcPos(double latDeg, double lonDeg) {
double lat = toRadians(latDeg);
double lon = toRadians(lonDeg);
double x = GLOBE_RADIUS * cos(lat) * sin(lon - lon0);
double y = GLOBE_RADIUS * (cos(lat0) * sin(lat) - sin(lat0) * cos(lat) * cos(lon - lon0));
return new VectorXZ(x, y);
}
@Override
public double calcLat(VectorXZ pos) {
double rho = sqrt(pos.x * pos.x + pos.z * pos.z);
double c = asin(rho / GLOBE_RADIUS);
if (rho > 0) {
return toDegrees(asin( cos(c) * sin(lat0) + ( pos.z * sin(c) * cos(lat0) ) / rho ));
} else {
return toDegrees(lat0);
}
}
@Override
public double calcLon(VectorXZ pos) {
double rho = sqrt(pos.x * pos.x + pos.z * pos.z);
double c = asin(rho / GLOBE_RADIUS);
double div = rho * cos(lat0) * cos(c) - pos.z * sin(lat0) * sin(c);
if (abs(div) > 1e-5) {
return toDegrees(lon0 + atan2( pos.x * sin(c), div ));
} else {
return toDegrees(lon0);
}
}
@Override
public VectorXZ getNorthUnit() {
return VectorXZ.Z_UNIT;
}
}