1 | // License: GPL. For details, see LICENSE file.
|
---|
2 | package org.openstreetmap.josm.data.projection.proj;
|
---|
3 |
|
---|
4 | import static org.openstreetmap.josm.tools.I18n.tr;
|
---|
5 |
|
---|
6 | import org.openstreetmap.josm.data.Bounds;
|
---|
7 | import org.openstreetmap.josm.data.projection.ProjectionConfigurationException;
|
---|
8 | import org.openstreetmap.josm.tools.Utils;
|
---|
9 |
|
---|
10 | /**
|
---|
11 | * Mercator Cylindrical Projection. The parallels and the meridians are straight lines and
|
---|
12 | * cross at right angles; this projection thus produces rectangular charts. The scale is true
|
---|
13 | * along the equator (by default) or along two parallels equidistant of the equator (if a scale
|
---|
14 | * factor other than 1 is used). This projection is used to represent areas close to the equator.
|
---|
15 | * It is also often used for maritime navigation because all the straight lines on the chart are
|
---|
16 | * <em>loxodrome</em> lines, i.e. a ship following this line would keep a constant azimuth on its
|
---|
17 | * compass.
|
---|
18 | * <p>
|
---|
19 | * This implementation handles both the 1 and 2 stardard parallel cases.
|
---|
20 | * For 1 SP (EPSG code 9804), the line of contact is the equator.
|
---|
21 | * For 2 SP (EPSG code 9805) lines of contact are symmetrical
|
---|
22 | * about the equator.
|
---|
23 | * <p>
|
---|
24 | * This class has been derived from the implementation of the Geotools project;
|
---|
25 | * git 8cbf52d, org.geotools.referencing.operation.projection.Mercator
|
---|
26 | * at the time of migration.
|
---|
27 | * <p>
|
---|
28 | * <b>References:</b>
|
---|
29 | * <ul>
|
---|
30 | * <li>John P. Snyder (Map Projections - A Working Manual,<br>
|
---|
31 | * U.S. Geological Survey Professional Paper 1395, 1987)</li>
|
---|
32 | * <li>"Coordinate Conversions and Transformations including Formulas",<br>
|
---|
33 | * EPSG Guidence Note Number 7, Version 19.</li>
|
---|
34 | * </ul>
|
---|
35 | *
|
---|
36 | * @author André Gosselin
|
---|
37 | * @author Martin Desruisseaux (PMO, IRD)
|
---|
38 | * @author Rueben Schulz
|
---|
39 | * @author Simone Giannecchini
|
---|
40 | *
|
---|
41 | * @see <A HREF="http://mathworld.wolfram.com/MercatorProjection.html">Mercator projection on MathWorld</A>
|
---|
42 | * @see <A HREF="http://www.remotesensing.org/geotiff/proj_list/mercator_1sp.html">"mercator_1sp" on RemoteSensing.org</A>
|
---|
43 | * @see <A HREF="http://www.remotesensing.org/geotiff/proj_list/mercator_2sp.html">"mercator_2sp" on RemoteSensing.org</A>
|
---|
44 | */
|
---|
45 | public class Mercator extends AbstractProj implements IScaleFactorProvider {
|
---|
46 | /**
|
---|
47 | * Maximum difference allowed when comparing real numbers.
|
---|
48 | */
|
---|
49 | private static final double EPSILON = 1E-6;
|
---|
50 |
|
---|
51 | protected double scaleFactor;
|
---|
52 |
|
---|
53 | @Override
|
---|
54 | public String getName() {
|
---|
55 | return tr("Mercator");
|
---|
56 | }
|
---|
57 |
|
---|
58 | @Override
|
---|
59 | public String getProj4Id() {
|
---|
60 | return "merc";
|
---|
61 | }
|
---|
62 |
|
---|
63 | @Override
|
---|
64 | public void initialize(ProjParameters params) throws ProjectionConfigurationException {
|
---|
65 | super.initialize(params);
|
---|
66 | scaleFactor = 1;
|
---|
67 | if (params.lat_ts != null) {
|
---|
68 | /*
|
---|
69 | * scaleFactor is not a parameter in the 2 SP case and is computed from
|
---|
70 | * the standard parallel.
|
---|
71 | */
|
---|
72 | double standardParallel = Utils.toRadians(params.lat_ts);
|
---|
73 | if (spherical) {
|
---|
74 | scaleFactor *= Math.cos(standardParallel);
|
---|
75 | } else {
|
---|
76 | scaleFactor *= msfn(Math.sin(standardParallel), Math.cos(standardParallel));
|
---|
77 | }
|
---|
78 | }
|
---|
79 | /*
|
---|
80 | * A correction that allows us to employs a latitude of origin that is not
|
---|
81 | * correspondent to the equator. See Snyder and al. for reference, page 47.
|
---|
82 | */
|
---|
83 | if (params.lat0 != null) {
|
---|
84 | final double lat0 = Utils.toRadians(params.lat0);
|
---|
85 | final double sinPhi = Math.sin(lat0);
|
---|
86 | scaleFactor *= (Math.cos(lat0) / (Math.sqrt(1 - e2 * sinPhi * sinPhi)));
|
---|
87 | }
|
---|
88 | }
|
---|
89 |
|
---|
90 | @Override
|
---|
91 | public double[] project(double y, double x) {
|
---|
92 | if (Math.abs(y) > (Math.PI/2 - EPSILON)) {
|
---|
93 | return new double[] {0, 0}; // this is an error and should be handled somehow
|
---|
94 | }
|
---|
95 | if (spherical) {
|
---|
96 | y = Math.log(Math.tan(Math.PI/4 + 0.5*y));
|
---|
97 | } else {
|
---|
98 | y = -Math.log(tsfn(y, Math.sin(y)));
|
---|
99 | }
|
---|
100 | return new double[] {x, y};
|
---|
101 | }
|
---|
102 |
|
---|
103 | @Override
|
---|
104 | public double[] invproject(double x, double y) {
|
---|
105 | if (spherical) {
|
---|
106 | y = Math.PI/2 - 2.0*Math.atan(Math.exp(-y));
|
---|
107 | } else {
|
---|
108 | y = Math.exp(-y);
|
---|
109 | y = cphi2(y);
|
---|
110 | }
|
---|
111 | return new double[] {y, x};
|
---|
112 | }
|
---|
113 |
|
---|
114 | @Override
|
---|
115 | public Bounds getAlgorithmBounds() {
|
---|
116 | return new Bounds(-89, -180, 89, 180, false);
|
---|
117 | }
|
---|
118 |
|
---|
119 | @Override
|
---|
120 | public double getScaleFactor() {
|
---|
121 | return scaleFactor;
|
---|
122 | }
|
---|
123 |
|
---|
124 | @Override
|
---|
125 | public boolean lonIsLinearToEast() {
|
---|
126 | return true;
|
---|
127 | }
|
---|
128 | }
|
---|