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

Last change on this file since 11143 was 11045, checked in by Don-vip, 8 years ago

fix #13674 - allow case insensitive lat/lon input + reject more invalid cases

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