source: josm/trunk/src/org/openstreetmap/josm/data/coor/conversion/LatLonParser.java @ 12792

Last change on this file since 12792 was 12792, checked in by bastiK, 3 months ago

closes #15273, see #15229, see #15182 - add command line interface module for projections

  • run josm project --help to see the options
  • extracts parser from LatLon and CustomProjection into LatLonParser
File size: 9.8 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.coor.conversion;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5import static org.openstreetmap.josm.tools.I18n.trc;
6
7import java.util.ArrayList;
8import java.util.List;
9import java.util.Locale;
10import java.util.regex.Matcher;
11import java.util.regex.Pattern;
12
13import org.openstreetmap.josm.data.coor.LatLon;
14
15/**
16 * Support for parsing a {@link LatLon} object from a string.
17 * @since 12792
18 */
19public class LatLonParser {
20
21    /** Character denoting South, as string */
22    public static final String SOUTH = trc("compass", "S");
23    /** Character denoting North, as string */
24    public static final String NORTH = trc("compass", "N");
25    /** Character denoting West, as string */
26    public static final String WEST = trc("compass", "W");
27    /** Character denoting East, as string */
28    public static final String EAST = trc("compass", "E");
29
30    private static final char N_TR = NORTH.charAt(0);
31    private static final char S_TR = SOUTH.charAt(0);
32    private static final char E_TR = EAST.charAt(0);
33    private static final char W_TR = WEST.charAt(0);
34
35    private static final String DEG = "\u00B0";
36    private static final String MIN = "\u2032";
37    private static final String SEC = "\u2033";
38
39    private static final Pattern P = Pattern.compile(
40            "([+|-]?\\d+[.,]\\d+)|"             // (1)
41            + "([+|-]?\\d+)|"                   // (2)
42            + "("+DEG+"|o|deg)|"                // (3)
43            + "('|"+MIN+"|min)|"                // (4)
44            + "(\"|"+SEC+"|sec)|"               // (5)
45            + "(,|;)|"                          // (6)
46            + "([NSEW"+N_TR+S_TR+E_TR+W_TR+"])|"// (7)
47            + "\\s+|"
48            + "(.+)", Pattern.CASE_INSENSITIVE);
49
50    private static final Pattern P_XML = Pattern.compile(
51            "lat=[\"']([+|-]?\\d+[.,]\\d+)[\"']\\s+lon=[\"']([+|-]?\\d+[.,]\\d+)[\"']");
52
53    private static class LatLonHolder {
54        private double lat = Double.NaN;
55        private double lon = Double.NaN;
56    }
57
58    /**
59     * Parses the given string as lat/lon.
60     * @param coord String to parse
61     * @return parsed lat/lon
62     * @since 12792 (moved from {@link LatLon}, there since 11045)
63     */
64    public static LatLon parse(String coord) {
65        final LatLonHolder latLon = new LatLonHolder();
66        final Matcher mXml = P_XML.matcher(coord);
67        if (mXml.matches()) {
68            setLatLonObj(latLon,
69                    Double.valueOf(mXml.group(1).replace(',', '.')), 0.0, 0.0, "N",
70                    Double.valueOf(mXml.group(2).replace(',', '.')), 0.0, 0.0, "E");
71        } else {
72            final Matcher m = P.matcher(coord);
73
74            final StringBuilder sb = new StringBuilder();
75            final List<Object> list = new ArrayList<>();
76
77            while (m.find()) {
78                if (m.group(1) != null) {
79                    sb.append('R');     // floating point number
80                    list.add(Double.valueOf(m.group(1).replace(',', '.')));
81                } else if (m.group(2) != null) {
82                    sb.append('Z');     // integer number
83                    list.add(Double.valueOf(m.group(2)));
84                } else if (m.group(3) != null) {
85                    sb.append('o');     // degree sign
86                } else if (m.group(4) != null) {
87                    sb.append('\'');    // seconds sign
88                } else if (m.group(5) != null) {
89                    sb.append('"');     // minutes sign
90                } else if (m.group(6) != null) {
91                    sb.append(',');     // separator
92                } else if (m.group(7) != null) {
93                    sb.append('x');     // cardinal direction
94                    String c = m.group(7).toUpperCase(Locale.ENGLISH);
95                    if ("N".equalsIgnoreCase(c) || "S".equalsIgnoreCase(c) || "E".equalsIgnoreCase(c) || "W".equalsIgnoreCase(c)) {
96                        list.add(c);
97                    } else {
98                        list.add(c.replace(N_TR, 'N').replace(S_TR, 'S')
99                                  .replace(E_TR, 'E').replace(W_TR, 'W'));
100                    }
101                } else if (m.group(8) != null) {
102                    throw new IllegalArgumentException("invalid token: " + m.group(8));
103                }
104            }
105
106            final String pattern = sb.toString();
107
108            final Object[] params = list.toArray();
109
110            if (pattern.matches("Ro?,?Ro?")) {
111                setLatLonObj(latLon,
112                        params[0], 0.0, 0.0, "N",
113                        params[1], 0.0, 0.0, "E");
114            } else if (pattern.matches("xRo?,?xRo?")) {
115                setLatLonObj(latLon,
116                        params[1], 0.0, 0.0, params[0],
117                        params[3], 0.0, 0.0, params[2]);
118            } else if (pattern.matches("Ro?x,?Ro?x")) {
119                setLatLonObj(latLon,
120                        params[0], 0.0, 0.0, params[1],
121                        params[2], 0.0, 0.0, params[3]);
122            } else if (pattern.matches("Zo[RZ]'?,?Zo[RZ]'?|Z[RZ],?Z[RZ]")) {
123                setLatLonObj(latLon,
124                        params[0], params[1], 0.0, "N",
125                        params[2], params[3], 0.0, "E");
126            } else if (pattern.matches("xZo[RZ]'?,?xZo[RZ]'?|xZo?[RZ],?xZo?[RZ]")) {
127                setLatLonObj(latLon,
128                        params[1], params[2], 0.0, params[0],
129                        params[4], params[5], 0.0, params[3]);
130            } else if (pattern.matches("Zo[RZ]'?x,?Zo[RZ]'?x|Zo?[RZ]x,?Zo?[RZ]x")) {
131                setLatLonObj(latLon,
132                        params[0], params[1], 0.0, params[2],
133                        params[3], params[4], 0.0, params[5]);
134            } else if (pattern.matches("ZoZ'[RZ]\"?x,?ZoZ'[RZ]\"?x|ZZ[RZ]x,?ZZ[RZ]x")) {
135                setLatLonObj(latLon,
136                        params[0], params[1], params[2], params[3],
137                        params[4], params[5], params[6], params[7]);
138            } else if (pattern.matches("xZoZ'[RZ]\"?,?xZoZ'[RZ]\"?|xZZ[RZ],?xZZ[RZ]")) {
139                setLatLonObj(latLon,
140                        params[1], params[2], params[3], params[0],
141                        params[5], params[6], params[7], params[4]);
142            } else if (pattern.matches("ZZ[RZ],?ZZ[RZ]")) {
143                setLatLonObj(latLon,
144                        params[0], params[1], params[2], "N",
145                        params[3], params[4], params[5], "E");
146            } else {
147                throw new IllegalArgumentException("invalid format: " + pattern);
148            }
149        }
150
151        return new LatLon(latLon.lat, latLon.lon);
152    }
153
154    private static void setLatLonObj(final LatLonHolder latLon,
155            final Object coord1deg, final Object coord1min, final Object coord1sec, final Object card1,
156            final Object coord2deg, final Object coord2min, final Object coord2sec, final Object card2) {
157
158        setLatLon(latLon,
159                (Double) coord1deg, (Double) coord1min, (Double) coord1sec, (String) card1,
160                (Double) coord2deg, (Double) coord2min, (Double) coord2sec, (String) card2);
161    }
162
163    private static void setLatLon(final LatLonHolder latLon,
164            final double coord1deg, final double coord1min, final double coord1sec, final String card1,
165            final double coord2deg, final double coord2min, final double coord2sec, final String card2) {
166
167        setLatLon(latLon, coord1deg, coord1min, coord1sec, card1);
168        setLatLon(latLon, coord2deg, coord2min, coord2sec, card2);
169        if (Double.isNaN(latLon.lat) || Double.isNaN(latLon.lon)) {
170            throw new IllegalArgumentException("invalid lat/lon parameters");
171        }
172    }
173
174    private static void setLatLon(final LatLonHolder latLon, final double coordDeg, final double coordMin, final double coordSec,
175            final String card) {
176        if (coordDeg < -180 || coordDeg > 180 || coordMin < 0 || coordMin >= 60 || coordSec < 0 || coordSec > 60) {
177            throw new IllegalArgumentException("out of range");
178        }
179
180        double coord = (coordDeg < 0 ? -1 : 1) * (Math.abs(coordDeg) + coordMin / 60 + coordSec / 3600);
181        coord = "N".equals(card) || "E".equals(card) ? coord : -coord;
182        if ("N".equals(card) || "S".equals(card)) {
183            latLon.lat = coord;
184        } else {
185            latLon.lon = coord;
186        }
187    }
188
189    /**
190     * Parse string coordinate from floating point or DMS format.
191     * @param angleStr the string to parse as coordinate e.g. -1.1 or 50d10'3"W
192     * @return the value, in degrees
193     * @throws IllegalArgumentException in case parsing fails
194     * @since 12792
195     */
196    public static double parseCoordinate(String angleStr) {
197        final String floatPattern = "(\\d+(\\.\\d*)?)";
198        // pattern does all error handling.
199        Matcher in = Pattern.compile("^(?<neg1>-)?"
200                + "(?=\\d)(?:(?<single>" + floatPattern + ")|"
201                + "((?<degree>" + floatPattern + ")d)?"
202                + "((?<minutes>" + floatPattern + ")\')?"
203                + "((?<seconds>" + floatPattern + ")\")?)"
204                + "(?:[NE]|(?<neg2>[SW]))?$").matcher(angleStr);
205
206        if (!in.find()) {
207            throw new IllegalArgumentException(
208                    tr("Unable to parse as coordinate value: '{0}'", angleStr));
209        }
210
211        double value = 0;
212        if (in.group("single") != null) {
213            value += Double.parseDouble(in.group("single"));
214        }
215        if (in.group("degree") != null) {
216            value += Double.parseDouble(in.group("degree"));
217        }
218        if (in.group("minutes") != null) {
219            value += Double.parseDouble(in.group("minutes")) / 60;
220        }
221        if (in.group("seconds") != null) {
222            value += Double.parseDouble(in.group("seconds")) / 3600;
223        }
224
225        if (in.group("neg1") != null ^ in.group("neg2") != null) {
226            value = -value;
227        }
228        return value;
229    }
230
231}
Note: See TracBrowser for help on using the repository browser.