001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: *
005: * (C) 2003-2006, Geotools Project Managment Committee (PMC)
006: * (C) 2001, Institut de Recherche pour le Développement
007: * (C) 2000, Frank Warmerdam
008: * (C) 1999, Fisheries and Oceans Canada
009: *
010: * This library is free software; you can redistribute it and/or
011: * modify it under the terms of the GNU Lesser General Public
012: * License as published by the Free Software Foundation; either
013: * version 2.1 of the License, or (at your option) any later version.
014: *
015: * This library is distributed in the hope that it will be useful,
016: * but WITHOUT ANY WARRANTY; without even the implied warranty of
017: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
018: * Lesser General Public License for more details.
019: *
020: * This package contains formulas from the PROJ package of USGS.
021: * USGS's work is fully acknowledged here. This derived work has
022: * been relicensed under LGPL with Frank Warmerdam's permission.
023: */
024: package org.geotools.referencing.operation.projection;
025:
026: // J2SE dependencies and extensions
027: import java.awt.geom.Point2D;
028: import java.util.Collection;
029: import javax.units.NonSI;
030:
031: // OpenGIS dependencies
032: import org.opengis.parameter.ParameterValueGroup;
033: import org.opengis.parameter.ParameterDescriptor;
034: import org.opengis.parameter.ParameterDescriptorGroup;
035: import org.opengis.parameter.ParameterNotFoundException;
036: import org.opengis.parameter.ParameterValueGroup;
037: import org.opengis.referencing.operation.MathTransform;
038:
039: // Geotools dependencies
040: import org.geotools.resources.i18n.Errors;
041: import org.geotools.resources.i18n.ErrorKeys;
042: import org.geotools.referencing.NamedIdentifier;
043: import org.geotools.metadata.iso.citation.Citations;
044:
045: /**
046: * The polar case of the {@linkplain Stereographic stereographic} projection.
047: * This default implementation uses USGS equation (i.e. iteration) for computing
048: * the {@linkplain #inverseTransformNormalized inverse transform}.
049: *
050: * @since 2.4
051: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/referencing/src/main/java/org/geotools/referencing/operation/projection/PolarStereographic.java $
052: * @version $Id: PolarStereographic.java 25778 2007-06-08 08:46:34Z desruisseaux $
053: * @author André Gosselin
054: * @author Martin Desruisseaux
055: * @author Rueben Schulz
056: */
057: public class PolarStereographic extends Stereographic {
058: /**
059: * Maximum number of iterations for iterative computations.
060: */
061: private static final int MAXIMUM_ITERATIONS = 15;
062:
063: /**
064: * Difference allowed in iterative computations.
065: */
066: private static final double ITERATION_TOLERANCE = 1E-10;
067:
068: /**
069: * Maximum difference allowed when comparing real numbers.
070: */
071: private static final double EPSILON = 1E-8;
072:
073: /**
074: * A constant used in the transformations.
075: * This is <strong>not</strong> equal to the {@link #scaleFactor}.
076: */
077: private final double k0;
078:
079: /**
080: * Latitude of true scale, in radians (a.k.a. {@code "standard_parallel_1").
081: */
082: final double standardParallel;
083:
084: /**
085: * {@code true} if this projection is for the south pole, or {@code false}
086: * if it is for the north pole.
087: */
088: final boolean southPole;
089:
090: /**
091: * {@code true} if {@link #southPole} was forced, or {@code false} if it was auto-detected.
092: */
093: private final boolean poleForced;
094:
095: /**
096: * Constructs a polar stereographic projection.
097: *
098: * @param parameters The group of parameter values.
099: * @param descriptor The expected parameter descriptor.
100: * @param forceSouthPole Forces projection to North pole if {@link Boolean#FALSE},
101: * to South pole if {@link Boolean#TRUE}, or do not force (i.e. detect
102: * from other parameter values) if {@code null}.
103: * @throws ParameterNotFoundException if a required parameter was not found.
104: */
105: PolarStereographic(final ParameterValueGroup parameters,
106: final ParameterDescriptorGroup descriptor,
107: final Boolean forceSouthPole)
108: throws ParameterNotFoundException {
109: super (parameters, descriptor);
110: /*
111: * Get "standard_parallel_1" parameter value. This parameter should be mutually exclusive
112: * with "latitude_of_origin", but this is not a strict requirement for this constructor.
113: *
114: * +-----------------------------------+--------------------+-------------+
115: * | Projection | Parameter | Force pole |
116: * | --------------------------------- | ------------------ | ----------- |
117: * | Polar Stereographic (variant A) | Latitude of origin | auto detect |
118: * | Polar Stereographic (variant B) | Standard Parallel | auto detect |
119: * | Stereographic North Pole | Standard Parallel | North pole |
120: * | Stereographic South Pole | Standard Parallel | South pole |
121: * +-----------------------------------+--------------------+-------------+
122: *
123: * "Standard Parallel" (a.k.a. "Latitude true scale") default to 90°N for every cases
124: * (including Polar A, but it is meanless in this case), except for "Stereographic South
125: * Pole" where it default to 90°S.
126: */
127: final ParameterDescriptor trueScaleDescriptor = Boolean.TRUE
128: .equals(forceSouthPole) ? ProviderSouth.STANDARD_PARALLEL
129: : ProviderNorth.STANDARD_PARALLEL;
130: final Collection expected = descriptor.descriptors();
131: double latitudeTrueScale;
132: if (isExpectedParameter(expected, trueScaleDescriptor)) {
133: // Any cases except Polar A
134: latitudeTrueScale = doubleValue(expected,
135: trueScaleDescriptor, parameters);
136: } else {
137: // Polar A case
138: latitudeTrueScale = (latitudeOfOrigin < 0) ? -Math.PI / 2
139: : Math.PI / 2;
140: }
141: ensureLatitudeInRange(trueScaleDescriptor, latitudeTrueScale,
142: true);
143: /*
144: * Forces the "standard_parallel_1" to the appropriate hemisphere,
145: * and forces the "latitude_of_origin" to ±90°.
146: */
147: poleForced = (forceSouthPole != null);
148: if (poleForced) {
149: southPole = forceSouthPole.booleanValue();
150: latitudeTrueScale = Math.abs(latitudeTrueScale);
151: if (southPole) {
152: latitudeTrueScale = -latitudeTrueScale;
153: }
154: } else {
155: southPole = (latitudeTrueScale < 0);
156: }
157: this .latitudeOfOrigin = (southPole) ? -(Math.PI / 2)
158: : +(Math.PI / 2);
159: this .standardParallel = latitudeTrueScale; // May be anything in [-90 .. +90] range.
160: /*
161: * Computes coefficients.
162: */
163: latitudeTrueScale = Math.abs(latitudeTrueScale);
164: if (Math.abs(latitudeTrueScale - Math.PI / 2) >= EPSILON) {
165: final double t = Math.sin(latitudeTrueScale);
166: k0 = msfn(t, Math.cos(latitudeTrueScale))
167: / tsfn(latitudeTrueScale, t); // Derives from (21-32 and 21-33)
168: } else {
169: // True scale at pole (part of (21-33))
170: k0 = 2.0 / Math.sqrt(Math.pow(1 + excentricity,
171: 1 + excentricity)
172: * Math.pow(1 - excentricity, 1 - excentricity));
173: }
174: }
175:
176: /**
177: * Transforms the specified (<var>λ</var>,<var>φ</var>) coordinates
178: * (units in radians) and stores the result in {@code ptDst} (linear distance
179: * on a unit sphere).
180: */
181: protected Point2D transformNormalized(double x, double y,
182: Point2D ptDst) throws ProjectionException {
183: final double sinlat = Math.sin(y);
184: final double coslon = Math.cos(x);
185: final double sinlon = Math.sin(x);
186: if (southPole) {
187: final double rho = k0 * tsfn(-y, -sinlat);
188: x = rho * sinlon;
189: y = rho * coslon;
190: } else {
191: final double rho = k0 * tsfn(y, sinlat);
192: x = rho * sinlon;
193: y = -rho * coslon;
194: }
195:
196: if (ptDst != null) {
197: ptDst.setLocation(x, y);
198: return ptDst;
199: }
200: return new Point2D.Double(x, y);
201: }
202:
203: /**
204: * Transforms the specified (<var>x</var>,<var>y</var>) coordinates (units in radians)
205: * and stores the result in {@code ptDst} (linear distance on a unit sphere).
206: */
207: protected Point2D inverseTransformNormalized(double x, double y,
208: Point2D ptDst) throws ProjectionException {
209: final double rho = Math.sqrt(x * x + y * y);
210: if (southPole) {
211: y = -y;
212: }
213: /*
214: * Compute latitude using iterative technique.
215: */
216: final double t = rho / k0;
217: final double halfe = excentricity / 2.0;
218: double phi0 = 0;
219: for (int i = MAXIMUM_ITERATIONS;;) {
220: final double esinphi = excentricity * Math.sin(phi0);
221: final double phi = (Math.PI / 2)
222: - 2.0
223: * Math.atan(t
224: * Math.pow((1 - esinphi) / (1 + esinphi),
225: halfe));
226: if (Math.abs(phi - phi0) < ITERATION_TOLERANCE) {
227: x = (Math.abs(rho) < EPSILON) ? 0.0 : Math.atan2(x, -y);
228: y = (southPole) ? -phi : phi;
229: break;
230: }
231: phi0 = phi;
232: if (--i < 0) {
233: throw new ProjectionException(Errors
234: .format(ErrorKeys.NO_CONVERGENCE));
235: }
236: }
237:
238: if (ptDst != null) {
239: ptDst.setLocation(x, y);
240: return ptDst;
241: }
242: return new Point2D.Double(x, y);
243: }
244:
245: /**
246: * {@inheritDoc}
247: */
248: public ParameterValueGroup getParameterValues() {
249: final ParameterDescriptor trueScaleDescriptor = poleForced ? (southPole ? ProviderSouth.STANDARD_PARALLEL
250: : // forced = true, south = true
251: ProviderNorth.STANDARD_PARALLEL)
252: : // forced = true, south = false
253: ProviderB.STANDARD_PARALLEL; // forced = false
254: final ParameterValueGroup values = super .getParameterValues();
255: final Collection expected = getParameterDescriptors()
256: .descriptors();
257: set(expected, trueScaleDescriptor, values, standardParallel);
258: return values;
259: }
260:
261: /**
262: * Returns a hash value for this map projection.
263: */
264: public int hashCode() {
265: final long code = Double.doubleToLongBits(k0);
266: return ((int) code ^ (int) (code >>> 32)) + 37
267: * super .hashCode();
268: }
269:
270: /**
271: * Compares the specified object with this map projection for equality.
272: */
273: public boolean equals(final Object object) {
274: if (object == this ) {
275: // Slight optimization
276: return true;
277: }
278: if (super .equals(object)) {
279: final PolarStereographic that = (PolarStereographic) object;
280: return this .southPole == that.southPole
281: && equals(this .k0, that.k0)
282: && equals(this .standardParallel,
283: that.standardParallel);
284: }
285: return false;
286: }
287:
288: /**
289: * Provides the transform equations for the spherical case of the polar
290: * stereographic projection.
291: *
292: * @version $Id: PolarStereographic.java 25778 2007-06-08 08:46:34Z desruisseaux $
293: * @author Martin Desruisseaux
294: * @author Rueben Schulz
295: */
296: static final class Spherical extends PolarStereographic {
297: /**
298: * A constant used in the transformations. This constant hides the {@code k0}
299: * constant from the ellipsoidal case. The spherical and ellipsoidal {@code k0}
300: * are not computed in the same way, and we preserve the ellipsoidal {@code k0}
301: * in {@link Stereographic} in order to allow assertions to work.
302: */
303: private final double k0;
304:
305: /**
306: * Constructs a spherical stereographic projection.
307: *
308: * @param parameters The group of parameter values.
309: * @param descriptor The expected parameter descriptor.
310: * @param forceSouthPole For projection to North pole if {@link Boolean#FALSE},
311: * to South pole if {@link Boolean#TRUE}, or do not force (i.e. detect
312: * from other parameter values) if {@code null}.
313: * @throws ParameterNotFoundException if a required parameter was not found.
314: */
315: Spherical(final ParameterValueGroup parameters,
316: final ParameterDescriptorGroup descriptor,
317: final Boolean forceSouthPole)
318: throws ParameterNotFoundException {
319: super (parameters, descriptor, forceSouthPole);
320: ensureSpherical();
321: final double phi = Math.abs(standardParallel);
322: if (Math.abs(phi - Math.PI / 2) >= EPSILON) {
323: k0 = 1 + Math.sin(phi); // derived from (21-7) and (21-11)
324: } else {
325: k0 = 2;
326: }
327: }
328:
329: /**
330: * Transforms the specified (<var>λ</var>,<var>φ</var>) coordinates
331: * (units in radians) and stores the result in {@code ptDst} (linear distance
332: * on a unit sphere).
333: */
334: protected Point2D transformNormalized(double x, double y,
335: Point2D ptDst) throws ProjectionException {
336: //Compute using ellipsoidal formulas, for comparaison later.
337: assert (ptDst = super .transformNormalized(x, y, ptDst)) != null;
338:
339: final double coslat = Math.cos(y);
340: final double sinlat = Math.sin(y);
341: final double coslon = Math.cos(x);
342: final double sinlon = Math.sin(x);
343:
344: if (southPole) {
345: if (Math.abs(1 - sinlat) < EPSILON) {
346: throw new ProjectionException(
347: Errors
348: .format(ErrorKeys.VALUE_TEND_TOWARD_INFINITY));
349: }
350: // (21-12)
351: final double f = k0 * coslat / (1 - sinlat); // == tan (pi/4 + phi/2)
352: x = f * sinlon; // (21-9)
353: y = f * coslon; // (21-10)
354: } else {
355: if (Math.abs(1 + sinlat) < EPSILON) {
356: throw new ProjectionException(
357: Errors
358: .format(ErrorKeys.VALUE_TEND_TOWARD_INFINITY));
359: }
360: // (21-8)
361: final double f = k0 * coslat / (1 + sinlat); // == tan (pi/4 - phi/2)
362: x = f * sinlon; // (21-5)
363: y = -f * coslon; // (21-6)
364: }
365:
366: assert checkTransform(x, y, ptDst);
367: if (ptDst != null) {
368: ptDst.setLocation(x, y);
369: return ptDst;
370: }
371: return new Point2D.Double(x, y);
372: }
373:
374: /**
375: * Transforms the specified (<var>x</var>,<var>y</var>) coordinates (units in radians)
376: * and stores the result in {@code ptDst} (linear distance on a unit sphere).
377: */
378: protected Point2D inverseTransformNormalized(double x,
379: double y, Point2D ptDst) throws ProjectionException {
380: // Compute using ellipsoidal formulas, for comparaison later.
381: assert (ptDst = super .inverseTransformNormalized(x, y,
382: ptDst)) != null;
383:
384: final double rho = Math.sqrt(x * x + y * y);
385:
386: if (!southPole) {
387: y = -y;
388: }
389: // (20-17) call atan2(x,y) to properly deal with y==0
390: x = (Math.abs(x) < EPSILON && Math.abs(y) < EPSILON) ? 0.0
391: : Math.atan2(x, y);
392: if (Math.abs(rho) < EPSILON) {
393: y = latitudeOfOrigin;
394: } else {
395: final double c = 2.0 * Math.atan(rho / k0);
396: final double cosc = Math.cos(c);
397: y = (southPole) ? Math.asin(-cosc) : Math.asin(cosc);
398: // (20-14) with phi1=90
399: }
400:
401: assert checkInverseTransform(x, y, ptDst);
402: if (ptDst != null) {
403: ptDst.setLocation(x, y);
404: return ptDst;
405: }
406: return new Point2D.Double(x, y);
407: }
408: }
409:
410: /**
411: * Overides {@link PolarStereographic} to use the a series for the
412: * {@link #inverseTransformNormalized inverseTransformNormalized(...)}
413: * method. This is the equation specified by the EPSG. Allows for a
414: * {@code "latitude_true_scale"} parameter to be used, but this
415: * parameter is not listed by the EPSG and is not given as a parameter
416: * by the provider.
417: * <p>
418: * Compared to the default {@link PolarStereographic} implementation, the series
419: * implementation is a little bit faster at the expense of a little bit less
420: * accuracy. The default {@link PolarStereographic} implementation
421: * is a derivated work of Proj4, and is therefor better tested.
422: *
423: * @version $Id: PolarStereographic.java 25778 2007-06-08 08:46:34Z desruisseaux $
424: * @author Rueben Schulz
425: */
426: static final class Series extends PolarStereographic {
427: /**
428: * Constants used for the inverse polar series
429: */
430: private final double A, B;
431:
432: /**
433: * Constants used for the inverse polar series
434: */
435: private double C, D;
436:
437: /**
438: * A constant used in the transformations. This constant hides the {@code k0}
439: * constant from the USGS case. The EPSG and USGS {@code k0} are not computed
440: * in the same way, and we preserve the USGS {@code k0} in order to allow
441: * assertions to work.
442: */
443: private final double k0;
444:
445: /**
446: * Constructs a polar stereographic projection (seires inverse equations).
447: *
448: * @param parameters The group of parameter values.
449: * @param descriptor The expected parameter descriptor.
450: * @param forceSouthPole For projection to North pole if {@link Boolean#FALSE},
451: * to South pole if {@link Boolean#TRUE}, or do not force (i.e. detect
452: * from other parameter values) if {@code null}.
453: * @throws ParameterNotFoundException if a required parameter was not found.
454: */
455: Series(final ParameterValueGroup parameters,
456: final ParameterDescriptorGroup descriptor,
457: final Boolean forceSouthPole)
458: throws ParameterNotFoundException {
459: super (parameters, descriptor, forceSouthPole);
460: // See Snyde P. 19, "Computation of Series"
461: final double e4 = excentricitySquared * excentricitySquared;
462: final double e6 = e4 * excentricitySquared;
463: final double e8 = e4 * e4;
464: C = 7.0 / 120.0 * e6 + 81.0 / 1120.0 * e8;
465: D = 4279.0 / 161280.0 * e8;
466: A = excentricitySquared / 2.0 + 5.0 / 24.0 * e4 + e6 / 12.0
467: + 13.0 / 360.0 * e8 - C;
468: B = 2.0
469: * (7.0 / 48.0 * e4 + 29.0 / 240.0 * e6 + 811.0 / 11520.0 * e8)
470: - 4.0 * D;
471: C *= 4.0;
472: D *= 8.0;
473:
474: final double latTrueScale = Math.abs(standardParallel);
475: if (Math.abs(latTrueScale - Math.PI / 2) >= EPSILON) {
476: final double t = Math.sin(latTrueScale);
477: k0 = msfn(t, Math.cos(latTrueScale))
478: * Math.sqrt(Math.pow(1 + excentricity,
479: 1 + excentricity)
480: * Math.pow(1 - excentricity,
481: 1 - excentricity))
482: / (2.0 * tsfn(latTrueScale, t));
483: } else {
484: k0 = 1.0;
485: }
486: }
487:
488: /**
489: * Transforms the specified (<var>x</var>,<var>y</var>) coordinates
490: * and stores the result in {@code ptDst}.
491: */
492: protected Point2D inverseTransformNormalized(double x,
493: double y, Point2D ptDst) throws ProjectionException {
494: // Compute using iteration formulas, for comparaison later.
495: assert (ptDst = super .inverseTransformNormalized(x, y,
496: ptDst)) != null;
497:
498: final double rho = Math.sqrt(x * x + y * y);
499: if (southPole) {
500: y = -y;
501: }
502: // The series form
503: final double t = (rho / k0)
504: * Math.sqrt(Math.pow(1 + excentricity,
505: 1 + excentricity)
506: * Math.pow(1 - excentricity,
507: 1 - excentricity)) / 2;
508: final double chi = Math.PI / 2 - 2 * Math.atan(t);
509:
510: x = (Math.abs(rho) < EPSILON) ? 0.0 : Math.atan2(x, -y);
511:
512: // See Snyde P. 19, "Computation of Series"
513: final double sin2chi = Math.sin(2.0 * chi);
514: final double cos2chi = Math.cos(2.0 * chi);
515: y = chi + sin2chi
516: * (A + cos2chi * (B + cos2chi * (C + D * cos2chi)));
517: y = (southPole) ? -y : y;
518:
519: assert checkInverseTransform(x, y, ptDst);
520: if (ptDst != null) {
521: ptDst.setLocation(x, y);
522: return ptDst;
523: }
524: return new Point2D.Double(x, y);
525: }
526: }
527:
528: //////////////////////////////////////////////////////////////////////////////////////////
529: //////////////////////////////////////////////////////////////////////////////////////////
530: //////// ////////
531: //////// PROVIDERS ////////
532: //////// ////////
533: //////////////////////////////////////////////////////////////////////////////////////////
534: //////////////////////////////////////////////////////////////////////////////////////////
535:
536: /**
537: * The {@linkplain org.geotools.referencing.operation.MathTransformProvider math transform
538: * provider} for a {@linkplain PolarStereographic Polar Stereographic} projection. This
539: * provider uses the series equations for the inverse elliptical calculations.
540: *
541: * @since 2.4
542: * @version $Id: PolarStereographic.java 25778 2007-06-08 08:46:34Z desruisseaux $
543: * @author Rueben Schulz
544: *
545: * @see org.geotools.referencing.operation.DefaultMathTransformFactory
546: */
547: public static final class ProviderA extends Stereographic.Provider {
548: /**
549: * The parameters group.
550: */
551: static final ParameterDescriptorGroup PARAMETERS = createDescriptorGroup(
552: new NamedIdentifier[] {
553: new NamedIdentifier(Citations.OGC,
554: "Polar_Stereographic"),
555: new NamedIdentifier(Citations.EPSG,
556: "Polar Stereographic (variant A)"),
557: new NamedIdentifier(Citations.EPSG, "9810"),
558: new NamedIdentifier(Citations.GEOTIFF,
559: "CT_PolarStereographic"),
560: new NamedIdentifier(Citations.GEOTOOLS,
561: Provider.NAME) },
562: new ParameterDescriptor[] { SEMI_MAJOR, SEMI_MINOR,
563: CENTRAL_MERIDIAN, LATITUDE_OF_ORIGIN,
564: SCALE_FACTOR, FALSE_EASTING, FALSE_NORTHING });
565:
566: /**
567: * Constructs a new provider.
568: */
569: public ProviderA() {
570: super (PARAMETERS);
571: }
572:
573: /**
574: * Creates a transform from the specified group of parameter values.
575: *
576: * @param parameters The group of parameter values.
577: * @return The created math transform.
578: * @throws ParameterNotFoundException if a required parameter was not found.
579: */
580: public MathTransform createMathTransform(
581: final ParameterValueGroup parameters)
582: throws ParameterNotFoundException {
583: if (isSpherical(parameters)) {
584: return new PolarStereographic.Spherical(parameters,
585: PARAMETERS, null);
586: } else {
587: return new PolarStereographic.Series(parameters,
588: PARAMETERS, null);
589: }
590: }
591: }
592:
593: /**
594: * The {@linkplain org.geotools.referencing.operation.MathTransformProvider math transform
595: * provider} for a {@linkplain PolarStereographic Polar Stereographic} (Variant B)
596: * projection. This provider includes a "Standard_Parallel_1" parameter and determines
597: * the hemisphere of the projection from the Standard_Parallel_1 value. It also uses the
598: * series equations for the inverse elliptical calculations.
599: *
600: * @since 2.4
601: * @version $Id: PolarStereographic.java 25778 2007-06-08 08:46:34Z desruisseaux $
602: * @author Rueben Schulz
603: *
604: * @see org.geotools.referencing.operation.DefaultMathTransformFactory
605: */
606: public static final class ProviderB extends Stereographic.Provider {
607: /**
608: * The operation parameter descriptor for the {@code standardParallel}
609: * parameter value. Valid values range is from -90 to 90°. The default
610: * value is 90°N.
611: */
612: public static final ParameterDescriptor STANDARD_PARALLEL = ProviderNorth.STANDARD_PARALLEL;
613:
614: /**
615: * The parameters group.
616: */
617: static final ParameterDescriptorGroup PARAMETERS = createDescriptorGroup(
618: new NamedIdentifier[] {
619: new NamedIdentifier(Citations.EPSG,
620: "Polar Stereographic (variant B)"),
621: new NamedIdentifier(Citations.EPSG, "9829"),
622: new NamedIdentifier(Citations.GEOTOOLS,
623: Provider.NAME) },
624: new ParameterDescriptor[] { SEMI_MAJOR, SEMI_MINOR,
625: CENTRAL_MERIDIAN, STANDARD_PARALLEL,
626: FALSE_EASTING, FALSE_NORTHING });
627:
628: /**
629: * Constructs a new provider.
630: */
631: public ProviderB() {
632: super (PARAMETERS);
633: }
634:
635: /**
636: * Creates a transform from the specified group of parameter values.
637: *
638: * @param parameters The group of parameter values.
639: * @return The created math transform.
640: * @throws ParameterNotFoundException if a required parameter was not found.
641: */
642: public MathTransform createMathTransform(
643: final ParameterValueGroup parameters)
644: throws ParameterNotFoundException {
645: if (isSpherical(parameters)) {
646: return new PolarStereographic.Spherical(parameters,
647: PARAMETERS, null);
648: } else {
649: return new PolarStereographic.Series(parameters,
650: PARAMETERS, null);
651: }
652: }
653: }
654:
655: /**
656: * The {@linkplain org.geotools.referencing.operation.MathTransformProvider math transform
657: * provider} for a {@linkplain PolarStereographic North Polar Stereographic} projection.
658: * This provider sets the "latitude_of_origin" parameter to +90.0 degrees and uses the
659: * iterative equations for the inverse elliptical calculations.
660: *
661: * @since 2.4
662: * @version $Id: PolarStereographic.java 25778 2007-06-08 08:46:34Z desruisseaux $
663: * @author Rueben Schulz
664: *
665: * @see org.geotools.referencing.operation.DefaultMathTransformFactory
666: */
667: public static final class ProviderNorth extends
668: Stereographic.Provider {
669: /**
670: * The operation parameter descriptor for the {@code standardParallel}
671: * parameter value. Valid values range is from -90 to 90°. The default
672: * value is 90°N.
673: */
674: public static final ParameterDescriptor STANDARD_PARALLEL = createDescriptor(
675: new NamedIdentifier[] {
676: new NamedIdentifier(Citations.ESRI,
677: "standard_parallel_1"),
678: new NamedIdentifier(Citations.EPSG,
679: "Latitude of standard parallel") }, 90,
680: -90, 90, NonSI.DEGREE_ANGLE);
681:
682: /**
683: * The parameters group.
684: */
685: static final ParameterDescriptorGroup PARAMETERS = createDescriptorGroup(
686: new NamedIdentifier[] {
687: new NamedIdentifier(Citations.ESRI,
688: "Stereographic_North_Pole"),
689: new NamedIdentifier(Citations.GEOTOOLS,
690: Provider.NAME) },
691: new ParameterDescriptor[] { SEMI_MAJOR, SEMI_MINOR,
692: CENTRAL_MERIDIAN, STANDARD_PARALLEL,
693: SCALE_FACTOR, FALSE_EASTING, FALSE_NORTHING });
694:
695: /**
696: * Constructs a new provider.
697: */
698: public ProviderNorth() {
699: super (PARAMETERS);
700: }
701:
702: /**
703: * Creates a transform from the specified group of parameter values.
704: *
705: * @param parameters The group of parameter values.
706: * @return The created math transform.
707: * @throws ParameterNotFoundException if a required parameter was not found.
708: */
709: public MathTransform createMathTransform(
710: final ParameterValueGroup parameters)
711: throws ParameterNotFoundException {
712: if (isSpherical(parameters)) {
713: return new PolarStereographic.Spherical(parameters,
714: PARAMETERS, Boolean.FALSE);
715: } else {
716: return new PolarStereographic(parameters, PARAMETERS,
717: Boolean.FALSE);
718: }
719: }
720: }
721:
722: /**
723: * The {@linkplain org.geotools.referencing.operation.MathTransformProvider math transform
724: * provider} for a {@linkplain PolarStereographic South Polar Stereographic} projection.
725: * This provider sets the "latitude_of_origin" parameter to -90.0 degrees and uses the
726: * iterative equations for the inverse elliptical calculations.
727: *
728: * @since 2.4
729: * @version $Id: PolarStereographic.java 25778 2007-06-08 08:46:34Z desruisseaux $
730: * @author Rueben Schulz
731: *
732: * @see org.geotools.referencing.operation.DefaultMathTransformFactory
733: */
734: public static final class ProviderSouth extends
735: Stereographic.Provider {
736: /**
737: * The operation parameter descriptor for the {@code standardParallel}
738: * parameter value. Valid values range is from -90 to 90°. The default
739: * value is 90°S.
740: */
741: public static final ParameterDescriptor STANDARD_PARALLEL = createDescriptor(
742: new NamedIdentifier[] {
743: new NamedIdentifier(Citations.ESRI,
744: "standard_parallel_1"),
745: new NamedIdentifier(Citations.EPSG,
746: "Latitude of standard parallel") },
747: -90, -90, 90, NonSI.DEGREE_ANGLE);
748:
749: /**
750: * The parameters group.
751: */
752: static final ParameterDescriptorGroup PARAMETERS = createDescriptorGroup(
753: new NamedIdentifier[] {
754: new NamedIdentifier(Citations.ESRI,
755: "Stereographic_South_Pole"),
756: new NamedIdentifier(Citations.GEOTOOLS,
757: Provider.NAME) },
758: new ParameterDescriptor[] { SEMI_MAJOR, SEMI_MINOR,
759: CENTRAL_MERIDIAN, STANDARD_PARALLEL,
760: SCALE_FACTOR, FALSE_EASTING, FALSE_NORTHING });
761:
762: /**
763: * Constructs a new provider.
764: */
765: public ProviderSouth() {
766: super (PARAMETERS);
767: }
768:
769: /**
770: * Creates a transform from the specified group of parameter values.
771: *
772: * @param parameters The group of parameter values.
773: * @return The created math transform.
774: * @throws ParameterNotFoundException if a required parameter was not found.
775: */
776: public MathTransform createMathTransform(
777: final ParameterValueGroup parameters)
778: throws ParameterNotFoundException {
779: if (isSpherical(parameters)) {
780: return new PolarStereographic.Spherical(parameters,
781: PARAMETERS, Boolean.TRUE);
782: } else {
783: return new PolarStereographic(parameters, PARAMETERS,
784: Boolean.TRUE);
785: }
786: }
787: }
788: }
|