source: josm/trunk/src/org/openstreetmap/josm/data/projection/proj/TransverseMercator.java@ 9112

Last change on this file since 9112 was 9112, checked in by bastiK, 8 years ago

applied #12185 - support latitude of natural origin for Transverse Mercator projection
(imports pieces of code from the Geotools project)

  • Property svn:eol-style set to native
File size: 7.3 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.projection.proj;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import org.openstreetmap.josm.data.projection.ProjectionConfigurationException;
7
8/**
9 * Transverse Mercator Projection (EPSG code 9807). This
10 * is a cylindrical projection, in which the cylinder has been rotated 90°.
11 * Instead of being tangent to the equator (or to an other standard latitude),
12 * it is tangent to a central meridian. Deformation are more important as we
13 * are going futher from the central meridian. The Transverse Mercator
14 * projection is appropriate for region wich have a greater extent north-south
15 * than east-west.
16 * <p>
17 *
18 * The elliptical equations used here are series approximations, and their accuracy
19 * decreases as points move farther from the central meridian of the projection.
20 * The forward equations here are accurate to a less than a mm &plusmn;10 degrees from
21 * the central meridian, a few mm &plusmn;15 degrees from the
22 * central meridian and a few cm &plusmn;20 degrees from the central meridian.
23 * The spherical equations are not approximations and should always give the
24 * correct values.
25 * <p>
26 *
27 * There are a number of versions of the transverse mercator projection
28 * including the Universal (UTM) and Modified (MTM) Transverses Mercator
29 * projections. In these cases the earth is divided into zones. For the UTM
30 * the zones are 6 degrees wide, numbered from 1 to 60 proceeding east from
31 * 180 degrees longitude, and between lats 84 degrees North and 80
32 * degrees South. The central meridian is taken as the center of the zone
33 * and the latitude of origin is the equator. A scale factor of 0.9996 and
34 * false easting of 500000m is used for all zones and a false northing of 10000000m
35 * is used for zones in the southern hemisphere.
36 * <p>
37 *
38 * NOTE: formulas used below are not those of Snyder, but rather those
39 * from the {@code proj4} package of the USGS survey, which
40 * have been reproduced verbatim. USGS work is acknowledged here.
41 * <p>
42 *
43 * This class has been derived from the implementation of the Geotools project;
44 * git 8cbf52d, org.geotools.referencing.operation.projection.TransverseMercator
45 * at the time of migration.
46 * <p>
47 *
48 * <b>References:</b>
49 * <ul>
50 * <li> Proj-4.4.6 available at <A HREF="http://www.remotesensing.org/proj">www.remotesensing.org/proj</A><br>
51 * Relevent files are: {@code PJ_tmerc.c}, {@code pj_mlfn.c}, {@code pj_fwd.c} and {@code pj_inv.c}.</li>
52 * <li> John P. Snyder (Map Projections - A Working Manual,
53 * U.S. Geological Survey Professional Paper 1395, 1987).</li>
54 * <li> "Coordinate Conversions and Transformations including Formulas",
55 * EPSG Guidence Note Number 7, Version 19.</li>
56 * </ul>
57 *
58 * @see <A HREF="http://mathworld.wolfram.com/MercatorProjection.html">Transverse Mercator projection on MathWorld</A>
59 * @see <A HREF="http://www.remotesensing.org/geotiff/proj_list/transverse_mercator.html">"Transverse_Mercator" on RemoteSensing.org</A>
60 *
61 * @author André Gosselin
62 * @author Martin Desruisseaux (PMO, IRD)
63 * @author Rueben Schulz
64 */
65public class TransverseMercator extends AbstractProj {
66
67 /**
68 * Contants used for the forward and inverse transform for the eliptical
69 * case of the Transverse Mercator.
70 */
71 private static final double FC1= 1.00000000000000000000000, // 1/1
72 FC2= 0.50000000000000000000000, // 1/2
73 FC3= 0.16666666666666666666666, // 1/6
74 FC4= 0.08333333333333333333333, // 1/12
75 FC5= 0.05000000000000000000000, // 1/20
76 FC6= 0.03333333333333333333333, // 1/30
77 FC7= 0.02380952380952380952380, // 1/42
78 FC8= 0.01785714285714285714285; // 1/56
79
80 /**
81 * Maximum difference allowed when comparing real numbers.
82 */
83 private static final double EPSILON = 1E-6;
84
85 /**
86 * A derived quantity of excentricity, computed by <code>e'² = (a²-b²)/b² = es/(1-es)</code>
87 * where <var>a</var> is the semi-major axis length and <var>b</bar> is the semi-minor axis
88 * length.
89 */
90 private double eb2;
91
92 /**
93 * Latitude of origin in <u>radians</u>. Default value is 0, the equator.
94 * This is called '<var>phi0</var>' in Snyder.
95 * <p>
96 * <strong>Consider this field as final</strong>. It is not final only
97 * because some classes need to modify it at construction time.
98 */
99 protected double latitudeOfOrigin;
100
101 /**
102 * Meridian distance at the {@code latitudeOfOrigin}.
103 * Used for calculations for the ellipsoid.
104 */
105 private double ml0;
106
107 @Override
108 public String getName() {
109 return tr("Transverse Mercator");
110 }
111
112 @Override
113 public String getProj4Id() {
114 return "tmerc";
115 }
116
117 @Override
118 public void initialize(ProjParameters params) throws ProjectionConfigurationException {
119 super.initialize(params);
120 eb2 = params.ellps.eb2;
121 latitudeOfOrigin = params.lat0 == null ? 0 : Math.toRadians(params.lat0);
122 ml0 = mlfn(latitudeOfOrigin, Math.sin(latitudeOfOrigin), Math.cos(latitudeOfOrigin));
123 }
124
125 @Override
126 public double[] project(double y, double x) {
127 double sinphi = Math.sin(y);
128 double cosphi = Math.cos(y);
129
130 double t = (Math.abs(cosphi) > EPSILON) ? sinphi/cosphi : 0;
131 t *= t;
132 double al = cosphi*x;
133 double als = al*al;
134 al /= Math.sqrt(1.0 - e2 * sinphi*sinphi);
135 double n = eb2 * cosphi*cosphi;
136
137 /* NOTE: meridinal distance at latitudeOfOrigin is always 0 */
138 y = (mlfn(y, sinphi, cosphi) - ml0 +
139 sinphi * al * x *
140 FC2 * ( 1.0 +
141 FC4 * als * (5.0 - t + n*(9.0 + 4.0*n) +
142 FC6 * als * (61.0 + t * (t - 58.0) + n*(270.0 - 330.0*t) +
143 FC8 * als * (1385.0 + t * ( t*(543.0 - t) - 3111.0))))));
144
145 x = al*(FC1 + FC3 * als*(1.0 - t + n +
146 FC5 * als * (5.0 + t*(t - 18.0) + n*(14.0 - 58.0*t) +
147 FC7 * als * (61.0+ t*(t*(179.0 - t) - 479.0 )))));
148
149 return new double[] { x, y };
150 }
151
152 @Override
153 public double[] invproject(double x, double y) {
154 double phi = inv_mlfn(ml0 + y);
155
156 if (Math.abs(phi) >= Math.PI/2) {
157 y = y<0.0 ? -(Math.PI/2) : (Math.PI/2);
158 x = 0.0;
159 } else {
160 double sinphi = Math.sin(phi);
161 double cosphi = Math.cos(phi);
162 double t = (Math.abs(cosphi) > EPSILON) ? sinphi/cosphi : 0.0;
163 double n = eb2 * cosphi*cosphi;
164 double con = 1.0 - e2 * sinphi*sinphi;
165 double d = x * Math.sqrt(con);
166 con *= t;
167 t *= t;
168 double ds = d*d;
169
170 y = phi - (con*ds / (1.0 - e2)) *
171 FC2 * (1.0 - ds *
172 FC4 * (5.0 + t*(3.0 - 9.0*n) + n*(1.0 - 4*n) - ds *
173 FC6 * (61.0 + t*(90.0 - 252.0*n + 45.0*t) + 46.0*n - ds *
174 FC8 * (1385.0 + t*(3633.0 + t*(4095.0 + 1574.0*t))))));
175
176 x = d*(FC1 - ds * FC3 * (1.0 + 2.0*t + n -
177 ds*FC5*(5.0 + t*(28.0 + 24* t + 8.0*n) + 6.0*n -
178 ds*FC7*(61.0 + t*(662.0 + t*(1320.0 + 720.0*t))))))/cosphi;
179 }
180 return new double[] { y, x };
181 }
182}
Note: See TracBrowser for help on using the repository browser.