source: josm/trunk/src/org/openstreetmap/josm/data/coor/LatLon.java@ 4574

Last change on this file since 4574 was 4574, checked in by Don-vip, 12 years ago

fix #7028 - Dragging map is extremely slow when large GPX file is loaded

  • Property svn:eol-style set to native
File size: 10.6 KB
Line 
1// License: GPL. Copyright 2007 by Immanuel Scholz and others
2package org.openstreetmap.josm.data.coor;
3
4import static org.openstreetmap.josm.tools.I18n.trc;
5
6import static java.lang.Math.PI;
7import static java.lang.Math.asin;
8import static java.lang.Math.atan2;
9import static java.lang.Math.cos;
10import static java.lang.Math.sin;
11import static java.lang.Math.sqrt;
12import static java.lang.Math.toRadians;
13
14import java.math.BigDecimal;
15import java.math.MathContext;
16import java.text.DecimalFormat;
17import java.text.NumberFormat;
18import java.util.Locale;
19
20import org.openstreetmap.josm.Main;
21import org.openstreetmap.josm.data.Bounds;
22
23/**
24 * LatLon are unprojected latitude / longitude coordinates.
25 *
26 * This class is immutable.
27 *
28 * @author Imi
29 */
30public class LatLon extends Coordinate {
31
32
33 /**
34 * Minimum difference in location to not be represented as the same position.
35 * The API returns 7 decimals.
36 */
37 public static final double MAX_SERVER_PRECISION = 1e-7;
38 public static final int MAX_SERVER_DIGITS = 7;
39
40 private static DecimalFormat cDmsMinuteFormatter = new DecimalFormat("00");
41 private static DecimalFormat cDmsSecondFormatter = new DecimalFormat("00.0");
42 private static DecimalFormat cDmMinuteFormatter = new DecimalFormat("00.000");
43 public static DecimalFormat cDdFormatter;
44 static {
45 // Don't use the localized decimal separator. This way we can present
46 // a comma separated list of coordinates.
47 cDdFormatter = (DecimalFormat) NumberFormat.getInstance(Locale.UK);
48 cDdFormatter.applyPattern("###0.0000000");
49 }
50
51 /**
52 * Replies true if lat is in the range [-90,90]
53 *
54 * @param lat the latitude
55 * @return true if lat is in the range [-90,90]
56 */
57 public static boolean isValidLat(double lat) {
58 return lat >= -90d && lat <= 90d;
59 }
60
61 /**
62 * Replies true if lon is in the range [-180,180]
63 *
64 * @param lon the longitude
65 * @return true if lon is in the range [-180,180]
66 */
67 public static boolean isValidLon(double lon) {
68 return lon >= -180d && lon <= 180d;
69 }
70
71 /**
72 * Replies true if lat is in the range [-90,90] and lon is in the range [-180,180]
73 *
74 * @return true if lat is in the range [-90,90] and lon is in the range [-180,180]
75 */
76 public boolean isValid() {
77 return isValidLat(lat()) && isValidLon(lon());
78 }
79
80 /**
81 * Replies the coordinate in degrees/minutes/seconds format
82 */
83 public static String dms(double pCoordinate) {
84
85 double tAbsCoord = Math.abs(pCoordinate);
86 int tDegree = (int) tAbsCoord;
87 double tTmpMinutes = (tAbsCoord - tDegree) * 60;
88 int tMinutes = (int) tTmpMinutes;
89 double tSeconds = (tTmpMinutes - tMinutes) * 60;
90
91 return tDegree + "\u00B0" + cDmsMinuteFormatter.format(tMinutes) + "\'"
92 + cDmsSecondFormatter.format(tSeconds) + "\"";
93 }
94
95 public static String dm(double pCoordinate) {
96
97 double tAbsCoord = Math.abs(pCoordinate);
98 int tDegree = (int) tAbsCoord;
99 double tMinutes = (tAbsCoord - tDegree) * 60;
100 return tDegree + "\u00B0" + cDmMinuteFormatter.format(tMinutes) + "\'";
101 }
102
103 public LatLon(double lat, double lon) {
104 super(lon, lat);
105 }
106
107 public LatLon(LatLon coor) {
108 super(coor.lon(), coor.lat());
109 }
110
111 public double lat() {
112 return y;
113 }
114
115 public final static String SOUTH = trc("compass", "S");
116 public final static String NORTH = trc("compass", "N");
117 public String latToString(CoordinateFormat d) {
118 switch(d) {
119 case DECIMAL_DEGREES: return cDdFormatter.format(y);
120 case DEGREES_MINUTES_SECONDS: return dms(y) + ((y < 0) ? SOUTH : NORTH);
121 case NAUTICAL: return dm(y) + ((y < 0) ? SOUTH : NORTH);
122 case EAST_NORTH: return cDdFormatter.format(Main.getProjection().latlon2eastNorth(this).north());
123 default: return "ERR";
124 }
125 }
126
127 public double lon() {
128 return x;
129 }
130
131 public final static String WEST = trc("compass", "W");
132 public final static String EAST = trc("compass", "E");
133 public String lonToString(CoordinateFormat d) {
134 switch(d) {
135 case DECIMAL_DEGREES: return cDdFormatter.format(x);
136 case DEGREES_MINUTES_SECONDS: return dms(x) + ((x < 0) ? WEST : EAST);
137 case NAUTICAL: return dm(x) + ((x < 0) ? WEST : EAST);
138 case EAST_NORTH: return cDdFormatter.format(Main.getProjection().latlon2eastNorth(this).east());
139 default: return "ERR";
140 }
141 }
142
143 /**
144 * @return <code>true</code> if the other point has almost the same lat/lon
145 * values, only differing by no more than
146 * 1 / {@link #MAX_SERVER_PRECISION MAX_SERVER_PRECISION}.
147 */
148 public boolean equalsEpsilon(LatLon other) {
149 double p = MAX_SERVER_PRECISION / 2;
150 return Math.abs(lat()-other.lat()) <= p && Math.abs(lon()-other.lon()) <= p;
151 }
152
153 /**
154 * @return <code>true</code>, if the coordinate is outside the world, compared
155 * by using lat/lon.
156 */
157 public boolean isOutSideWorld() {
158 Bounds b = Main.getProjection().getWorldBoundsLatLon();
159 return lat() < b.getMin().lat() || lat() > b.getMax().lat() ||
160 lon() < b.getMin().lon() || lon() > b.getMax().lon();
161 }
162
163 /**
164 * @return <code>true</code> if this is within the given bounding box.
165 */
166 public boolean isWithin(Bounds b) {
167 return lat() >= b.getMin().lat() && lat() <= b.getMax().lat() && lon() > b.getMin().lon() && lon() < b.getMax().lon();
168 }
169
170 /**
171 * Computes the distance between this lat/lon and another point on the earth.
172 * Uses Haversine formular.
173 * @param other the other point.
174 * @return distance in metres.
175 */
176 public double greatCircleDistance(LatLon other) {
177 double R = 6378135;
178 double sinHalfLat = sin(toRadians(other.lat() - this.lat()) / 2);
179 double sinHalfLon = sin(toRadians(other.lon() - this.lon()) / 2);
180 double d = 2 * R * asin(
181 sqrt(sinHalfLat*sinHalfLat +
182 cos(toRadians(this.lat()))*cos(toRadians(other.lat()))*sinHalfLon*sinHalfLon));
183 // For points opposite to each other on the sphere,
184 // rounding errors could make the argument of asin greater than 1
185 // (This should almost never happen.)
186 if (java.lang.Double.isNaN(d)) {
187 System.err.println("Error: NaN in greatCircleDistance");
188 d = PI * R;
189 }
190 return d;
191 }
192
193 /**
194 * Returns the heading, in radians, that you have to use to get from
195 * this lat/lon to another.
196 *
197 * (I don't know the original source of this formula, but see
198 * http://math.stackexchange.com/questions/720/how-to-calculate-a-heading-on-the-earths-surface
199 * for some hints how it is derived.)
200 *
201 * @param other the "destination" position
202 * @return heading in the range 0 <= hd < 2*PI
203 */
204 public double heading(LatLon other) {
205 double hd = atan2(sin(toRadians(this.lon() - other.lon())) * cos(toRadians(other.lat())),
206 cos(toRadians(this.lat())) * sin(toRadians(other.lat())) -
207 sin(toRadians(this.lat())) * cos(toRadians(other.lat())) * cos(toRadians(this.lon() - other.lon())));
208 hd %= 2 * PI;
209 if (hd < 0) {
210 hd += 2 * PI;
211 }
212 return hd;
213 }
214
215 /**
216 * Returns this lat/lon pair in human-readable format.
217 *
218 * @return String in the format "lat=1.23456 deg, lon=2.34567 deg"
219 */
220 public String toDisplayString() {
221 NumberFormat nf = NumberFormat.getInstance();
222 nf.setMaximumFractionDigits(5);
223 return "lat=" + nf.format(lat()) + "\u00B0, lon=" + nf.format(lon()) + "\u00B0";
224 }
225
226 public LatLon interpolate(LatLon ll2, double proportion) {
227 return new LatLon(this.lat() + proportion * (ll2.lat() - this.lat()),
228 this.lon() + proportion * (ll2.lon() - this.lon()));
229 }
230
231 public LatLon getCenter(LatLon ll2) {
232 return new LatLon((this.lat() + ll2.lat())/2.0, (this.lon() + ll2.lon())/2.0);
233 }
234
235 @Override public String toString() {
236 return "LatLon[lat="+lat()+",lon="+lon()+"]";
237 }
238
239 /**
240 * Returns the value rounded to OSM precisions, i.e. to
241 * LatLon.MAX_SERVER_PRECISION
242 *
243 * @return rounded value
244 */
245 public static double roundToOsmPrecision(double value) {
246 return Math.round(value / MAX_SERVER_PRECISION) * MAX_SERVER_PRECISION; // causes tiny rounding errors (see LatLonTest)
247 }
248
249 /**
250 * Returns the value rounded to OSM precisions, i.e. to
251 * LatLon.MAX_SERVER_PRECISION. The result is guaranteed to be exact, but at a great cost.
252 * This function is about 1000 times slower than roundToOsmPrecision(), use it with caution.
253 *
254 * @return rounded value
255 */
256 public static double roundToOsmPrecisionStrict(double value) {
257 double absV = Math.abs(value);
258 int numOfDigits = MAX_SERVER_DIGITS + (absV < 1 ? 0 : (absV < 10 ? 1 : (absV < 100 ? 2 : 3)));
259 return BigDecimal.valueOf(value).round(new MathContext(numOfDigits)).doubleValue();
260 }
261
262 /**
263 * Replies a clone of this lat LatLon, rounded to OSM precisions, i.e. to
264 * MAX_SERVER_PRECISION
265 *
266 * @return a clone of this lat LatLon
267 */
268 public LatLon getRoundedToOsmPrecision() {
269 return new LatLon(
270 roundToOsmPrecision(lat()),
271 roundToOsmPrecision(lon())
272 );
273 }
274
275 /**
276 * Replies a clone of this lat LatLon, rounded to OSM precisions, i.e. to
277 * MAX_SERVER_PRECISION
278 *
279 * @return a clone of this lat LatLon
280 */
281 public LatLon getRoundedToOsmPrecisionStrict() {
282 return new LatLon(
283 roundToOsmPrecisionStrict(lat()),
284 roundToOsmPrecisionStrict(lon())
285 );
286 }
287
288 @Override
289 public int hashCode() {
290 final int prime = 31;
291 int result = super.hashCode();
292 long temp;
293 temp = java.lang.Double.doubleToLongBits(x);
294 result = prime * result + (int) (temp ^ (temp >>> 32));
295 temp = java.lang.Double.doubleToLongBits(y);
296 result = prime * result + (int) (temp ^ (temp >>> 32));
297 return result;
298 }
299
300 @Override
301 public boolean equals(Object obj) {
302 if (this == obj)
303 return true;
304 if (!super.equals(obj))
305 return false;
306 if (getClass() != obj.getClass())
307 return false;
308 Coordinate other = (Coordinate) obj;
309 if (java.lang.Double.doubleToLongBits(x) != java.lang.Double.doubleToLongBits(other.x))
310 return false;
311 if (java.lang.Double.doubleToLongBits(y) != java.lang.Double.doubleToLongBits(other.y))
312 return false;
313 return true;
314 }
315}
Note: See TracBrowser for help on using the repository browser.