root / org.gvsig.jcrs / libJCRS / src / org / geotools / referencing / operation / projection / IdrMercator.java @ 38
History | View | Annotate | Download (18.7 KB)
1 |
/*
|
---|---|
2 |
* Geotools 2 - OpenSource mapping toolkit
|
3 |
* (C) 2003, Geotools Project Managment Committee (PMC)
|
4 |
* (C) 2001, Institut de Recherche pour le Dveloppement
|
5 |
* (C) 1999, Fisheries and Oceans Canada
|
6 |
*
|
7 |
* This library is free software; you can redistribute it and/or
|
8 |
* modify it under the terms of the GNU Lesser General Public
|
9 |
* License as published by the Free Software Foundation; either
|
10 |
* version 2.1 of the License, or (at your option) any later version.
|
11 |
*
|
12 |
* This library is distributed in the hope that it will be useful,
|
13 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
15 |
* Lesser General Public License for more details.
|
16 |
*
|
17 |
* You should have received a copy of the GNU Lesser General Public
|
18 |
* License along with this library; if not, write to the Free Software
|
19 |
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
20 |
*
|
21 |
*
|
22 |
* This package contains formulas from the PROJ package of USGS.
|
23 |
* USGS's work is fully acknowledged here.
|
24 |
*/
|
25 |
package org.geotools.referencing.operation.projection; |
26 |
|
27 |
// J2SE dependencies and extensions
|
28 |
import java.awt.geom.Point2D; |
29 |
import java.util.Collection; |
30 |
import javax.units.NonSI; |
31 |
|
32 |
// OpenGIS dependencies
|
33 |
import org.opengis.parameter.ParameterDescriptor; |
34 |
import org.opengis.parameter.ParameterDescriptorGroup; |
35 |
import org.opengis.parameter.ParameterNotFoundException; |
36 |
import org.opengis.parameter.ParameterValueGroup; |
37 |
import org.opengis.referencing.operation.CylindricalProjection; |
38 |
import org.opengis.referencing.operation.MathTransform; |
39 |
|
40 |
// Geotools dependencies
|
41 |
import org.geotools.measure.Latitude; |
42 |
import org.geotools.metadata.iso.citation.CitationImpl; |
43 |
import org.geotools.referencing.NamedIdentifier; |
44 |
import org.geotools.referencing.operation.MathTransformProvider; |
45 |
import org.geotools.resources.cts.ResourceKeys; |
46 |
import org.geotools.resources.cts.Resources; |
47 |
|
48 |
|
49 |
/**
|
50 |
* Mercator Cylindrical Projection. The parallels and the meridians are straight lines and
|
51 |
* cross at right angles; this projection thus produces rectangular charts. The scale is true
|
52 |
* along the equator (by default) or along two parallels equidistant of the equator (if a scale
|
53 |
* factor other than 1 is used). This projection is used to represent areas close to the equator.
|
54 |
* It is also often used for maritime navigation because all the straight lines on the chart are
|
55 |
* <em>loxodrome</em> lines, i.e. a ship following this line would keep a constant azimuth on its
|
56 |
* compass.
|
57 |
* <br><br>
|
58 |
*
|
59 |
* This implementation handles both the 1 and 2 stardard parallel cases.
|
60 |
* For <code>Mercator_1SP</code> (EPSG code 9804), the line of contact is the equator.
|
61 |
* For <code>Mercator_2SP</code> (EPSG code 9805) lines of contact are symmetrical
|
62 |
* about the equator.
|
63 |
* <br><br>
|
64 |
*
|
65 |
* <strong>References:</strong><ul>
|
66 |
* <li>John P. Snyder (Map Projections - A Working Manual,<br>
|
67 |
* U.S. Geological Survey Professional Paper 1395, 1987)</li>
|
68 |
* <li>"Coordinate Conversions and Transformations including Formulas",<br>
|
69 |
* EPSG Guidence Note Number 7, Version 19.</li>
|
70 |
* </ul>
|
71 |
*
|
72 |
* @see <A HREF="http://mathworld.wolfram.com/MercatorProjection.html">Mercator projection on MathWorld</A>
|
73 |
* @see <A HREF="http://www.remotesensing.org/geotiff/proj_list/mercator_1sp.html">"mercator_1sp" on Remote Sensing</A>
|
74 |
* @see <A HREF="http://www.remotesensing.org/geotiff/proj_list/mercator_2sp.html">"mercator_2sp" on Remote Sensing</A>
|
75 |
*
|
76 |
* @version $Id: IdrMercator.java 12357 2007-06-27 09:05:41Z dguerrero $
|
77 |
* @author Andr Gosselin
|
78 |
* @author Martin Desruisseaux
|
79 |
* @author Rueben Schulz
|
80 |
*/
|
81 |
public class IdrMercator extends MapProjection { |
82 |
/**
|
83 |
* Standard Parallel used for the <code>Mercator_2SP</code> case.
|
84 |
* Set to {@link Double#NaN} for the <code>Mercator_1SP</code> case.
|
85 |
*/
|
86 |
protected final double standardParallel; |
87 |
//protected final double latitudeOfOrigin;
|
88 |
|
89 |
/**
|
90 |
* The {@link MathTransformProvider} for a {@link IdrMercator} 1SP projection.
|
91 |
*
|
92 |
* @see <A HREF="http://www.remotesensing.org/geotiff/proj_list/mercator_1sp.html">"mercator_1sp" on Remote Sensing</A>
|
93 |
* @see org.geotools.referencing.operation.DefaultMathTransformFactory
|
94 |
*
|
95 |
* @version $Id: IdrMercator.java 12357 2007-06-27 09:05:41Z dguerrero $
|
96 |
* @author Martin Desruisseaux
|
97 |
* @author Rueben Schulz
|
98 |
*/
|
99 |
public static final class Provider1SP extends AbstractProvider { |
100 |
/**
|
101 |
* The parameters group.
|
102 |
*/
|
103 |
public static final ParameterDescriptor LATITUDE_OF_ORIGIN = createDescriptor( |
104 |
new NamedIdentifier[] { |
105 |
new NamedIdentifier(CitationImpl.OGC, "latitude_of_origin"), |
106 |
new NamedIdentifier(CitationImpl.EPSG, "CenterLat"), |
107 |
new NamedIdentifier(CitationImpl.EPSG, "Latitude of projection centre"), |
108 |
new NamedIdentifier(CitationImpl.GEOTIFF, "NatOriginLat"), |
109 |
new NamedIdentifier(CitationImpl.EPSG, "FalseOriginLat"), |
110 |
new NamedIdentifier(CitationImpl.EPSG, "Latitude of false origin"), |
111 |
new NamedIdentifier(CitationImpl.EPSG, "Latitude of natural origin"), |
112 |
new NamedIdentifier(CitationImpl.EPSG, "Latitude of projection centre"), |
113 |
new NamedIdentifier(CitationImpl.EPSG, "ProjCenterLat") |
114 |
}, 0.0, -90.0, 90.0, NonSI.DEGREE_ANGLE); |
115 |
|
116 |
static final ParameterDescriptorGroup PARAMETERS = createDescriptorGroup(new NamedIdentifier[] { |
117 |
new NamedIdentifier(CitationImpl.OGC, "Mercator_1SP"), |
118 |
new NamedIdentifier(CitationImpl.EPSG, "Mercator (1SP)"), |
119 |
new NamedIdentifier(CitationImpl.EPSG, "9804"), |
120 |
new NamedIdentifier(CitationImpl.GEOTIFF, "CT_Mercator"), |
121 |
new NamedIdentifier(CitationImpl.GEOTOOLS, Resources.formatInternational(
|
122 |
ResourceKeys.CYLINDRICAL_MERCATOR_PROJECTION)), |
123 |
new NamedIdentifier(new CitationImpl("IDR"), "IDR") |
124 |
}, new ParameterDescriptor[] { |
125 |
SEMI_MAJOR, SEMI_MINOR, |
126 |
CENTRAL_MERIDIAN, SCALE_FACTOR, |
127 |
LATITUDE_OF_ORIGIN, |
128 |
FALSE_EASTING, FALSE_NORTHING |
129 |
}); |
130 |
|
131 |
/**
|
132 |
* Constructs a new provider.
|
133 |
*/
|
134 |
public Provider1SP() {
|
135 |
super(PARAMETERS);
|
136 |
} |
137 |
|
138 |
/**
|
139 |
* Returns the operation type for this map projection.
|
140 |
*/
|
141 |
protected Class getOperationType() { |
142 |
return CylindricalProjection.class;
|
143 |
} |
144 |
|
145 |
/**
|
146 |
* Creates a transform from the specified group of parameter values.
|
147 |
*
|
148 |
* @param parameters The group of parameter values.
|
149 |
* @return The created math transform.
|
150 |
* @throws ParameterNotFoundException if a required parameter was not found.
|
151 |
*/
|
152 |
public MathTransform createMathTransform(final ParameterValueGroup parameters) |
153 |
throws ParameterNotFoundException
|
154 |
{ |
155 |
final Collection descriptors = PARAMETERS.descriptors(); |
156 |
if (isSpherical(parameters)) {
|
157 |
return new Spherical(parameters, descriptors); |
158 |
} else {
|
159 |
return new IdrMercator (parameters, descriptors); |
160 |
} |
161 |
} |
162 |
} |
163 |
|
164 |
/**
|
165 |
* The {@link MathTransformProvider} for a {@link IdrMercator} 2SP projection.
|
166 |
*
|
167 |
* @see <A HREF="http://www.remotesensing.org/geotiff/proj_list/mercator_2sp.html">"mercator_2sp" on Remote Sensing</A>
|
168 |
* @see org.geotools.referencing.operation.DefaultMathTransformFactory
|
169 |
*
|
170 |
* @version $Id: IdrMercator.java 12357 2007-06-27 09:05:41Z dguerrero $
|
171 |
* @author Martin Desruisseaux
|
172 |
* @author Rueben Schulz
|
173 |
*/
|
174 |
public static final class Provider2SP extends AbstractProvider { |
175 |
/**
|
176 |
* The operation parameter descriptor for the {@link #standardParallel standard parallel}
|
177 |
* parameter value. Valid values range is from -90 to 90. Default value is 0.
|
178 |
*/
|
179 |
public static final ParameterDescriptor STANDARD_PARALLEL = createDescriptor( |
180 |
new NamedIdentifier[] { |
181 |
new NamedIdentifier(CitationImpl.OGC, "standard_parallel_1"), |
182 |
new NamedIdentifier(CitationImpl.EPSG, "Latitude of 1st standard parallel"), |
183 |
new NamedIdentifier(CitationImpl.GEOTIFF, "StdParallel1") |
184 |
}, |
185 |
0, -90, 90, NonSI.DEGREE_ANGLE); |
186 |
|
187 |
/**
|
188 |
* The parameters group.
|
189 |
*/
|
190 |
static final ParameterDescriptorGroup PARAMETERS = createDescriptorGroup(new NamedIdentifier[] { |
191 |
new NamedIdentifier(CitationImpl.OGC, "Mercator_2SP"), |
192 |
new NamedIdentifier(CitationImpl.EPSG, "Mercator (2SP)"), |
193 |
new NamedIdentifier(CitationImpl.EPSG, "9805"), |
194 |
new NamedIdentifier(CitationImpl.GEOTIFF, "CT_Mercator"), |
195 |
new NamedIdentifier(CitationImpl.ESRI, "Mercator"), |
196 |
new NamedIdentifier(CitationImpl.GEOTOOLS, Resources.formatInternational(
|
197 |
ResourceKeys.CYLINDRICAL_MERCATOR_PROJECTION)), |
198 |
new NamedIdentifier(new CitationImpl("IDR"), "IDR") |
199 |
}, new ParameterDescriptor[] { |
200 |
SEMI_MAJOR, SEMI_MINOR, |
201 |
CENTRAL_MERIDIAN, STANDARD_PARALLEL, |
202 |
FALSE_EASTING, FALSE_NORTHING |
203 |
}); |
204 |
|
205 |
/**
|
206 |
* Constructs a new provider.
|
207 |
*/
|
208 |
public Provider2SP() {
|
209 |
super(PARAMETERS);
|
210 |
} |
211 |
|
212 |
/**
|
213 |
* Returns the operation type for this map projection.
|
214 |
*/
|
215 |
protected Class getOperationType() { |
216 |
return CylindricalProjection.class;
|
217 |
} |
218 |
|
219 |
/**
|
220 |
* Creates a transform from the specified group of parameter values.
|
221 |
*
|
222 |
* @param parameters The group of parameter values.
|
223 |
* @return The created math transform.
|
224 |
* @throws ParameterNotFoundException if a required parameter was not found.
|
225 |
*/
|
226 |
public MathTransform createMathTransform(final ParameterValueGroup parameters) |
227 |
throws ParameterNotFoundException
|
228 |
{ |
229 |
final Collection descriptors = PARAMETERS.descriptors(); |
230 |
if (isSpherical(parameters)) {
|
231 |
return new Spherical(parameters, descriptors); |
232 |
} else {
|
233 |
return new IdrMercator (parameters, descriptors); |
234 |
} |
235 |
} |
236 |
} |
237 |
|
238 |
|
239 |
/**
|
240 |
* Constructs a new map projection from the supplied parameters.
|
241 |
*
|
242 |
* @param parameters The parameter values in standard units.
|
243 |
* @throws ParameterNotFoundException if a mandatory parameter is missing.
|
244 |
*/
|
245 |
protected IdrMercator(final ParameterValueGroup parameters) |
246 |
throws ParameterNotFoundException
|
247 |
{ |
248 |
this(parameters, getDescriptor(parameters).descriptors());
|
249 |
} |
250 |
|
251 |
/**
|
252 |
* Work around for RFE #4093999 in Sun's bug database
|
253 |
* ("Relax constraint on placement of this()/super() call in constructors").
|
254 |
*/
|
255 |
private static ParameterDescriptorGroup getDescriptor(final ParameterValueGroup parameters) { |
256 |
try {
|
257 |
parameters.parameter(Provider2SP.STANDARD_PARALLEL.getName().getCode()); |
258 |
return Provider2SP.PARAMETERS;
|
259 |
} catch (ParameterNotFoundException ignore) {
|
260 |
return Provider1SP.PARAMETERS;
|
261 |
} |
262 |
} |
263 |
|
264 |
/**
|
265 |
* Constructs a new map projection from the supplied parameters.
|
266 |
*
|
267 |
* @param parameters The parameter values in standard units.
|
268 |
* @param expected The expected parameter descriptors.
|
269 |
* @throws ParameterNotFoundException if a mandatory parameter is missing.
|
270 |
*/
|
271 |
IdrMercator(final ParameterValueGroup parameters, final Collection expected) |
272 |
throws ParameterNotFoundException
|
273 |
{ |
274 |
//Fetch parameters
|
275 |
super(parameters, expected);
|
276 |
if (expected.contains(Provider2SP.STANDARD_PARALLEL)) {
|
277 |
// scaleFactor is not a parameter in the Mercator_2SP case and is computed from
|
278 |
// the standard parallel. The super-class constructor should have initialized
|
279 |
// 'scaleFactor' to 1. We still use the '*=' operator rather than '=' in case a
|
280 |
// user implementation still provides a scale factor for its custom projections.
|
281 |
standardParallel = Math.abs(doubleValue(expected,
|
282 |
Provider2SP.STANDARD_PARALLEL, parameters)); |
283 |
ensureLatitudeInRange(Provider2SP.STANDARD_PARALLEL, standardParallel, false);
|
284 |
if (isSpherical) {
|
285 |
scaleFactor *= Math.cos(standardParallel);
|
286 |
} else {
|
287 |
scaleFactor *= msfn(Math.sin(standardParallel),
|
288 |
Math.cos(standardParallel));
|
289 |
} |
290 |
globalScale = scaleFactor*semiMajor; |
291 |
} else {
|
292 |
// No standard parallel. Instead, uses the scale factor explicitely provided.
|
293 |
standardParallel = Double.NaN;
|
294 |
} |
295 |
//assert latitudeOfOrigin == 0 : latitudeOfOrigin;
|
296 |
} |
297 |
|
298 |
/**
|
299 |
* {@inheritDoc}
|
300 |
*/
|
301 |
public ParameterDescriptorGroup getParameterDescriptors() {
|
302 |
return Double.isNaN(standardParallel) ? Provider1SP.PARAMETERS |
303 |
: Provider2SP.PARAMETERS; |
304 |
} |
305 |
|
306 |
/**
|
307 |
* {@inheritDoc}
|
308 |
*/
|
309 |
public ParameterValueGroup getParameterValues() {
|
310 |
final ParameterValueGroup values = super.getParameterValues(); |
311 |
if (!Double.isNaN(standardParallel)) { |
312 |
final Collection expected = getParameterDescriptors().descriptors(); |
313 |
set(expected, Provider2SP.STANDARD_PARALLEL, values, standardParallel); |
314 |
} |
315 |
return values;
|
316 |
} |
317 |
|
318 |
/**
|
319 |
* Transforms the specified (<var>x</var>,<var>y</var>) coordinate (units in radians)
|
320 |
* and stores the result in <code>ptDst</code> (linear distance on a unit sphere).
|
321 |
*/
|
322 |
protected Point2D transformNormalized(double x, double y, final Point2D ptDst) |
323 |
throws ProjectionException
|
324 |
{ |
325 |
if (Math.abs(y) > (Math.PI/2 - EPS)) { |
326 |
throw new ProjectionException(Resources.format( |
327 |
ResourceKeys.ERROR_POLE_PROJECTION_$1, new Latitude(Math.toDegrees(y)))); |
328 |
} |
329 |
|
330 |
y = - Math.log(tsfn(y, Math.sin(y))); |
331 |
|
332 |
if (ptDst != null) { |
333 |
ptDst.setLocation(x,y); |
334 |
return ptDst;
|
335 |
} |
336 |
return new Point2D.Double(x,y); |
337 |
} |
338 |
|
339 |
/**
|
340 |
* Transforms the specified (<var>x</var>,<var>y</var>) coordinate
|
341 |
* and stores the result in <code>ptDst</code>.
|
342 |
*/
|
343 |
protected Point2D inverseTransformNormalized(double x, double y, final Point2D ptDst) |
344 |
throws ProjectionException
|
345 |
{ |
346 |
y = Math.exp(-y);
|
347 |
y = cphi2(y); |
348 |
|
349 |
if (ptDst != null) { |
350 |
ptDst.setLocation(x,y); |
351 |
return ptDst;
|
352 |
} |
353 |
return new Point2D.Double(x,y); |
354 |
} |
355 |
|
356 |
|
357 |
/**
|
358 |
* Provides the transform equations for the spherical case of the Mercator projection.
|
359 |
*
|
360 |
* @version $Id: IdrMercator.java 12357 2007-06-27 09:05:41Z dguerrero $
|
361 |
* @author Martin Desruisseaux
|
362 |
* @author Rueben Schulz
|
363 |
*/
|
364 |
private static final class Spherical extends IdrMercator { |
365 |
/**
|
366 |
* Constructs a new map projection from the suplied parameters.
|
367 |
*
|
368 |
* @param parameters The parameter values in standard units.
|
369 |
* @param expected The expected parameter descriptors.
|
370 |
* @throws ParameterNotFoundException if a mandatory parameter is missing.
|
371 |
*/
|
372 |
protected Spherical(final ParameterValueGroup parameters, final Collection expected) |
373 |
throws ParameterNotFoundException
|
374 |
{ |
375 |
super(parameters, expected);
|
376 |
//assert isSpherical;
|
377 |
} |
378 |
|
379 |
/**
|
380 |
* Transforms the specified (<var>x</var>,<var>y</var>) coordinate
|
381 |
* and stores the result in <code>ptDst</code> using equations for a Sphere.
|
382 |
*/
|
383 |
protected Point2D transformNormalized(double x, double y, Point2D ptDst) |
384 |
throws ProjectionException
|
385 |
{ |
386 |
if (Math.abs(y) > (Math.PI/2 - EPS)) { |
387 |
throw new ProjectionException(Resources.format( |
388 |
ResourceKeys.ERROR_POLE_PROJECTION_$1, new Latitude(Math.toDegrees(y)))); |
389 |
} |
390 |
// Compute using ellipsoidal formulas, for comparaison later.
|
391 |
//assert (ptDst = super.transformNormalized(x, y, ptDst)) != null;
|
392 |
|
393 |
y = Math.log(Math.tan((Math.PI/4) + 0.5*y)); |
394 |
|
395 |
//assert Math.abs(ptDst.getX()-x) <= EPS*globalScale : x;
|
396 |
//assert Math.abs(ptDst.getY()-y) <= EPS*globalScale : y;
|
397 |
if (ptDst != null) { |
398 |
ptDst.setLocation(x,y); |
399 |
return ptDst;
|
400 |
} |
401 |
return new Point2D.Double(x,y); |
402 |
} |
403 |
|
404 |
/**
|
405 |
* Transforms the specified (<var>x</var>,<var>y</var>) coordinate
|
406 |
* and stores the result in <code>ptDst</code> using equations for a sphere.
|
407 |
*/
|
408 |
protected Point2D inverseTransformNormalized(double x, double y, Point2D ptDst) |
409 |
throws ProjectionException
|
410 |
{ |
411 |
// Compute using ellipsoidal formulas, for comparaison later.
|
412 |
//assert (ptDst = super.inverseTransformNormalized(x, y, ptDst)) != null;
|
413 |
|
414 |
y = (Math.PI/2) - 2.0*Math.atan(Math.exp(-y)); |
415 |
|
416 |
//assert Math.abs(ptDst.getX()-x) <= EPS : x;
|
417 |
//assert Math.abs(ptDst.getY()-y) <= EPS : y;
|
418 |
if (ptDst != null) { |
419 |
ptDst.setLocation(x,y); |
420 |
return ptDst;
|
421 |
} |
422 |
return new Point2D.Double(x,y); |
423 |
} |
424 |
} |
425 |
|
426 |
|
427 |
/**
|
428 |
* Returns a hash value for this projection.
|
429 |
*/
|
430 |
public int hashCode() { |
431 |
final long code = Double.doubleToLongBits(standardParallel); |
432 |
return ((int)code ^ (int)(code >>> 32)) + 37*super.hashCode(); |
433 |
} |
434 |
|
435 |
/**
|
436 |
* Compares the specified object with this map projection for equality.
|
437 |
*/
|
438 |
public boolean equals(final Object object) { |
439 |
if (object == this) { |
440 |
// Slight optimization
|
441 |
return true; |
442 |
} |
443 |
if (super.equals(object)) { |
444 |
final IdrMercator that = (IdrMercator) object;
|
445 |
return equals(this.standardParallel, that.standardParallel); |
446 |
} |
447 |
return false; |
448 |
} |
449 |
} |