source: josm/trunk/src/org/openstreetmap/josm/gui/history/CoordinateInfoViewer.java@ 9450

Last change on this file since 9450 was 9441, checked in by simon04, 8 years ago

fix #12364 - History dialog: show old and new coordinate on map

  • Property svn:eol-style set to native
File size: 14.4 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.history;
3import static org.openstreetmap.josm.tools.I18n.tr;
4
5import java.awt.Color;
6import java.awt.GridBagConstraints;
7import java.awt.GridBagLayout;
8import java.awt.Insets;
9import java.util.Observable;
10import java.util.Observer;
11
12import javax.swing.BorderFactory;
13import javax.swing.JLabel;
14import javax.swing.JPanel;
15
16import org.openstreetmap.gui.jmapviewer.JMapViewer;
17import org.openstreetmap.gui.jmapviewer.MapMarkerDot;
18import org.openstreetmap.josm.data.coor.CoordinateFormat;
19import org.openstreetmap.josm.data.coor.LatLon;
20import org.openstreetmap.josm.data.osm.history.HistoryNode;
21import org.openstreetmap.josm.data.osm.history.HistoryOsmPrimitive;
22import org.openstreetmap.josm.gui.NavigatableComponent;
23import org.openstreetmap.josm.gui.util.GuiHelper;
24import org.openstreetmap.josm.tools.CheckParameterUtil;
25import org.openstreetmap.josm.tools.Pair;
26
27/**
28 * An UI widget for displaying differences in the coordinates of two
29 * {@link HistoryNode}s.
30 *
31 */
32public class CoordinateInfoViewer extends JPanel {
33
34 /** the model */
35 private transient HistoryBrowserModel model;
36 /** the common info panel for the history node in role REFERENCE_POINT_IN_TIME */
37 private VersionInfoPanel referenceInfoPanel;
38 /** the common info panel for the history node in role CURRENT_POINT_IN_TIME */
39 private VersionInfoPanel currentInfoPanel;
40 /** the info panel for coordinates for the node in role REFERENCE_POINT_IN_TIME */
41 private LatLonViewer referenceLatLonViewer;
42 /** the info panel for coordinates for the node in role CURRENT_POINT_IN_TIME */
43 private LatLonViewer currentLatLonViewer;
44 /** the info panel for distance between the two coordinates */
45 private DistanceViewer distanceViewer;
46 /** the map panel showing the old+new coordinate */
47 private MapViewer mapViewer;
48
49 protected void build() {
50 setLayout(new GridBagLayout());
51 GridBagConstraints gc = new GridBagConstraints();
52
53 // ---------------------------
54 gc.gridx = 0;
55 gc.gridy = 0;
56 gc.gridwidth = 1;
57 gc.gridheight = 1;
58 gc.weightx = 0.5;
59 gc.weighty = 0.0;
60 gc.insets = new Insets(5, 5, 5, 0);
61 gc.fill = GridBagConstraints.HORIZONTAL;
62 gc.anchor = GridBagConstraints.FIRST_LINE_START;
63 referenceInfoPanel = new VersionInfoPanel(model, PointInTimeType.REFERENCE_POINT_IN_TIME);
64 add(referenceInfoPanel, gc);
65
66 gc.gridx = 1;
67 gc.gridy = 0;
68 gc.fill = GridBagConstraints.HORIZONTAL;
69 gc.weightx = 0.5;
70 gc.weighty = 0.0;
71 gc.anchor = GridBagConstraints.FIRST_LINE_START;
72 currentInfoPanel = new VersionInfoPanel(model, PointInTimeType.CURRENT_POINT_IN_TIME);
73 add(currentInfoPanel, gc);
74
75 // ---------------------------
76 // the two coordinate panels
77 gc.gridx = 0;
78 gc.gridy = 1;
79 gc.weightx = 0.5;
80 gc.weighty = 0.0;
81 gc.fill = GridBagConstraints.HORIZONTAL;
82 gc.anchor = GridBagConstraints.NORTHWEST;
83 add(referenceLatLonViewer = new LatLonViewer(model, PointInTimeType.REFERENCE_POINT_IN_TIME), gc);
84
85 gc.gridx = 1;
86 gc.gridy = 1;
87 gc.weightx = 0.5;
88 gc.weighty = 0.0;
89 gc.fill = GridBagConstraints.HORIZONTAL;
90 gc.anchor = GridBagConstraints.NORTHWEST;
91 add(currentLatLonViewer = new LatLonViewer(model, PointInTimeType.CURRENT_POINT_IN_TIME), gc);
92
93 // --------------------
94 // the distance panel
95 gc.gridx = 0;
96 gc.gridy = 2;
97 gc.gridwidth = 2;
98 gc.fill = GridBagConstraints.HORIZONTAL;
99 gc.weightx = 1.0;
100 gc.weighty = 0.0;
101 add(distanceViewer = new DistanceViewer(model), gc);
102
103 // the map panel
104 gc.gridx = 0;
105 gc.gridy = 3;
106 gc.gridwidth = 2;
107 gc.fill = GridBagConstraints.BOTH;
108 gc.weightx = 1.0;
109 gc.weighty = 1.0;
110 gc.insets = new Insets(5, 5, 5, 5);
111 add(mapViewer = new MapViewer(model), gc);
112 mapViewer.setZoomContolsVisible(false);
113 }
114
115 /**
116 * Constructs a new {@code CoordinateInfoViewer}.
117 * @param model the model. Must not be null.
118 * @throws IllegalArgumentException if model is null
119 */
120 public CoordinateInfoViewer(HistoryBrowserModel model) {
121 CheckParameterUtil.ensureParameterNotNull(model, "model");
122 setModel(model);
123 build();
124 registerAsObserver(model);
125 }
126
127 protected void unregisterAsObserver(HistoryBrowserModel model) {
128 if (currentInfoPanel != null) {
129 model.deleteObserver(currentInfoPanel);
130 }
131 if (referenceInfoPanel != null) {
132 model.deleteObserver(referenceInfoPanel);
133 }
134 if (currentLatLonViewer != null) {
135 model.deleteObserver(currentLatLonViewer);
136 }
137 if (referenceLatLonViewer != null) {
138 model.deleteObserver(referenceLatLonViewer);
139 }
140 if (distanceViewer != null) {
141 model.deleteObserver(distanceViewer);
142 }
143 if (mapViewer != null) {
144 model.deleteObserver(mapViewer);
145 }
146 }
147
148 protected void registerAsObserver(HistoryBrowserModel model) {
149 if (currentInfoPanel != null) {
150 model.addObserver(currentInfoPanel);
151 }
152 if (referenceInfoPanel != null) {
153 model.addObserver(referenceInfoPanel);
154 }
155 if (currentLatLonViewer != null) {
156 model.addObserver(currentLatLonViewer);
157 }
158 if (referenceLatLonViewer != null) {
159 model.addObserver(referenceLatLonViewer);
160 }
161 if (distanceViewer != null) {
162 model.addObserver(distanceViewer);
163 }
164 if (mapViewer != null) {
165 model.addObserver(mapViewer);
166 }
167 }
168
169 /**
170 * Sets the model for this viewer
171 *
172 * @param model the model.
173 */
174 public void setModel(HistoryBrowserModel model) {
175 if (this.model != null) {
176 unregisterAsObserver(model);
177 }
178 this.model = model;
179 if (this.model != null) {
180 registerAsObserver(model);
181 }
182 }
183
184 private static class Updater {
185 private final transient HistoryBrowserModel model;
186 private final PointInTimeType role;
187
188 public Updater(HistoryBrowserModel model, PointInTimeType role) {
189 this.model = model;
190 this.role = role;
191 }
192
193 protected HistoryOsmPrimitive getPrimitive() {
194 if (model == null || role == null)
195 return null;
196 return model.getPointInTime(role);
197 }
198
199 protected HistoryOsmPrimitive getOppositePrimitive() {
200 if (model == null || role == null)
201 return null;
202 return model.getPointInTime(role.opposite());
203 }
204
205 protected final Pair<LatLon, LatLon> getCoordinates() {
206 HistoryOsmPrimitive p = getPrimitive();
207 HistoryOsmPrimitive opposite = getOppositePrimitive();
208 if (!(p instanceof HistoryNode)) return null;
209 if (!(opposite instanceof HistoryNode)) return null;
210 HistoryNode node = (HistoryNode) p;
211 HistoryNode oppositeNode = (HistoryNode) opposite;
212
213 return Pair.create(node.getCoords(), oppositeNode.getCoords());
214 }
215
216 }
217
218 /**
219 * A UI widgets which displays the Lan/Lon-coordinates of a
220 * {@link HistoryNode}.
221 *
222 */
223 private static class LatLonViewer extends JPanel implements Observer {
224
225 private JLabel lblLat;
226 private JLabel lblLon;
227 private final Updater updater;
228 private final Color modifiedColor;
229
230 protected void build() {
231 setLayout(new GridBagLayout());
232 setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY));
233 GridBagConstraints gc = new GridBagConstraints();
234
235 // --------
236 gc.gridx = 0;
237 gc.gridy = 0;
238 gc.fill = GridBagConstraints.NONE;
239 gc.weightx = 0.0;
240 gc.insets = new Insets(5, 5, 5, 5);
241 gc.anchor = GridBagConstraints.NORTHWEST;
242 add(new JLabel(tr("Latitude: ")), gc);
243
244 // --------
245 gc.gridx = 1;
246 gc.gridy = 0;
247 gc.fill = GridBagConstraints.HORIZONTAL;
248 gc.weightx = 1.0;
249 add(lblLat = new JLabel(), gc);
250 GuiHelper.setBackgroundReadable(lblLat, Color.WHITE);
251 lblLat.setOpaque(true);
252 lblLat.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
253
254 // --------
255 gc.gridx = 0;
256 gc.gridy = 1;
257 gc.fill = GridBagConstraints.NONE;
258 gc.weightx = 0.0;
259 gc.anchor = GridBagConstraints.NORTHWEST;
260 add(new JLabel(tr("Longitude: ")), gc);
261
262 // --------
263 gc.gridx = 1;
264 gc.gridy = 1;
265 gc.fill = GridBagConstraints.HORIZONTAL;
266 gc.weightx = 1.0;
267 add(lblLon = new JLabel(), gc);
268 GuiHelper.setBackgroundReadable(lblLon, Color.WHITE);
269 lblLon.setOpaque(true);
270 lblLon.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
271 }
272
273 /**
274 *
275 * @param model a model
276 * @param role the role for this viewer.
277 */
278 LatLonViewer(HistoryBrowserModel model, PointInTimeType role) {
279 this.updater = new Updater(model, role);
280 this.modifiedColor = PointInTimeType.CURRENT_POINT_IN_TIME.equals(role)
281 ? TwoColumnDiff.Item.DiffItemType.INSERTED.getColor()
282 : TwoColumnDiff.Item.DiffItemType.DELETED.getColor();
283 build();
284 }
285
286 protected void refresh() {
287 final Pair<LatLon, LatLon> coordinates = updater.getCoordinates();
288 if (coordinates == null) return;
289 final LatLon coord = coordinates.a;
290 final LatLon oppositeCoord = coordinates.b;
291
292 // display the coordinates
293 lblLat.setText(coord != null ? coord.latToString(CoordinateFormat.DECIMAL_DEGREES) : tr("(none)"));
294 lblLon.setText(coord != null ? coord.lonToString(CoordinateFormat.DECIMAL_DEGREES) : tr("(none)"));
295
296 // update background color to reflect differences in the coordinates
297 if (coord == oppositeCoord ||
298 (coord != null && oppositeCoord != null && coord.lat() == oppositeCoord.lat())) {
299 GuiHelper.setBackgroundReadable(lblLat, Color.WHITE);
300 } else {
301 GuiHelper.setBackgroundReadable(lblLat, modifiedColor);
302 }
303 if (coord == oppositeCoord ||
304 (coord != null && oppositeCoord != null && coord.lon() == oppositeCoord.lon())) {
305 GuiHelper.setBackgroundReadable(lblLon, Color.WHITE);
306 } else {
307 GuiHelper.setBackgroundReadable(lblLon, modifiedColor);
308 }
309 }
310
311 @Override
312 public void update(Observable o, Object arg) {
313 refresh();
314 }
315 }
316
317 private static class MapViewer extends JMapViewer implements Observer {
318
319 private final Updater updater;
320
321 public MapViewer(HistoryBrowserModel model) {
322 this.updater = new Updater(model, PointInTimeType.REFERENCE_POINT_IN_TIME);
323 setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY));
324 }
325
326 @Override
327 public void update(Observable o, Object arg) {
328 final Pair<LatLon, LatLon> coordinates = updater.getCoordinates();
329 if (coordinates == null) {
330 return;
331 }
332
333 final MapMarkerDot oldMarker = new MapMarkerDot(coordinates.a.lat(), coordinates.a.lon());
334 final MapMarkerDot newMarker = new MapMarkerDot(coordinates.b.lat(), coordinates.b.lon());
335 oldMarker.setBackColor(TwoColumnDiff.Item.DiffItemType.DELETED.getColor());
336 newMarker.setBackColor(TwoColumnDiff.Item.DiffItemType.INSERTED.getColor());
337
338 removeAllMapMarkers();
339 addMapMarker(oldMarker);
340 addMapMarker(newMarker);
341 setDisplayToFitMapMarkers();
342 }
343 }
344
345 private static class DistanceViewer extends JPanel implements Observer {
346
347 private JLabel lblDistance;
348 private final Updater updater;
349
350 DistanceViewer(HistoryBrowserModel model) {
351 this.updater = new Updater(model, PointInTimeType.REFERENCE_POINT_IN_TIME);
352 build();
353 }
354
355 protected void build() {
356 setLayout(new GridBagLayout());
357 setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY));
358 GridBagConstraints gc = new GridBagConstraints();
359
360 // --------
361 gc.gridx = 0;
362 gc.gridy = 0;
363 gc.fill = GridBagConstraints.NONE;
364 gc.weightx = 0.0;
365 gc.insets = new Insets(5, 5, 5, 5);
366 gc.anchor = GridBagConstraints.NORTHWEST;
367 add(new JLabel(tr("Distance: ")), gc);
368
369 // --------
370 gc.gridx = 1;
371 gc.gridy = 0;
372 gc.fill = GridBagConstraints.HORIZONTAL;
373 gc.weightx = 1.0;
374 add(lblDistance = new JLabel(), gc);
375 GuiHelper.setBackgroundReadable(lblDistance, Color.WHITE);
376 lblDistance.setOpaque(true);
377 lblDistance.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
378 }
379
380 protected void refresh() {
381 final Pair<LatLon, LatLon> coordinates = updater.getCoordinates();
382 if (coordinates == null) return;
383 final LatLon coord = coordinates.a;
384 final LatLon oppositeCoord = coordinates.b;
385
386 // update distance
387 //
388 if (coord != null && oppositeCoord != null) {
389 double distance = coord.greatCircleDistance(oppositeCoord);
390 GuiHelper.setBackgroundReadable(lblDistance, distance > 0
391 ? TwoColumnDiff.Item.DiffItemType.CHANGED.getColor()
392 : Color.WHITE);
393 lblDistance.setText(NavigatableComponent.getDistText(distance));
394 } else {
395 GuiHelper.setBackgroundReadable(lblDistance, coord != oppositeCoord
396 ? TwoColumnDiff.Item.DiffItemType.CHANGED.getColor()
397 : Color.WHITE);
398 lblDistance.setText(tr("(none)"));
399 }
400 }
401
402 @Override
403 public void update(Observable o, Object arg) {
404 refresh();
405 }
406 }
407}
Note: See TracBrowser for help on using the repository browser.