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

Last change on this file since 19050 was 19050, checked in by taylor.smock, 4 weeks ago

Revert most var changes from r19048, fix most new compile warnings and checkstyle issues

Also, document why various ErrorProne checks were originally disabled and fix
generic SonarLint issues.

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