source: josm/trunk/src/org/openstreetmap/josm/gui/dialogs/LatLonDialog.java@ 10611

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

see #11390 - sonar - squid:S1604 - Java 8: Anonymous inner classes containing only one method should become lambdas

  • Property svn:eol-style set to native
File size: 19.1 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.dialogs;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.Color;
7import java.awt.Component;
8import java.awt.GridBagLayout;
9import java.awt.event.FocusEvent;
10import java.awt.event.FocusListener;
11import java.util.ArrayList;
12import java.util.Arrays;
13import java.util.List;
14import java.util.Locale;
15import java.util.regex.Matcher;
16import java.util.regex.Pattern;
17
18import javax.swing.BorderFactory;
19import javax.swing.JLabel;
20import javax.swing.JPanel;
21import javax.swing.JSeparator;
22import javax.swing.JTabbedPane;
23import javax.swing.UIManager;
24import javax.swing.event.DocumentEvent;
25import javax.swing.event.DocumentListener;
26
27import org.openstreetmap.josm.Main;
28import org.openstreetmap.josm.data.coor.CoordinateFormat;
29import org.openstreetmap.josm.data.coor.EastNorth;
30import org.openstreetmap.josm.data.coor.LatLon;
31import org.openstreetmap.josm.gui.ExtendedDialog;
32import org.openstreetmap.josm.gui.widgets.HtmlPanel;
33import org.openstreetmap.josm.gui.widgets.JosmTextField;
34import org.openstreetmap.josm.tools.GBC;
35import org.openstreetmap.josm.tools.Utils;
36import org.openstreetmap.josm.tools.WindowGeometry;
37
38public class LatLonDialog extends ExtendedDialog {
39 private static final Color BG_COLOR_ERROR = new Color(255, 224, 224);
40
41 public JTabbedPane tabs;
42 private JosmTextField tfLatLon, tfEastNorth;
43 private LatLon latLonCoordinates;
44 private EastNorth eastNorthCoordinates;
45
46 private static final Double ZERO = 0.0;
47 private static final String DEG = "\u00B0";
48 private static final String MIN = "\u2032";
49 private static final String SEC = "\u2033";
50
51 private static final char N_TR = LatLon.NORTH.charAt(0);
52 private static final char S_TR = LatLon.SOUTH.charAt(0);
53 private static final char E_TR = LatLon.EAST.charAt(0);
54 private static final char W_TR = LatLon.WEST.charAt(0);
55
56 private static final Pattern P = Pattern.compile(
57 "([+|-]?\\d+[.,]\\d+)|" // (1)
58 + "([+|-]?\\d+)|" // (2)
59 + "("+DEG+"|o|deg)|" // (3)
60 + "('|"+MIN+"|min)|" // (4)
61 + "(\"|"+SEC+"|sec)|" // (5)
62 + "(,|;)|" // (6)
63 + "([NSEW"+N_TR+S_TR+E_TR+W_TR+"])|"// (7)
64 + "\\s+|"
65 + "(.+)");
66
67 private static final Pattern P_XML = Pattern.compile(
68 "lat=[\"']([+|-]?\\d+[.,]\\d+)[\"']\\s+lon=[\"']([+|-]?\\d+[.,]\\d+)[\"']");
69
70 protected JPanel buildLatLon() {
71 JPanel pnl = new JPanel(new GridBagLayout());
72 pnl.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
73
74 pnl.add(new JLabel(tr("Coordinates:")), GBC.std().insets(0, 10, 5, 0));
75 tfLatLon = new JosmTextField(24);
76 pnl.add(tfLatLon, GBC.eol().insets(0, 10, 0, 0).fill(GBC.HORIZONTAL).weight(1.0, 0.0));
77
78 pnl.add(new JSeparator(), GBC.eol().fill(GBC.HORIZONTAL).insets(0, 5, 0, 5));
79
80 pnl.add(new HtmlPanel(
81 Utils.join("<br/>", Arrays.asList(
82 tr("Enter the coordinates for the new node."),
83 tr("You can separate longitude and latitude with space, comma or semicolon."),
84 tr("Use positive numbers or N, E characters to indicate North or East cardinal direction."),
85 tr("For South and West cardinal directions you can use either negative numbers or S, W characters."),
86 tr("Coordinate value can be in one of three formats:")
87 )) +
88 Utils.joinAsHtmlUnorderedList(Arrays.asList(
89 tr("<i>degrees</i><tt>&deg;</tt>"),
90 tr("<i>degrees</i><tt>&deg;</tt> <i>minutes</i><tt>&#39;</tt>"),
91 tr("<i>degrees</i><tt>&deg;</tt> <i>minutes</i><tt>&#39;</tt> <i>seconds</i><tt>&quot</tt>")
92 )) +
93 Utils.join("<br/><br/>", Arrays.asList(
94 tr("Symbols <tt>&deg;</tt>, <tt>&#39;</tt>, <tt>&prime;</tt>, <tt>&quot;</tt>, <tt>&Prime;</tt> are optional."),
95 tr("You can also use the syntax <tt>lat=\"...\" lon=\"...\"</tt> or <tt>lat=''...'' lon=''...''</tt>."),
96 tr("Some examples:")
97 )) +
98 "<table><tr><td>" +
99 Utils.joinAsHtmlUnorderedList(Arrays.asList(
100 "49.29918&deg; 19.24788&deg;",
101 "N 49.29918 E 19.24788",
102 "W 49&deg;29.918&#39; S 19&deg;24.788&#39;",
103 "N 49&deg;29&#39;04&quot; E 19&deg;24&#39;43&quot;",
104 "49.29918 N, 19.24788 E",
105 "49&deg;29&#39;21&quot; N 19&deg;24&#39;38&quot; E",
106 "49 29 51, 19 24 18",
107 "49 29, 19 24",
108 "E 49 29, N 19 24"
109 )) +
110 "</td><td>" +
111 Utils.joinAsHtmlUnorderedList(Arrays.asList(
112 "49&deg; 29; 19&deg; 24",
113 "N 49&deg; 29, W 19&deg; 24",
114 "49&deg; 29.5 S, 19&deg; 24.6 E",
115 "N 49 29.918 E 19 15.88",
116 "49 29.4 19 24.5",
117 "-49 29.4 N -19 24.5 W",
118 "48 deg 42&#39; 52.13\" N, 21 deg 11&#39; 47.60\" E",
119 "lat=\"49.29918\" lon=\"19.24788\"",
120 "lat='49.29918' lon='19.24788'"
121 )) +
122 "</td></tr></table>"),
123 GBC.eol().fill().weight(1.0, 1.0));
124
125 // parse and verify input on the fly
126 //
127 LatLonInputVerifier inputVerifier = new LatLonInputVerifier();
128 tfLatLon.getDocument().addDocumentListener(inputVerifier);
129
130 // select the text in the field on focus
131 //
132 TextFieldFocusHandler focusHandler = new TextFieldFocusHandler();
133 tfLatLon.addFocusListener(focusHandler);
134 return pnl;
135 }
136
137 private JPanel buildEastNorth() {
138 JPanel pnl = new JPanel(new GridBagLayout());
139 pnl.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
140
141 pnl.add(new JLabel(tr("Projected coordinates:")), GBC.std().insets(0, 10, 5, 0));
142 tfEastNorth = new JosmTextField(24);
143
144 pnl.add(tfEastNorth, GBC.eol().insets(0, 10, 0, 0).fill(GBC.HORIZONTAL).weight(1.0, 0.0));
145
146 pnl.add(new JSeparator(), GBC.eol().fill(GBC.HORIZONTAL).insets(0, 5, 0, 5));
147
148 pnl.add(new HtmlPanel(
149 tr("Enter easting and northing (x and y) separated by space, comma or semicolon.")),
150 GBC.eol().fill(GBC.HORIZONTAL));
151
152 pnl.add(GBC.glue(1, 1), GBC.eol().fill().weight(1.0, 1.0));
153
154 EastNorthInputVerifier inputVerifier = new EastNorthInputVerifier();
155 tfEastNorth.getDocument().addDocumentListener(inputVerifier);
156
157 TextFieldFocusHandler focusHandler = new TextFieldFocusHandler();
158 tfEastNorth.addFocusListener(focusHandler);
159
160 return pnl;
161 }
162
163 protected void build() {
164 tabs = new JTabbedPane();
165 tabs.addTab(tr("Lat/Lon"), buildLatLon());
166 tabs.addTab(tr("East/North"), buildEastNorth());
167 tabs.getModel().addChangeListener(e -> {
168 switch (tabs.getModel().getSelectedIndex()) {
169 case 0: parseLatLonUserInput(); break;
170 case 1: parseEastNorthUserInput(); break;
171 default: throw new AssertionError();
172 }
173 });
174 setContent(tabs, false);
175 }
176
177 public LatLonDialog(Component parent, String title, String help) {
178 super(parent, title, new String[] {tr("Ok"), tr("Cancel")});
179 setButtonIcons(new String[] {"ok", "cancel"});
180 configureContextsensitiveHelp(help, true);
181
182 build();
183 setCoordinates(null);
184 }
185
186 public boolean isLatLon() {
187 return tabs.getModel().getSelectedIndex() == 0;
188 }
189
190 public void setCoordinates(LatLon ll) {
191 if (ll == null) {
192 ll = LatLon.ZERO;
193 }
194 this.latLonCoordinates = ll;
195 tfLatLon.setText(ll.latToString(CoordinateFormat.getDefaultFormat()) + ' ' + ll.lonToString(CoordinateFormat.getDefaultFormat()));
196 EastNorth en = Main.getProjection().latlon2eastNorth(ll);
197 tfEastNorth.setText(Double.toString(en.east()) + ' ' + Double.toString(en.north()));
198 setOkEnabled(true);
199 }
200
201 public LatLon getCoordinates() {
202 if (isLatLon()) {
203 return latLonCoordinates;
204 } else {
205 if (eastNorthCoordinates == null) return null;
206 return Main.getProjection().eastNorth2latlon(eastNorthCoordinates);
207 }
208 }
209
210 public LatLon getLatLonCoordinates() {
211 return latLonCoordinates;
212 }
213
214 public EastNorth getEastNorthCoordinates() {
215 return eastNorthCoordinates;
216 }
217
218 protected void setErrorFeedback(JosmTextField tf, String message) {
219 tf.setBorder(BorderFactory.createLineBorder(Color.RED, 1));
220 tf.setToolTipText(message);
221 tf.setBackground(BG_COLOR_ERROR);
222 }
223
224 protected void clearErrorFeedback(JosmTextField tf, String message) {
225 tf.setBorder(UIManager.getBorder("TextField.border"));
226 tf.setToolTipText(message);
227 tf.setBackground(UIManager.getColor("TextField.background"));
228 }
229
230 protected void parseLatLonUserInput() {
231 LatLon latLon;
232 try {
233 latLon = parseLatLon(tfLatLon.getText());
234 if (!LatLon.isValidLat(latLon.lat()) || !LatLon.isValidLon(latLon.lon())) {
235 latLon = null;
236 }
237 } catch (IllegalArgumentException e) {
238 latLon = null;
239 }
240 if (latLon == null) {
241 setErrorFeedback(tfLatLon, tr("Please enter a GPS coordinates"));
242 latLonCoordinates = null;
243 setOkEnabled(false);
244 } else {
245 clearErrorFeedback(tfLatLon, tr("Please enter a GPS coordinates"));
246 latLonCoordinates = latLon;
247 setOkEnabled(true);
248 }
249 }
250
251 protected void parseEastNorthUserInput() {
252 EastNorth en;
253 try {
254 en = parseEastNorth(tfEastNorth.getText());
255 } catch (IllegalArgumentException e) {
256 en = null;
257 }
258 if (en == null) {
259 setErrorFeedback(tfEastNorth, tr("Please enter a Easting and Northing"));
260 latLonCoordinates = null;
261 setOkEnabled(false);
262 } else {
263 clearErrorFeedback(tfEastNorth, tr("Please enter a Easting and Northing"));
264 eastNorthCoordinates = en;
265 setOkEnabled(true);
266 }
267 }
268
269 private void setOkEnabled(boolean b) {
270 if (buttons != null && !buttons.isEmpty()) {
271 buttons.get(0).setEnabled(b);
272 }
273 }
274
275 @Override
276 public void setVisible(boolean visible) {
277 final String preferenceKey = getClass().getName() + ".geometry";
278 if (visible) {
279 new WindowGeometry(
280 preferenceKey,
281 WindowGeometry.centerInWindow(getParent(), getSize())
282 ).applySafe(this);
283 } else {
284 new WindowGeometry(this).remember(preferenceKey);
285 }
286 super.setVisible(visible);
287 }
288
289 class LatLonInputVerifier implements DocumentListener {
290 @Override
291 public void changedUpdate(DocumentEvent e) {
292 parseLatLonUserInput();
293 }
294
295 @Override
296 public void insertUpdate(DocumentEvent e) {
297 parseLatLonUserInput();
298 }
299
300 @Override
301 public void removeUpdate(DocumentEvent e) {
302 parseLatLonUserInput();
303 }
304 }
305
306 class EastNorthInputVerifier implements DocumentListener {
307 @Override
308 public void changedUpdate(DocumentEvent e) {
309 parseEastNorthUserInput();
310 }
311
312 @Override
313 public void insertUpdate(DocumentEvent e) {
314 parseEastNorthUserInput();
315 }
316
317 @Override
318 public void removeUpdate(DocumentEvent e) {
319 parseEastNorthUserInput();
320 }
321 }
322
323 static class TextFieldFocusHandler implements FocusListener {
324 @Override
325 public void focusGained(FocusEvent e) {
326 Component c = e.getComponent();
327 if (c instanceof JosmTextField) {
328 JosmTextField tf = (JosmTextField) c;
329 tf.selectAll();
330 }
331 }
332
333 @Override
334 public void focusLost(FocusEvent e) {
335 // Not used
336 }
337 }
338
339 public static LatLon parseLatLon(final String coord) {
340 final LatLonHolder latLon = new LatLonHolder();
341 final Matcher mXml = P_XML.matcher(coord);
342 if (mXml.matches()) {
343 setLatLonObj(latLon,
344 Double.valueOf(mXml.group(1).replace(',', '.')), ZERO, ZERO, "N",
345 Double.valueOf(mXml.group(2).replace(',', '.')), ZERO, ZERO, "E");
346 } else {
347 final Matcher m = P.matcher(coord);
348
349 final StringBuilder sb = new StringBuilder();
350 final List<Object> list = new ArrayList<>();
351
352 while (m.find()) {
353 if (m.group(1) != null) {
354 sb.append('R'); // floating point number
355 list.add(Double.valueOf(m.group(1).replace(',', '.')));
356 } else if (m.group(2) != null) {
357 sb.append('Z'); // integer number
358 list.add(Double.valueOf(m.group(2)));
359 } else if (m.group(3) != null) {
360 sb.append('o'); // degree sign
361 } else if (m.group(4) != null) {
362 sb.append('\''); // seconds sign
363 } else if (m.group(5) != null) {
364 sb.append('"'); // minutes sign
365 } else if (m.group(6) != null) {
366 sb.append(','); // separator
367 } else if (m.group(7) != null) {
368 sb.append('x'); // cardinal direction
369 String c = m.group(7).toUpperCase(Locale.ENGLISH);
370 if ("N".equals(c) || "S".equals(c) || "E".equals(c) || "W".equals(c)) {
371 list.add(c);
372 } else {
373 list.add(c.replace(N_TR, 'N').replace(S_TR, 'S')
374 .replace(E_TR, 'E').replace(W_TR, 'W'));
375 }
376 } else if (m.group(8) != null) {
377 throw new IllegalArgumentException("invalid token: " + m.group(8));
378 }
379 }
380
381 final String pattern = sb.toString();
382
383 final Object[] params = list.toArray();
384
385 if (pattern.matches("Ro?,?Ro?")) {
386 setLatLonObj(latLon,
387 params[0], ZERO, ZERO, "N",
388 params[1], ZERO, ZERO, "E");
389 } else if (pattern.matches("xRo?,?xRo?")) {
390 setLatLonObj(latLon,
391 params[1], ZERO, ZERO, params[0],
392 params[3], ZERO, ZERO, params[2]);
393 } else if (pattern.matches("Ro?x,?Ro?x")) {
394 setLatLonObj(latLon,
395 params[0], ZERO, ZERO, params[1],
396 params[2], ZERO, ZERO, params[3]);
397 } else if (pattern.matches("Zo[RZ]'?,?Zo[RZ]'?|Z[RZ],?Z[RZ]")) {
398 setLatLonObj(latLon,
399 params[0], params[1], ZERO, "N",
400 params[2], params[3], ZERO, "E");
401 } else if (pattern.matches("xZo[RZ]'?,?xZo[RZ]'?|xZo?[RZ],?xZo?[RZ]")) {
402 setLatLonObj(latLon,
403 params[1], params[2], ZERO, params[0],
404 params[4], params[5], ZERO, params[3]);
405 } else if (pattern.matches("Zo[RZ]'?x,?Zo[RZ]'?x|Zo?[RZ]x,?Zo?[RZ]x")) {
406 setLatLonObj(latLon,
407 params[0], params[1], ZERO, params[2],
408 params[3], params[4], ZERO, params[5]);
409 } else if (pattern.matches("ZoZ'[RZ]\"?x,?ZoZ'[RZ]\"?x|ZZ[RZ]x,?ZZ[RZ]x")) {
410 setLatLonObj(latLon,
411 params[0], params[1], params[2], params[3],
412 params[4], params[5], params[6], params[7]);
413 } else if (pattern.matches("xZoZ'[RZ]\"?,?xZoZ'[RZ]\"?|xZZ[RZ],?xZZ[RZ]")) {
414 setLatLonObj(latLon,
415 params[1], params[2], params[3], params[0],
416 params[5], params[6], params[7], params[4]);
417 } else if (pattern.matches("ZZ[RZ],?ZZ[RZ]")) {
418 setLatLonObj(latLon,
419 params[0], params[1], params[2], "N",
420 params[3], params[4], params[5], "E");
421 } else {
422 throw new IllegalArgumentException("invalid format: " + pattern);
423 }
424 }
425
426 return new LatLon(latLon.lat, latLon.lon);
427 }
428
429 public static EastNorth parseEastNorth(String s) {
430 String[] en = s.split("[;, ]+");
431 if (en.length != 2) return null;
432 try {
433 double east = Double.parseDouble(en[0]);
434 double north = Double.parseDouble(en[1]);
435 return new EastNorth(east, north);
436 } catch (NumberFormatException nfe) {
437 return null;
438 }
439 }
440
441 private static class LatLonHolder {
442 private double lat;
443 private double lon;
444 }
445
446 private static void setLatLonObj(final LatLonHolder latLon,
447 final Object coord1deg, final Object coord1min, final Object coord1sec, final Object card1,
448 final Object coord2deg, final Object coord2min, final Object coord2sec, final Object card2) {
449
450 setLatLon(latLon,
451 (Double) coord1deg, (Double) coord1min, (Double) coord1sec, (String) card1,
452 (Double) coord2deg, (Double) coord2min, (Double) coord2sec, (String) card2);
453 }
454
455 private static void setLatLon(final LatLonHolder latLon,
456 final double coord1deg, final double coord1min, final double coord1sec, final String card1,
457 final double coord2deg, final double coord2min, final double coord2sec, final String card2) {
458
459 setLatLon(latLon, coord1deg, coord1min, coord1sec, card1);
460 setLatLon(latLon, coord2deg, coord2min, coord2sec, card2);
461 }
462
463 private static void setLatLon(final LatLonHolder latLon, final double coordDeg, final double coordMin, final double coordSec,
464 final String card) {
465 if (coordDeg < -180 || coordDeg > 180 || coordMin < 0 || coordMin >= 60 || coordSec < 0 || coordSec > 60) {
466 throw new IllegalArgumentException("out of range");
467 }
468
469 double coord = (coordDeg < 0 ? -1 : 1) * (Math.abs(coordDeg) + coordMin / 60 + coordSec / 3600);
470 coord = "N".equals(card) || "E".equals(card) ? coord : -coord;
471 if ("N".equals(card) || "S".equals(card)) {
472 latLon.lat = coord;
473 } else {
474 latLon.lon = coord;
475 }
476 }
477
478 public String getLatLonText() {
479 return tfLatLon.getText();
480 }
481
482 public void setLatLonText(String text) {
483 tfLatLon.setText(text);
484 }
485
486 public String getEastNorthText() {
487 return tfEastNorth.getText();
488 }
489
490 public void setEastNorthText(String text) {
491 tfEastNorth.setText(text);
492 }
493}
Note: See TracBrowser for help on using the repository browser.