001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2004-2006, GeoTools Project Managment Committee (PMC)
005: * (C) 2004, Institut de Recherche pour le Développement
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: * This package contains documentation from OpenGIS specifications.
018: * OpenGIS consortium's work is fully acknowledged here.
019: */
020: package org.geotools.referencing.cs;
021:
022: // J2SE dependencies and extensions
023: import java.util.Map;
024: import javax.units.Converter;
025: import javax.units.NonSI;
026: import javax.units.SI;
027: import javax.units.Unit;
028:
029: // OpenGIS dependencies
030: import org.opengis.referencing.cs.EllipsoidalCS;
031: import org.opengis.referencing.cs.AxisDirection;
032: import org.opengis.referencing.cs.CoordinateSystemAxis;
033: import org.opengis.geometry.MismatchedDimensionException;
034:
035: // Geotools dependencies
036: import org.geotools.resources.i18n.Errors;
037: import org.geotools.resources.i18n.ErrorKeys;
038: import org.geotools.resources.i18n.VocabularyKeys;
039:
040: /**
041: * A two- or three-dimensional coordinate system in which position is specified by geodetic
042: * latitude, geodetic longitude, and (in the three-dimensional case) ellipsoidal height. An
043: * {@code EllipsoidalCS} shall have two or three {@linkplain #getAxis axis}.
044: *
045: * <TABLE CELLPADDING='6' BORDER='1'>
046: * <TR BGCOLOR="#EEEEFF"><TH NOWRAP>Used with CRS type(s)</TH></TR>
047: * <TR><TD>
048: * {@link org.geotools.referencing.crs.DefaultGeographicCRS Geographic},
049: * {@link org.geotools.referencing.crs.DefaultEngineeringCRS Engineering}
050: * </TD></TR></TABLE>
051: *
052: * @since 2.1
053: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/referencing/src/main/java/org/geotools/referencing/cs/DefaultEllipsoidalCS.java $
054: * @version $Id: DefaultEllipsoidalCS.java 24925 2007-03-27 20:12:08Z jgarnett $
055: * @author Martin Desruisseaux
056: */
057: public class DefaultEllipsoidalCS extends AbstractCS implements
058: EllipsoidalCS {
059: /**
060: * Serial number for interoperability with different versions.
061: */
062: private static final long serialVersionUID = -1452492488902329211L;
063:
064: /**
065: * A two-dimensional ellipsoidal CS with
066: * <var>{@linkplain DefaultCoordinateSystemAxis#GEODETIC_LONGITUDE longitude}</var>,
067: * <var>{@linkplain DefaultCoordinateSystemAxis#GEODETIC_LATITUDE latitude}</var>
068: * axis in decimal degrees.
069: */
070: public static DefaultEllipsoidalCS GEODETIC_2D = new DefaultEllipsoidalCS(
071: name(VocabularyKeys.GEODETIC_2D),
072: DefaultCoordinateSystemAxis.GEODETIC_LONGITUDE,
073: DefaultCoordinateSystemAxis.GEODETIC_LATITUDE);
074:
075: /**
076: * A three-dimensional ellipsoidal CS with
077: * <var>{@linkplain DefaultCoordinateSystemAxis#GEODETIC_LONGITUDE longitude}</var>,
078: * <var>{@linkplain DefaultCoordinateSystemAxis#GEODETIC_LATITUDE latitude}</var>,
079: * <var>{@linkplain DefaultCoordinateSystemAxis#ELLIPSOIDAL_HEIGHT height}</var>
080: * axis.
081: */
082: public static DefaultEllipsoidalCS GEODETIC_3D = new DefaultEllipsoidalCS(
083: name(VocabularyKeys.GEODETIC_3D),
084: DefaultCoordinateSystemAxis.GEODETIC_LONGITUDE,
085: DefaultCoordinateSystemAxis.GEODETIC_LATITUDE,
086: DefaultCoordinateSystemAxis.ELLIPSOIDAL_HEIGHT);
087:
088: /**
089: * The axis number for longitude, latitude and height.
090: * Will be constructed only when first needed.
091: */
092: private transient int longitudeAxis, latitudeAxis, heightAxis;
093:
094: /**
095: * The unit converters for longitude, latitude and height.
096: * Will be constructed only when first needed.
097: */
098: private transient Converter longitudeConverter, latitudeConverter,
099: heightConverter;
100:
101: /**
102: * Constructs a new coordinate system with the same values than the specified one.
103: * This copy constructor provides a way to wrap an arbitrary implementation into a
104: * Geotools one or a user-defined one (as a subclass), usually in order to leverage
105: * some implementation-specific API. This constructor performs a shallow copy,
106: * i.e. the properties are not cloned.
107: *
108: * @since 2.2
109: */
110: public DefaultEllipsoidalCS(final EllipsoidalCS cs) {
111: super (cs);
112: }
113:
114: /**
115: * Constructs a two-dimensional coordinate system from a name.
116: *
117: * @param name The coordinate system name.
118: * @param axis0 The first axis.
119: * @param axis1 The second axis.
120: */
121: public DefaultEllipsoidalCS(final String name,
122: final CoordinateSystemAxis axis0,
123: final CoordinateSystemAxis axis1) {
124: super (name, new CoordinateSystemAxis[] { axis0, axis1 });
125: }
126:
127: /**
128: * Constructs a three-dimensional coordinate system from a name.
129: *
130: * @param name The coordinate system name.
131: * @param axis0 The first axis.
132: * @param axis1 The second axis.
133: * @param axis2 The third axis.
134: */
135: public DefaultEllipsoidalCS(final String name,
136: final CoordinateSystemAxis axis0,
137: final CoordinateSystemAxis axis1,
138: final CoordinateSystemAxis axis2) {
139: super (name, new CoordinateSystemAxis[] { axis0, axis1, axis2 });
140: }
141:
142: /**
143: * Constructs a two-dimensional coordinate system from a set of properties.
144: * The properties map is given unchanged to the
145: * {@linkplain AbstractCS#AbstractCS(Map,CoordinateSystemAxis[]) super-class constructor}.
146: *
147: * @param properties Set of properties. Should contains at least <code>"name"</code>.
148: * @param axis0 The first axis.
149: * @param axis1 The second axis.
150: */
151: public DefaultEllipsoidalCS(final Map properties,
152: final CoordinateSystemAxis axis0,
153: final CoordinateSystemAxis axis1) {
154: super (properties, new CoordinateSystemAxis[] { axis0, axis1 });
155: }
156:
157: /**
158: * Constructs a three-dimensional coordinate system from a set of properties.
159: * The properties map is given unchanged to the
160: * {@linkplain AbstractCS#AbstractCS(Map,CoordinateSystemAxis[]) super-class constructor}.
161: *
162: * @param properties Set of properties. Should contains at least <code>"name"</code>.
163: * @param axis0 The first axis.
164: * @param axis1 The second axis.
165: * @param axis2 The third axis.
166: */
167: public DefaultEllipsoidalCS(final Map properties,
168: final CoordinateSystemAxis axis0,
169: final CoordinateSystemAxis axis1,
170: final CoordinateSystemAxis axis2) {
171: super (properties, new CoordinateSystemAxis[] { axis0, axis1,
172: axis2 });
173: }
174:
175: /**
176: * For {@link #usingUnit} usage only.
177: */
178: private DefaultEllipsoidalCS(final Map properties,
179: final CoordinateSystemAxis[] axis) {
180: super (properties, axis);
181: }
182:
183: /**
184: * Returns {@code true} if the specified axis direction is allowed for this coordinate
185: * system. The default implementation accepts only the following directions:
186: * {@link AxisDirection#NORTH NORTH}, {@link AxisDirection#SOUTH SOUTH},
187: * {@link AxisDirection#EAST EAST}, {@link AxisDirection#WEST WEST},
188: * {@link AxisDirection#UP UP} and {@link AxisDirection#DOWN DOWN}.
189: */
190: protected boolean isCompatibleDirection(AxisDirection direction) {
191: direction = direction.absolute();
192: return AxisDirection.NORTH.equals(direction)
193: || AxisDirection.EAST.equals(direction)
194: || AxisDirection.UP.equals(direction);
195: }
196:
197: /**
198: * Returns {@code true} if the specified unit is compatible with
199: * {@linkplain NonSI#DEGREE_ANGLE decimal degrees} (or {@linkplain SI#METER meters} in the
200: * special case of height). This method is invoked at construction time for checking units
201: * compatibility.
202: *
203: * @since 2.2
204: */
205: protected boolean isCompatibleUnit(AxisDirection direction,
206: final Unit unit) {
207: direction = direction.absolute();
208: final Unit expected = AxisDirection.UP.equals(direction) ? SI.METER
209: : NonSI.DEGREE_ANGLE;
210: return expected.isCompatible(unit);
211: }
212:
213: /**
214: * Update the converters.
215: */
216: private void update() {
217: for (int i = getDimension(); --i >= 0;) {
218: final CoordinateSystemAxis axis = getAxis(i);
219: final AxisDirection direction = axis.getDirection()
220: .absolute();
221: final Unit unit = axis.getUnit();
222: if (AxisDirection.EAST.equals(direction)) {
223: longitudeAxis = i;
224: longitudeConverter = unit
225: .getConverterTo(NonSI.DEGREE_ANGLE);
226: continue;
227: }
228: if (AxisDirection.NORTH.equals(direction)) {
229: latitudeAxis = i;
230: latitudeConverter = unit
231: .getConverterTo(NonSI.DEGREE_ANGLE);
232: continue;
233: }
234: if (AxisDirection.UP.equals(direction)) {
235: heightAxis = i;
236: heightConverter = unit.getConverterTo(SI.METER);
237: continue;
238: }
239: // Should not happen, since 'isCompatibleDirection'
240: // has already checked axis directions.
241: throw new AssertionError(direction);
242: }
243: }
244:
245: /**
246: * Returns the longitude found in the specified coordinate point,
247: * always in {@linkplain NonSI#DEGREE_ANGLE decimal degrees}.
248: *
249: * @param coordinates The coordinate point expressed in this coordinate system.
250: * @return The longitude in the specified array, in {@linkplain NonSI#DEGREE_ANGLE decimal degrees}.
251: * @throws MismatchedDimensionException is the coordinate point doesn't have the expected dimension.
252: */
253: public double getLongitude(final double[] coordinates)
254: throws MismatchedDimensionException {
255: ensureDimensionMatch("coordinates", coordinates);
256: if (longitudeConverter == null) {
257: update();
258: }
259: return longitudeConverter.convert(coordinates[longitudeAxis]);
260: }
261:
262: /**
263: * Returns the latitude found in the specified coordinate point,
264: * always in {@linkplain NonSI#DEGREE_ANGLE decimal degrees}.
265: *
266: * @param coordinates The coordinate point expressed in this coordinate system.
267: * @return The latitude in the specified array, in {@linkplain NonSI#DEGREE_ANGLE decimal degrees}.
268: * @throws MismatchedDimensionException is the coordinate point doesn't have the expected dimension.
269: */
270: public double getLatitude(final double[] coordinates)
271: throws MismatchedDimensionException {
272: ensureDimensionMatch("coordinates", coordinates);
273: if (latitudeConverter == null) {
274: update();
275: }
276: return latitudeConverter.convert(coordinates[latitudeAxis]);
277: }
278:
279: /**
280: * Returns the height found in the specified coordinate point,
281: * always in {@linkplain SI#METER meters}.
282: *
283: * @param coordinates The coordinate point expressed in this coordinate system.
284: * @return The height in the specified array, in {@linkplain SI#METER meters}.
285: * @throws MismatchedDimensionException is the coordinate point doesn't have the expected
286: * dimension.
287: */
288: public double getHeight(final double[] coordinates)
289: throws MismatchedDimensionException {
290: ensureDimensionMatch("coordinates", coordinates);
291: if (heightConverter == null) {
292: update();
293: if (heightConverter == null) {
294: throw new IllegalStateException(Errors
295: .format(ErrorKeys.NOT_THREE_DIMENSIONAL_CS));
296: }
297: }
298: return heightConverter.convert(coordinates[heightAxis]);
299: }
300:
301: /**
302: * Returns a new coordinate system with the same properties than the current one except for
303: * axis units.
304: *
305: * @param unit The unit for the new axis.
306: * @return A coordinate system with axis using the specified units.
307: * @throws IllegalArgumentException If the specified unit is incompatible with the expected one.
308: *
309: * @todo Current implementation can't work for 3D coordinate systems.
310: *
311: * @since 2.2
312: */
313: public DefaultEllipsoidalCS usingUnit(final Unit unit)
314: throws IllegalArgumentException {
315: final CoordinateSystemAxis[] axis = axisUsingUnit(unit);
316: if (axis == null) {
317: return this ;
318: }
319: return new DefaultEllipsoidalCS(getProperties(this, null), axis);
320: }
321: }
|