001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2003-2006, GeoTools Project Managment Committee (PMC)
005: * (C) 2001, 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.crs;
021:
022: // J2SE dependencies
023: import java.util.ArrayList;
024: import java.util.Arrays;
025: import java.util.Collections;
026: import java.util.Iterator;
027: import java.util.List;
028: import java.util.Map;
029:
030: // OpenGIS dependencies
031: import org.opengis.referencing.crs.CompoundCRS;
032: import org.opengis.referencing.crs.CoordinateReferenceSystem;
033: import org.opengis.referencing.crs.SingleCRS;
034: import org.opengis.referencing.cs.CoordinateSystem;
035: import org.opengis.referencing.datum.Datum;
036:
037: // Geotools dependencies
038: import org.geotools.referencing.AbstractIdentifiedObject;
039: import org.geotools.referencing.AbstractReferenceSystem;
040: import org.geotools.referencing.cs.DefaultCompoundCS;
041: import org.geotools.referencing.wkt.Formatter;
042: import org.geotools.resources.i18n.ErrorKeys;
043: import org.geotools.resources.i18n.Errors;
044:
045: /**
046: * A coordinate reference system describing the position of points through two or more
047: * independent coordinate reference systems. Thus it is associated with two or more
048: * {@linkplain CoordinateSystem coordinate systems} and {@linkplain Datum datums} by
049: * defining the compound CRS as an ordered set of two or more instances of
050: * {@link CoordinateReferenceSystem}.
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/crs/DefaultCompoundCRS.java $
054: * @version $Id: DefaultCompoundCRS.java 24609 2007-02-26 23:35:53Z desruisseaux $
055: * @author Martin Desruisseaux
056: */
057: public class DefaultCompoundCRS extends AbstractCRS implements
058: CompoundCRS {
059: /**
060: * Serial number for interoperability with different versions.
061: */
062: private static final long serialVersionUID = -2656710314586929286L;
063:
064: /**
065: * The coordinate reference systems in this compound CRS.
066: */
067: private final List/*<CoordinateReferenceSystem>*/crs;
068:
069: /**
070: * Constructs a new compound CRS with the same values than the specified one.
071: * This copy constructor provides a way to wrap an arbitrary implementation into a
072: * Geotools one or a user-defined one (as a subclass), usually in order to leverage
073: * some implementation-specific API. This constructor performs a shallow copy,
074: * i.e. the properties are not cloned.
075: *
076: * @since 2.2
077: */
078: public DefaultCompoundCRS(final CompoundCRS crs) {
079: super (crs);
080: this .crs = crs.getCoordinateReferenceSystems();
081: }
082:
083: /**
084: * Constructs a coordinate reference system from a name and two CRS.
085: *
086: * @param name The name.
087: * @param head The head CRS.
088: * @param tail The tail CRS.
089: */
090: public DefaultCompoundCRS(final String name,
091: final CoordinateReferenceSystem head,
092: final CoordinateReferenceSystem tail) {
093: this (name, new CoordinateReferenceSystem[] { head, tail });
094: }
095:
096: /**
097: * Constructs a coordinate reference system from a name and three CRS.
098: *
099: * @param name The name.
100: * @param head The head CRS.
101: * @param middle The middle CRS.
102: * @param tail The tail CRS.
103: */
104: public DefaultCompoundCRS(final String name,
105: final CoordinateReferenceSystem head,
106: final CoordinateReferenceSystem middle,
107: final CoordinateReferenceSystem tail) {
108: this (name,
109: new CoordinateReferenceSystem[] { head, middle, tail });
110: }
111:
112: /**
113: * Constructs a coordinate reference system from a name.
114: *
115: * @param name The name.
116: * @param crs The array of coordinate reference system making this compound CRS.
117: */
118: public DefaultCompoundCRS(final String name,
119: final CoordinateReferenceSystem[] crs) {
120: this (Collections.singletonMap(NAME_KEY, name), crs);
121: }
122:
123: /**
124: * Constructs a coordinate reference system from a set of properties.
125: * The properties are given unchanged to the
126: * {@linkplain AbstractReferenceSystem#AbstractReferenceSystem(Map) super-class constructor}.
127: *
128: * @param properties Set of properties. Should contains at least <code>"name"</code>.
129: * @param crs The array of coordinate reference system making this compound CRS.
130: */
131: public DefaultCompoundCRS(final Map properties,
132: CoordinateReferenceSystem[] crs) {
133: super (properties, createCoordinateSystem(crs));
134: ensureNonNull("crs", crs);
135: crs = (CoordinateReferenceSystem[]) crs.clone();
136: for (int i = 0; i < crs.length; i++) {
137: ensureNonNull("crs", crs, i);
138: }
139: if (crs.length < 2) {
140: throw new IllegalArgumentException(Errors.format(
141: ErrorKeys.MISSING_PARAMETER_$1, "crs[" + crs.length
142: + ']'));
143: }
144: this .crs = Collections.unmodifiableList(Arrays.asList(crs));
145: }
146:
147: /**
148: * Returns a compound coordinate system for the specified array of CRS objects.
149: * This method is a work around for RFE #4093999 in Sun's bug database
150: * ("Relax constraint on placement of this()/super() call in constructors").
151: */
152: private static CoordinateSystem createCoordinateSystem(
153: final CoordinateReferenceSystem[] crs) {
154: if (crs == null) {
155: return null;
156: }
157: final CoordinateSystem[] cs = new CoordinateSystem[crs.length];
158: for (int i = 0; i < crs.length; i++) {
159: cs[i] = crs[i].getCoordinateSystem();
160: }
161: return new DefaultCompoundCS(cs);
162: }
163:
164: /**
165: * The ordered list of coordinate reference systems.
166: *
167: * @return The coordinate reference systems.
168: */
169: public List/*<CoordinateReferenceSystem>*/getCoordinateReferenceSystems() {
170: return crs;
171: }
172:
173: /**
174: * Returns the ordered list of single coordinate reference systems.
175: * If this compound CRS contains other compound CRS, all of
176: * them are expanded in an array of {@code SingleCRS} objects.
177: *
178: * @return The single coordinate reference systems.
179: * @throws ClassCastException if a CRS is neither a {@link SingleCRS} or a {@link CompoundCRS}.
180: */
181: public SingleCRS[] getSingleCRS() {
182: final List singles = new ArrayList(crs.size());
183: getSingleCRS(crs, singles);
184: return (SingleCRS[]) singles.toArray(new SingleCRS[singles
185: .size()]);
186: }
187:
188: /**
189: * Returns the ordered list of single coordinate reference systems
190: * for the specified CRS. The specified CRS doesn't need to be a
191: * Geotools implementation.
192: *
193: * @param crs The coordinate reference system.
194: * @return The single coordinate reference systems.
195: * @throws ClassCastException if a CRS is neither a {@link SingleCRS} or a {@link CompoundCRS}.
196: */
197: public static SingleCRS[] getSingleCRS(
198: final CoordinateReferenceSystem crs) {
199: if (crs instanceof DefaultCompoundCRS) {
200: return ((DefaultCompoundCRS) crs).getSingleCRS();
201: }
202: if (crs instanceof CompoundCRS) {
203: final List/*<CoordinateReferenceSystem>*/elements = ((CompoundCRS) crs)
204: .getCoordinateReferenceSystems();
205: final List singles = new ArrayList(elements.size());
206: getSingleCRS(elements, singles);
207: return (SingleCRS[]) singles.toArray(new SingleCRS[singles
208: .size()]);
209: }
210: return new SingleCRS[] { (SingleCRS) crs };
211: }
212:
213: /**
214: * Recursively add all {@link SingleCRS} in the specified list.
215: *
216: * @throws ClassCastException if a CRS is neither a {@link SingleCRS} or a
217: * {@link CompoundCRS}.
218: */
219: private static void getSingleCRS(
220: final List/*<CoordinateReferenceSystem>*/crs,
221: final List/*<SingleCRS>*/singles) {
222: for (final Iterator it = crs.iterator(); it.hasNext();) {
223: final CoordinateReferenceSystem candidate = (CoordinateReferenceSystem) it
224: .next();
225: if (candidate instanceof CompoundCRS) {
226: getSingleCRS(((CompoundCRS) candidate)
227: .getCoordinateReferenceSystems(), singles);
228: } else {
229: singles.add((SingleCRS) candidate);
230: }
231: }
232: }
233:
234: /**
235: * Compares this coordinate reference system with the specified object for equality.
236: *
237: * @param object The object to compare to {@code this}.
238: * @param compareMetadata {@code true} for performing a strict comparaison, or
239: * {@code false} for comparing only properties relevant to transformations.
240: * @return {@code true} if both objects are equal.
241: */
242: public boolean equals(final AbstractIdentifiedObject object,
243: final boolean compareMetadata) {
244: if (object == this ) {
245: return true; // Slight optimization.
246: }
247: if (super .equals(object, compareMetadata)) {
248: final DefaultCompoundCRS that = (DefaultCompoundCRS) object;
249: return equals(this .crs, that.crs, compareMetadata);
250: }
251: return false;
252: }
253:
254: /**
255: * Returns a hash value for this compound CRS.
256: *
257: * @return The hash code value. This value doesn't need to be the same
258: * in past or future versions of this class.
259: */
260: public int hashCode() {
261: // Don't call superclass method since 'coordinateSystem' and 'datum' may be null.
262: return crs.hashCode() ^ (int) serialVersionUID;
263: }
264:
265: /**
266: * Format the inner part of a
267: * <A HREF="http://geoapi.sourceforge.net/snapshot/javadoc/org/opengis/referencing/doc-files/WKT.html"><cite>Well
268: * Known Text</cite> (WKT)</A> element.
269: *
270: * @param formatter The formatter to use.
271: * @return The name of the WKT element type, which is {@code "COMPD_CS"}.
272: */
273: protected String formatWKT(final Formatter formatter) {
274: for (final Iterator it = crs.iterator(); it.hasNext();) {
275: formatter.append((CoordinateReferenceSystem) it.next());
276: }
277: return "COMPD_CS";
278: }
279: }
|