KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > osgi > util > position > Position


1 /*
2  * $Header: /cvshome/build/org.osgi.util.position/src/org/osgi/util/position/Position.java,v 1.9 2006/07/12 21:21:35 hargrave Exp $
3  *
4  * Copyright (c) OSGi Alliance (2002, 2006). All Rights Reserved.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */

18 package org.osgi.util.position;
19
20 import org.osgi.util.measurement.Measurement;
21 import org.osgi.util.measurement.Unit;
22
23 /**
24  * Position represents a geographic location, based on the WGS84 System (World
25  * Geodetic System 1984).
26  * <p>
27  * The <code>org.osgi.util.measurement.Measurement</code> class is used to
28  * represent the values that make up a position.
29  * <p>
30  * <p>
31  * A given position object may lack any of it's components, i.e. the altitude
32  * may not be known. Such missing values will be represented by null.
33  * <p>
34  * Position does not override the implementation of either equals() or
35  * hashCode() because it is not clear how missing values should be handled. It
36  * is up to the user of a position to determine how best to compare two position
37  * objects. A <code>Position</code> object is immutable.
38  */

39 public class Position {
40     private Measurement altitude;
41     private Measurement longitude;
42     private Measurement latitude;
43     private Measurement speed;
44     private Measurement track;
45
46     /**
47      * Contructs a <code>Position</code> object with the given values.
48      *
49      * @param lat a <code>Measurement</code> object specifying the latitude in
50      * radians, or null
51      * @param lon a <code>Measurement</code> object specifying the longitude in
52      * radians, or null
53      * @param alt a <code>Measurement</code> object specifying the altitude in
54      * meters, or null
55      * @param speed a <code>Measurement</code> object specifying the speed in
56      * meters per second, or null
57      * @param track a <code>Measurement</code> object specifying the track in
58      * radians, or null
59      */

60     public Position(Measurement lat, Measurement lon, Measurement alt,
61             Measurement speed, Measurement track) {
62         if (lat != null) {
63             if (!Unit.rad.equals(lat.getUnit())) {
64                 throw new IllegalArgumentException JavaDoc("Invalid Latitude");
65             }
66             this.latitude = lat;
67         }
68         if (lon != null) {
69             if (!Unit.rad.equals(lon.getUnit())) {
70                 throw new IllegalArgumentException JavaDoc("Invalid Longitude");
71             }
72             this.longitude = lon;
73         }
74         normalizeLatLon();
75         if (alt != null) {
76             if (!Unit.m.equals(alt.getUnit())) {
77                 throw new IllegalArgumentException JavaDoc("Invalid Altitude");
78             }
79             this.altitude = alt;
80         }
81         if (speed != null) {
82             if (!Unit.m_s.equals(speed.getUnit())) {
83                 throw new IllegalArgumentException JavaDoc("Invalid Speed");
84             }
85             this.speed = speed;
86         }
87         if (track != null) {
88             if (!Unit.rad.equals(track.getUnit())) {
89                 throw new IllegalArgumentException JavaDoc("Invalid Track");
90             }
91             this.track = normalizeTrack(track);
92         }
93     }
94
95     /**
96      * Returns the altitude of this position in meters.
97      *
98      * @return a <code>Measurement</code> object in <code>Unit.m</code> representing
99      * the altitude in meters above the ellipsoid <code>null</code> if the
100      * altitude is not known.
101      */

102     public Measurement getAltitude() {
103         return altitude;
104     }
105
106     /**
107      * Returns the longitude of this position in radians.
108      *
109      * @return a <code>Measurement</code> object in <code>Unit.rad</code>
110      * representing the longitude, or <code>null</code> if the longitude
111      * is not known.
112      */

113     public Measurement getLongitude() {
114         return longitude;
115     }
116
117     /**
118      * Returns the latitude of this position in radians.
119      *
120      * @return a <code>Measurement</code> object in <code>Unit.rad</code>
121      * representing the latitude, or <code>null</code> if the latitude is
122      * not known..
123      */

124     public Measurement getLatitude() {
125         return latitude;
126     }
127
128     /**
129      * Returns the ground speed of this position in meters per second.
130      *
131      * @return a <code>Measurement</code> object in <code>Unit.m_s</code>
132      * representing the speed, or <code>null</code> if the speed is not
133      * known..
134      */

135     public Measurement getSpeed() {
136         return speed;
137     }
138
139     /**
140      * Returns the track of this position in radians as a compass heading. The
141      * track is the extrapolation of previous previously measured positions to a
142      * future position.
143      *
144      * @return a <code>Measurement</code> object in <code>Unit.rad</code>
145      * representing the track, or <code>null</code> if the track is not
146      * known..
147      */

148     public Measurement getTrack() {
149         return track;
150     }
151
152     private static final double LON_RANGE = Math.PI;
153     private static final double LAT_RANGE = Math.PI / 2.0D;
154
155     /**
156      * Verify the longitude and latitude parameters so they fit the normal
157      * coordinate system. A latitude is between -90 (south) and +90 (north). A A
158      * longitude is between -180 (Western hemisphere) and +180 (eastern
159      * hemisphere). This method first normalizes the latitude and longitude
160      * between +/- 180. If the |latitude| > 90, then the longitude is added 180
161      * and the latitude is normalized to fit +/-90. (Example are with degrees
162      * though radians are used) <br>
163      * No normalization takes place when either lon or lat is null.
164      */

165     private void normalizeLatLon() {
166         if (longitude == null || latitude == null)
167             return;
168         double dlon = longitude.getValue();
169         double dlat = latitude.getValue();
170         if (dlon >= -LON_RANGE && dlon < LON_RANGE && dlat >= -LAT_RANGE
171                 && dlat <= LAT_RANGE)
172             return;
173         dlon = normalize(dlon, LON_RANGE);
174         dlat = normalize(dlat, LAT_RANGE * 2.0D); // First over 180 degree
175
// Check if we have to move to other side of the earth
176
if (dlat > LAT_RANGE || dlat < -LAT_RANGE) {
177             dlon = normalize(dlon - LON_RANGE, LON_RANGE);
178             dlat = normalize((LAT_RANGE * 2.0D) - dlat, LAT_RANGE);
179         }
180         longitude = new Measurement(dlon, longitude.getError(), longitude
181                 .getUnit(), longitude.getTime());
182         latitude = new Measurement(dlat, latitude.getError(), latitude
183                 .getUnit(), latitude.getTime());
184     }
185
186     /**
187      * This function normalizes the a value according to a range. This is not
188      * simple modulo (as I thought when I started), but requires some special
189      * handling. For positive numbers we subtract 2*range from the number so
190      * that end up between -/+ range. For negative numbers we add this value.
191      * For example, if the value is 270 and the range is +/- 180. Then sign=1 so
192      * the (int) factor becomes 270+180/360 = 1. This means that 270-360=-90 is
193      * the result. (degrees are only used to make it easier to understand, this
194      * function is agnostic for radians/degrees). The result will be in
195      * [range,range&gt; The algorithm is not very fast, but it handling the
196      * [&gt; ranges made it very messy using integer arithmetic, and this is
197      * very readable. Note that it is highly unlikely that this method is called
198      * in normal situations. Normally input values to position are already
199      * normalized because they come from a GPS. And this is much more readable.
200      *
201      * @param value The value that needs adjusting
202      * @param range -range = < value < range
203      */

204     private double normalize(double value, double range) {
205         double twiceRange = 2.0D * range;
206         while (value >= range) {
207             value -= twiceRange;
208         }
209         while (value < -range) {
210             value += twiceRange;
211         }
212         return value;
213     }
214
215     private static final double TRACK_RANGE = Math.PI * 2.0D;
216
217     /**
218      * Normalize track to be a value such that: 0 <= value < +2PI. This
219      * corresponds to 0 deg to +360 deg. 0 is North 0.5*PI is East PI is South
220      * 1.5*PI is West
221      *
222      * @param track Value to be normalized
223      * @return Normalized value
224      */

225     private Measurement normalizeTrack(Measurement track) {
226         double value = track.getValue();
227         if ((0.0D <= value) && (value < TRACK_RANGE)) {
228             return track; /* value is already normalized */
229         }
230         value %= TRACK_RANGE;
231         if (value < 0.0D) {
232             value += TRACK_RANGE;
233         }
234         return new Measurement(value, track.getError(), track.getUnit(), track
235                 .getTime());
236     }
237 }
238
Popular Tags