Statistics
| Revision:

root / org.gvsig.jcrs / libJCRS / src / org / geotools / referencing / operation / projection / IdrObliqueMercator.java @ 38

History | View | Annotate | Download (45.7 KB)

1
/*
2
 * Geotools - OpenSource mapping toolkit
3
 * (C) 2005, Geotools Project Managment Committee (PMC)
4
 *
5
 *    This library is free software; you can redistribute it and/or
6
 *    modify it under the terms of the GNU Lesser General Public
7
 *    License as published by the Free Software Foundation; either
8
 *    version 2.1 of the License, or (at your option) any later version.
9
 *
10
 *    This library is distributed in the hope that it will be useful,
11
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
 *    Lesser General Public License for more details.
14
 *
15
 *    You should have received a copy of the GNU Lesser General Public
16
 *    License along with this library; if not, write to the Free Software
17
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
 *
19
 *
20
 *    This package contains formulas from the PROJ package of USGS.
21
 *    USGS's work is fully acknowledged here.
22
 */
23
/*
24
** libproj -- library of cartographic projections
25
** Some parts Copyright (c) 2003   Gerald I. Evenden
26
**
27
** Permission is hereby granted, free of charge, to any person obtaining
28
** a copy of this software and associated documentation files (the
29
** "Software"), to deal in the Software without restriction, including
30
** without limitation the rights to use, copy, modify, merge, publish,
31
** distribute, sublicense, and/or sell copies of the Software, and to
32
** permit persons to whom the Software is furnished to do so, subject to
33
** the following conditions:
34
**
35
** The above copyright notice and this permission notice shall be
36
** included in all copies or substantial portions of the Software.
37
**
38
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
39
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
40
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
41
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
42
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
43
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
44
** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
45
*/
46
package org.geotools.referencing.operation.projection;
47

    
48
// J2SE dependencies and extensions
49
import java.awt.geom.Point2D;
50
import java.util.Collection;
51

    
52
import javax.units.NonSI;
53
import javax.units.SI;
54
import javax.units.Unit;
55

    
56
import org.geotools.metadata.iso.citation.CitationImpl;
57
import org.geotools.referencing.NamedIdentifier;
58
import org.geotools.referencing.operation.projection.IdrAzimuthalEquidistant.Provider;
59
import org.geotools.resources.cts.ResourceKeys;
60
import org.geotools.resources.cts.Resources;
61
import org.opengis.parameter.ParameterDescriptor;
62
import org.opengis.parameter.ParameterDescriptorGroup;
63
import org.opengis.parameter.ParameterNotFoundException;
64
import org.opengis.parameter.ParameterValueGroup;
65
import org.opengis.referencing.operation.CylindricalProjection;
66
import org.opengis.referencing.operation.MathTransform;
67

    
68

    
69
/**
70
 * Oblique Mercator Projection. A conformal, oblique, cylindrical projection 
71
 * with the cylinder touching the ellipsoid (or sphere) along a great circle 
72
 * path (the central line). The Mercator and Transverse Mercator projections 
73
 * can be thought of as special cases of the oblique mercator, where the central 
74
 * line is along the equator or a meridian, respectively. The Oblique Mercator 
75
 * projection has been used in Switzerland, Hungary, Madagascar, 
76
 * Malaysia, Borneo and the panhandle of Alaska.
77
 * <br><br>
78
 * 
79
 * The Oblique Mercator projection uses a (U,V) coordinate system, with the 
80
 * U axis along the central line. During the forward projection, coordinates 
81
 * from the ellipsoid are projected conformally to a sphere of constant total 
82
 * curvature, called the 'aposphere', before being projected onto the plane. 
83
 * The projection coordinates are further convented to a (X,Y) coordinate system 
84
 * by rotating the calculated (u,v) coordinates to give output (x,y) coordinates. 
85
 * The rotation value is usually the same as the projection azimuth (the angle, 
86
 * east of north, of the central line), but some cases allow a separate 
87
 * rotation parameter. 
88
 * <br><br>
89
 * 
90
 * There are two forms of the oblique mercator, differing in the origin of
91
 * their grid coordinates. The Hotine_Oblique_Mercator (EPSG code 9812) has grid 
92
 * coordinates start at the intersection of the central line and the equator of the 
93
 * aposphere. The Oblique_Mercator (EPSG code 9815) is the same, except the grid 
94
 * coordinates begin at the central point (where the latitude of center and 
95
 * central line intersect). ESRI separates these two case by appending
96
 * "Natural_Origin" (for the Hotine_Oblique_Mercator) and "Center" 
97
 * (for the Obique_Mercator) to the projection names.
98
 * <br><br>
99
 * 
100
 * Two different methods are used to specify the central line for the 
101
 * oblique mercator: 1) a central point and an azimuth, 
102
 * east of north, describing the central line and 
103
 * 2) two points on the central line. The EPSG does not use the two point method, 
104
 * while ESRI separates the two cases by putting "Azimuth" and "Two_Point" in 
105
 * their projection names. Both cases use the point where the "latitude_of_center" 
106
 * parameter crosses the central line as the projection's central point. 
107
 * The central meridian is not a projection parameter, and is instead calculated 
108
 * as the intersection between the central line and the equator of the aposphere. 
109
 * <br><br>
110
 *
111
 * For the azimuth method, the central latitude cannot be +- 90.0 degrees
112
 * and the central line cannot be at a maximum or minimum latitude at the central point.
113
 * In the two point method, the latitude of the first and second points cannot be
114
 * equal. Also, the latitude of the first point and central point cannot be
115
 * +- 90.0 degrees. Furthermore, the latitude of the first point cannot be 0.0 and 
116
 * the latitude of the second point cannot be - 90.0 degrees. A change of 
117
 * 10^-7 radians can allow calculation at these special cases. Snyder's restriction
118
 * of the central latitude being 0.0 has been removed, since the equaitons appear
119
 * to work correctly in this case.
120
 * <br><br>
121
 *
122
 * Azimuth values of 0.0 and +- 90.0 degrees are allowed (and used in Hungary
123
 * and Switzerland), though these cases would usually use a Mercator or 
124
 * Transverse Mercator projection instead. Azimuth values > 90 degrees cause
125
 * errors in the equations.
126
 * <br><br>
127
 * 
128
 * The oblique mercator is also called the "Rectified Skew Orthomorphic" (RSO). 
129
 * It appears is that the only difference from the oblique mercator is that
130
 * the RSO allows the rotation from the (U,V) to (X,Y) coordinate system to be different
131
 * from the azimuth. This separate parameter is called "rectified_grid_angle" (or 
132
 * "XY_Plane_Rotation" by ESRI) and is also included in the EPSG's parameters
133
 * for the Oblique Mercator and Hotine Oblique Mercator. 
134
 * The rotation parameter is optional in all the non-two point projections and will be
135
 * set to the azimuth if not specified.
136
 * <br><br>
137
 * 
138
 * Projection cases and aliases implemented by the {@link IdrObliqueMercator} are:
139
 * <ul>
140
 * <li>Oblique_Mercator (EPSG code 9815) - grid coordinates begin at the central point, has "rectified_grid_angle" parameter.</li>
141
 * <li>Hotine_Oblique_Mercator_Azimuth_Center (ESRI) - grid coordinates begin at the central point.</li>
142
 * <li>Rectified_Skew_Orthomorphic_Center (ESRI) - grid coordinates begin at the central point, has "rectified_grid_angle" parameter.</li>
143
 * 
144
 * <li>Hotine_Oblique_Mercator (EPSG code 9812) - grid coordinates begin at the interseciton of the central line and aposphere equator, has "rectified_grid_angle" parameter.</li>
145
 * <li>Hotine_Oblique_Mercator_Azimuth_Natural_Origin (ESRI) - grid coordinates begin at the interseciton of the central line and aposphere equator.</li>
146
 * <li>Rectified_Skew_Orthomorphic_Natural_Origin (ESRI) - grid coordinates begin at the interseciton of the central line and aposphere equator, has "rectified_grid_angle" parameter.</li>
147
 * 
148
 * <li>Hotine_Oblique_Mercator_Two_Point_Center (ESRI) - grid coordinates begin at the central point.</li>
149
 * <li>Hotine_Oblique_Mercator_Two_Point_Natural_Origin (ESRI) - grid coordinates begin at the interseciton of the central line and aposphere equator.</li>
150
 * </ul>
151
 * 
152
 * <strong>References:</strong>
153
 * <ul>
154
 *   <li><code>libproj4</code> is available at
155
 *       <A HREF="http://members.bellatlantic.net/~vze2hc4d/proj4/">libproj4 Miscellanea</A><br>
156
 *        Relevent files are: <code>PJ_omerc.c</code>, <code>pj_tsfn.c</code>,
157
 *        <code>pj_fwd.c</code>, <code>pj_inv.c</code> and <code>lib_proj.h</code></li>
158
 *   <li> John P. Snyder (Map Projections - A Working Manual,
159
 *        U.S. Geological Survey Professional Paper 1395, 1987)</li>
160
 *   <li> "Coordinate Conversions and Transformations including Formulas",
161
 *        EPSG Guidence Note Number 7 part 2, Version 24.</li>
162
 *   <li>Gerald Evenden, 2004, <a href="http://members.verizon.net/~vze2hc4d/proj4/omerc.pdf">
163
 *         Documentation of revised Oblique Mercator</a></li>
164
 * </ul>
165
 * 
166
 * @see <A HREF="http://mathworld.wolfram.com/MercatorProjection.html">Oblique Mercator projection on MathWorld</A>
167
 * @see <A HREF="http://www.remotesensing.org/geotiff/proj_list/hotine_oblique_mercator.html">hotine_oblique_mercator on Remote Sensing</A>
168
 * @see <A HREF="http://www.remotesensing.org/geotiff/proj_list/oblique_mercator.html">oblique_mercator on Remote Sensing</A>
169
 *
170
 * @version $Id: IdrObliqueMercator.java 18139 2008-01-16 16:21:08Z jlgomez $
171
 * @author  Rueben Schulz
172
 */
