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

Last change on this file since 14426 was 14120, checked in by Don-vip, 6 years ago

see #15229 - deprecate all Main methods related to projections. New ProjectionRegistry class

  • Property svn:eol-style set to native
File size: 14.3 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.awt.event.WindowAdapter;
12import java.awt.event.WindowEvent;
13import java.util.Arrays;
14import java.util.Optional;
15
16import javax.swing.BorderFactory;
17import javax.swing.JLabel;
18import javax.swing.JPanel;
19import javax.swing.JSeparator;
20import javax.swing.JTabbedPane;
21import javax.swing.UIManager;
22import javax.swing.event.DocumentEvent;
23import javax.swing.event.DocumentListener;
24
25import org.openstreetmap.josm.data.coor.EastNorth;
26import org.openstreetmap.josm.data.coor.LatLon;
27import org.openstreetmap.josm.data.coor.conversion.CoordinateFormatManager;
28import org.openstreetmap.josm.data.coor.conversion.LatLonParser;
29import org.openstreetmap.josm.data.projection.ProjectionRegistry;
30import org.openstreetmap.josm.gui.ExtendedDialog;
31import org.openstreetmap.josm.gui.util.WindowGeometry;
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.Logging;
36import org.openstreetmap.josm.tools.Utils;
37
38/**
39 * A dialog that lets the user add a node at the coordinates he enters.
40 */
41public class LatLonDialog extends ExtendedDialog {
42 private static final Color BG_COLOR_ERROR = new Color(255, 224, 224);
43
44 /**
45 * The tabs that define the coordinate mode.
46 */
47 public JTabbedPane tabs;
48 private JosmTextField tfLatLon, tfEastNorth;
49 private LatLon latLonCoordinates;
50 private EastNorth eastNorthCoordinates;
51
52 protected JPanel buildLatLon() {
53 JPanel pnl = new JPanel(new GridBagLayout());
54 pnl.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
55
56 pnl.add(new JLabel(tr("Coordinates:")), GBC.std().insets(0, 10, 5, 0));
57 tfLatLon = new JosmTextField(24);
58 pnl.add(tfLatLon, GBC.eol().insets(0, 10, 0, 0).fill(GBC.HORIZONTAL).weight(1.0, 0.0));
59
60 pnl.add(new JSeparator(), GBC.eol().fill(GBC.HORIZONTAL).insets(0, 5, 0, 5));
61
62 pnl.add(new HtmlPanel(
63 Utils.join("<br/>", Arrays.asList(
64 tr("Enter the coordinates for the new node."),
65 tr("You can separate longitude and latitude with space, comma or semicolon."),
66 tr("Use positive numbers or N, E characters to indicate North or East cardinal direction."),
67 tr("For South and West cardinal directions you can use either negative numbers or S, W characters."),
68 tr("Coordinate value can be in one of three formats:")
69 )) +
70 Utils.joinAsHtmlUnorderedList(Arrays.asList(
71 tr("<i>degrees</i><tt>&deg;</tt>"),
72 tr("<i>degrees</i><tt>&deg;</tt> <i>minutes</i><tt>&#39;</tt>"),
73 tr("<i>degrees</i><tt>&deg;</tt> <i>minutes</i><tt>&#39;</tt> <i>seconds</i><tt>&quot</tt>")
74 )) +
75 Utils.join("<br/><br/>", Arrays.asList(
76 tr("Symbols <tt>&deg;</tt>, <tt>&#39;</tt>, <tt>&prime;</tt>, <tt>&quot;</tt>, <tt>&Prime;</tt> are optional."),
77 tr("You can also use the syntax <tt>lat=\"...\" lon=\"...\"</tt> or <tt>lat=''...'' lon=''...''</tt>."),
78 tr("Some examples:")
79 )) +
80 "<table><tr><td>" +
81 Utils.joinAsHtmlUnorderedList(Arrays.asList(
82 "49.29918 19.24788",
83 "49.29918, 19.24788",
84 "49.29918&deg; 19.24788&deg;",
85 "N 49.29918 E 19.24788",
86 "W 49&deg;29.918&#39; S 19&deg;24.788&#39;",
87 "N 49&deg;29&#39;04&quot; E 19&deg;24&#39;43&quot;",
88 "49.29918 N, 19.24788 E",
89 "49&deg;29&#39;21&quot; N 19&deg;24&#39;38&quot; E",
90 "49 29 51, 19 24 18",
91 "49 29, 19 24"
92 )) +
93 "</td><td>" +
94 Utils.joinAsHtmlUnorderedList(Arrays.asList(
95 "E 49 29, N 19 24",
96 "49&deg; 29; 19&deg; 24",
97 "N 49&deg; 29, W 19&deg; 24",
98 "49&deg; 29.5 S, 19&deg; 24.6 E",
99 "N 49 29.918 E 19 15.88",
100 "49 29.4 19 24.5",
101 "-49 29.4 N -19 24.5 W",
102 "48 deg 42&#39; 52.13\" N, 21 deg 11&#39; 47.60\" E",
103 "lat=\"49.29918\" lon=\"19.24788\"",
104 "lat='49.29918' lon='19.24788'"
105 )) +
106 "</td></tr></table>"),
107 GBC.eol().fill().weight(1.0, 1.0));
108
109 // parse and verify input on the fly
110 //
111 LatLonInputVerifier inputVerifier = new LatLonInputVerifier();
112 tfLatLon.getDocument().addDocumentListener(inputVerifier);
113
114 // select the text in the field on focus
115 //
116 TextFieldFocusHandler focusHandler = new TextFieldFocusHandler();
117 tfLatLon.addFocusListener(focusHandler);
118 return pnl;
119 }
120
121 private JPanel buildEastNorth() {
122 JPanel pnl = new JPanel(new GridBagLayout());
123 pnl.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
124
125 pnl.add(new JLabel(tr("Projected coordinates:")), GBC.std().insets(0, 10, 5, 0));
126 tfEastNorth = new JosmTextField(24);
127
128 pnl.add(tfEastNorth, GBC.eol().insets(0, 10, 0, 0).fill(GBC.HORIZONTAL).weight(1.0, 0.0));
129
130 pnl.add(new JSeparator(), GBC.eol().fill(GBC.HORIZONTAL).insets(0, 5, 0, 5));
131
132 pnl.add(new HtmlPanel(
133 tr("Enter easting and northing (x and y) separated by space, comma or semicolon.")),
134 GBC.eol().fill(GBC.HORIZONTAL));
135
136 pnl.add(GBC.glue(1, 1), GBC.eol().fill().weight(1.0, 1.0));
137
138 EastNorthInputVerifier inputVerifier = new EastNorthInputVerifier();
139 tfEastNorth.getDocument().addDocumentListener(inputVerifier);
140
141 TextFieldFocusHandler focusHandler = new TextFieldFocusHandler();
142 tfEastNorth.addFocusListener(focusHandler);
143
144 return pnl;
145 }
146
147 protected void build() {
148 tabs = new JTabbedPane();
149 tabs.addTab(tr("Lat/Lon"), buildLatLon());
150 tabs.addTab(tr("East/North"), buildEastNorth());
151 tabs.getModel().addChangeListener(e -> {
152 switch (tabs.getModel().getSelectedIndex()) {
153 case 0: parseLatLonUserInput(); break;
154 case 1: parseEastNorthUserInput(); break;
155 default: throw new AssertionError();
156 }
157 });
158 setContent(tabs, false);
159 addWindowListener(new WindowAdapter() {
160 @Override
161 public void windowOpened(WindowEvent e) {
162 tfLatLon.requestFocusInWindow();
163 }
164 });
165 }
166
167 /**
168 * Creates a new {@link LatLonDialog}
169 * @param parent The parent
170 * @param title The title of this dialog
171 * @param help The help text to use
172 */
173 public LatLonDialog(Component parent, String title, String help) {
174 super(parent, title, tr("Ok"), tr("Cancel"));
175 setButtonIcons("ok", "cancel");
176 configureContextsensitiveHelp(help, true);
177
178 build();
179 setCoordinates(null);
180 }
181
182 /**
183 * Check if lat/lon mode is active
184 * @return <code>true</code> iff the user selects lat/lon coordinates
185 */
186 public boolean isLatLon() {
187 return tabs.getModel().getSelectedIndex() == 0;
188 }
189
190 /**
191 * Sets the coordinate fields to the given coordinates
192 * @param ll The lat/lon coordinates
193 */
194 public void setCoordinates(LatLon ll) {
195 LatLon llc = Optional.ofNullable(ll).orElse(LatLon.ZERO);
196 tfLatLon.setText(CoordinateFormatManager.getDefaultFormat().latToString(llc) + ' ' +
197 CoordinateFormatManager.getDefaultFormat().lonToString(llc));
198 EastNorth en = ProjectionRegistry.getProjection().latlon2eastNorth(llc);
199 tfEastNorth.setText(Double.toString(en.east()) + ' ' + Double.toString(en.north()));
200 // Both latLonCoordinates and eastNorthCoordinates may have been reset to null if ll is out of the world
201 latLonCoordinates = llc;
202 eastNorthCoordinates = en;
203 setOkEnabled(true);
204 }
205
206 /**
207 * Gets the coordinates that are entered by the user.
208 * @return The coordinates
209 */
210 public LatLon getCoordinates() {
211 if (isLatLon()) {
212 return latLonCoordinates;
213 } else {
214 if (eastNorthCoordinates == null) return null;
215 return ProjectionRegistry.getProjection().eastNorth2latlon(eastNorthCoordinates);
216 }
217 }
218
219 /**
220 * Gets the coordinates that are entered in the lat/lon field
221 * @return The lat/lon coordinates
222 */
223 public LatLon getLatLonCoordinates() {
224 return latLonCoordinates;
225 }
226
227 /**
228 * Gets the coordinates that are entered in the east/north field
229 * @return The east/north coordinates
230 */
231 public EastNorth getEastNorthCoordinates() {
232 return eastNorthCoordinates;
233 }
234
235 protected void setErrorFeedback(JosmTextField tf, String message) {
236 tf.setBorder(BorderFactory.createLineBorder(Color.RED, 1));
237 tf.setToolTipText(message);
238 tf.setBackground(BG_COLOR_ERROR);
239 }
240
241 protected void clearErrorFeedback(JosmTextField tf, String message) {
242 tf.setBorder(UIManager.getBorder("TextField.border"));
243 tf.setToolTipText(message);
244 tf.setBackground(UIManager.getColor("TextField.background"));
245 }
246
247 protected void parseLatLonUserInput() {
248 LatLon latLon;
249 try {
250 latLon = LatLonParser.parse(tfLatLon.getText());
251 if (!LatLon.isValidLat(latLon.lat()) || !LatLon.isValidLon(latLon.lon())) {
252 latLon = null;
253 }
254 } catch (IllegalArgumentException e) {
255 Logging.trace(e);
256 latLon = null;
257 }
258 if (latLon == null) {
259 setErrorFeedback(tfLatLon, tr("Please enter a GPS coordinates"));
260 latLonCoordinates = null;
261 setOkEnabled(false);
262 } else {
263 clearErrorFeedback(tfLatLon, tr("Please enter a GPS coordinates"));
264 latLonCoordinates = latLon;
265 setOkEnabled(true);
266 }
267 }
268
269 protected void parseEastNorthUserInput() {
270 EastNorth en;
271 try {
272 en = parseEastNorth(tfEastNorth.getText());
273 } catch (IllegalArgumentException e) {
274 Logging.trace(e);
275 en = null;
276 }
277 if (en == null) {
278 setErrorFeedback(tfEastNorth, tr("Please enter a Easting and Northing"));
279 latLonCoordinates = null;
280 setOkEnabled(false);
281 } else {
282 clearErrorFeedback(tfEastNorth, tr("Please enter a Easting and Northing"));
283 eastNorthCoordinates = en;
284 setOkEnabled(true);
285 }
286 }
287
288 private void setOkEnabled(boolean b) {
289 if (buttons != null && !buttons.isEmpty()) {
290 buttons.get(0).setEnabled(b);
291 }
292 }
293
294 @Override
295 public void setVisible(boolean visible) {
296 final String preferenceKey = getClass().getName() + ".geometry";
297 if (visible) {
298 new WindowGeometry(
299 preferenceKey,
300 WindowGeometry.centerInWindow(getParent(), getSize())
301 ).applySafe(this);
302 } else {
303 new WindowGeometry(this).remember(preferenceKey);
304 }
305 super.setVisible(visible);
306 }
307
308 class LatLonInputVerifier implements DocumentListener {
309 @Override
310 public void changedUpdate(DocumentEvent e) {
311 parseLatLonUserInput();
312 }
313
314 @Override
315 public void insertUpdate(DocumentEvent e) {
316 parseLatLonUserInput();
317 }
318
319 @Override
320 public void removeUpdate(DocumentEvent e) {
321 parseLatLonUserInput();
322 }
323 }
324
325 class EastNorthInputVerifier implements DocumentListener {
326 @Override
327 public void changedUpdate(DocumentEvent e) {
328 parseEastNorthUserInput();
329 }
330
331 @Override
332 public void insertUpdate(DocumentEvent e) {
333 parseEastNorthUserInput();
334 }
335
336 @Override
337 public void removeUpdate(DocumentEvent e) {
338 parseEastNorthUserInput();
339 }
340 }
341
342 static class TextFieldFocusHandler implements FocusListener {
343 @Override
344 public void focusGained(FocusEvent e) {
345 Component c = e.getComponent();
346 if (c instanceof JosmTextField) {
347 JosmTextField tf = (JosmTextField) c;
348 tf.selectAll();
349 }
350 }
351
352 @Override
353 public void focusLost(FocusEvent e) {
354 // Not used
355 }
356 }
357
358 /**
359 * Parses a east/north coordinate string
360 * @param s The coordinates. Dot has to be used as decimal separator, as comma can be used to delimit values
361 * @return The east/north coordinates or <code>null</code> on error.
362 */
363 public static EastNorth parseEastNorth(String s) {
364 String[] en = s.split("[;, ]+");
365 if (en.length != 2) return null;
366 try {
367 double east = Double.parseDouble(en[0]);
368 double north = Double.parseDouble(en[1]);
369 return new EastNorth(east, north);
370 } catch (NumberFormatException nfe) {
371 return null;
372 }
373 }
374
375 /**
376 * Gets the text entered in the lat/lon text field.
377 * @return The text the user entered
378 */
379 public String getLatLonText() {
380 return tfLatLon.getText();
381 }
382
383 /**
384 * Set the text in the lat/lon text field.
385 * @param text The new text
386 */
387 public void setLatLonText(String text) {
388 tfLatLon.setText(text);
389 }
390
391 /**
392 * Gets the text entered in the east/north text field.
393 * @return The text the user entered
394 */
395 public String getEastNorthText() {
396 return tfEastNorth.getText();
397 }
398
399 /**
400 * Set the text in the east/north text field.
401 * @param text The new text
402 */
403 public void setEastNorthText(String text) {
404 tfEastNorth.setText(text);
405 }
406}
Note: See TracBrowser for help on using the repository browser.