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 and extensions
023: import java.util.Collections;
024: import java.util.HashMap;
025: import java.util.Map;
026: import javax.units.NonSI;
027: import javax.units.Unit;
028:
029: // OpenGIS dependencies
030: import org.opengis.referencing.cs.AxisDirection;
031: import org.opengis.referencing.cs.CoordinateSystem;
032: import org.opengis.referencing.cs.CoordinateSystemAxis;
033: import org.opengis.referencing.cs.EllipsoidalCS;
034: import org.opengis.referencing.datum.Ellipsoid;
035: import org.opengis.referencing.datum.GeodeticDatum;
036: import org.opengis.referencing.crs.GeographicCRS;
037: import org.opengis.geometry.MismatchedDimensionException;
038:
039: // Geotools dependencies
040: import org.geotools.measure.Measure;
041: import org.geotools.metadata.iso.extent.ExtentImpl;
042: import org.geotools.referencing.wkt.Formatter;
043: import org.geotools.referencing.AbstractReferenceSystem;
044: import org.geotools.referencing.cs.DefaultEllipsoidalCS;
045: import org.geotools.referencing.datum.DefaultEllipsoid;
046: import org.geotools.referencing.datum.DefaultGeodeticDatum;
047: import org.geotools.util.UnsupportedImplementationException;
048:
049: /**
050: * A coordinate reference system based on an ellipsoidal approximation of the geoid; this provides
051: * an accurate representation of the geometry of geographic features for a large portion of the
052: * earth's surface.
053: *
054: * <TABLE CELLPADDING='6' BORDER='1'>
055: * <TR BGCOLOR="#EEEEFF"><TH NOWRAP>Used with CS type(s)</TH></TR>
056: * <TR><TD>
057: * {@link EllipsoidalCS Ellipsoidal}
058: * </TD></TR></TABLE>
059: *
060: * @since 2.1
061: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/referencing/src/main/java/org/geotools/referencing/crs/DefaultGeographicCRS.java $
062: * @version $Id: DefaultGeographicCRS.java 28264 2007-12-05 21:53:08Z desruisseaux $
063: * @author Martin Desruisseaux
064: */
065: public class DefaultGeographicCRS extends AbstractSingleCRS implements
066: GeographicCRS {
067: /**
068: * Serial number for interoperability with different versions.
069: */
070: private static final long serialVersionUID = 861224913438092335L;
071:
072: /**
073: * A two-dimensional geographic coordinate reference system using WGS84 datum.
074: * This CRS uses (<var>longitude</var>,<var>latitude</var>) ordinates with longitude values
075: * increasing East and latitude values increasing North. Angular units are decimal degrees and
076: * prime meridian is Greenwich.
077: */
078: public static final DefaultGeographicCRS WGS84;
079:
080: /**
081: * A three-dimensional geographic coordinate reference system using WGS84 datum.
082: * This CRS uses (<var>longitude</var>,<var>latitude</var>,<var>height</var>)
083: * ordinates with longitude values increasing East, latitude values increasing
084: * North and height above the ellipsoid in metres. Angular units are decimal degrees and
085: * prime meridian is Greenwich.
086: */
087: public static final DefaultGeographicCRS WGS84_3D;
088: static {
089: final Map properties = new HashMap(4);
090: properties.put(NAME_KEY, "WGS84");
091: properties.put(DOMAIN_OF_VALIDITY_KEY, ExtentImpl.WORLD);
092: WGS84 = new DefaultGeographicCRS(properties,
093: DefaultGeodeticDatum.WGS84,
094: DefaultEllipsoidalCS.GEODETIC_2D);
095: WGS84_3D = new DefaultGeographicCRS(properties,
096: DefaultGeodeticDatum.WGS84,
097: DefaultEllipsoidalCS.GEODETIC_3D);
098: }
099:
100: /**
101: * Constructs a new geographic CRS with the same values than the specified one.
102: * This copy constructor provides a way to wrap an arbitrary implementation into a
103: * Geotools one or a user-defined one (as a subclass), usually in order to leverage
104: * some implementation-specific API. This constructor performs a shallow copy,
105: * i.e. the properties are not cloned.
106: *
107: * @since 2.2
108: */
109: public DefaultGeographicCRS(final GeographicCRS crs) {
110: super (crs);
111: }
112:
113: /**
114: * Constructs a geographic CRS from a name.
115: *
116: * @param name The name.
117: * @param datum The datum.
118: * @param cs The coordinate system.
119: */
120: public DefaultGeographicCRS(final String name,
121: final GeodeticDatum datum, final EllipsoidalCS cs) {
122: this (Collections.singletonMap(NAME_KEY, name), datum, cs);
123: }
124:
125: /**
126: * Constructs a geographic CRS from a set of properties. The properties are given unchanged to
127: * the {@linkplain AbstractReferenceSystem#AbstractReferenceSystem(Map) super-class constructor}.
128: *
129: * @param properties Set of properties. Should contains at least <code>"name"</code>.
130: * @param datum The datum.
131: * @param cs The coordinate system.
132: */
133: public DefaultGeographicCRS(final Map properties,
134: final GeodeticDatum datum, final EllipsoidalCS cs) {
135: super (properties, datum, cs);
136: }
137:
138: /**
139: * Computes the orthodromic distance between two points. This convenience method delegates
140: * the work to the underlyling {@linkplain DefaultEllipsoid ellipsoid}, if possible.
141: *
142: * @param coord1 Coordinates of the first point.
143: * @param coord2 Coordinates of the second point.
144: * @return The distance between {@code coord1} and {@code coord2}.
145: * @throws UnsupportedOperationException if this coordinate reference system can't compute
146: * distances.
147: * @throws MismatchedDimensionException if a coordinate doesn't have the expected dimension.
148: */
149: public Measure distance(final double[] coord1, final double[] coord2)
150: throws UnsupportedOperationException,
151: MismatchedDimensionException {
152: final DefaultEllipsoidalCS cs;
153: final DefaultEllipsoid e;
154: if (!(coordinateSystem instanceof DefaultEllipsoidalCS)) {
155: throw new UnsupportedImplementationException(
156: coordinateSystem.getClass());
157: }
158: final Ellipsoid ellipsoid = ((GeodeticDatum) datum)
159: .getEllipsoid();
160: if (!(ellipsoid instanceof DefaultEllipsoid)) {
161: throw new UnsupportedImplementationException(ellipsoid
162: .getClass());
163: }
164: cs = (DefaultEllipsoidalCS) coordinateSystem;
165: e = (DefaultEllipsoid) ellipsoid;
166: if (coord1.length != 2 || coord2.length != 2
167: || cs.getDimension() != 2) {
168: /*
169: * Not yet implemented (an exception will be thrown later).
170: * We should probably revisit the way we compute distances.
171: */
172: return super .distance(coord1, coord2);
173: }
174: return new Measure(e.orthodromicDistance(cs
175: .getLongitude(coord1), cs.getLatitude(coord1), cs
176: .getLongitude(coord2), cs.getLatitude(coord2)), e
177: .getAxisUnit());
178: }
179:
180: /**
181: * Returns a hash value for this geographic CRS.
182: *
183: * @return The hash code value. This value doesn't need to be the same
184: * in past or future versions of this class.
185: */
186: public int hashCode() {
187: return (int) serialVersionUID ^ super .hashCode();
188: }
189:
190: /**
191: * Returns the angular unit of the specified coordinate system.
192: * The preference will be given to the longitude axis, if found.
193: */
194: static Unit getAngularUnit(final CoordinateSystem coordinateSystem) {
195: Unit unit = NonSI.DEGREE_ANGLE;
196: for (int i = coordinateSystem.getDimension(); --i >= 0;) {
197: final CoordinateSystemAxis axis = coordinateSystem
198: .getAxis(i);
199: final Unit candidate = axis.getUnit();
200: if (NonSI.DEGREE_ANGLE.isCompatible(candidate)) {
201: unit = candidate;
202: if (AxisDirection.EAST.equals(axis.getDirection()
203: .absolute())) {
204: break; // Found the longitude axis.
205: }
206: }
207: }
208: return unit;
209: }
210:
211: /**
212: * Format the inner part of a
213: * <A HREF="http://geoapi.sourceforge.net/snapshot/javadoc/org/opengis/referencing/doc-files/WKT.html"><cite>Well
214: * Known Text</cite> (WKT)</A> element.
215: *
216: * @param formatter The formatter to use.
217: * @return The name of the WKT element type, which is {@code "GEOGCS"}.
218: */
219: protected String formatWKT(final Formatter formatter) {
220: final Unit oldUnit = formatter.getAngularUnit();
221: final Unit unit = getAngularUnit(coordinateSystem);
222: formatter.setAngularUnit(unit);
223: formatter.append(datum);
224: formatter.append(((GeodeticDatum) datum).getPrimeMeridian());
225: formatter.append(unit);
226: final int dimension = coordinateSystem.getDimension();
227: for (int i = 0; i < dimension; i++) {
228: formatter.append(coordinateSystem.getAxis(i));
229: }
230: if (!unit.equals(getUnit())) {
231: formatter.setInvalidWKT(GeographicCRS.class);
232: }
233: formatter.setAngularUnit(oldUnit);
234: return "GEOGCS";
235: }
236: }
|