001 // License: GPL. For details, see LICENSE file. 002 package org.openstreetmap.josm.data.projection.proj; 003 004 import static java.lang.Math.*; 005 006 import static org.openstreetmap.josm.tools.I18n.tr; 007 008 import org.openstreetmap.josm.data.projection.Ellipsoid; 009 import org.openstreetmap.josm.data.projection.ProjectionConfigurationException; 010 011 /** 012 * Transverse Mercator projection. 013 * 014 * @author Dirk St??cker 015 * code based on JavaScript from Chuck Taylor 016 * 017 */ 018 public class TransverseMercator implements Proj { 019 020 protected double a, b; 021 022 @Override 023 public String getName() { 024 return tr("Transverse Mercator"); 025 } 026 027 @Override 028 public String getProj4Id() { 029 return "tmerc"; 030 } 031 032 @Override 033 public void initialize(ProjParameters params) throws ProjectionConfigurationException { 034 this.a = params.ellps.a; 035 this.b = params.ellps.b; 036 } 037 038 /** 039 * Converts a latitude/longitude pair to x and y coordinates in the 040 * Transverse Mercator projection. Note that Transverse Mercator is not 041 * the same as UTM; a scale factor is required to convert between them. 042 * 043 * Reference: Hoffmann-Wellenhof, B., Lichtenegger, H., and Collins, J., 044 * GPS: Theory and Practice, 3rd ed. New York: Springer-Verlag Wien, 1994. 045 * 046 * @param phi Latitude of the point, in radians 047 * @param lambda Longitude of the point, in radians 048 * @return A 2-element array containing the x and y coordinates 049 * of the computed point 050 */ 051 @Override 052 public double[] project(double phi, double lambda) { 053 054 /* Precalculate ep2 */ 055 double ep2 = (pow(a, 2.0) - pow(b, 2.0)) / pow(b, 2.0); 056 057 /* Precalculate nu2 */ 058 double nu2 = ep2 * pow(cos(phi), 2.0); 059 060 /* Precalculate N / a */ 061 double N_a = a / (b * sqrt(1 + nu2)); 062 063 /* Precalculate t */ 064 double t = tan(phi); 065 double t2 = t * t; 066 067 /* Precalculate l */ 068 double l = lambda; 069 070 /* Precalculate coefficients for l**n in the equations below 071 so a normal human being can read the expressions for easting 072 and northing 073 -- l**1 and l**2 have coefficients of 1.0 */ 074 double l3coef = 1.0 - t2 + nu2; 075 076 double l4coef = 5.0 - t2 + 9 * nu2 + 4.0 * (nu2 * nu2); 077 078 double l5coef = 5.0 - 18.0 * t2 + (t2 * t2) + 14.0 * nu2 079 - 58.0 * t2 * nu2; 080 081 double l6coef = 61.0 - 58.0 * t2 + (t2 * t2) + 270.0 * nu2 082 - 330.0 * t2 * nu2; 083 084 double l7coef = 61.0 - 479.0 * t2 + 179.0 * (t2 * t2) - (t2 * t2 * t2); 085 086 double l8coef = 1385.0 - 3111.0 * t2 + 543.0 * (t2 * t2) - (t2 * t2 * t2); 087 088 return new double[] { 089 /* Calculate easting (x) */ 090 N_a * cos(phi) * l 091 + (N_a / 6.0 * pow(cos(phi), 3.0) * l3coef * pow(l, 3.0)) 092 + (N_a / 120.0 * pow(cos(phi), 5.0) * l5coef * pow(l, 5.0)) 093 + (N_a / 5040.0 * pow(cos(phi), 7.0) * l7coef * pow(l, 7.0)), 094 /* Calculate northing (y) */ 095 ArcLengthOfMeridian (phi) / a 096 + (t / 2.0 * N_a * pow(cos(phi), 2.0) * pow(l, 2.0)) 097 + (t / 24.0 * N_a * pow(cos(phi), 4.0) * l4coef * pow(l, 4.0)) 098 + (t / 720.0 * N_a * pow(cos(phi), 6.0) * l6coef * pow(l, 6.0)) 099 + (t / 40320.0 * N_a * pow(cos(phi), 8.0) * l8coef * pow(l, 8.0)) }; 100 } 101 102 /** 103 * Converts x and y coordinates in the Transverse Mercator projection to 104 * a latitude/longitude pair. Note that Transverse Mercator is not 105 * the same as UTM; a scale factor is required to convert between them. 106 * 107 * Reference: Hoffmann-Wellenhof, B., Lichtenegger, H., and Collins, J., 108 * GPS: Theory and Practice, 3rd ed. New York: Springer-Verlag Wien, 1994. 109 * 110 * Remarks: 111 * The local variables Nf, nuf2, tf, and tf2 serve the same purpose as 112 * N, nu2, t, and t2 in MapLatLonToXY, but they are computed with respect 113 * to the footpoint latitude phif. 114 * 115 * x1frac, x2frac, x2poly, x3poly, etc. are to enhance readability and 116 * to optimize computations. 117 * 118 * @param x The easting of the point, in meters, divided by the semi major axis of the ellipsoid 119 * @param y The northing of the point, in meters, divided by the semi major axis of the ellipsoid 120 * @return A 2-element containing the latitude and longitude 121 * in radians 122 */ 123 @Override 124 public double[] invproject(double x, double y) { 125 /* Get the value of phif, the footpoint latitude. */ 126 double phif = footpointLatitude(y); 127 128 /* Precalculate ep2 */ 129 double ep2 = (a*a - b*b) 130 / (b*b); 131 132 /* Precalculate cos(phif) */ 133 double cf = cos(phif); 134 135 /* Precalculate nuf2 */ 136 double nuf2 = ep2 * pow(cf, 2.0); 137 138 /* Precalculate Nf / a and initialize Nfpow */ 139 double Nf_a = a / (b * sqrt(1 + nuf2)); 140 double Nfpow = Nf_a; 141 142 /* Precalculate tf */ 143 double tf = tan(phif); 144 double tf2 = tf * tf; 145 double tf4 = tf2 * tf2; 146 147 /* Precalculate fractional coefficients for x**n in the equations 148 below to simplify the expressions for latitude and longitude. */ 149 double x1frac = 1.0 / (Nfpow * cf); 150 151 Nfpow *= Nf_a; /* now equals Nf**2) */ 152 double x2frac = tf / (2.0 * Nfpow); 153 154 Nfpow *= Nf_a; /* now equals Nf**3) */ 155 double x3frac = 1.0 / (6.0 * Nfpow * cf); 156 157 Nfpow *= Nf_a; /* now equals Nf**4) */ 158 double x4frac = tf / (24.0 * Nfpow); 159 160 Nfpow *= Nf_a; /* now equals Nf**5) */ 161 double x5frac = 1.0 / (120.0 * Nfpow * cf); 162 163 Nfpow *= Nf_a; /* now equals Nf**6) */ 164 double x6frac = tf / (720.0 * Nfpow); 165 166 Nfpow *= Nf_a; /* now equals Nf**7) */ 167 double x7frac = 1.0 / (5040.0 * Nfpow * cf); 168 169 Nfpow *= Nf_a; /* now equals Nf**8) */ 170 double x8frac = tf / (40320.0 * Nfpow); 171 172 /* Precalculate polynomial coefficients for x**n. 173 -- x**1 does not have a polynomial coefficient. */ 174 double x2poly = -1.0 - nuf2; 175 double x3poly = -1.0 - 2 * tf2 - nuf2; 176 double x4poly = 5.0 + 3.0 * tf2 + 6.0 * nuf2 - 6.0 * tf2 * nuf2 - 3.0 * (nuf2 *nuf2) - 9.0 * tf2 * (nuf2 * nuf2); 177 double x5poly = 5.0 + 28.0 * tf2 + 24.0 * tf4 + 6.0 * nuf2 + 8.0 * tf2 * nuf2; 178 double x6poly = -61.0 - 90.0 * tf2 - 45.0 * tf4 - 107.0 * nuf2 + 162.0 * tf2 * nuf2; 179 double x7poly = -61.0 - 662.0 * tf2 - 1320.0 * tf4 - 720.0 * (tf4 * tf2); 180 double x8poly = 1385.0 + 3633.0 * tf2 + 4095.0 * tf4 + 1575 * (tf4 * tf2); 181 182 return new double[] { 183 /* Calculate latitude */ 184 phif + x2frac * x2poly * (x * x) 185 + x4frac * x4poly * pow(x, 4.0) 186 + x6frac * x6poly * pow(x, 6.0) 187 + x8frac * x8poly * pow(x, 8.0), 188 /* Calculate longitude */ 189 x1frac * x 190 + x3frac * x3poly * pow(x, 3.0) 191 + x5frac * x5poly * pow(x, 5.0) 192 + x7frac * x7poly * pow(x, 7.0) }; 193 } 194 195 /** 196 * ArcLengthOfMeridian 197 * 198 * Computes the ellipsoidal distance from the equator to a point at a 199 * given latitude. 200 * 201 * Reference: Hoffmann-Wellenhof, B., Lichtenegger, H., and Collins, J., 202 * GPS: Theory and Practice, 3rd ed. New York: Springer-Verlag Wien, 1994. 203 * 204 * @param phi Latitude of the point, in radians 205 * @return The ellipsoidal distance of the point from the equator 206 * (in meters, divided by the semi major axis of the ellipsoid) 207 */ 208 private double ArcLengthOfMeridian(double phi) { 209 /* Precalculate n */ 210 double n = (a - b) / (a + b); 211 212 /* Precalculate alpha */ 213 double alpha = ((a + b) / 2.0) 214 * (1.0 + (pow(n, 2.0) / 4.0) + (pow(n, 4.0) / 64.0)); 215 216 /* Precalculate beta */ 217 double beta = (-3.0 * n / 2.0) + (9.0 * pow(n, 3.0) / 16.0) 218 + (-3.0 * pow(n, 5.0) / 32.0); 219 220 /* Precalculate gamma */ 221 double gamma = (15.0 * pow(n, 2.0) / 16.0) 222 + (-15.0 * pow(n, 4.0) / 32.0); 223 224 /* Precalculate delta */ 225 double delta = (-35.0 * pow(n, 3.0) / 48.0) 226 + (105.0 * pow(n, 5.0) / 256.0); 227 228 /* Precalculate epsilon */ 229 double epsilon = (315.0 * pow(n, 4.0) / 512.0); 230 231 /* Now calculate the sum of the series and return */ 232 return alpha 233 * (phi + (beta * sin(2.0 * phi)) 234 + (gamma * sin(4.0 * phi)) 235 + (delta * sin(6.0 * phi)) 236 + (epsilon * sin(8.0 * phi))); 237 } 238 239 /** 240 * FootpointLatitude 241 * 242 * Computes the footpoint latitude for use in converting transverse 243 * Mercator coordinates to ellipsoidal coordinates. 244 * 245 * Reference: Hoffmann-Wellenhof, B., Lichtenegger, H., and Collins, J., 246 * GPS: Theory and Practice, 3rd ed. New York: Springer-Verlag Wien, 1994. 247 * 248 * @param y northing coordinate, in meters, divided by the semi major axis of the ellipsoid 249 * @return The footpoint latitude, in radians 250 */ 251 private double footpointLatitude(double y) { 252 /* Precalculate n (Eq. 10.18) */ 253 double n = (a - b) / (a + b); 254 255 /* Precalculate alpha_ (Eq. 10.22) */ 256 /* (Same as alpha in Eq. 10.17) */ 257 double alpha_ = ((a + b) / 2.0) 258 * (1 + (pow(n, 2.0) / 4) + (pow(n, 4.0) / 64)); 259 260 /* Precalculate y_ (Eq. 10.23) */ 261 double y_ = y / alpha_ * a; 262 263 /* Precalculate beta_ (Eq. 10.22) */ 264 double beta_ = (3.0 * n / 2.0) + (-27.0 * pow(n, 3.0) / 32.0) 265 + (269.0 * pow(n, 5.0) / 512.0); 266 267 /* Precalculate gamma_ (Eq. 10.22) */ 268 double gamma_ = (21.0 * pow(n, 2.0) / 16.0) 269 + (-55.0 * pow(n, 4.0) / 32.0); 270 271 /* Precalculate delta_ (Eq. 10.22) */ 272 double delta_ = (151.0 * pow(n, 3.0) / 96.0) 273 + (-417.0 * pow(n, 5.0) / 128.0); 274 275 /* Precalculate epsilon_ (Eq. 10.22) */ 276 double epsilon_ = (1097.0 * pow(n, 4.0) / 512.0); 277 278 /* Now calculate the sum of the series (Eq. 10.21) */ 279 return y_ + (beta_ * sin(2.0 * y_)) 280 + (gamma_ * sin(4.0 * y_)) 281 + (delta_ * sin(6.0 * y_)) 282 + (epsilon_ * sin(8.0 * y_)); 283 } 284 285 }