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.Unit;
026:
027: // OpenGIS dependencies
028: import org.opengis.referencing.cs.AxisDirection;
029: import org.opengis.referencing.cs.CartesianCS;
030: import org.opengis.referencing.cs.CoordinateSystemAxis;
031: import org.opengis.geometry.MismatchedDimensionException;
032:
033: // Geotools dependencies
034: import org.geotools.measure.Measure;
035: import org.geotools.resources.i18n.Errors;
036: import org.geotools.resources.i18n.ErrorKeys;
037: import org.geotools.resources.i18n.VocabularyKeys;
038:
039: /**
040: * A 1-, 2-, or 3-dimensional coordinate system. Gives the position of points relative to
041: * orthogonal straight axes in the 2- and 3-dimensional cases. In the 1-dimensional case,
042: * it contains a single straight coordinate axis. In the multi-dimensional case, all axes
043: * shall have the same length unit of measure. A {@code CartesianCS} shall have one,
044: * two, or three {@linkplain #getAxis axis}.
045: *
046: * <TABLE CELLPADDING='6' BORDER='1'>
047: * <TR BGCOLOR="#EEEEFF"><TH NOWRAP>Used with CRS type(s)</TH></TR>
048: * <TR><TD>
049: * {@link org.geotools.referencing.crs.DefaultGeocentricCRS Geocentric},
050: * {@link org.geotools.referencing.crs.DefaultProjectedCRS Projected},
051: * {@link org.geotools.referencing.crs.DefaultEngineeringCRS Engineering},
052: * {@link org.geotools.referencing.crs.DefaultImageCRS Image}
053: * </TD></TR></TABLE>
054: *
055: * @since 2.1
056: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/referencing/src/main/java/org/geotools/referencing/cs/DefaultCartesianCS.java $
057: * @version $Id: DefaultCartesianCS.java 26137 2007-07-03 17:59:44Z desruisseaux $
058: * @author Martin Desruisseaux
059: *
060: * @see DefaultAffineCS
061: */
062: public class DefaultCartesianCS extends DefaultAffineCS implements
063: CartesianCS {
064: /**
065: * Serial number for interoperability with different versions.
066: */
067: private static final long serialVersionUID = -6182037957705712945L;
068:
069: /**
070: * A two-dimensional cartesian CS with
071: * <var>{@linkplain DefaultCoordinateSystemAxis#EASTING Easting}</var>,
072: * <var>{@linkplain DefaultCoordinateSystemAxis#NORTHING Northing}</var>
073: * axis in metres.
074: */
075: public static DefaultCartesianCS PROJECTED = new DefaultCartesianCS(
076: name(VocabularyKeys.PROJECTED),
077: DefaultCoordinateSystemAxis.EASTING,
078: DefaultCoordinateSystemAxis.NORTHING);
079:
080: /**
081: * A three-dimensional cartesian CS with geocentric
082: * <var>{@linkplain DefaultCoordinateSystemAxis#GEOCENTRIC_X x}</var>,
083: * <var>{@linkplain DefaultCoordinateSystemAxis#GEOCENTRIC_Y y}</var>,
084: * <var>{@linkplain DefaultCoordinateSystemAxis#GEOCENTRIC_Z z}</var>
085: * axis in metres.
086: *
087: * @see DefaultSphericalCS#GEOCENTRIC
088: */
089: public static DefaultCartesianCS GEOCENTRIC = new DefaultCartesianCS(
090: name(VocabularyKeys.GEOCENTRIC),
091: DefaultCoordinateSystemAxis.GEOCENTRIC_X,
092: DefaultCoordinateSystemAxis.GEOCENTRIC_Y,
093: DefaultCoordinateSystemAxis.GEOCENTRIC_Z);
094:
095: /**
096: * A two-dimensional cartesian CS with
097: * <var>{@linkplain DefaultCoordinateSystemAxis#X x}</var>,
098: * <var>{@linkplain DefaultCoordinateSystemAxis#Y y}</var>
099: * axis in metres.
100: */
101: public static DefaultCartesianCS GENERIC_2D = new DefaultCartesianCS(
102: name(VocabularyKeys.CARTESIAN_2D),
103: DefaultCoordinateSystemAxis.X,
104: DefaultCoordinateSystemAxis.Y);
105:
106: /**
107: * A three-dimensional cartesian CS with
108: * <var>{@linkplain DefaultCoordinateSystemAxis#X x}</var>,
109: * <var>{@linkplain DefaultCoordinateSystemAxis#Y y}</var>,
110: * <var>{@linkplain DefaultCoordinateSystemAxis#Z z}</var>
111: * axis in metres.
112: */
113: public static DefaultCartesianCS GENERIC_3D = new DefaultCartesianCS(
114: name(VocabularyKeys.CARTESIAN_3D),
115: DefaultCoordinateSystemAxis.X,
116: DefaultCoordinateSystemAxis.Y,
117: DefaultCoordinateSystemAxis.Z);
118:
119: /**
120: * A two-dimensional cartesian CS with
121: * <var>{@linkplain DefaultCoordinateSystemAxis#COLUMN column}</var>,
122: * <var>{@linkplain DefaultCoordinateSystemAxis#ROW row}</var>
123: * axis.
124: */
125: public static DefaultCartesianCS GRID = new DefaultCartesianCS(
126: name(VocabularyKeys.GRID),
127: DefaultCoordinateSystemAxis.COLUMN,
128: DefaultCoordinateSystemAxis.ROW);
129:
130: /**
131: * A two-dimensional cartesian CS with
132: * <var>{@linkplain DefaultCoordinateSystemAxis#DISPLAY_X display x}</var>,
133: * <var>{@linkplain DefaultCoordinateSystemAxis#DISPLAY_Y display y}</var>
134: * axis.
135: *
136: * @since 2.2
137: */
138: public static DefaultCartesianCS DISPLAY = new DefaultCartesianCS(
139: name(VocabularyKeys.DISPLAY),
140: DefaultCoordinateSystemAxis.DISPLAY_X,
141: DefaultCoordinateSystemAxis.DISPLAY_Y);
142:
143: /**
144: * Converters from {@linkplain CoordinateSystemAxis#getUnit axis units} to
145: * {@linkplain #getDistanceUnit distance unit}. Will be constructed only when
146: * first needed.
147: */
148: private transient Converter[] converters;
149:
150: /**
151: * Constructs a new coordinate system with the same values than the specified one.
152: * This copy constructor provides a way to wrap an arbitrary implementation into a
153: * Geotools one or a user-defined one (as a subclass), usually in order to leverage
154: * some implementation-specific API. This constructor performs a shallow copy,
155: * i.e. the properties are not cloned.
156: *
157: * @since 2.2
158: */
159: public DefaultCartesianCS(final CartesianCS cs) {
160: super (cs);
161: ensurePerpendicularAxis();
162: }
163:
164: /**
165: * Constructs a two-dimensional coordinate system from a name.
166: *
167: * @param name The coordinate system name.
168: * @param axis0 The first axis.
169: * @param axis1 The second axis.
170: */
171: public DefaultCartesianCS(final String name,
172: final CoordinateSystemAxis axis0,
173: final CoordinateSystemAxis axis1) {
174: super (name, axis0, axis1);
175: ensurePerpendicularAxis();
176: }
177:
178: /**
179: * Constructs a three-dimensional coordinate system from a name.
180: *
181: * @param name The coordinate system name.
182: * @param axis0 The first axis.
183: * @param axis1 The second axis.
184: * @param axis2 The third axis.
185: */
186: public DefaultCartesianCS(final String name,
187: final CoordinateSystemAxis axis0,
188: final CoordinateSystemAxis axis1,
189: final CoordinateSystemAxis axis2) {
190: super (name, axis0, axis1, axis2);
191: ensurePerpendicularAxis();
192: }
193:
194: /**
195: * Constructs a two-dimensional coordinate system from a set of properties.
196: * The properties map is given unchanged to the
197: * {@linkplain AbstractCS#AbstractCS(Map,CoordinateSystemAxis[]) super-class constructor}.
198: *
199: * @param properties Set of properties. Should contains at least <code>"name"</code>.
200: * @param axis0 The first axis.
201: * @param axis1 The second axis.
202: */
203: public DefaultCartesianCS(final Map properties,
204: final CoordinateSystemAxis axis0,
205: final CoordinateSystemAxis axis1) {
206: super (properties, axis0, axis1);
207: ensurePerpendicularAxis();
208: }
209:
210: /**
211: * Constructs a three-dimensional coordinate system from a set of properties.
212: * The properties map is given unchanged to the
213: * {@linkplain AbstractCS#AbstractCS(Map,CoordinateSystemAxis[]) super-class constructor}.
214: *
215: * @param properties Set of properties. Should contains at least <code>"name"</code>.
216: * @param axis0 The first axis.
217: * @param axis1 The second axis.
218: * @param axis2 The third axis.
219: */
220: public DefaultCartesianCS(final Map properties,
221: final CoordinateSystemAxis axis0,
222: final CoordinateSystemAxis axis1,
223: final CoordinateSystemAxis axis2) {
224: super (properties, axis0, axis1, axis2);
225: ensurePerpendicularAxis();
226: }
227:
228: /**
229: * For {@link #usingUnit} and {@link PredefinedCS#rightHanded} usage only.
230: */
231: DefaultCartesianCS(final Map properties,
232: final CoordinateSystemAxis[] axis) {
233: super (properties, axis);
234: ensurePerpendicularAxis();
235: }
236:
237: /**
238: * Ensures that all axis are perpendicular.
239: */
240: private void ensurePerpendicularAxis()
241: throws IllegalArgumentException {
242: final int dimension = getDimension();
243: for (int i = 0; i < dimension; i++) {
244: final AxisDirection axis0 = getAxis(i).getDirection();
245: for (int j = i; ++j < dimension;) {
246: final AxisDirection axis1 = getAxis(j).getDirection();
247: final double angle = DefaultCoordinateSystemAxis
248: .getAngle(axis0, axis1);
249: if (Math.abs(Math.abs(angle) - 90) > DirectionAlongMeridian.EPS) {
250: throw new IllegalArgumentException(Errors.format(
251: ErrorKeys.NON_PERPENDICULAR_AXIS_$2, axis0
252: .name(), axis1.name()));
253: }
254: }
255: }
256: }
257:
258: /**
259: * Computes the distance between two points.
260: *
261: * @param coord1 Coordinates of the first point.
262: * @param coord2 Coordinates of the second point.
263: * @return The distance between {@code coord1} and {@code coord2}.
264: * @throws MismatchedDimensionException if a coordinate doesn't have the expected dimension.
265: */
266: public Measure distance(final double[] coord1, final double[] coord2)
267: throws MismatchedDimensionException {
268: ensureDimensionMatch("coord1", coord1);
269: ensureDimensionMatch("coord2", coord2);
270: final Unit unit = getDistanceUnit();
271: Converter[] converters = this .converters; // Avoid the need for synchronization.
272: if (converters == null) {
273: converters = new Converter[getDimension()];
274: for (int i = 0; i < converters.length; i++) {
275: converters[i] = getAxis(i).getUnit().getConverterTo(
276: unit);
277: }
278: this .converters = converters;
279: }
280: double sum = 0;
281: for (int i = 0; i < converters.length; i++) {
282: final Converter c = converters[i];
283: final double delta = c.convert(coord1[i])
284: - c.convert(coord2[i]);
285: sum += delta * delta;
286: }
287: return new Measure(Math.sqrt(sum), unit);
288: }
289:
290: /**
291: * Returns a new coordinate system with the same properties than the current one except for
292: * axis units.
293: *
294: * @param unit The unit for the new axis.
295: * @return A coordinate system with axis using the specified units.
296: * @throws IllegalArgumentException If the specified unit is incompatible with the expected one.
297: *
298: * @since 2.2
299: */
300: public DefaultCartesianCS usingUnit(final Unit unit)
301: throws IllegalArgumentException {
302: final CoordinateSystemAxis[] axis = axisUsingUnit(unit);
303: if (axis == null) {
304: return this ;
305: }
306: return new DefaultCartesianCS(getProperties(this, null), axis);
307: }
308: }
|