173
public class IdrObliqueMercator extends MapProjection {
174

    
175
        private final double scaleFactorLocal;
176
        private final double falseEastingLocal;
177
        private final double falseNorthingLocal;
178
        
179
        /**
180
     * Latitude of the projection centre. This is similar to the 
181
     * {@link #latitudeOfOrigin}, but the latitude of origin is the
182
     * Earth equator on aposphere for the oblique mercator. Needed
183
     * for WKT.
184
     */
185
    private final double latitudeOfCentre;
186
    
187
    /**
188
     * Longitude of the projection centre. This is <strong>NOT</strong> equal
189
     * to the {@link #centralMeridian}, which is the meridian where the
190
     * central line intersects the Earth equator on aposphere. Needed for
191
     * for non-two point WKT.
192
     */
193
    private final double longitudeOfCentre;
194
    
195
    /**
196
     * The azimuth of the central line passing throught the centre of the
197
     * projection, needed for for non-two point WKT. 
198
     */
199
    private double alpha_c; 
200
    
201
    /**
202
     * The rectified bearing of the central line, needed for non-two point WKT. Equals
203
     * {@link #alpha_c} if the "rectified_grid_angle" parameter value is not set.
204
     */
205
    private double rectGridAngle;
206
    
207
    /**
208
     * The latitude of the 1st point used to specify the central line, needed for two point
209
     * WKT. 
210
     */
211
    private final double latitudeOf1stPoint;
212
    
213
    /**
214
     * The longitude of the 1st point used to specify the central line, needed for two point
215
     * WKT. 
216
     */
217
    private final double longitudeOf1stPoint;
218
    
219
    /**
220
     * The latitude of the 2nd point used to specify the central line, needed for two point
221
     * WKT. 
222
     */
223
    private final double latitudeOf2ndPoint;
224
    
225
    /**
226
     * The longitude of the 2nd point used to specify the central line, needed for two point
227
     * WKT.  
228
     */
229
    private double longitudeOf2ndPoint;
230
    
231
    /**
232
     * Constants used in the transformation.
233
     */
234
    private double B, A, E;  
235
    
236
    /**
237
     * Convenience values equal to {@link #A} / {@link #B}, 
238
     * {@link #A}&times;{@link #B}, and {@link #B} / {@link #A}.
239
     */
240
    private final double ArB, AB, BrA;
241
    
242
    /**
243
     * v values when the input latitude is a pole.
244
     */
245
    private final double v_pole_n, v_pole_s;
246
    
247
    /**
248
     * Sine and Cosine values for gamma0 (the angle between the meridian
249
     * and central line at the intersection between the central line and 
250
     * the Earth equator on aposphere).
251
     */
252
    private final double singamma0, cosgamma0;
253
    
254
    /**
255
     * Sine and Cosine values for the rotation between (U,V) and 
256
     * (X,Y) coordinate systems
257
     */
258
    private final double sinrot, cosrot;
259
    
260
    /**
261
     * u value (in (U,V) coordinate system) of the central point. Used in the 
262
     * oblique mercater case. The v value of the central point is 0.0.
263
     */
264
    private double u_c;
265
    
266
    /**
267
     * <code>true</code> if using two points on the central line to specify 
268
     * the azimuth.
269
     */
270
    private final boolean twoPoint;
271
    
272
    /**
273
     * <code>true</code> for hotine oblique mercator, or <code>false</code> 
274
     * for the oblique mercator case. 
275
     */
276
    private final boolean hotine;
277
    
278
    /**
279
     * The {@link org.geotools.referencing.operation.MathTransformProvider}
280
     * for an {@link IdrObliqueMercator} projection.
281
     *
282
     * @see org.geotools.referencing.operation.DefaultMathTransformFactory
283
     *
284
     * @version $Id: IdrObliqueMercator.java 18139 2008-01-16 16:21:08Z jlgomez $
285
     * @author Rueben Schulz
286
     */
287
    public static class Provider extends AbstractProvider {
288

    
289
        public static final ParameterDescriptor SCALE_FACTOR_LOCAL = createDescriptor(
290
                new NamedIdentifier[] {
291
                    new NamedIdentifier(CitationImpl.OGC,      "scale_factor"),
292
                    new NamedIdentifier(CitationImpl.EPSG,     "Scale factor on initial line"),
293
                    new NamedIdentifier(CitationImpl.EPSG,    "Scale factor at natural origin"),
294
                    new NamedIdentifier(CitationImpl.GEOTIFF, "ScaleAtNatOrigin"),
295
                    new NamedIdentifier(CitationImpl.GEOTIFF, "ScaleAtCenter")
296
                },
297
                1, 0, Double.POSITIVE_INFINITY, Unit.ONE);
298
        
299
        public static final ParameterDescriptor FALSE_EASTING_LOCAL = createDescriptor(
300
                new NamedIdentifier[] {
301
                    new NamedIdentifier(CitationImpl.OGC,     "false_easting"),
302
                    new NamedIdentifier(CitationImpl.EPSG,    "False easting"),
303
                    new NamedIdentifier(CitationImpl.EPSG,    "Easting at projection centre"),
304
                    new NamedIdentifier(CitationImpl.EPSG,    "Easting at false origin"),
305
                    new NamedIdentifier(CitationImpl.GEOTIFF, "FalseEasting")
306
                },
307
                0, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, SI.METER);
308
            
309
        public static final ParameterDescriptor FALSE_NORTHING_LOCAL = createDescriptor(
310
                new NamedIdentifier[] {
311
                    new NamedIdentifier(CitationImpl.OGC,     "false_northing"),
312
                    new NamedIdentifier(CitationImpl.EPSG,    "False northing"),
313
                    new NamedIdentifier(CitationImpl.EPSG,    "Northing at projection centre"),
314
                    new NamedIdentifier(CitationImpl.EPSG,    "Northing at false origin"),
315
                    new NamedIdentifier(CitationImpl.GEOTIFF, "FalseNorthing")
316
                },
317
                0, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, SI.METER);
318
            
319
            
320
            /**
321
         * The operation parameter descriptor for the {@link #latitudeOfCentre}
322
         * parameter value. Valid values range is from -90 to 90. Default value is 0.
323
         */
324
        public static final ParameterDescriptor LAT_OF_CENTRE = createDescriptor(
325
                new NamedIdentifier[] {
326
                    new NamedIdentifier(CitationImpl.OGC,      "latitude_of_center"),
327
                    new NamedIdentifier(CitationImpl.EPSG,     "Latitude of projection centre"),
328
                    new NamedIdentifier(CitationImpl.ESRI,     "Latitude_Of_Center"),
329
                    new NamedIdentifier(CitationImpl.GEOTIFF,  "CenterLat")
330
                },
331
                0, -90, 90, NonSI.DEGREE_ANGLE);
332
        
333
        /**
334
         * The operation parameter descriptor for the {@link #longitudeOfCentre}
335
         * parameter value. Valid values range is from -180 to 180. Default value is 0.
336
         */
337
        public static final ParameterDescriptor LONG_OF_CENTRE = createDescriptor(
338
                new NamedIdentifier[] {
339
                    new NamedIdentifier(CitationImpl.OGC,      "longitude_of_center"),
340
                    new NamedIdentifier(CitationImpl.EPSG,     "Longitude of projection centre"),
341
                    new NamedIdentifier(CitationImpl.ESRI,     "Longitude_Of_Center"),
342
                    new NamedIdentifier(CitationImpl.GEOTIFF,  "CenterLong")
343
                },
344
                0, -180, 180, NonSI.DEGREE_ANGLE);
345
                
346
        /**
347
         * The operation parameter descriptor for the {@link #alpha_c}
348
         * parameter value. Valid values range is from -360 to -270, -90 to 90, 
349
         * and 270 to 360 degrees. Default value is 0.
350
         */
351
        public static final ParameterDescriptor AZIMUTH = createDescriptor(
352
                new NamedIdentifier[] {
353
                    new NamedIdentifier(CitationImpl.OGC,      "azimuth"),
354
                    new NamedIdentifier(CitationImpl.ESRI,     "Azimuth"),
355
                    new NamedIdentifier(CitationImpl.EPSG,     "Azimuth of initial line"),
356
                    new NamedIdentifier(CitationImpl.GEOTIFF,  "AzimuthAngle")
357
                },
358
                0, -360, 360, NonSI.DEGREE_ANGLE);
359
                
360
        /**
361
         * The operation parameter descriptor for the {@link #rectGridAngle}
362
         * parameter value. It is an optional parameter with valid values ranging
363
         * from -360 to 360. Default value is {@link #alpha_c}.
364
         */
365
        public static final ParameterDescriptor RECTIFIED_GRID_ANGLE = createOptionalDescriptor(
366
                new NamedIdentifier[] {
367
                    new NamedIdentifier(CitationImpl.OGC,      "rectified_grid_angle"),
368
                    new NamedIdentifier(CitationImpl.EPSG,     "Angle from Rectified to Skew Grid"),
369
                    new NamedIdentifier(CitationImpl.ESRI,     "XY_Plane_Rotation"),
370
                    new NamedIdentifier(CitationImpl.GEOTIFF,  "RectifiedGridAngle")
371
                },
372
                -360, 360, NonSI.DEGREE_ANGLE);
373
                
374
        /**
375
         * The parameters group.
376
         */
377
        static final ParameterDescriptorGroup PARAMETERS = createDescriptorGroup(new NamedIdentifier[] {
378
                new NamedIdentifier(CitationImpl.OGC,      "Oblique_Mercator"),
379
                new NamedIdentifier(CitationImpl.EPSG,     "Oblique Mercator"),
380
                new NamedIdentifier(CitationImpl.EPSG,     "9815"),
381
                new NamedIdentifier(CitationImpl.GEOTIFF,  "CT_ObliqueMercator"),
382
                new NamedIdentifier(CitationImpl.ESRI,     "Hotine_Oblique_Mercator_Azimuth_Center"),
383
                new NamedIdentifier(CitationImpl.ESRI,     "Rectified_Skew_Orthomorphic_Center"),
384
                new NamedIdentifier(CitationImpl.GEOTOOLS, Resources.formatInternational(
385
                                                           ResourceKeys.OBLIQUE_MERCATOR_PROJECTION)),
386
                new NamedIdentifier(new CitationImpl("IDR"), "IDR")
387
            }, new ParameterDescriptor[] {
388
                SEMI_MAJOR,          SEMI_MINOR,
389
                LONG_OF_CENTRE,      LAT_OF_CENTRE,
390
                AZIMUTH,             RECTIFIED_GRID_ANGLE,
391
                SCALE_FACTOR_LOCAL,
392
                FALSE_EASTING_LOCAL,       FALSE_NORTHING_LOCAL
393
            });
394

    
395
        /**
396
         * Constructs a new provider. 
397
         */
398
        public Provider() {
399
            super(PARAMETERS);
400
        }
401
        
402
        /**
403
         * Constructs a new provider. 
404
         */
405
        protected Provider(final ParameterDescriptorGroup params) {
406
            super(params);
407
        }
408

    
409
        /**
410
         * Returns the operation type for this map projection.
411
         */
412
        protected Class getOperationType() {
413
            return CylindricalProjection.class;
414
        }
415

    
416
        /**
417
         * Creates a transform from the specified group of parameter values.
418
         *
419
         * @param  parameters The group of parameter values.
420
         * @return The created math transform.
421
         * @throws ParameterNotFoundException if a required parameter was not found.
422
         */
423
        public MathTransform createMathTransform(final ParameterValueGroup parameters)
424
                throws ParameterNotFoundException
425
        {
426
            final Collection descriptors = PARAMETERS.descriptors();
427
            return new IdrObliqueMercator(parameters, descriptors, false, false);
428
        }
429
    }
430
    
431
    /**
432
     * The {@link org.geotools.referencing.operation.MathTransformProvider}
433
     * for a Hotine {@link IdrObliqueMercator} projection.
434
     *
435
     * @see org.geotools.referencing.operation.DefaultMathTransformFactory
436
     *
437
     * @version $Id: IdrObliqueMercator.java 18139 2008-01-16 16:21:08Z jlgomez $
438
     * @author Rueben Schulz
439
     */
440
    public static final class Provider_Hotine extends Provider {
441
        /**
442
         * The parameters group.
443
         */
444
        static final ParameterDescriptorGroup PARAMETERS = createDescriptorGroup(new NamedIdentifier[] {
445
                new NamedIdentifier(CitationImpl.OGC,      "Hotine_Oblique_Mercator"),
446
                new NamedIdentifier(CitationImpl.EPSG,     "Hotine Oblique Mercator"),
447
                new NamedIdentifier(CitationImpl.EPSG,     "9812"),
448
                new NamedIdentifier(CitationImpl.GEOTIFF,  "CT_ObliqueMercator_Hotine"),
449
                new NamedIdentifier(CitationImpl.ESRI,     "Hotine_Oblique_Mercator_Azimuth_Natural_Origin"),
450
                new NamedIdentifier(CitationImpl.ESRI,     "Rectified_Skew_Orthomorphic_Natural_Origin"),
451
                new NamedIdentifier(CitationImpl.GEOTOOLS, Resources.formatInternational(
452
                                                           ResourceKeys.OBLIQUE_MERCATOR_PROJECTION)),
453
                new NamedIdentifier(new CitationImpl("IDR"), "IDR")
454
            }, new ParameterDescriptor[] {
455
                SEMI_MAJOR,          SEMI_MINOR,
456
                LONG_OF_CENTRE,      LAT_OF_CENTRE,
457
                AZIMUTH,             RECTIFIED_GRID_ANGLE,
458
                SCALE_FACTOR_LOCAL,
459
                FALSE_EASTING_LOCAL,       FALSE_NORTHING_LOCAL
460
        });
461
        
462
        /**
463
         * Constructs a new provider. 
464
         */
465
        public Provider_Hotine() {
466
            super(PARAMETERS);
467
        }
468

    
469
        /**
470
         * Returns the operation type for this map projection.
471
         */
472
        protected Class getOperationType() {
473
            return CylindricalProjection.class;
474
        }
475

    
476
        /**
477
         * Creates a transform from the specified group of parameter values.
478
         *
479
         * @param  parameters The group of parameter values.
480
         * @return The created math transform.
481
         * @throws ParameterNotFoundException if a required parameter was not found.
482
         */
483
        public MathTransform createMathTransform(final ParameterValueGroup parameters)
484
                throws ParameterNotFoundException
485
        {
486
            final Collection descriptors = PARAMETERS.descriptors();
487
            return new IdrObliqueMercator(parameters, descriptors, true, false);
488
        }
489
    }
490
   
491
    /**
492
     * The {@link org.geotools.referencing.operation.MathTransformProvider}
493
     * for a {@link IdrObliqueMercator} projection, specified with
494
     * two points on the central line (instead of a central point and azimuth).
495
     *
496
     * @see org.geotools.referencing.operation.DefaultMathTransformFactory
497
     *
498
     * @version $Id: IdrObliqueMercator.java 18139 2008-01-16 16:21:08Z jlgomez $
499
     * @author Rueben Schulz
500
     */
501
    public static class Provider_TwoPoint extends Provider {
502
                    /**
503
         * The operation parameter descriptor for the {@link #latitudeOfCentre}
504
         * parameter value. Valid values range is from -90 to 90. Default value is 0.
505
         */
506
        public static final ParameterDescriptor LAT_OF_CENTRE = createDescriptor(
507
                new NamedIdentifier[] {
508
                    new NamedIdentifier(CitationImpl.OGC,      "latitude_of_center"),
509
                    new NamedIdentifier(CitationImpl.EPSG,     "Latitude of projection centre"),
510
                    new NamedIdentifier(CitationImpl.ESRI,     "Latitude_Of_Center"),
511
                    new NamedIdentifier(CitationImpl.GEOTIFF,  "CenterLat")
512
                },
513
                0, -90, 90, NonSI.DEGREE_ANGLE);
514
        
515
                /**
516
         * The operation parameter descriptor for the {@link #latitudeOf1stPoint}
517
         * parameter value. Valid values range is from -90 to 90. Default value is 0.
518
         */
519
        public static final ParameterDescriptor LAT_OF_1ST_POINT = createDescriptor(
520
                new NamedIdentifier[] {
521
                    new NamedIdentifier(CitationImpl.ESRI, "Latitude_Of_1st_Point")
522
                },
523
                0, -90, 90, NonSI.DEGREE_ANGLE);
524
        
525
        /**
526
         * The operation parameter descriptor for the {@link #longitudeOf1stPoint}
527
         * parameter value. Valid values range is from -180 to 180. Default value is 0.
528
         */
529
        public static final ParameterDescriptor LONG_OF_1ST_POINT = createDescriptor(
530
                new NamedIdentifier[] {
531
                    new NamedIdentifier(CitationImpl.ESRI, "Longitude_Of_1st_Point")
532
                },
533
                0, -180, 180, NonSI.DEGREE_ANGLE);
534
        
535
        /**
536
         * The operation parameter descriptor for the {@link #latitudeOf2ndPoint}
537
         * parameter value. Valid values range is from -90 to 90. Default value is 0.
538
         */
539
        public static final ParameterDescriptor LAT_OF_2ND_POINT = createDescriptor(
540
                new NamedIdentifier[] {
541
                    new NamedIdentifier(CitationImpl.ESRI, "Latitude_Of_2nd_Point")
542
                },
543
                0, -90, 90, NonSI.DEGREE_ANGLE);
544
        
545
        /**
546
         * The operation parameter descriptor for the {@link #longitudeOf2ndPoint}
547
         * parameter value. Valid values range is from -180 to 180. Default value is 0.
548
         */
549
        public static final ParameterDescriptor LONG_OF_2ND_POINT = createDescriptor(
550
                new NamedIdentifier[] {
551
                    new NamedIdentifier(CitationImpl.ESRI, "Longitude_Of_2nd_Point")
552
                },
553
                0, -180, 180, NonSI.DEGREE_ANGLE);
554
        
555
        /**
556
         * The parameters group.
557
         */
558
        static final ParameterDescriptorGroup PARAMETERS = createDescriptorGroup(new NamedIdentifier[] {
559
                new NamedIdentifier(CitationImpl.ESRI,     "Hotine_Oblique_Mercator_Two_Point_Center"),
560
                new NamedIdentifier(CitationImpl.GEOTOOLS, Resources.formatInternational(
561
                                                           ResourceKeys.OBLIQUE_MERCATOR_PROJECTION)),
562
                new NamedIdentifier(new CitationImpl("IDR"), "IDR")
563
            }, new ParameterDescriptor[] {
564
                SEMI_MAJOR,          SEMI_MINOR,
565
                LAT_OF_1ST_POINT,    LONG_OF_1ST_POINT,
566
                LAT_OF_2ND_POINT,    LONG_OF_2ND_POINT,
567
                           LAT_OF_CENTRE,       SCALE_FACTOR_LOCAL,
568
                FALSE_EASTING_LOCAL,       FALSE_NORTHING_LOCAL
569
            });
570

    
571
        /**
572
         * Constructs a new provider. 
573
         */
574
        public Provider_TwoPoint() {
575
            super(PARAMETERS);
576
        }
577
        
578
        /**
579
         * Constructs a new provider. 
580
         */
581
        protected Provider_TwoPoint(final ParameterDescriptorGroup params) {
582
            super(params);
583
        }
584

    
585
        /**
586
         * Returns the operation type for this map projection.
587
         */
588
        protected Class getOperationType() {
589
            return CylindricalProjection.class;
590
        }
591

    
592
        /**
593
         * Creates a transform from the specified group of parameter values.
594
         *
595
         * @param  parameters The group of parameter values.
596
         * @return The created math transform.
597
         * @throws ParameterNotFoundException if a required parameter was not found.
598
         */
599
        public MathTransform createMathTransform(final ParameterValueGroup parameters)
600
                throws ParameterNotFoundException
601
        {
602
            final Collection descriptors = PARAMETERS.descriptors();
603
            return new IdrObliqueMercator(parameters, descriptors, false, true);
604
        }
605
        
606
    }
607

    
608

    
609
    /**
610
     * The {@link org.geotools.referencing.operation.MathTransformProvider}
611
     * for a Hotine {@link IdrObliqueMercator} projection, specified with
612
     * two points on the central line (instead of a central point and azimuth).
613
     *
614
     * @see org.geotools.referencing.operation.DefaultMathTransformFactory
615
     *
616
     * @version $Id: IdrObliqueMercator.java 18139 2008-01-16 16:21:08Z jlgomez $
617
     * @author Rueben Schulz
618
     */
619
    public static final class Provider_Hotine_TwoPoint extends Provider_TwoPoint {
620
            /**
621
         * The parameters group.
622
         */
623
        static final ParameterDescriptorGroup PARAMETERS = createDescriptorGroup(new NamedIdentifier[] {
624
                new NamedIdentifier(CitationImpl.ESRI,     "Hotine_Oblique_Mercator_Two_Point_Natural_Origin"),
625
                new NamedIdentifier(CitationImpl.GEOTOOLS, Resources.formatInternational(
626
                                                           ResourceKeys.OBLIQUE_MERCATOR_PROJECTION)),
627
                new NamedIdentifier(new CitationImpl("IDR"), "IDR")
628
            }, new ParameterDescriptor[] {
629
                SEMI_MAJOR,          SEMI_MINOR,
630
                LAT_OF_1ST_POINT,    LONG_OF_1ST_POINT,
631
                LAT_OF_2ND_POINT,    LONG_OF_2ND_POINT,
632
                        LAT_OF_CENTRE,       SCALE_FACTOR_LOCAL,
633
                FALSE_EASTING_LOCAL,       FALSE_NORTHING_LOCAL
634
            });
635

    
636
        /**
637
         * Constructs a new provider. 
638
         */
639
        public Provider_Hotine_TwoPoint() {
640
            super(PARAMETERS);
641
        }
642

    
643
        /**
644
         * Returns the operation type for this map projection.
645
         */
646
        protected Class getOperationType() {
647
            return CylindricalProjection.class;
648
        }
649

    
650
        /**
651
         * Creates a transform from the specified group of parameter values.
652
         *
653
         * @param  parameters The group of parameter values.
654
         * @return The created math transform.
655
         * @throws ParameterNotFoundException if a required parameter was not found.
656
         */
657
        public MathTransform createMathTransform(final ParameterValueGroup parameters)
658
                throws ParameterNotFoundException
659
        {
660
            final Collection descriptors = PARAMETERS.descriptors();
661
            return new IdrObliqueMercator(parameters, descriptors, true, true);
662
        }
663

    
664
    }
665
    
666
    /**
667
     * Constructs a new map projection from the supplied parameters.
668
     *
669
     * @param  parameters The parameter values in standard units.
670
     * @param  expected The expected parameter descriptors.
671
     * @throws ParameterNotFoundException if a mandatory parameter is missing.
672
     */
673
    IdrObliqueMercator(final ParameterValueGroup parameters, final Collection expected,
674
                    final boolean hotine, final boolean twoPoint) 
675
            throws ParameterNotFoundException 
676
    {
677
        
678
            //Fetch parameters 
679
        super(parameters, expected);
680
        
681
        this.hotine = hotine;
682
        this.twoPoint = twoPoint;
683
        
684
        //NaN for safety (centralMeridian calculated below)
685
        latitudeOfOrigin = Double.NaN;
686
        centralMeridian  = Double.NaN;
687

    
688
        //scaleFactorLocal=1.0;
689
        //falseEastingLocal=0.0;
690
        //falseNorthingLocal=0.0;
691
        
692
        final Collection miExpected = getParameterDescriptors().descriptors();
693
        if (miExpected.contains(Provider.SCALE_FACTOR_LOCAL)) {
694
                scaleFactorLocal = Math.abs(doubleValue(expected,
695
                                        Provider.SCALE_FACTOR_LOCAL, parameters));
696
            //ensureInRange(Provider.SCALE_FACTOR_LOCAL, scaleFactorLocal, false);
697
        } else {
698
                scaleFactorLocal = 1.0;
699
        }
700
        if (miExpected.contains(Provider.FALSE_EASTING_LOCAL)) {
701
                falseEastingLocal = Math.abs(doubleValue(expected,
702
                                        Provider.FALSE_EASTING_LOCAL, parameters));
703
            //ensureLatitudeInRange(Provider.FALSE_EASTING_LOCAL, falseEastingLocal, false);
704
        } else {
705
                falseEastingLocal = 0.0;
706
        }
707
        if (miExpected.contains(Provider.FALSE_NORTHING_LOCAL)) {
708
                falseNorthingLocal = Math.abs(doubleValue(expected,
709
                                        Provider.FALSE_NORTHING_LOCAL, parameters));
710
            //ensureLatitudeInRange(Provider.FALSE_NORTHING_LOCAL, falseNorthingLocal, false);
711
        } else {
712
                falseNorthingLocal = 0.0;
713
        }
714

    
715
        latitudeOfCentre = doubleValue(expected, Provider.LAT_OF_CENTRE, parameters);
716
        //checks that latitudeOfCentre is not +- 90 degrees
717
        //not checking if latitudeOfCentere is 0, since equations behave correctly
718
        ensureLatitudeInRange(Provider.LAT_OF_CENTRE, latitudeOfCentre, false);
719
        
720
        if (twoPoint) {
721
            longitudeOfCentre  = Double.NaN;
722
            latitudeOf1stPoint = doubleValue(expected, Provider_TwoPoint.LAT_OF_1ST_POINT, parameters);
723
            //checks that latOf1stPoint is not +-90 degrees
724
            ensureLatitudeInRange(Provider_TwoPoint.LAT_OF_1ST_POINT, latitudeOf1stPoint, false);
725
            longitudeOf1stPoint = doubleValue(expected, Provider_TwoPoint.LONG_OF_1ST_POINT, parameters);
726
            ensureLongitudeInRange(Provider_TwoPoint.LONG_OF_1ST_POINT, longitudeOf1stPoint, true);
727
            latitudeOf2ndPoint = doubleValue(expected, Provider_TwoPoint.LAT_OF_2ND_POINT, parameters);
728
            ensureLatitudeInRange(Provider_TwoPoint.LAT_OF_2ND_POINT, latitudeOf2ndPoint, true);
729
            longitudeOf2ndPoint = doubleValue(expected, Provider_TwoPoint.LONG_OF_2ND_POINT, parameters);
730
            ensureLongitudeInRange(Provider_TwoPoint.LONG_OF_2ND_POINT, longitudeOf2ndPoint, true);
731
            
732
            /*
733
            double con = Math.abs(latitudeOf1stPoint);
734
            if (Math.abs(latitudeOf1stPoint - latitudeOf2ndPoint) < TOL) {
735
                throw new IllegalArgumentException(Resources.format(ResourceKeys.ERROR_LAT1_EQ_LAT2));
736
            }
737
            if (Math.abs(latitudeOf1stPoint) < TOL) {
738
                throw new IllegalArgumentException(Resources.format(ResourceKeys.ERROR_LAT1_EQ_ZERO));
739
            }
740
            if (Math.abs(latitudeOf2ndPoint + Math.PI/2.0) < TOL) {
741
                throw new IllegalArgumentException(Resources.format(ResourceKeys.ERROR_LAT2_EQ_NEG_90));
742
            }
743
            */
744
        } else {
745
            latitudeOf1stPoint  = Double.NaN;
746
            longitudeOf1stPoint = Double.NaN;
747
            latitudeOf2ndPoint  = Double.NaN;
748
            longitudeOf2ndPoint = Double.NaN;
749
                       
750
            longitudeOfCentre = doubleValue(expected, Provider.LONG_OF_CENTRE, parameters);
751
            ensureLongitudeInRange(Provider.LONG_OF_CENTRE, longitudeOfCentre, true);
752
            
753
            alpha_c = doubleValue(expected, Provider.AZIMUTH, parameters);
754
            //already checked for +-360 deg. above. 
755
            if ((alpha_c > -1.5*Math.PI && alpha_c < -0.5*Math.PI) ||
756
                (alpha_c > 0.5*Math.PI && alpha_c < 1.5*Math.PI)) {
757
                    throw new IllegalArgumentException(
758
                        Resources.format(ResourceKeys.ERROR_VALUE_OUT_OF_BOUNDS_$3,
759
                        new Double(Math.toDegrees(alpha_c)), new Double(-90), new Double(90)));
760
            }
761
            
762
            rectGridAngle = doubleValue(expected, Provider.RECTIFIED_GRID_ANGLE, parameters);
763
            if (Double.isNaN(rectGridAngle)) {
764
                rectGridAngle = alpha_c;
765
            }
766
        }   
767
        /*
768
        double com = Math.sqrt(1.0-excentricitySquared);
769
        double sinphi0 = Math.sin(latitudeOfCentre);
770
        double cosphi0 = Math.cos(latitudeOfCentre);
771
        B = cosphi0 * cosphi0;
772
        B = Math.sqrt(1.0 + excentricitySquared * B * B / (1.0-excentricitySquared));
773
        double con = 1.0 - excentricitySquared * sinphi0 * sinphi0;
774
        A = B * com / con;
775
        double D = B * com / (cosphi0 * Math.sqrt(con));
776
        double F = D * D - 1.0;
777
        if (F < 0.0) {
778
            F = 0.0;
779
        } else {
780
            F = Math.sqrt(F);
781
            if (latitudeOfCentre < 0.0) {  //taking sign of latOfCentre
782
                F = -F;
783
            }
784
        }
785
        F = F += D;
786
        E = F* Math.pow(tsfn(latitudeOfCentre, sinphi0), B);          
787
        double gamma0;
788
        if (twoPoint) {
789
            double H = Math.pow(tsfn(latitudeOf1stPoint, Math.sin(latitudeOf1stPoint)), B);
790
            double L = Math.pow(tsfn(latitudeOf2ndPoint, Math.sin(latitudeOf2ndPoint)), B);
791
            double Fp = E / H;
792
            double P = (L - H) / (L + H);
793
            double J = E * E;
794
            J = (J - L * H) / (J + L * H);        
795
            double diff = longitudeOf1stPoint - longitudeOf2ndPoint;
796
            if (diff < -Math.PI) {
797
                longitudeOf2ndPoint -= 2.0* Math.PI;
798
            } else if (diff > Math.PI) {
799
                longitudeOf2ndPoint += 2.0* Math.PI;
800
            }
801
            
802
            centralMeridian = rollLongitude(0.5 * (longitudeOf1stPoint + longitudeOf2ndPoint) -
803
                              Math.atan(J * Math.tan(0.5 * B * (longitudeOf1stPoint - longitudeOf2ndPoint)) / P) / B);
804
            gamma0 = Math.atan(2.0 * Math.sin(B * rollLongitude(longitudeOf1stPoint - centralMeridian)) /
805
                     (Fp - 1.0 / Fp));
806
            alpha_c = Math.asin(D * Math.sin(gamma0));
807
            rectGridAngle = alpha_c;
808
        } else {
809
            gamma0 = Math.asin(Math.sin(alpha_c) / D);
810
            //check for asin(+-1.00000001)
811
            double temp = 0.5 * (F - 1.0 / F) * Math.tan(gamma0);
812
            if (Math.abs(temp) > 1.0) {
813
                if (Math.abs(Math.abs(temp) - 1.0) > EPS) {
814
                    throw new IllegalArgumentException("Tolerance condition error");
815
                }
816
                temp = (temp > 0) ? 1.0 : -1.0;
817
            }
818
            centralMeridian = longitudeOfCentre - Math.asin(temp) / B; 
819
        }
820
        singamma0 = Math.sin(gamma0);
821
        cosgamma0 = Math.cos(gamma0);
822
                sinrot = Math.sin(rectGridAngle);
823
                cosrot = Math.cos(rectGridAngle);
824
        ArB = A/B;
825
        AB = A*B;
826
        BrA = B/A;
827
        v_pole_n = ArB * Math.log(Math.tan(0.5 * (Math.PI/2.0 - gamma0)));
828
        v_pole_s = ArB * Math.log(Math.tan(0.5 * (Math.PI/2.0 + gamma0)));
829
        if (hotine) {
830
            u_c = 0.0;
831
        } else {
832
            if (Math.abs(Math.abs(alpha_c) - Math.PI/2.0) < TOL) {
833
                //longitudeOfCentre = NaN in twopoint, but alpha_c cannot be 90 here (lat1 != lat2)
834
                u_c = A * (longitudeOfCentre - centralMeridian);
835
            } else {
836
                u_c = Math.abs(ArB * Math.atan2(Math.sqrt(D * D - 1.0), Math.cos(alpha_c)));
837
                if (latitudeOfCentre < 0.0) {
838
                    u_c = -u_c;
839
                }
840
            }
841
        }
842
        */
843
        singamma0 = 0;
844
        cosgamma0 = 0;
845
                sinrot = 0;
846
                cosrot = 0;
847
        ArB = 0;
848
        AB = 0;
849
        BrA = 0;
850
        v_pole_n =0;
851
        v_pole_s =0;
852
   }
853
    
854
    /**
855
     * {@inheritDoc}
856
     */
857
    public ParameterDescriptorGroup getParameterDescriptors() {
858
        if (hotine) {
859
            return (twoPoint) ? Provider_Hotine_TwoPoint.PARAMETERS : Provider_Hotine.PARAMETERS;
860
        } else {
861
            return (twoPoint) ? Provider_TwoPoint.PARAMETERS : Provider.PARAMETERS;
862
        }
863
    }
864
    
865
    /**
866
     * {@inheritDoc}
867
     */
868
    public ParameterValueGroup getParameterValues() {
869
        final ParameterValueGroup values = super.getParameterValues();
870
        final Collection expected = getParameterDescriptors().descriptors();
871
        if (twoPoint) {
872
            set(expected, Provider_TwoPoint.LAT_OF_CENTRE, values, latitudeOfCentre);
873
            set(expected, Provider_TwoPoint.LAT_OF_1ST_POINT, values, latitudeOf1stPoint);
874
            set(expected, Provider_TwoPoint.LONG_OF_1ST_POINT, values, longitudeOf1stPoint);
875
            set(expected, Provider_TwoPoint.LAT_OF_2ND_POINT, values, latitudeOf2ndPoint);
876
            set(expected, Provider_TwoPoint.LONG_OF_2ND_POINT, values, longitudeOf2ndPoint);
877
        } else {
878
            set(expected, Provider.LAT_OF_CENTRE, values, latitudeOfCentre);
879
            set(expected, Provider.LONG_OF_CENTRE, values, longitudeOfCentre);
880
            set(expected, Provider.AZIMUTH, values, alpha_c );
881
            set(expected, Provider.RECTIFIED_GRID_ANGLE, values, rectGridAngle);
882
        }
883
        set(expected, Provider.SCALE_FACTOR_LOCAL, values, scaleFactorLocal);
884
        set(expected, Provider.FALSE_NORTHING_LOCAL, values, falseNorthingLocal);
885
        set(expected, Provider.FALSE_EASTING_LOCAL, values, falseEastingLocal);
886
        return values;
887
    }
888

    
889
    /**
890
     * {@inheritDoc}
891
     */
892
    protected Point2D transformNormalized(double x, double y, Point2D ptDst)
893
            throws ProjectionException 
894
    {
895
        double u, v;
896
        if (Math.abs(Math.abs(y) - Math.PI/2.0) > EPS) {
897
            double Q = E / Math.pow(tsfn(y, Math.sin(y)), B);
898
            double temp = 1.0 / Q;
899
            double S = 0.5 * (Q - temp);
900
            double V = Math.sin(B * x);
901
            double U = (S * singamma0 - V * cosgamma0) / (0.5 * (Q + temp));
902
            if (Math.abs(Math.abs(U) - 1.0) < EPS) {
903
                throw new ProjectionException(Resources.format(ResourceKeys.ERROR_V_INFINITE));
904
            }
905
            v = 0.5 * ArB * Math.log((1.0 - U) / (1.0 + U));
906
            temp = Math.cos(B * x);
907
            if (Math.abs(temp) < TOL) {
908
                u = AB * x;
909
            } else {
910
                u = ArB * Math.atan2((S * cosgamma0 + V * singamma0), temp);
911
            }   
912
        } else {
913
            v = y > 0 ? v_pole_n : v_pole_s;
914
            u = ArB * y;
915
        }
916
        
917
        u -= u_c;
918
                x = v * cosrot + u * sinrot;
919
                y = u * cosrot - v * sinrot;
920

    
921
        if (ptDst != null) {
922
            ptDst.setLocation(x,y);
923
            return ptDst;
924
        }
925
        return new Point2D.Double(x,y);
926
    }
927
    
928
    /**
929
     * {@inheritDoc}
930
     */
931
    protected Point2D inverseTransformNormalized(double x, double y, Point2D ptDst) 
932
            throws ProjectionException 
933
    {
934
        double v = x * cosrot - y * sinrot;
935
        double u = y * cosrot + x * sinrot + u_c;
936
        
937
        double Qp = Math.exp(-BrA * v);
938
        double temp = 1.0 / Qp;
939
        double Sp = 0.5 * (Qp - temp);
940
        double Vp = Math.sin(BrA * u);
941
        double Up = (Vp * cosgamma0 + Sp * singamma0) / (0.5 * (Qp + temp));
942
        if (Math.abs(Math.abs(Up) - 1.0) < EPS) {
943
            x = 0.0;
944
            y = Up < 0.0 ? -Math.PI / 2.0 : Math.PI / 2.0;
945
        } else {
946
            y = Math.pow(E / Math.sqrt((1. + Up) / (1. - Up)), 1.0 / B);  //calculate t
947
            y = cphi2(y);
948
            x = -Math.atan2((Sp * cosgamma0 - Vp * singamma0), Math.cos(BrA * u)) / B;
949
        }
950

    
951
        if (ptDst != null) {
952
            ptDst.setLocation(x,y);
953
            return ptDst;
954
        }
955
        return new Point2D.Double(x,y);
956
    }    
957

    
958
    /**
959
     * Maximal error (in metres) tolerated for assertion, if enabled.
960
     *
961
     * @param  longitude The longitude in degrees.
962
     * @param  latitude The latitude in degrees.
963
     * @return The tolerance level for assertions, in meters.
964
     */
965
    protected double getToleranceForAssertions(final double longitude, final double latitude) {
966
        if (Math.abs(longitude - centralMeridian)/2 +
967
            Math.abs(latitude  - latitudeOfCentre) > 10)
968
        {
969
            // When far from the valid area, use a larger tolerance.
970
            return 1;
971
        }
972
        return super.getToleranceForAssertions(longitude, latitude);
973
    }
974
    
975
    /**
976
     * Returns a hash value for this projection.
977
     */
978
    public int hashCode() {
979
        long code =      Double.doubleToLongBits(latitudeOfCentre);
980
        code = code*37 + Double.doubleToLongBits(longitudeOfCentre);
981
        code = code*37 + Double.doubleToLongBits(alpha_c);
982
        code = code*37 + Double.doubleToLongBits(rectGridAngle);
983
        code = code*37 + Double.doubleToLongBits(latitudeOf1stPoint);
984
        code = code*37 + Double.doubleToLongBits(latitudeOf2ndPoint);
985
        return ((int)code ^ (int)(code >>> 32)) + 37*super.hashCode();
986
    }
987

    
988
    /**
989
     * Compares the specified object with this map projection for equality.
990
     */
991
    public boolean equals(final Object object) {
992
        if (object == this) {
993
            // Slight optimization
994
            return true;
995
        }
996
        if (super.equals(object)) {
997
            final IdrObliqueMercator that = (IdrObliqueMercator) object;
998
            return equals(this.latitudeOfCentre   , that.latitudeOfCentre   ) &&
999
                   equals(this.longitudeOfCentre  , that.longitudeOfCentre  ) &&
1000
                   equals(this.alpha_c            , that.alpha_c            ) &&
1001
                   equals(this.rectGridAngle      , that.rectGridAngle      ) &&
1002
                   equals(this.u_c                , that.u_c                ) &&
1003
                   equals(this.latitudeOf1stPoint , that.latitudeOf1stPoint ) &&
1004
                   equals(this.longitudeOf1stPoint, that.longitudeOf1stPoint) &&
1005
                   equals(this.latitudeOf2ndPoint , that.latitudeOf2ndPoint ) &&
1006
                   equals(this.longitudeOf2ndPoint, that.longitudeOf2ndPoint) &&
1007
                   this.twoPoint == that.twoPoint &&
1008
                   this.hotine   == that.hotine;
1009
        }
1010
        return false;
1011
    }
1012
}