001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: *
005: * (C) 2005-2006, Geotools Project Managment Committee (PMC)
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation;
010: * version 2.1 of the License.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: */
017: package org.geotools.referencing.operation.projection;
018:
019: // J2SE dependencies and extensions
020: import java.awt.geom.Point2D;
021: import java.util.Collection;
022: import javax.units.NonSI;
023:
024: // OpenGIS dependencies
025: import org.opengis.parameter.ParameterDescriptor;
026: import org.opengis.parameter.ParameterDescriptorGroup;
027: import org.opengis.parameter.ParameterNotFoundException;
028: import org.opengis.parameter.ParameterValueGroup;
029: import org.opengis.referencing.operation.CylindricalProjection;
030: import org.opengis.referencing.operation.MathTransform;
031: import org.opengis.referencing.FactoryException;
032:
033: // Geotools dependencies
034: import org.geotools.metadata.iso.citation.Citations;
035: import org.geotools.referencing.NamedIdentifier;
036: import org.geotools.resources.i18n.VocabularyKeys;
037: import org.geotools.resources.i18n.Vocabulary;
038: import org.geotools.resources.i18n.ErrorKeys;
039: import org.geotools.resources.i18n.Errors;
040:
041: /**
042: * Equidistant cylindrical projection (EPSG code 9823). In the particular case where the
043: * {@code standard_parallel_1} is 0°, this projection is also called
044: * {@linkplain PlateCarree Plate Carree} or Equirectangular.
045: *
046: * This is used in, for example, <cite>WGS84 / Plate Carree</cite> (EPSG:32662).
047: *
048: * <strong>References:</strong><ul>
049: * <li>John P. Snyder (Map Projections - A Working Manual,<br>
050: * U.S. Geological Survey Professional Paper 1395, 1987)</li>
051: * <li>"Coordinate Conversions and Transformations including Formulas",<br>
052: * EPSG Guidence Note Number 7 part 2, Version 24.</li>
053: * </ul>
054: *
055: * @see <A HREF="http://mathworld.wolfram.com/CylindricalEquidistantProjection.html">Cylindrical Equidistant projection on MathWorld</A>
056: * @see <A HREF="http://www.remotesensing.org/geotiff/proj_list/equirectangular.html">"Equirectangular" on RemoteSensing.org</A>
057: *
058: * @since 2.2
059: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/referencing/src/main/java/org/geotools/referencing/operation/projection/EquidistantCylindrical.java $
060: * @version $Id: EquidistantCylindrical.java 26540 2007-08-14 12:20:36Z desruisseaux $
061: * @author John Grange
062: * @author Martin Desruisseaux
063: */
064: public class EquidistantCylindrical extends MapProjection {
065: /**
066: * Cosinus of the {@code "standard_parallel_1"} parameter.
067: */
068: private final double cosStandardParallel;
069:
070: /**
071: * {@linkplain Provider#STANDARD_PARALLEL_1 Standard parallel} parameter.
072: * Set to 0° for the {@link PlateCarree} case.
073: */
074: protected final double standardParallel;
075:
076: /**
077: * Constructs a new map projection from the supplied parameters.
078: *
079: * @param parameters The parameter values in standard units.
080: * @throws ParameterNotFoundException if a mandatory parameter is missing.
081: */
082: protected EquidistantCylindrical(
083: final ParameterValueGroup parameters)
084: throws ParameterNotFoundException {
085: // Fetch parameters
086: super (parameters);
087: final Collection expected = getParameterDescriptors()
088: .descriptors();
089: if (expected.contains(Provider.STANDARD_PARALLEL_1)) {
090: standardParallel = Math.abs(doubleValue(expected,
091: Provider.STANDARD_PARALLEL_1, parameters));
092: ensureLatitudeInRange(Provider.STANDARD_PARALLEL_1,
093: standardParallel, false);
094: cosStandardParallel = Math.cos(standardParallel);
095: } else {
096: // standard parallel is the equator (Plate Carree or Equirectangular)
097: standardParallel = 0;
098: cosStandardParallel = 1.0;
099: }
100: assert latitudeOfOrigin == 0 : latitudeOfOrigin;
101: }
102:
103: /**
104: * {@inheritDoc}
105: */
106: public ParameterDescriptorGroup getParameterDescriptors() {
107: return Provider.PARAMETERS;
108: }
109:
110: /**
111: * {@inheritDoc}
112: */
113: public ParameterValueGroup getParameterValues() {
114: final ParameterValueGroup values = super .getParameterValues();
115: if (!Double.isNaN(standardParallel)) {
116: final Collection expected = getParameterDescriptors()
117: .descriptors();
118: set(expected, Provider.STANDARD_PARALLEL_1, values,
119: standardParallel);
120: }
121: return values;
122: }
123:
124: /**
125: * Transforms the specified (<var>λ</var>,<var>φ</var>) coordinates
126: * (units in radians) and stores the result in {@code ptDst} (linear distance
127: * on a unit sphere).
128: */
129: protected Point2D transformNormalized(double x, double y,
130: final Point2D ptDst) throws ProjectionException {
131: x *= cosStandardParallel;
132: if (ptDst != null) {
133: ptDst.setLocation(x, y);
134: return ptDst;
135: }
136: return new Point2D.Double(x, y);
137: }
138:
139: /**
140: * Transforms the specified (<var>x</var>,<var>y</var>) coordinates
141: * and stores the result in {@code ptDst}.
142: */
143: protected Point2D inverseTransformNormalized(double x, double y,
144: final Point2D ptDst) throws ProjectionException {
145: x /= cosStandardParallel;
146: if (ptDst != null) {
147: ptDst.setLocation(x, y);
148: return ptDst;
149: }
150: return new Point2D.Double(x, y);
151: }
152:
153: /**
154: * Returns a hash value for this projection.
155: */
156: public int hashCode() {
157: final long code = Double.doubleToLongBits(standardParallel);
158: return ((int) code ^ (int) (code >>> 32)) + 37
159: * super .hashCode();
160: }
161:
162: /**
163: * Compares the specified object with this map projection for equality.
164: */
165: public boolean equals(final Object object) {
166: if (object == this ) {
167: // Slight optimization
168: return true;
169: }
170: if (super .equals(object)) {
171: final EquidistantCylindrical that = (EquidistantCylindrical) object;
172: return equals(this .standardParallel, that.standardParallel);
173: }
174: return false;
175: }
176:
177: //////////////////////////////////////////////////////////////////////////////////////////
178: //////////////////////////////////////////////////////////////////////////////////////////
179: //////// ////////
180: //////// PROVIDERS ////////
181: //////// ////////
182: //////////////////////////////////////////////////////////////////////////////////////////
183: //////////////////////////////////////////////////////////////////////////////////////////
184:
185: /**
186: * The {@linkplain org.geotools.referencing.operation.MathTransformProvider math transform
187: * provider} for an {@linkplain EquidistantCylindrical Equidistant Cylindrical} projection
188: * (EPSG code 9823).
189: *
190: * @since 2.2
191: * @version $Id: EquidistantCylindrical.java 26540 2007-08-14 12:20:36Z desruisseaux $
192: * @author John Grange
193: *
194: * @see org.geotools.referencing.operation.DefaultMathTransformFactory
195: */
196: public static class Provider extends AbstractProvider {
197: /**
198: * The operation parameter descriptor for the {@linkplain #standardParallel standard parallel}
199: * parameter value. Valid values range is from -90 to 90°. Default value is 0.
200: * <p>
201: * <b>Note:</b> EPSG includes a "<cite>Latitude of natural origin</cite>" parameter
202: * instead of {@code "standard_parallel_1"}. The EPSG name is declared as an alias.
203: */
204: public static final ParameterDescriptor STANDARD_PARALLEL = createDescriptor(
205: new NamedIdentifier[] {
206: new NamedIdentifier(Citations.OGC,
207: "latitude_of_origin"),
208: new NamedIdentifier(Citations.ESRI,
209: "standard_parallel_1"),
210: new NamedIdentifier(Citations.EPSG,
211: "Latitude of natural origin"),
212: new NamedIdentifier(Citations.GEOTIFF,
213: "ProjCenterLat"),
214: new NamedIdentifier(Citations.GEOTIFF,
215: "NatOriginLat") }, 0, -90, 90,
216: NonSI.DEGREE_ANGLE);
217:
218: /**
219: * The parameters group. Note the EPSG includes a "Latitude of natural origin" parameter instead
220: * of "standard_parallel_1". I have sided with ESRI and Snyder in this case.
221: */
222: static final ParameterDescriptorGroup PARAMETERS = createDescriptorGroup(
223: new NamedIdentifier[] {
224: new NamedIdentifier(Citations.OGC,
225: "Equidistant_Cylindrical"),
226: new NamedIdentifier(Citations.EPSG,
227: "Equidistant Cylindrical"),
228: new NamedIdentifier(Citations.ESRI,
229: "Equidistant_Cylindrical"),
230: new NamedIdentifier(Citations.EPSG, "9823"),
231: new NamedIdentifier(
232: Citations.GEOTOOLS,
233: Vocabulary
234: .formatInternational(VocabularyKeys.EQUIDISTANT_CYLINDRICAL_PROJECTION)) },
235: new ParameterDescriptor[] { SEMI_MAJOR, SEMI_MINOR,
236: CENTRAL_MERIDIAN, STANDARD_PARALLEL,
237: FALSE_EASTING, FALSE_NORTHING });
238:
239: /**
240: * Constructs a new provider.
241: */
242: public Provider() {
243: super (PARAMETERS);
244: }
245:
246: /**
247: * Returns the operation type for this map projection.
248: */
249: public Class getOperationType() {
250: return CylindricalProjection.class;
251: }
252:
253: /**
254: * Creates a transform from the specified group of parameter values.
255: *
256: * @param parameters The group of parameter values.
257: * @return The created math transform.
258: * @throws ParameterNotFoundException if a required parameter was not found.
259: */
260: protected MathTransform createMathTransform(
261: final ParameterValueGroup parameters)
262: throws ParameterNotFoundException, FactoryException {
263: if (isSpherical(parameters)) {
264: return new EquidistantCylindrical(parameters);
265: } else {
266: throw new FactoryException(Errors
267: .format(ErrorKeys.ELLIPTICAL_NOT_SUPPORTED));
268: }
269: }
270: }
271: }
|