001: /*
002: * JScience - Java(TM) Tools and Libraries for the Advancement of Sciences.
003: * Copyright (C) 2006 - JScience (http://jscience.org/)
004: * All rights reserved.
005: *
006: * Permission to use, copy, modify, and distribute this software is
007: * freely granted, provided that this notice is preserved.
008: */
009: package org.jscience.geography.coordinates;
010:
011: import javax.measure.Measure;
012: import javax.measure.converter.UnitConverter;
013: import javax.measure.quantity.*;
014: import static javax.measure.unit.SI.*;
015: import javax.measure.unit.Unit;
016:
017: import javolution.context.ObjectFactory;
018: import javolution.xml.XMLFormat;
019: import javolution.xml.stream.XMLStreamException;
020:
021: import static org.jscience.geography.coordinates.crs.ReferenceEllipsoid.WGS84;
022:
023: import org.jscience.geography.coordinates.crs.GeocentricCRS;
024: import org.jscience.mathematics.vector.DimensionException;
025: import org.jscience.mathematics.vector.Float64Vector;
026: import org.opengis.referencing.cs.CoordinateSystem;
027:
028: /**
029: * This class represents the {@link GeocentricCRS geocentric} Earth-Centered,
030: * Earth-Fixed (ECEF) cartesian coordinates used in GPS/GLONASS.
031: *
032: * @author Paul D. Anderson
033: * @version 3.0, February 18, 2006
034: */
035: public final class XYZ extends Coordinates<GeocentricCRS<XYZ>> {
036:
037: /**
038: * Holds the coordinate reference system for all instances of this class.
039: */
040: public static final GeocentricCRS<XYZ> CRS = new GeocentricCRS<XYZ>() {
041:
042: @Override
043: protected XYZ coordinatesOf(AbsolutePosition position) {
044: double latitude = position.latitudeWGS84
045: .doubleValue(RADIAN);
046: double longitude = position.longitudeWGS84
047: .doubleValue(RADIAN);
048: double height = (position.heightWGS84 != null) ? position.heightWGS84
049: .doubleValue(METRE)
050: : 0.0;
051:
052: double cosLat = Math.cos(latitude);
053: double sinLat = Math.sin(latitude);
054: double cosLon = Math.cos(longitude);
055: double sinLon = Math.sin(longitude);
056:
057: double roc = WGS84.verticalRadiusOfCurvature(latitude);
058: double x = (roc + height) * cosLat * cosLon;
059: double y = (roc + height) * cosLat * sinLon;
060: double z = ((1.0 - WGS84.getEccentricitySquared()) * roc + height)
061: * sinLat;
062:
063: return XYZ.valueOf(x, y, z, METRE);
064: }
065:
066: @Override
067: protected AbsolutePosition positionOf(XYZ coordinates,
068: AbsolutePosition position) {
069: final double x = coordinates._x;
070: final double y = coordinates._y;
071: final double z = coordinates._z;
072:
073: final double longitude = Math.atan2(y, x);
074:
075: final double latitude;
076: final double xy = Math.hypot(x, y);
077: // conventional result if xy == 0.0...
078: if (xy == 0.0) {
079: latitude = (z >= 0.0) ? Math.PI / 2.0 : -Math.PI / 2.0;
080: } else {
081: final double a = WGS84.getSemimajorAxis().doubleValue(
082: METRE);
083: final double b = WGS84.getsSemiminorAxis().doubleValue(
084: METRE);
085: final double ea2 = WGS84.getEccentricitySquared();
086: final double eb2 = WGS84.getSecondEccentricitySquared();
087: final double beta = Math.atan2(a * z, b * xy);
088: double numerator = z + b * eb2 * cube(Math.sin(beta));
089: double denominator = xy - a * ea2
090: * cube(Math.cos(beta));
091: latitude = Math.atan2(numerator, denominator);
092: }
093:
094: final double height = xy / Math.cos(latitude)
095: - WGS84.verticalRadiusOfCurvature(latitude);
096: position.latitudeWGS84 = Measure.valueOf(latitude, RADIAN);
097: position.longitudeWGS84 = Measure
098: .valueOf(longitude, RADIAN);
099: position.heightWGS84 = Measure.valueOf(height, METRE);
100: return position;
101: }
102:
103: @Override
104: public CoordinateSystem getCoordinateSystem() {
105: return GeocentricCRS.XYZ_CS;
106: }
107: };
108:
109: private static double cube(final double x) {
110: return x * x * x;
111: }
112:
113: /**
114: * Holds the x position in meters.
115: */
116: private double _x;
117:
118: /**
119: * Holds the y position in meters.
120: */
121: private double _y;
122:
123: /**
124: * Holds the z position in meters.
125: */
126: private double _z;
127:
128: /**
129: * Returns the spatial position corresponding to the specified coordinates.
130: *
131: * @param x the x value stated in the specified unit.
132: * @param y the y value stated in the specified unit.
133: * @param z the z value stated in the specified unit.
134: * @param unit the length unit in which the coordinates are stated.
135: * @return the corresponding 3D position.
136: */
137: public static XYZ valueOf(double x, double y, double z,
138: Unit<Length> unit) {
139: XYZ xyz = FACTORY.object();
140: if (unit == METRE) {
141: xyz._x = x;
142: xyz._y = y;
143: xyz._z = z;
144: } else {
145: UnitConverter toMeter = unit.getConverterTo(METRE);
146: xyz._x = toMeter.convert(x);
147: xyz._y = toMeter.convert(y);
148: xyz._z = toMeter.convert(z);
149: }
150: return xyz;
151: }
152:
153: private static final ObjectFactory<XYZ> FACTORY = new ObjectFactory<XYZ>() {
154:
155: @Override
156: protected XYZ create() {
157: return new XYZ();
158: }
159: };
160:
161: private XYZ() {
162: }
163:
164: /**
165: * Returns the spatial position corresponding to the specified
166: * 3-dimensional vector.
167: *
168: * @param vector the 3-dimensional vector holding the x/y/z coordinates.
169: * @param unit the length unit in which the coordinates are stated.
170: * @return the corresponding 3D position.
171: */
172: public static XYZ valueOf(Float64Vector vector, Unit<Length> unit) {
173: if (vector.getDimension() != 3)
174: throw new DimensionException(
175: "3-dimensional vector expected");
176: return XYZ.valueOf(vector.getValue(0), vector.getValue(1),
177: vector.getValue(2), unit);
178: }
179:
180: /**
181: * Returns the x coordinate value as <code>double</code>
182: *
183: * @param unit the length unit of the x coordinate value to return.
184: * @return the x coordinate stated in the specified unit.
185: */
186: public double xValue(Unit<Length> unit) {
187: return (unit == METRE) ? _x : METRE.getConverterTo(unit)
188: .convert(_x);
189: }
190:
191: /**
192: * Returns the y coordinate value as <code>double</code>
193: *
194: * @param unit the length unit of the x coordinate value to return.
195: * @return the z coordinate stated in the specified unit.
196: */
197: public double yValue(Unit<Length> unit) {
198: return (unit == METRE) ? _y : METRE.getConverterTo(unit)
199: .convert(_y);
200: }
201:
202: /**
203: * Returns the z coordinate value as <code>double</code>
204: *
205: * @param unit the length unit of the x coordinate value to return.
206: * @return the z coordinate stated in the specified unit.
207: */
208: public double zValue(Unit<Length> unit) {
209: return (unit == METRE) ? _z : METRE.getConverterTo(unit)
210: .convert(_z);
211: }
212:
213: /**
214: * Returns the x/y/z coordinates value as a 3-dimensional vector.
215: *
216: * @param unit the length unit of the vector coordinates.
217: * @return a vector holding the x/y/z coordinates stated in the
218: * specified unit.
219: */
220: public Float64Vector toVector(Unit<Length> unit) {
221: if (unit == METRE)
222: return Float64Vector.valueOf(_x, _y, _z);
223: UnitConverter cvtr = METRE.getConverterTo(unit);
224: return Float64Vector.valueOf(cvtr.convert(_x),
225: cvtr.convert(_y), cvtr.convert(_z));
226: }
227:
228: @Override
229: public GeocentricCRS<XYZ> getCoordinateReferenceSystem() {
230: return CRS;
231: }
232:
233: // OpenGIS Interface.
234: public int getDimension() {
235: return 3;
236: }
237:
238: // OpenGIS Interface.
239: public double getOrdinate(int dimension)
240: throws IndexOutOfBoundsException {
241: if (dimension == 0) {
242: Unit<?> u = GeocentricCRS.XYZ_CS.getAxis(0).getUnit();
243: return METRE.getConverterTo(u).convert(_x);
244: } else if (dimension == 1) {
245: Unit<?> u = GeocentricCRS.XYZ_CS.getAxis(1).getUnit();
246: return METRE.getConverterTo(u).convert(_y);
247: } else if (dimension == 2) {
248: Unit<?> u = GeocentricCRS.XYZ_CS.getAxis(2).getUnit();
249: return METRE.getConverterTo(u).convert(_z);
250: } else {
251: throw new IndexOutOfBoundsException();
252: }
253: }
254:
255: @Override
256: public XYZ copy() {
257: return XYZ.valueOf(_x, _y, _z, METRE);
258: }
259:
260: // Default serialization.
261: //
262:
263: static final XMLFormat<XYZ> XML = new XMLFormat<XYZ>(XYZ.class) {
264:
265: @Override
266: public XYZ newInstance(Class<XYZ> cls, InputElement xml)
267: throws XMLStreamException {
268: return FACTORY.object();
269: }
270:
271: public void write(XYZ xyz, OutputElement xml)
272: throws XMLStreamException {
273: xml.setAttribute("x", xyz._x);
274: xml.setAttribute("y", xyz._y);
275: xml.setAttribute("z", xyz._z);
276: }
277:
278: public void read(InputElement xml, XYZ xyz)
279: throws XMLStreamException {
280: xyz._x = xml.getAttribute("x", 0.0);
281: xyz._y = xml.getAttribute("y", 0.0);
282: xyz._z = xml.getAttribute("z", 0.0);
283: }
284: };
285:
286: private static final long serialVersionUID = 1L;
287:
288: }
|