001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2005-2006, GeoTools Project Managment Committee (PMC)
005: * (C) 2005, 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: package org.geotools.referencing.cs;
018:
019: // J2SE dependencies and extensions
020: import java.util.List;
021: import java.util.Arrays;
022: import java.util.Comparator;
023: import java.util.Map;
024: import javax.units.SI;
025: import javax.units.Unit;
026:
027: // OpenGIS dependencies
028: import org.opengis.referencing.cs.*;
029:
030: // Geotools dependencies
031: import org.geotools.resources.i18n.Errors;
032: import org.geotools.resources.i18n.ErrorKeys;
033: import org.geotools.referencing.cs.DefaultCoordinateSystemAxis;
034:
035: /**
036: * Converts an arbitrary CS into one of the predefined constants provided in the
037: * {@link org.geotools.referencing.cs} package. The main usage for this class is
038: * to reorder the axis in some "standard" order like (<var>x</var>, <var>y</var>,
039: * <var>z</var>) or (<var>longitude</var>, <var>latitude</var>). What "standard"
040: * order means is sometime an arbitrary choice, which explain why this class is
041: * not public at this time.
042: *
043: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/referencing/src/main/java/org/geotools/referencing/cs/PredefinedCS.java $
044: * @version $Id: PredefinedCS.java 24683 2007-03-06 06:02:15Z desruisseaux $
045: * @author Martin Desruisseaux
046: */
047: final class PredefinedCS implements Comparator {
048: /**
049: * An instance of {@link PredefinedCS}. Will be created only when first needed.
050: */
051: private static Comparator csComparator;
052:
053: /**
054: * Our ordering for coordinate system objects.
055: */
056: private final Class[] types = { CartesianCS.class, AffineCS.class,
057: EllipsoidalCS.class, SphericalCS.class,
058: CylindricalCS.class, PolarCS.class, VerticalCS.class,
059: TimeCS.class, LinearCS.class, UserDefinedCS.class };
060:
061: /**
062: * Creates a comparator.
063: */
064: private PredefinedCS() {
065: }
066:
067: /**
068: * Compares the ordering between two coordinate systems. This comparator is used for sorting
069: * the axis in an user-supplied compound CS in an order closes to some "standard" order.
070: */
071: public int compare(final Object object1, final Object object2) {
072: final Class type1 = object1.getClass();
073: final Class type2 = object2.getClass();
074: for (int i = 0; i < types.length; i++) {
075: final Class type = types[i];
076: final boolean a1 = type.isAssignableFrom(type1);
077: final boolean a2 = type.isAssignableFrom(type2);
078: if (a1)
079: return a2 ? 0 : -1;
080: if (a2)
081: return a1 ? 0 : +1;
082: }
083: return 0;
084: }
085:
086: /**
087: * Implementation of the {@link AbstractCS#standard} method.
088: */
089: static CoordinateSystem standard(final CoordinateSystem cs)
090: throws IllegalArgumentException {
091: final int dimension = cs.getDimension();
092: if (cs instanceof CartesianCS) {
093: switch (dimension) {
094: case 2: {
095: if (DefaultCartesianCS.PROJECTED.axisColinearWith(cs)) {
096: return DefaultCartesianCS.PROJECTED;
097: }
098: if (DefaultCartesianCS.GRID.axisColinearWith(cs)) {
099: return DefaultCartesianCS.GRID;
100: }
101: if (DefaultCartesianCS.GENERIC_2D
102: .directionColinearWith(cs)) {
103: return DefaultCartesianCS.GENERIC_2D;
104: }
105: return rightHanded((CartesianCS) cs);
106: }
107: case 3: {
108: if (DefaultCartesianCS.GEOCENTRIC.axisColinearWith(cs)) {
109: return DefaultCartesianCS.GEOCENTRIC;
110: }
111: if (DefaultCartesianCS.GENERIC_3D
112: .directionColinearWith(cs)) {
113: return DefaultCartesianCS.GENERIC_3D;
114: }
115: return rightHanded((CartesianCS) cs);
116: }
117: }
118: }
119: if (cs instanceof AffineCS) {
120: return rightHanded((AffineCS) cs);
121: }
122: if (cs instanceof EllipsoidalCS) {
123: switch (dimension) {
124: case 2:
125: return DefaultEllipsoidalCS.GEODETIC_2D;
126: case 3:
127: return DefaultEllipsoidalCS.GEODETIC_3D;
128: }
129: }
130: if (cs instanceof SphericalCS) {
131: switch (dimension) {
132: case 3:
133: return DefaultSphericalCS.GEOCENTRIC;
134: }
135: }
136: if (cs instanceof VerticalCS) {
137: switch (dimension) {
138: case 1: {
139: return DefaultVerticalCS.ELLIPSOIDAL_HEIGHT;
140: }
141: }
142: }
143: if (cs instanceof TimeCS) {
144: switch (dimension) {
145: case 1:
146: return DefaultTimeCS.DAYS;
147: }
148: }
149: if (cs instanceof DefaultCompoundCS) {
150: final List components = ((DefaultCompoundCS) cs)
151: .getCoordinateSystems();
152: final CoordinateSystem[] user = new CoordinateSystem[components
153: .size()];
154: final CoordinateSystem[] std = new CoordinateSystem[user.length];
155: for (int i = 0; i < std.length; i++) {
156: std[i] = standard(user[i] = (CoordinateSystem) components
157: .get(i));
158: }
159: if (csComparator == null) {
160: csComparator = new PredefinedCS();
161: }
162: Arrays.sort(std, csComparator);
163: return Arrays.equals(user, std) ? cs
164: : new DefaultCompoundCS(std);
165: }
166: throw new IllegalArgumentException(Errors.format(
167: ErrorKeys.UNSUPPORTED_COORDINATE_SYSTEM_$1, cs
168: .getName().getCode()));
169: }
170:
171: /**
172: * Reorder the axis in the specified Affine CS in an attempt to get a right-handed system.
173: * Units are standardized to meters in the process. If no axis change is needed, then this
174: * method returns {@code cs} unchanged.
175: */
176: private static AffineCS rightHanded(final AffineCS cs) {
177: boolean changed = false;
178: final int dimension = cs.getDimension();
179: final CoordinateSystemAxis[] axis = new CoordinateSystemAxis[dimension];
180: for (int i = 0; i < dimension; i++) {
181: /*
182: * Gets the axis and replaces it by one of the predefined constants declared in
183: * DefaultCoordinateSystemAxis, if possible. The predefined constants use ISO 19111
184: * names with metres or degrees units, so it is pretty close to the "standard" axis
185: * we are looking for.
186: */
187: CoordinateSystemAxis axe = axis[i] = cs.getAxis(i);
188: DefaultCoordinateSystemAxis standard = DefaultCoordinateSystemAxis
189: .getPredefined(axe);
190: if (standard != null) {
191: axe = standard;
192: }
193: /*
194: * Changes units to meters. Every units in an affine CS should be linear or
195: * dimensionless (the later is used for grid coordinates). The 'usingUnit'
196: * method will thrown an exception if the unit is incompatible. See
197: * DefaultAffineCS.isCompatibleUnit(Unit).
198: */
199: final Unit unit = axe.getUnit();
200: if (!Unit.ONE.equals(unit) && !SI.METER.equals(unit)) {
201: if (!(axe instanceof DefaultCoordinateSystemAxis)) {
202: axe = new DefaultCoordinateSystemAxis(axe);
203: }
204: axe = ((DefaultCoordinateSystemAxis) axe)
205: .usingUnit(SI.METER);
206: }
207: changed |= (axe != axis[i]);
208: axis[i] = axe;
209: }
210: /*
211: * Sorts the axis in an attempt to create a right-handed system
212: * and creates a new Coordinate System if at least one axis changed.
213: */
214: changed |= ComparableAxisWrapper.sort(axis);
215: if (!changed) {
216: return cs;
217: }
218: final Map properties = DefaultAffineCS.getProperties(cs, null);
219: if (cs instanceof CartesianCS) {
220: return new DefaultCartesianCS(properties, axis);
221: }
222: return new DefaultAffineCS(properties, axis);
223: }
224: }
|