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

Last change on this file since 12795 was 12795, checked in by bastiK, 7 years ago

see #15273, see #15229 - fix unit tests, PMD, etc.

File size: 9.9 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 private LatLonParser() {
59 // private constructor
60 }
61
62 /**
63 * Parses the given string as lat/lon.
64 * @param coord String to parse
65 * @return parsed lat/lon
66 * @since 12792 (moved from {@link LatLon}, there since 11045)
67 */
68 public static LatLon parse(String coord) {
69 final LatLonHolder latLon = new LatLonHolder();
70 final Matcher mXml = P_XML.matcher(coord);
71 if (mXml.matches()) {
72 setLatLonObj(latLon,
73 Double.valueOf(mXml.group(1).replace(',', '.')), 0.0, 0.0, "N",
74 Double.valueOf(mXml.group(2).replace(',', '.')), 0.0, 0.0, "E");
75 } else {
76 final Matcher m = P.matcher(coord);
77
78 final StringBuilder sb = new StringBuilder();
79 final List<Object> list = new ArrayList<>();
80
81 while (m.find()) {
82 if (m.group(1) != null) {
83 sb.append('R'); // floating point number
84 list.add(Double.valueOf(m.group(1).replace(',', '.')));
85 } else if (m.group(2) != null) {
86 sb.append('Z'); // integer number
87 list.add(Double.valueOf(m.group(2)));
88 } else if (m.group(3) != null) {
89 sb.append('o'); // degree sign
90 } else if (m.group(4) != null) {
91 sb.append('\''); // seconds sign
92 } else if (m.group(5) != null) {
93 sb.append('"'); // minutes sign
94 } else if (m.group(6) != null) {
95 sb.append(','); // separator
96 } else if (m.group(7) != null) {
97 sb.append('x'); // cardinal direction
98 String c = m.group(7).toUpperCase(Locale.ENGLISH);
99 if ("N".equalsIgnoreCase(c) || "S".equalsIgnoreCase(c) || "E".equalsIgnoreCase(c) || "W".equalsIgnoreCase(c)) {
100 list.add(c);
101 } else {
102 list.add(c.replace(N_TR, 'N').replace(S_TR, 'S')
103 .replace(E_TR, 'E').replace(W_TR, 'W'));
104 }
105 } else if (m.group(8) != null) {
106 throw new IllegalArgumentException("invalid token: " + m.group(8));
107 }
108 }
109
110 final String pattern = sb.toString();
111
112 final Object[] params = list.toArray();
113
114 if (pattern.matches("Ro?,?Ro?")) {
115 setLatLonObj(latLon,
116 params[0], 0.0, 0.0, "N",
117 params[1], 0.0, 0.0, "E");
118 } else if (pattern.matches("xRo?,?xRo?")) {
119 setLatLonObj(latLon,
120 params[1], 0.0, 0.0, params[0],
121 params[3], 0.0, 0.0, params[2]);
122 } else if (pattern.matches("Ro?x,?Ro?x")) {
123 setLatLonObj(latLon,
124 params[0], 0.0, 0.0, params[1],
125 params[2], 0.0, 0.0, params[3]);
126 } else if (pattern.matches("Zo[RZ]'?,?Zo[RZ]'?|Z[RZ],?Z[RZ]")) {
127 setLatLonObj(latLon,
128 params[0], params[1], 0.0, "N",
129 params[2], params[3], 0.0, "E");
130 } else if (pattern.matches("xZo[RZ]'?,?xZo[RZ]'?|xZo?[RZ],?xZo?[RZ]")) {
131 setLatLonObj(latLon,
132 params[1], params[2], 0.0, params[0],
133 params[4], params[5], 0.0, params[3]);
134 } else if (pattern.matches("Zo[RZ]'?x,?Zo[RZ]'?x|Zo?[RZ]x,?Zo?[RZ]x")) {
135 setLatLonObj(latLon,
136 params[0], params[1], 0.0, params[2],
137 params[3], params[4], 0.0, params[5]);
138 } else if (pattern.matches("ZoZ'[RZ]\"?x,?ZoZ'[RZ]\"?x|ZZ[RZ]x,?ZZ[RZ]x")) {
139 setLatLonObj(latLon,
140 params[0], params[1], params[2], params[3],
141 params[4], params[5], params[6], params[7]);
142 } else if (pattern.matches("xZoZ'[RZ]\"?,?xZoZ'[RZ]\"?|xZZ[RZ],?xZZ[RZ]")) {
143 setLatLonObj(latLon,
144 params[1], params[2], params[3], params[0],
145 params[5], params[6], params[7], params[4]);
146 } else if (pattern.matches("ZZ[RZ],?ZZ[RZ]")) {
147 setLatLonObj(latLon,
148 params[0], params[1], params[2], "N",
149 params[3], params[4], params[5], "E");
150 } else {
151 throw new IllegalArgumentException("invalid format: " + pattern);
152 }
153 }
154
155 return new LatLon(latLon.lat, latLon.lon);
156 }
157
158 private static void setLatLonObj(final LatLonHolder latLon,
159 final Object coord1deg, final Object coord1min, final Object coord1sec, final Object card1,
160 final Object coord2deg, final Object coord2min, final Object coord2sec, final Object card2) {
161
162 setLatLon(latLon,
163 (Double) coord1deg, (Double) coord1min, (Double) coord1sec, (String) card1,
164 (Double) coord2deg, (Double) coord2min, (Double) coord2sec, (String) card2);
165 }
166
167 private static void setLatLon(final LatLonHolder latLon,
168 final double coord1deg, final double coord1min, final double coord1sec, final String card1,
169 final double coord2deg, final double coord2min, final double coord2sec, final String card2) {
170
171 setLatLon(latLon, coord1deg, coord1min, coord1sec, card1);
172 setLatLon(latLon, coord2deg, coord2min, coord2sec, card2);
173 if (Double.isNaN(latLon.lat) || Double.isNaN(latLon.lon)) {
174 throw new IllegalArgumentException("invalid lat/lon parameters");
175 }
176 }
177
178 private static void setLatLon(final LatLonHolder latLon, final double coordDeg, final double coordMin, final double coordSec,
179 final String card) {
180 if (coordDeg < -180 || coordDeg > 180 || coordMin < 0 || coordMin >= 60 || coordSec < 0 || coordSec > 60) {
181 throw new IllegalArgumentException("out of range");
182 }
183
184 double coord = (coordDeg < 0 ? -1 : 1) * (Math.abs(coordDeg) + coordMin / 60 + coordSec / 3600);
185 coord = "N".equals(card) || "E".equals(card) ? coord : -coord;
186 if ("N".equals(card) || "S".equals(card)) {
187 latLon.lat = coord;
188 } else {
189 latLon.lon = coord;
190 }
191 }
192
193 /**
194 * Parse string coordinate from floating point or DMS format.
195 * @param angleStr the string to parse as coordinate e.g. -1.1 or 50d10'3"W
196 * @return the value, in degrees
197 * @throws IllegalArgumentException in case parsing fails
198 * @since 12792
199 */
200 public static double parseCoordinate(String angleStr) {
201 final String floatPattern = "(\\d+(\\.\\d*)?)";
202 // pattern does all error handling.
203 Matcher in = Pattern.compile("^(?<neg1>-)?"
204 + "(?=\\d)(?:(?<single>" + floatPattern + ")|"
205 + "((?<degree>" + floatPattern + ")d)?"
206 + "((?<minutes>" + floatPattern + ")\')?"
207 + "((?<seconds>" + floatPattern + ")\")?)"
208 + "(?:[NE]|(?<neg2>[SW]))?$").matcher(angleStr);
209
210 if (!in.find()) {
211 throw new IllegalArgumentException(
212 tr("Unable to parse as coordinate value: '{0}'", angleStr));
213 }
214
215 double value = 0;
216 if (in.group("single") != null) {
217 value += Double.parseDouble(in.group("single"));
218 }
219 if (in.group("degree") != null) {
220 value += Double.parseDouble(in.group("degree"));
221 }
222 if (in.group("minutes") != null) {
223 value += Double.parseDouble(in.group("minutes")) / 60;
224 }
225 if (in.group("seconds") != null) {
226 value += Double.parseDouble(in.group("seconds")) / 3600;
227 }
228
229 if (in.group("neg1") != null ^ in.group("neg2") != null) {
230 value = -value;
231 }
232 return value;
233 }
234
235}
Note: See TracBrowser for help on using the repository browser.