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

Last change on this file since 12375 was 12375, checked in by michael2402, 7 years ago

Document the data.coor package

  • Property svn:eol-style set to native
File size: 25.8 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.coor;
3
4import static java.lang.Math.PI;
5import static java.lang.Math.asin;
6import static java.lang.Math.atan2;
7import static java.lang.Math.cos;
8import static java.lang.Math.sin;
9import static java.lang.Math.sqrt;
10import static org.openstreetmap.josm.data.projection.Ellipsoid.WGS84;
11import static org.openstreetmap.josm.tools.I18n.trc;
12import static org.openstreetmap.josm.tools.Utils.toRadians;
13
14import java.awt.geom.Area;
15import java.text.DecimalFormat;
16import java.text.NumberFormat;
17import java.util.ArrayList;
18import java.util.Arrays;
19import java.util.List;
20import java.util.Locale;
21import java.util.Objects;
22import java.util.regex.Matcher;
23import java.util.regex.Pattern;
24
25import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate;
26import org.openstreetmap.josm.Main;
27import org.openstreetmap.josm.data.Bounds;
28import org.openstreetmap.josm.tools.Utils;
29
30/**
31 * LatLon are unprojected latitude / longitude coordinates.
32 * <br>
33 * <b>Latitude</b> specifies the north-south position in degrees
34 * where valid values are in the [-90,90] and positive values specify positions north of the equator.
35 * <br>
36 * <b>Longitude</b> specifies the east-west position in degrees
37 * where valid values are in the [-180,180] and positive values specify positions east of the prime meridian.
38 * <br>
39 * <img alt="lat/lon" src="https://upload.wikimedia.org/wikipedia/commons/6/62/Latitude_and_Longitude_of_the_Earth.svg">
40 * <br>
41 * This class is immutable.
42 *
43 * @author Imi
44 */
45public class LatLon extends Coordinate implements ILatLon {
46
47 private static final long serialVersionUID = 1L;
48
49 /**
50 * Minimum difference in location to not be represented as the same position.
51 * The API returns 7 decimals.
52 */
53 public static final double MAX_SERVER_PRECISION = 1e-7;
54 /**
55 * The inverse of the server precision
56 * @see #MAX_SERVER_PRECISION
57 */
58 public static final double MAX_SERVER_INV_PRECISION = 1e7;
59
60 /**
61 * The (0,0) coordinates.
62 * @since 6178
63 */
64 public static final LatLon ZERO = new LatLon(0, 0);
65
66 /** North pole. */
67 public static final LatLon NORTH_POLE = new LatLon(90, 0);
68 /** South pole. */
69 public static final LatLon SOUTH_POLE = new LatLon(-90, 0);
70
71 private static DecimalFormat cDmsMinuteFormatter = new DecimalFormat("00");
72 private static DecimalFormat cDmsSecondFormatter = new DecimalFormat(
73 Main.pref == null ? "00.0" : Main.pref.get("latlon.dms.decimal-format", "00.0"));
74 private static DecimalFormat cDmMinuteFormatter = new DecimalFormat(
75 Main.pref == null ? "00.000" : Main.pref.get("latlon.dm.decimal-format", "00.000"));
76 /**
77 * The normal number format for server precision coordinates
78 */
79 public static final DecimalFormat cDdFormatter;
80 /**
81 * The number format used for high precision coordinates
82 */
83 public static final DecimalFormat cDdHighPecisionFormatter;
84 static {
85 // Don't use the localized decimal separator. This way we can present
86 // a comma separated list of coordinates.
87 cDdFormatter = (DecimalFormat) NumberFormat.getInstance(Locale.UK);
88 cDdFormatter.applyPattern("###0.0######");
89 cDdHighPecisionFormatter = (DecimalFormat) NumberFormat.getInstance(Locale.UK);
90 cDdHighPecisionFormatter.applyPattern("###0.0##########");
91 }
92
93 private static final String cDms60 = cDmsSecondFormatter.format(60.0);
94 private static final String cDms00 = cDmsSecondFormatter.format(0.0);
95 private static final String cDm60 = cDmMinuteFormatter.format(60.0);
96 private static final String cDm00 = cDmMinuteFormatter.format(0.0);
97
98 /** Character denoting South, as string */
99 public static final String SOUTH = trc("compass", "S");
100 /** Character denoting North, as string */
101 public static final String NORTH = trc("compass", "N");
102 /** Character denoting West, as string */
103 public static final String WEST = trc("compass", "W");
104 /** Character denoting East, as string */
105 public static final String EAST = trc("compass", "E");
106
107 private static final char N_TR = NORTH.charAt(0);
108 private static final char S_TR = SOUTH.charAt(0);
109 private static final char E_TR = EAST.charAt(0);
110 private static final char W_TR = WEST.charAt(0);
111
112 private static final String DEG = "\u00B0";
113 private static final String MIN = "\u2032";
114 private static final String SEC = "\u2033";
115
116 private static final Pattern P = Pattern.compile(
117 "([+|-]?\\d+[.,]\\d+)|" // (1)
118 + "([+|-]?\\d+)|" // (2)
119 + "("+DEG+"|o|deg)|" // (3)
120 + "('|"+MIN+"|min)|" // (4)
121 + "(\"|"+SEC+"|sec)|" // (5)
122 + "(,|;)|" // (6)
123 + "([NSEW"+N_TR+S_TR+E_TR+W_TR+"])|"// (7)
124 + "\\s+|"
125 + "(.+)", Pattern.CASE_INSENSITIVE);
126
127 private static final Pattern P_XML = Pattern.compile(
128 "lat=[\"']([+|-]?\\d+[.,]\\d+)[\"']\\s+lon=[\"']([+|-]?\\d+[.,]\\d+)[\"']");
129
130 /**
131 * Replies true if lat is in the range [-90,90]
132 *
133 * @param lat the latitude
134 * @return true if lat is in the range [-90,90]
135 */
136 public static boolean isValidLat(double lat) {
137 return lat >= -90d && lat <= 90d;
138 }
139
140 /**
141 * Replies true if lon is in the range [-180,180]
142 *
143 * @param lon the longitude
144 * @return true if lon is in the range [-180,180]
145 */
146 public static boolean isValidLon(double lon) {
147 return lon >= -180d && lon <= 180d;
148 }
149
150 /**
151 * Make sure longitude value is within <code>[-180, 180]</code> range.
152 * @param lon the longitude in degrees
153 * @return lon plus/minus multiples of <code>360</code>, as needed to get
154 * in <code>[-180, 180]</code> range
155 */
156 public static double normalizeLon(double lon) {
157 if (lon >= -180 && lon <= 180)
158 return lon;
159 else {
160 lon = lon % 360.0;
161 if (lon > 180) {
162 return lon - 360;
163 } else if (lon < -180) {
164 return lon + 360;
165 }
166 return lon;
167 }
168 }
169
170 /**
171 * Replies true if lat is in the range [-90,90] and lon is in the range [-180,180]
172 *
173 * @return true if lat is in the range [-90,90] and lon is in the range [-180,180]
174 */
175 public boolean isValid() {
176 return isValidLat(lat()) && isValidLon(lon());
177 }
178
179 /**
180 * Clamp the lat value to be inside the world.
181 * @param value The value
182 * @return The value clamped to the world.
183 */
184 public static double toIntervalLat(double value) {
185 return Utils.clamp(value, -90, 90);
186 }
187
188 /**
189 * Returns a valid OSM longitude [-180,+180] for the given extended longitude value.
190 * For example, a value of -181 will return +179, a value of +181 will return -179.
191 * @param value A longitude value not restricted to the [-180,+180] range.
192 * @return a valid OSM longitude [-180,+180]
193 */
194 public static double toIntervalLon(double value) {
195 if (isValidLon(value))
196 return value;
197 else {
198 int n = (int) (value + Math.signum(value)*180.0) / 360;
199 return value - n*360.0;
200 }
201 }
202
203 /**
204 * Replies the coordinate in degrees/minutes/seconds format
205 * @param pCoordinate The coordinate to convert
206 * @return The coordinate in degrees/minutes/seconds format
207 */
208 public static String dms(double pCoordinate) {
209
210 double tAbsCoord = Math.abs(pCoordinate);
211 int tDegree = (int) tAbsCoord;
212 double tTmpMinutes = (tAbsCoord - tDegree) * 60;
213 int tMinutes = (int) tTmpMinutes;
214 double tSeconds = (tTmpMinutes - tMinutes) * 60;
215
216 String sDegrees = Integer.toString(tDegree);
217 String sMinutes = cDmsMinuteFormatter.format(tMinutes);
218 String sSeconds = cDmsSecondFormatter.format(tSeconds);
219
220 if (cDms60.equals(sSeconds)) {
221 sSeconds = cDms00;
222 sMinutes = cDmsMinuteFormatter.format(tMinutes+1L);
223 }
224 if ("60".equals(sMinutes)) {
225 sMinutes = "00";
226 sDegrees = Integer.toString(tDegree+1);
227 }
228
229 return sDegrees + '\u00B0' + sMinutes + '\'' + sSeconds + '\"';
230 }
231
232 /**
233 * Replies the coordinate in degrees/minutes format
234 * @param pCoordinate The coordinate to convert
235 * @return The coordinate in degrees/minutes format
236 */
237 public static String dm(double pCoordinate) {
238
239 double tAbsCoord = Math.abs(pCoordinate);
240 int tDegree = (int) tAbsCoord;
241 double tMinutes = (tAbsCoord - tDegree) * 60;
242
243 String sDegrees = Integer.toString(tDegree);
244 String sMinutes = cDmMinuteFormatter.format(tMinutes);
245
246 if (sMinutes.equals(cDm60)) {
247 sMinutes = cDm00;
248 sDegrees = Integer.toString(tDegree+1);
249 }
250
251 return sDegrees + '\u00B0' + sMinutes + '\'';
252 }
253
254 /**
255 * Constructs a new object representing the given latitude/longitude.
256 * @param lat the latitude, i.e., the north-south position in degrees
257 * @param lon the longitude, i.e., the east-west position in degrees
258 */
259 public LatLon(double lat, double lon) {
260 super(lon, lat);
261 }
262
263 /**
264 * Creates a new LatLon object for the given coordinate
265 * @param coor The coordinates to copy from.
266 */
267 public LatLon(ILatLon coor) {
268 super(coor.lon(), coor.lat());
269 }
270
271 /**
272 * Constructs a new object for the given coordinate
273 * @param coor the coordinate
274 */
275 public LatLon(ICoordinate coor) {
276 this(coor.getLat(), coor.getLon());
277 }
278
279 @Override
280 public double lat() {
281 return y;
282 }
283
284 /**
285 * Formats the latitude part according to the given format
286 * @param d the coordinate format to use
287 * @return the formatted latitude
288 */
289 public String latToString(CoordinateFormat d) {
290 switch(d) {
291 case DECIMAL_DEGREES: return cDdFormatter.format(y);
292 case DEGREES_MINUTES_SECONDS: return dms(y) + ((y < 0) ? SOUTH : NORTH);
293 case NAUTICAL: return dm(y) + ((y < 0) ? SOUTH : NORTH);
294 case EAST_NORTH: return cDdFormatter.format(this.getEastNorth().north());
295 default: return "ERR";
296 }
297 }
298
299 @Override
300 public double lon() {
301 return x;
302 }
303
304 /**
305 * Formats the longitude part according to the given format
306 * @param d the coordinate format to use
307 * @return the formatted longitude
308 */
309 public String lonToString(CoordinateFormat d) {
310 switch(d) {
311 case DECIMAL_DEGREES: return cDdFormatter.format(x);
312 case DEGREES_MINUTES_SECONDS: return dms(x) + ((x < 0) ? WEST : EAST);
313 case NAUTICAL: return dm(x) + ((x < 0) ? WEST : EAST);
314 case EAST_NORTH: return cDdFormatter.format(this.getEastNorth().east());
315 default: return "ERR";
316 }
317 }
318
319 /**
320 * @param other other lat/lon
321 * @return <code>true</code> if the other point has almost the same lat/lon
322 * values, only differing by no more than 1 / {@link #MAX_SERVER_PRECISION MAX_SERVER_PRECISION}.
323 */
324 public boolean equalsEpsilon(LatLon other) {
325 double p = MAX_SERVER_PRECISION / 2;
326 return Math.abs(lat()-other.lat()) <= p && Math.abs(lon()-other.lon()) <= p;
327 }
328
329 /**
330 * Determines if this lat/lon is outside of the world
331 * @return <code>true</code>, if the coordinate is outside the world, compared by using lat/lon.
332 */
333 public boolean isOutSideWorld() {
334 Bounds b = Main.getProjection().getWorldBoundsLatLon();
335 return lat() < b.getMinLat() || lat() > b.getMaxLat() ||
336 lon() < b.getMinLon() || lon() > b.getMaxLon();
337 }
338
339 /**
340 * Determines if this lat/lon is within the given bounding box.
341 * @param b bounding box
342 * @return <code>true</code> if this is within the given bounding box.
343 */
344 public boolean isWithin(Bounds b) {
345 return b.contains(this);
346 }
347
348 /**
349 * Check if this is contained in given area or area is null.
350 *
351 * @param a Area
352 * @return <code>true</code> if this is contained in given area or area is null.
353 */
354 public boolean isIn(Area a) {
355 return a == null || a.contains(x, y);
356 }
357
358 /**
359 * Computes the distance between this lat/lon and another point on the earth.
360 * Uses Haversine formular.
361 * @param other the other point.
362 * @return distance in metres.
363 */
364 public double greatCircleDistance(LatLon other) {
365 double sinHalfLat = sin(toRadians(other.lat() - this.lat()) / 2);
366 double sinHalfLon = sin(toRadians(other.lon() - this.lon()) / 2);
367 double d = 2 * WGS84.a * asin(
368 sqrt(sinHalfLat*sinHalfLat +
369 cos(toRadians(this.lat()))*cos(toRadians(other.lat()))*sinHalfLon*sinHalfLon));
370 // For points opposite to each other on the sphere,
371 // rounding errors could make the argument of asin greater than 1
372 // (This should almost never happen.)
373 if (java.lang.Double.isNaN(d)) {
374 Main.error("NaN in greatCircleDistance");
375 d = PI * WGS84.a;
376 }
377 return d;
378 }
379
380 /**
381 * Returns the heading that you have to use to get from this lat/lon to another.
382 *
383 * Angle starts from north and increases counterclockwise (!), PI/2 means west.
384 * You can get usual clockwise angle from {@link #bearing(LatLon)} method.
385 * This method is kept as deprecated because it is called from many plugins.
386 *
387 * (I don't know the original source of this formula, but see
388 * <a href="https://math.stackexchange.com/questions/720/how-to-calculate-a-heading-on-the-earths-surface">this question</a>
389 * for some hints how it is derived.)
390 *
391 * @deprecated see bearing method
392 * @param other the "destination" position
393 * @return heading in radians in the range 0 &lt;= hd &lt; 2*PI
394 */
395 @Deprecated
396 public double heading(LatLon other) {
397 double hd = atan2(sin(toRadians(this.lon() - other.lon())) * cos(toRadians(other.lat())),
398 cos(toRadians(this.lat())) * sin(toRadians(other.lat())) -
399 sin(toRadians(this.lat())) * cos(toRadians(other.lat())) * cos(toRadians(this.lon() - other.lon())));
400 hd %= 2 * PI;
401 if (hd < 0) {
402 hd += 2 * PI;
403 }
404 return hd;
405 }
406
407 /**
408 * Returns bearing from this point to another.
409 *
410 * Angle starts from north and increases clockwise, PI/2 means east.
411 * Old deprecated method {@link #heading(LatLon)} used unusual reverse angle.
412 *
413 * Please note that reverse bearing (from other point to this point) should NOT be
414 * calculated from return value of this method, because great circle path
415 * between the two points have different bearings at each position.
416 *
417 * To get bearing from another point to this point call other.bearing(this)
418 *
419 * @param other the "destination" position
420 * @return heading in radians in the range 0 &lt;= hd &lt; 2*PI
421 */
422 public double bearing(LatLon other) {
423 double lat1 = toRadians(this.lat());
424 double lat2 = toRadians(other.lat());
425 double dlon = toRadians(other.lon() - this.lon());
426 double bearing = atan2(
427 sin(dlon) * cos(lat2),
428 cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(dlon)
429 );
430 bearing %= 2 * PI;
431 if (bearing < 0) {
432 bearing += 2 * PI;
433 }
434 return bearing;
435 }
436
437 /**
438 * Returns this lat/lon pair in human-readable format.
439 *
440 * @return String in the format "lat=1.23456 deg, lon=2.34567 deg"
441 */
442 public String toDisplayString() {
443 NumberFormat nf = NumberFormat.getInstance();
444 nf.setMaximumFractionDigits(5);
445 return "lat=" + nf.format(lat()) + "\u00B0, lon=" + nf.format(lon()) + '\u00B0';
446 }
447
448 /**
449 * Returns this lat/lon pair in human-readable format separated by {@code separator}.
450 * @param separator values separator
451 * @return String in the format {@code "1.23456[separator]2.34567"}
452 */
453 public String toStringCSV(String separator) {
454 return Utils.join(separator, Arrays.asList(
455 latToString(CoordinateFormat.DECIMAL_DEGREES),
456 lonToString(CoordinateFormat.DECIMAL_DEGREES)
457 ));
458 }
459
460 /**
461 * Interpolate between this and a other latlon
462 * @param ll2 The other lat/lon object
463 * @param proportion The proportion to interpolate
464 * @return a new latlon at this position if proportion is 0, at the other position it proportion is 1 and lineary interpolated otherwise.
465 */
466 public LatLon interpolate(LatLon ll2, double proportion) {
467 // this is an alternate form of this.lat() + proportion * (ll2.lat() - this.lat()) that is slightly faster
468 return new LatLon((1 - proportion) * this.lat() + proportion * ll2.lat(),
469 (1 - proportion) * this.lon() + proportion * ll2.lon());
470 }
471
472 /**
473 * Get the center between two lat/lon points
474 * @param ll2 The other {@link LatLon}
475 * @return The center at the average coordinates of the two points. Does not take the 180° meridian into account.
476 */
477 public LatLon getCenter(LatLon ll2) {
478 // The JIT will inline this for us, it is as fast as the normal /2 approach
479 return interpolate(ll2, .5);
480 }
481
482 /**
483 * Returns the euclidean distance from this {@code LatLon} to a specified {@code LatLon}.
484 *
485 * @param ll the specified coordinate to be measured against this {@code LatLon}
486 * @return the euclidean distance from this {@code LatLon} to a specified {@code LatLon}
487 * @since 6166
488 */
489 public double distance(final LatLon ll) {
490 return super.distance(ll);
491 }
492
493 /**
494 * Returns the square of the euclidean distance from this {@code LatLon} to a specified {@code LatLon}.
495 *
496 * @param ll the specified coordinate to be measured against this {@code LatLon}
497 * @return the square of the euclidean distance from this {@code LatLon} to a specified {@code LatLon}
498 * @since 6166
499 */
500 public double distanceSq(final LatLon ll) {
501 return super.distanceSq(ll);
502 }
503
504 @Override
505 public String toString() {
506 return "LatLon[lat="+lat()+",lon="+lon()+']';
507 }
508
509 /**
510 * Returns the value rounded to OSM precisions, i.e. to {@link #MAX_SERVER_PRECISION}.
511 * @param value lat/lon value
512 *
513 * @return rounded value
514 */
515 public static double roundToOsmPrecision(double value) {
516 return Math.round(value * MAX_SERVER_INV_PRECISION) / MAX_SERVER_INV_PRECISION;
517 }
518
519 /**
520 * Replies a clone of this lat LatLon, rounded to OSM precisions, i.e. to {@link #MAX_SERVER_PRECISION}
521 *
522 * @return a clone of this lat LatLon
523 */
524 public LatLon getRoundedToOsmPrecision() {
525 return new LatLon(
526 roundToOsmPrecision(lat()),
527 roundToOsmPrecision(lon())
528 );
529 }
530
531 @Override
532 public int hashCode() {
533 return Objects.hash(x, y);
534 }
535
536 @Override
537 public boolean equals(Object obj) {
538 if (this == obj) return true;
539 if (obj == null || getClass() != obj.getClass()) return false;
540 LatLon that = (LatLon) obj;
541 return Double.compare(that.x, x) == 0 &&
542 Double.compare(that.y, y) == 0;
543 }
544
545 /**
546 * Converts this latitude/longitude to an instance of {@link ICoordinate}.
547 * @return a {@link ICoordinate} instance of this latitude/longitude
548 */
549 public ICoordinate toCoordinate() {
550 return new org.openstreetmap.gui.jmapviewer.Coordinate(lat(), lon());
551 }
552
553 private static class LatLonHolder {
554 private double lat = Double.NaN;
555 private double lon = Double.NaN;
556 }
557
558 private static void setLatLonObj(final LatLonHolder latLon,
559 final Object coord1deg, final Object coord1min, final Object coord1sec, final Object card1,
560 final Object coord2deg, final Object coord2min, final Object coord2sec, final Object card2) {
561
562 setLatLon(latLon,
563 (Double) coord1deg, (Double) coord1min, (Double) coord1sec, (String) card1,
564 (Double) coord2deg, (Double) coord2min, (Double) coord2sec, (String) card2);
565 }
566
567 private static void setLatLon(final LatLonHolder latLon,
568 final double coord1deg, final double coord1min, final double coord1sec, final String card1,
569 final double coord2deg, final double coord2min, final double coord2sec, final String card2) {
570
571 setLatLon(latLon, coord1deg, coord1min, coord1sec, card1);
572 setLatLon(latLon, coord2deg, coord2min, coord2sec, card2);
573 if (Double.isNaN(latLon.lat) || Double.isNaN(latLon.lon)) {
574 throw new IllegalArgumentException("invalid lat/lon parameters");
575 }
576 }
577
578 private static void setLatLon(final LatLonHolder latLon, final double coordDeg, final double coordMin, final double coordSec,
579 final String card) {
580 if (coordDeg < -180 || coordDeg > 180 || coordMin < 0 || coordMin >= 60 || coordSec < 0 || coordSec > 60) {
581 throw new IllegalArgumentException("out of range");
582 }
583
584 double coord = (coordDeg < 0 ? -1 : 1) * (Math.abs(coordDeg) + coordMin / 60 + coordSec / 3600);
585 coord = "N".equals(card) || "E".equals(card) ? coord : -coord;
586 if ("N".equals(card) || "S".equals(card)) {
587 latLon.lat = coord;
588 } else {
589 latLon.lon = coord;
590 }
591 }
592
593 /**
594 * Parses the given string as lat/lon.
595 * @param coord String to parse
596 * @return parsed lat/lon
597 * @since 11045
598 */
599 public static LatLon parse(String coord) {
600 final LatLonHolder latLon = new LatLonHolder();
601 final Matcher mXml = P_XML.matcher(coord);
602 if (mXml.matches()) {
603 setLatLonObj(latLon,
604 Double.valueOf(mXml.group(1).replace(',', '.')), 0.0, 0.0, "N",
605 Double.valueOf(mXml.group(2).replace(',', '.')), 0.0, 0.0, "E");
606 } else {
607 final Matcher m = P.matcher(coord);
608
609 final StringBuilder sb = new StringBuilder();
610 final List<Object> list = new ArrayList<>();
611
612 while (m.find()) {
613 if (m.group(1) != null) {
614 sb.append('R'); // floating point number
615 list.add(Double.valueOf(m.group(1).replace(',', '.')));
616 } else if (m.group(2) != null) {
617 sb.append('Z'); // integer number
618 list.add(Double.valueOf(m.group(2)));
619 } else if (m.group(3) != null) {
620 sb.append('o'); // degree sign
621 } else if (m.group(4) != null) {
622 sb.append('\''); // seconds sign
623 } else if (m.group(5) != null) {
624 sb.append('"'); // minutes sign
625 } else if (m.group(6) != null) {
626 sb.append(','); // separator
627 } else if (m.group(7) != null) {
628 sb.append('x'); // cardinal direction
629 String c = m.group(7).toUpperCase(Locale.ENGLISH);
630 if ("N".equalsIgnoreCase(c) || "S".equalsIgnoreCase(c) || "E".equalsIgnoreCase(c) || "W".equalsIgnoreCase(c)) {
631 list.add(c);
632 } else {
633 list.add(c.replace(N_TR, 'N').replace(S_TR, 'S')
634 .replace(E_TR, 'E').replace(W_TR, 'W'));
635 }
636 } else if (m.group(8) != null) {
637 throw new IllegalArgumentException("invalid token: " + m.group(8));
638 }
639 }
640
641 final String pattern = sb.toString();
642
643 final Object[] params = list.toArray();
644
645 if (pattern.matches("Ro?,?Ro?")) {
646 setLatLonObj(latLon,
647 params[0], 0.0, 0.0, "N",
648 params[1], 0.0, 0.0, "E");
649 } else if (pattern.matches("xRo?,?xRo?")) {
650 setLatLonObj(latLon,
651 params[1], 0.0, 0.0, params[0],
652 params[3], 0.0, 0.0, params[2]);
653 } else if (pattern.matches("Ro?x,?Ro?x")) {
654 setLatLonObj(latLon,
655 params[0], 0.0, 0.0, params[1],
656 params[2], 0.0, 0.0, params[3]);
657 } else if (pattern.matches("Zo[RZ]'?,?Zo[RZ]'?|Z[RZ],?Z[RZ]")) {
658 setLatLonObj(latLon,
659 params[0], params[1], 0.0, "N",
660 params[2], params[3], 0.0, "E");
661 } else if (pattern.matches("xZo[RZ]'?,?xZo[RZ]'?|xZo?[RZ],?xZo?[RZ]")) {
662 setLatLonObj(latLon,
663 params[1], params[2], 0.0, params[0],
664 params[4], params[5], 0.0, params[3]);
665 } else if (pattern.matches("Zo[RZ]'?x,?Zo[RZ]'?x|Zo?[RZ]x,?Zo?[RZ]x")) {
666 setLatLonObj(latLon,
667 params[0], params[1], 0.0, params[2],
668 params[3], params[4], 0.0, params[5]);
669 } else if (pattern.matches("ZoZ'[RZ]\"?x,?ZoZ'[RZ]\"?x|ZZ[RZ]x,?ZZ[RZ]x")) {
670 setLatLonObj(latLon,
671 params[0], params[1], params[2], params[3],
672 params[4], params[5], params[6], params[7]);
673 } else if (pattern.matches("xZoZ'[RZ]\"?,?xZoZ'[RZ]\"?|xZZ[RZ],?xZZ[RZ]")) {
674 setLatLonObj(latLon,
675 params[1], params[2], params[3], params[0],
676 params[5], params[6], params[7], params[4]);
677 } else if (pattern.matches("ZZ[RZ],?ZZ[RZ]")) {
678 setLatLonObj(latLon,
679 params[0], params[1], params[2], "N",
680 params[3], params[4], params[5], "E");
681 } else {
682 throw new IllegalArgumentException("invalid format: " + pattern);
683 }
684 }
685
686 return new LatLon(latLon.lat, latLon.lon);
687 }
688}
Note: See TracBrowser for help on using the repository browser.