| | 1 | // License: GPL. For details, see LICENSE file. |
| | 2 | package org.openstreetmap.josm.data.projection.proj; |
| | 3 | |
| | 4 | import static java.lang.Math.PI; |
| | 5 | import static java.lang.Math.abs; |
| | 6 | import static java.lang.Math.asin; |
| | 7 | import static java.lang.Math.atan; |
| | 8 | import static java.lang.Math.cos; |
| | 9 | import static java.lang.Math.exp; |
| | 10 | import static java.lang.Math.log; |
| | 11 | import static java.lang.Math.pow; |
| | 12 | import static java.lang.Math.sin; |
| | 13 | import static java.lang.Math.sqrt; |
| | 14 | import static java.lang.Math.tan; |
| | 15 | import static java.lang.Math.toRadians; |
| | 16 | import static org.openstreetmap.josm.tools.I18n.tr; |
| | 17 | |
| | 18 | import org.openstreetmap.josm.data.projection.Ellipsoid; |
| | 19 | import org.openstreetmap.josm.data.projection.ProjectionConfigurationException; |
| | 20 | |
| | 21 | /** |
| | 22 | * Implementation of the stereographic double projection, |
| | 23 | * also known as Oblique Stereographic and the Schreiber double stereographic projection. |
| | 24 | * |
| | 25 | * @author vholten |
| | 26 | * |
| | 27 | * Source: IOGP Publication 373-7-2 – Geomatics Guidance Note number 7, part 2, |
| | 28 | * Sec. 1.3.7.1 Oblique and Equatorial Stereographic, http://www.epsg.org/GuidanceNotes |
| | 29 | */ |
| | 30 | |
| | 31 | public class DoubleStereographic implements Proj { |
| | 32 | |
| | 33 | private Ellipsoid ellps; |
| | 34 | private double e; |
| | 35 | private double n; |
| | 36 | private double c; |
| | 37 | private double chi0; |
| | 38 | private double R; |
| | 39 | |
| | 40 | private static final double EPSILON = 1e-12; |
| | 41 | |
| | 42 | @Override |
| | 43 | public String getName() { |
| | 44 | return tr("Double Stereographic"); |
| | 45 | } |
| | 46 | |
| | 47 | @Override |
| | 48 | public String getProj4Id() { |
| | 49 | return "sterea"; |
| | 50 | } |
| | 51 | |
| | 52 | @Override |
| | 53 | public void initialize(ProjParameters params) throws ProjectionConfigurationException { |
| | 54 | if (params.lat0 == null) |
| | 55 | throw new ProjectionConfigurationException(tr("Parameter ''{0}'' required.", "lat_0")); |
| | 56 | ellps = params.ellps; |
| | 57 | e = ellps.e; |
| | 58 | initialize(params.lat0); |
| | 59 | } |
| | 60 | |
| | 61 | private void initialize(double lat_0) { |
| | 62 | double phi0 = toRadians(lat_0); |
| | 63 | double e2 = ellps.e2; |
| | 64 | R = sqrt(1-e2) / (1 - e2*pow(sin(phi0), 2)); |
| | 65 | n = sqrt(1 + ellps.eb2 * pow(cos(phi0),4)); |
| | 66 | double S1 = (1 + sin(phi0)) / (1 - sin(phi0)); |
| | 67 | double S2 = (1 - e * sin(phi0)) / (1 + e * sin(phi0)); |
| | 68 | double w1 = pow(S1 * pow(S2, e), n); |
| | 69 | double sinchi00 = (w1 - 1) / (w1 + 1); |
| | 70 | c = (n + sin(phi0)) * (1 - sinchi00) / ((n - sin(phi0)) * (1 + sinchi00)); |
| | 71 | double w2 = c * w1; |
| | 72 | chi0 = asin((w2 - 1) / (w2 + 1)); |
| | 73 | } |
| | 74 | |
| | 75 | @Override |
| | 76 | public double[] project(double phi, double lambda) { |
| | 77 | double Lambda = n * lambda; |
| | 78 | double Sa = (1 + sin(phi)) / (1 - sin(phi)); |
| | 79 | double Sb = (1 - e * sin(phi)) / (1 + e * sin(phi)); |
| | 80 | double w = c * pow(Sa * pow(Sb, e), n); |
| | 81 | double chi = asin((w - 1) / (w + 1)); |
| | 82 | double B = 1 + sin(chi) * sin(chi0) + cos(chi) * cos(chi0) * cos(Lambda); |
| | 83 | double x = 2 * R * cos(chi) * sin(Lambda) / B; |
| | 84 | double y = 2 * R * (sin(chi) * cos(chi0) - cos(chi) * sin(chi0) * cos(Lambda)) / B; |
| | 85 | return new double[] {x, y}; |
| | 86 | } |
| | 87 | |
| | 88 | @Override |
| | 89 | public double[] invproject(double x, double y) { |
| | 90 | double e2 = ellps.e2; |
| | 91 | double g = 2 * R * tan(PI/4 - chi0/2); |
| | 92 | double h = 4 * R * tan(chi0) + g; |
| | 93 | double i = atan(x/(h + y)); |
| | 94 | double j = atan(x/(g - y)) - i; |
| | 95 | double chi = chi0 + 2 * atan((y - x * tan(j/2)) / (2 * R)); |
| | 96 | double Lambda = j + 2*i; |
| | 97 | double lambda = Lambda / n; |
| | 98 | double psi = 0.5 * log((1 + sin(chi)) / (c*(1 - sin(chi)))) / n; |
| | 99 | double phiprev = -1000; |
| | 100 | int iteration = 0; |
| | 101 | double phi = 2 * atan(exp(psi)) - PI/2; |
| | 102 | while (abs(phi - phiprev) > EPSILON) { |
| | 103 | if (++iteration > 10) |
| | 104 | throw new RuntimeException("Too many iterations"); |
| | 105 | phiprev = phi; |
| | 106 | double psii = log(tan(phi/2 + PI/4) * pow((1 - e * sin(phi)) / (1 + e * sin(phi)), e/2)); |
| | 107 | phi = phi - (psii - psi) * cos(phi) * (1 - e2 * pow(sin(phi), 2)) / (1 - e2); |
| | 108 | } |
| | 109 | return new double[] {phi, lambda}; |
| | 110 | } |
| | 111 | } |