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 */
009package org.jscience.geography.coordinates;
010
011import javax.measure.Measure;
012import javax.measure.converter.UnitConverter;
013import javax.measure.quantity.Angle;
014import static javax.measure.unit.NonSI.DEGREE_ANGLE;
015import static javax.measure.unit.SI.RADIAN;
016import javax.measure.unit.Unit;
017
018import javolution.context.ObjectFactory;
019import javolution.xml.XMLFormat;
020import javolution.xml.stream.XMLStreamException;
021
022import org.jscience.geography.coordinates.crs.GeographicCRS;
023import org.opengis.referencing.cs.CoordinateSystem;
024
025/**
026 * This class represents the {@link GeographicCRS geographic} latitude/longitude
027 * coordinates onto the WGS84 ellipsoid.
028 * 
029 * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
030 * @version 3.0, February 13, 2006
031 */
032public final class LatLong extends Coordinates<GeographicCRS<?>> {
033
034    /**
035     * Holds the coordinate reference system for all instances of this class. 
036     */
037    public static final GeographicCRS<LatLong> CRS = new GeographicCRS<LatLong>() {
038
039        @Override
040        protected LatLong coordinatesOf(AbsolutePosition position) {
041            return LatLong.valueOf(position.latitudeWGS84.doubleValue(DEGREE_ANGLE),
042                    position.longitudeWGS84.doubleValue(DEGREE_ANGLE), DEGREE_ANGLE);
043        }
044
045        @Override
046        protected AbsolutePosition positionOf(LatLong coordinates,
047                AbsolutePosition position) {
048            position.latitudeWGS84 = Measure.valueOf(coordinates._latitude,
049                    DEGREE_ANGLE);
050            position.longitudeWGS84 = Measure.valueOf(coordinates._longitude,
051                    DEGREE_ANGLE);
052            return position;
053        }
054
055        @Override
056        public CoordinateSystem getCoordinateSystem() {
057            return GeographicCRS.LATITUDE_LONGITUDE_CS;
058        }
059
060    };
061
062    /**
063     * Holds converter from degree to radian. 
064     */
065    private static final UnitConverter DEGREE_TO_RADIAN = DEGREE_ANGLE
066            .getConverterTo(RADIAN);
067
068    /**
069     * Holds converter from radian to degree. 
070     */
071    private static final UnitConverter RADIAN_TO_DEGREE = DEGREE_TO_RADIAN
072            .inverse();
073
074    /**
075     * Holds the latitude in degrees. 
076     */
077    private double _latitude;
078
079    /**
080     * Holds the longitude in degrees. 
081     */
082    private double _longitude;
083
084    /**
085     * Returns the surface position corresponding to the specified coordinates.
086     * 
087     * @param latitude the latitude value stated in the specified unit.
088     * @param longitude the longitude value stated in the specified unit.
089     * @param unit the angle unit in which the coordinates are stated
090     *        ({@link javax.measure.unit.NonSI#DEGREE_ANGLE Degree} typically).
091     * @return the corresponding surface position.
092     */
093    public static LatLong valueOf(double latitude, double longitude,
094            Unit<Angle> unit) {
095        LatLong latLong = FACTORY.object();
096        if (unit == DEGREE_ANGLE) {
097            latLong._latitude = latitude;
098            latLong._longitude = longitude;
099        } else if (unit == RADIAN) {
100            latLong._latitude = RADIAN_TO_DEGREE.convert(latitude);
101            latLong._longitude = RADIAN_TO_DEGREE.convert(longitude);
102        } else { // Other angle unit.
103            UnitConverter toDegree = unit.getConverterTo(DEGREE_ANGLE);
104            latLong._latitude = toDegree.convert(latitude);
105            latLong._longitude = toDegree.convert(longitude);
106        }
107        return latLong;
108    }
109
110    private static final ObjectFactory<LatLong> FACTORY = new ObjectFactory<LatLong>() {
111
112        @Override
113        protected LatLong create() {
114            return new LatLong();
115        }
116    };
117
118    private LatLong() {
119    }
120
121    /**
122     * Returns the latitude value as <code>double</code>
123     * 
124     * @param unit the angle unit of the latitude to return.
125     * @return the latitude stated in the specified unit.
126     */
127    public final double latitudeValue(Unit<Angle> unit) {
128        return (unit == DEGREE_ANGLE) ? _latitude
129                : (unit == RADIAN) ? DEGREE_TO_RADIAN.convert(_latitude)
130                        : DEGREE_ANGLE.getConverterTo(unit).convert(_latitude);
131    }
132
133    /**
134     * Returns the longitude value as <code>double</code>
135     * 
136     * @param unit the angle unit of the longitude to return.
137     * @return the longitude stated in the specified unit.
138     */
139    public final double longitudeValue(Unit<Angle> unit) {
140        return (unit == DEGREE_ANGLE) ? _longitude
141                : (unit == RADIAN) ? DEGREE_TO_RADIAN.convert(_longitude)
142                        : DEGREE_ANGLE.getConverterTo(unit).convert(_longitude);
143    }
144
145    @Override
146    public GeographicCRS<LatLong> getCoordinateReferenceSystem() {
147        return CRS;
148    }
149
150    // OpenGIS Interface.
151    public int getDimension() {
152        return 2;
153    }
154
155    // OpenGIS Interface.
156    public double getOrdinate(int dimension) throws IndexOutOfBoundsException {
157        if (dimension == 0) {
158            Unit<?> u = GeographicCRS.LATITUDE_LONGITUDE_CS.getAxis(0).getUnit();
159            return DEGREE_ANGLE.getConverterTo(u).convert(_latitude);
160        } else if (dimension == 1) {
161            Unit<?> u = GeographicCRS.LATITUDE_LONGITUDE_CS.getAxis(1).getUnit();
162            return DEGREE_ANGLE.getConverterTo(u).convert(_longitude);
163        } else {
164            throw new IndexOutOfBoundsException();
165        }
166    }
167
168    // Implements Realtime.
169    public LatLong copy() {
170        return LatLong.valueOf(_latitude, _longitude, DEGREE_ANGLE);
171    }
172
173    // Default serialization.
174    //
175
176    static final XMLFormat<LatLong> XML = new XMLFormat<LatLong>(LatLong.class) {
177
178        @Override
179        public LatLong newInstance(Class<LatLong> cls, InputElement xml)
180                throws XMLStreamException {
181            return FACTORY.object();
182        }
183
184        public void write(LatLong latLong, OutputElement xml)
185                throws XMLStreamException {
186            xml.setAttribute("latitude", latLong._latitude);
187            xml.setAttribute("longitude", latLong._longitude);
188        }
189
190        public void read(InputElement xml, LatLong latLong)
191                throws XMLStreamException {
192            latLong._latitude = xml.getAttribute("latitude", 0.0);
193            latLong._longitude = xml.getAttribute("longitude", 0.0);
194        }
195    };
196
197    private static final long serialVersionUID = 1L;
198}