001    // License: GPL. For details, see LICENSE file.
002    package org.openstreetmap.josm.gui.history;
003    import static org.openstreetmap.josm.tools.I18n.tr;
004    
005    import java.awt.Color;
006    import java.awt.GridBagConstraints;
007    import java.awt.GridBagLayout;
008    import java.awt.Insets;
009    import java.util.Observable;
010    import java.util.Observer;
011    
012    import javax.swing.BorderFactory;
013    import javax.swing.JLabel;
014    import javax.swing.JPanel;
015    
016    import org.openstreetmap.josm.data.coor.CoordinateFormat;
017    import org.openstreetmap.josm.data.coor.LatLon;
018    import org.openstreetmap.josm.data.osm.history.HistoryNode;
019    import org.openstreetmap.josm.data.osm.history.HistoryOsmPrimitive;
020    import org.openstreetmap.josm.gui.NavigatableComponent;
021    import org.openstreetmap.josm.tools.CheckParameterUtil;
022    
023    /**
024     * An UI widget for displaying differences in the coordinates of two
025     * {@link HistoryNode}s.
026     *
027     */
028    public class CoordinateInfoViewer extends JPanel {
029    
030        /** background color used when the coordinates are different */
031        public final static Color BGCOLOR_DIFFERENCE = new Color(255,197,197);
032    
033        /** the model */
034        private HistoryBrowserModel model;
035        /** the common info panel for the history node in role REFERENCE_POINT_IN_TIME */
036        private VersionInfoPanel referenceInfoPanel;
037        /** the common info panel for the history node in role CURRENT_POINT_IN_TIME */
038        private VersionInfoPanel currentInfoPanel;
039        /** the info panel for coordinates for the node in role REFERENCE_POINT_IN_TIME */
040        private LatLonViewer referenceLatLonViewer;
041        /** the info panel for coordinates for the node in role CURRENT_POINT_IN_TIME */
042        private LatLonViewer currentLatLonViewer;
043        /** the info panel for distance between the two coordinates */
044        private DistanceViewer distanceViewer;
045    
046        protected void build() {
047            setLayout(new GridBagLayout());
048            GridBagConstraints gc = new GridBagConstraints();
049    
050            // ---------------------------
051            gc.gridx = 0;
052            gc.gridy = 0;
053            gc.gridwidth = 1;
054            gc.gridheight = 1;
055            gc.weightx = 0.5;
056            gc.weighty = 0.0;
057            gc.insets = new Insets(5,5,5,0);
058            gc.fill = GridBagConstraints.HORIZONTAL;
059            gc.anchor = GridBagConstraints.FIRST_LINE_START;
060            referenceInfoPanel = new VersionInfoPanel(model, PointInTimeType.REFERENCE_POINT_IN_TIME);
061            add(referenceInfoPanel,gc);
062    
063            gc.gridx = 1;
064            gc.gridy = 0;
065            gc.fill = GridBagConstraints.HORIZONTAL;
066            gc.weightx = 0.5;
067            gc.weighty = 0.0;
068            gc.anchor = GridBagConstraints.FIRST_LINE_START;
069            currentInfoPanel = new VersionInfoPanel(model, PointInTimeType.CURRENT_POINT_IN_TIME);
070            add(currentInfoPanel,gc);
071    
072            // ---------------------------
073            // the two coordinate panels
074            gc.gridx = 0;
075            gc.gridy = 1;
076            gc.weightx = 0.5;
077            gc.weighty = 1.0;
078            gc.fill = GridBagConstraints.BOTH;
079            gc.anchor = GridBagConstraints.NORTHWEST;
080            add(referenceLatLonViewer = new LatLonViewer(model, PointInTimeType.REFERENCE_POINT_IN_TIME), gc);
081    
082            gc.gridx = 1;
083            gc.gridy = 1;
084            gc.weightx = 0.5;
085            gc.weighty = 1.0;
086            gc.fill = GridBagConstraints.BOTH;
087            gc.anchor = GridBagConstraints.NORTHWEST;
088            add(currentLatLonViewer = new LatLonViewer(model, PointInTimeType.CURRENT_POINT_IN_TIME), gc);
089    
090            // --------------------
091            // the distance panel
092            gc.gridx = 0;
093            gc.gridy = 2;
094            gc.gridwidth = 2;
095            gc.fill = GridBagConstraints.HORIZONTAL;
096            gc.weightx = 1.0;
097            gc.weighty = 0.0;
098            add(distanceViewer = new DistanceViewer(model), gc);
099        }
100    
101        /**
102         *
103         * @param model the model. Must not be null.
104         * @throws IllegalArgumentException thrown if model is null
105         */
106        public CoordinateInfoViewer(HistoryBrowserModel model) throws IllegalArgumentException{
107            CheckParameterUtil.ensureParameterNotNull(model, "model");
108            setModel(model);
109            build();
110            registerAsObserver(model);
111        }
112    
113        protected void unregisterAsObserver(HistoryBrowserModel model) {
114            if (currentInfoPanel != null) {
115                model.deleteObserver(currentInfoPanel);
116            }
117            if (referenceInfoPanel != null) {
118                model.deleteObserver(referenceInfoPanel);
119            }
120            if (currentLatLonViewer != null) {
121                model.deleteObserver(currentLatLonViewer);
122            }
123            if (referenceLatLonViewer != null) {
124                model.deleteObserver(referenceLatLonViewer);
125            }
126            if (distanceViewer != null) {
127                model.deleteObserver(distanceViewer);
128            }
129        }
130    
131        protected void registerAsObserver(HistoryBrowserModel model) {
132            if (currentInfoPanel != null) {
133                model.addObserver(currentInfoPanel);
134            }
135            if (referenceInfoPanel != null) {
136                model.addObserver(referenceInfoPanel);
137            }
138            if (currentLatLonViewer != null) {
139                model.addObserver(currentLatLonViewer);
140            }
141            if (referenceLatLonViewer != null) {
142                model.addObserver(referenceLatLonViewer);
143            }
144            if (distanceViewer != null) {
145                model.addObserver(distanceViewer);
146            }
147        }
148    
149        /**
150         * Sets the model for this viewer
151         *
152         * @param model the model.
153         */
154        public void setModel(HistoryBrowserModel model) {
155            if (this.model != null) {
156                unregisterAsObserver(model);
157            }
158            this.model = model;
159            if (this.model != null) {
160                registerAsObserver(model);
161            }
162        }
163    
164        /**
165         * A UI widgets which displays the Lan/Lon-coordinates of a
166         * {@link HistoryNode}.
167         *
168         */
169        private static class LatLonViewer extends JPanel implements Observer{
170    
171            private JLabel lblLat;
172            private JLabel lblLon;
173            private HistoryBrowserModel model;
174            private PointInTimeType role;
175    
176            protected HistoryOsmPrimitive getPrimitive() {
177                if (model == null || role == null)
178                    return null;
179                return model.getPointInTime(role);
180            }
181    
182            protected HistoryOsmPrimitive getOppositePrimitive() {
183                if (model == null || role == null)
184                    return null;
185                return model.getPointInTime(role.opposite());
186            }
187    
188            protected void build() {
189                setLayout(new GridBagLayout());
190                setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY));
191                GridBagConstraints gc = new GridBagConstraints();
192    
193                // --------
194                gc.gridx = 0;
195                gc.gridy = 0;
196                gc.fill = GridBagConstraints.NONE;
197                gc.weightx = 0.0;
198                gc.insets = new Insets(5,5,5,5);
199                gc.anchor = GridBagConstraints.NORTHWEST;
200                add(new JLabel(tr("Latitude: ")), gc);
201    
202                // --------
203                gc.gridx = 1;
204                gc.gridy = 0;
205                gc.fill = GridBagConstraints.HORIZONTAL;
206                gc.weightx = 1.0;
207                add(lblLat = new JLabel(), gc);
208                lblLat.setBackground(Color.WHITE);
209                lblLat.setOpaque(true);
210                lblLat.setBorder(BorderFactory.createEmptyBorder(2,2,2,2));
211    
212                // --------
213                gc.gridx = 0;
214                gc.gridy = 1;
215                gc.fill = GridBagConstraints.NONE;
216                gc.weightx = 0.0;
217                gc.anchor = GridBagConstraints.NORTHWEST;
218                add(new JLabel(tr("Longitude: ")), gc);
219    
220                // --------
221                gc.gridx = 1;
222                gc.gridy = 1;
223                gc.fill = GridBagConstraints.HORIZONTAL;
224                gc.weightx = 1.0;
225                add(lblLon = new JLabel(), gc);
226                lblLon.setBackground(Color.WHITE);
227                lblLon.setOpaque(true);
228                lblLon.setBorder(BorderFactory.createEmptyBorder(2,2,2,2));
229    
230                // fill the remaining space
231                gc.gridx = 0;
232                gc.gridy = 2;
233                gc.gridwidth = 2;
234                gc.fill = GridBagConstraints.BOTH;
235                gc.weightx = 1.0;
236                gc.weighty = 1.0;
237                add(new JPanel(), gc);
238            }
239    
240            /**
241             *
242             * @param model a model
243             * @param role the role for this viewer.
244             */
245            public LatLonViewer(HistoryBrowserModel model, PointInTimeType role) {
246                build();
247                this.model = model;
248                this.role = role;
249            }
250    
251            protected void refresh() {
252                HistoryOsmPrimitive p = getPrimitive();
253                HistoryOsmPrimitive  opposite = getOppositePrimitive();
254                if (p == null || ! ( p instanceof HistoryNode)) return;
255                if (opposite == null || ! (opposite instanceof HistoryNode)) return;
256                HistoryNode node = (HistoryNode)p;
257                HistoryNode oppositeNode = (HistoryNode) opposite;
258    
259                LatLon coord = node.getCoords();
260                LatLon oppositeCoord = oppositeNode.getCoords();
261                
262                // display the coordinates
263                //
264                lblLat.setText(coord != null ? coord.latToString(CoordinateFormat.DECIMAL_DEGREES) : tr("(none)"));
265                lblLon.setText(coord != null ? coord.lonToString(CoordinateFormat.DECIMAL_DEGREES) : tr("(none)"));
266    
267                // update background color to reflect differences in the coordinates
268                //
269                if (coord == oppositeCoord || 
270                        (coord != null && oppositeCoord != null && coord.lat() == oppositeCoord.lat())) {
271                    lblLat.setBackground(Color.WHITE);
272                } else {
273                    lblLat.setBackground(BGCOLOR_DIFFERENCE);
274                }
275                if (coord == oppositeCoord || 
276                        (coord != null && oppositeCoord != null && coord.lon() == oppositeCoord.lon())) {
277                    lblLon.setBackground(Color.WHITE);
278                } else {
279                    lblLon.setBackground(BGCOLOR_DIFFERENCE);
280                }
281            }
282    
283            public void update(Observable o, Object arg) {
284                refresh();
285            }
286        }
287        
288        private static class DistanceViewer extends LatLonViewer {
289    
290            private JLabel lblDistance;
291            
292            public DistanceViewer(HistoryBrowserModel model) {
293                super(model, PointInTimeType.REFERENCE_POINT_IN_TIME);
294            }
295    
296            protected void build() {
297                setLayout(new GridBagLayout());
298                setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY));
299                GridBagConstraints gc = new GridBagConstraints();
300    
301                // --------
302                gc.gridx = 0;
303                gc.gridy = 0;
304                gc.fill = GridBagConstraints.NONE;
305                gc.weightx = 0.0;
306                gc.insets = new Insets(5,5,5,5);
307                gc.anchor = GridBagConstraints.NORTHWEST;
308                add(new JLabel(tr("Distance: ")), gc);
309    
310                // --------
311                gc.gridx = 1;
312                gc.gridy = 0;
313                gc.fill = GridBagConstraints.HORIZONTAL;
314                gc.weightx = 1.0;
315                add(lblDistance = new JLabel(), gc);
316                lblDistance.setBackground(Color.WHITE);
317                lblDistance.setOpaque(true);
318                lblDistance.setBorder(BorderFactory.createEmptyBorder(2,2,2,2));
319            }
320    
321            protected void refresh() {
322                HistoryOsmPrimitive p = getPrimitive();
323                HistoryOsmPrimitive opposite = getOppositePrimitive();
324                if (p == null || ! ( p instanceof HistoryNode)) return;
325                if (opposite == null || ! (opposite instanceof HistoryNode)) return;
326                HistoryNode node = (HistoryNode) p;
327                HistoryNode oppositeNode = (HistoryNode) opposite;
328    
329                LatLon coord = node.getCoords();
330                LatLon oppositeCoord = oppositeNode.getCoords();
331                
332                // update distance
333                //
334                if (coord != null && oppositeCoord != null) {
335                    double distance = coord.greatCircleDistance(oppositeCoord);
336                    if (distance > 0) {
337                        lblDistance.setBackground(BGCOLOR_DIFFERENCE);
338                    } else {
339                        lblDistance.setBackground(Color.WHITE);
340                    }
341                    lblDistance.setText(NavigatableComponent.getDistText(distance));
342                } else {
343                    lblDistance.setBackground(coord != oppositeCoord ? BGCOLOR_DIFFERENCE : Color.WHITE);
344                    lblDistance.setText(tr("(none)"));
345                }
346            }
347        }
348    }