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

Last change on this file since 12745 was 12745, checked in by bastiK, 3 weeks ago

see #15229 - deprecate LatLon#toStringCSV

removes last references of LatLon to CoordinateFormat classes

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