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

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

fix sonar squid:S2039 - Member variable visibility should be specified

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