001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.history;
003import static org.openstreetmap.josm.tools.I18n.tr;
004
005import java.awt.Color;
006import java.awt.GridBagConstraints;
007import java.awt.GridBagLayout;
008import java.awt.Insets;
009import java.util.Observable;
010import java.util.Observer;
011
012import javax.swing.BorderFactory;
013import javax.swing.JLabel;
014import javax.swing.JPanel;
015
016import org.openstreetmap.josm.data.coor.CoordinateFormat;
017import org.openstreetmap.josm.data.coor.LatLon;
018import org.openstreetmap.josm.data.osm.history.HistoryNode;
019import org.openstreetmap.josm.data.osm.history.HistoryOsmPrimitive;
020import org.openstreetmap.josm.gui.NavigatableComponent;
021import 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 */
028public 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 instanceof HistoryNode)) return;
255            if (!(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        @Override
284        public void update(Observable o, Object arg) {
285            refresh();
286        }
287    }
288
289    private static class DistanceViewer extends LatLonViewer {
290
291        private JLabel lblDistance;
292
293        public DistanceViewer(HistoryBrowserModel model) {
294            super(model, PointInTimeType.REFERENCE_POINT_IN_TIME);
295        }
296
297        @Override
298        protected void build() {
299            setLayout(new GridBagLayout());
300            setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY));
301            GridBagConstraints gc = new GridBagConstraints();
302
303            // --------
304            gc.gridx = 0;
305            gc.gridy = 0;
306            gc.fill = GridBagConstraints.NONE;
307            gc.weightx = 0.0;
308            gc.insets = new Insets(5,5,5,5);
309            gc.anchor = GridBagConstraints.NORTHWEST;
310            add(new JLabel(tr("Distance: ")), gc);
311
312            // --------
313            gc.gridx = 1;
314            gc.gridy = 0;
315            gc.fill = GridBagConstraints.HORIZONTAL;
316            gc.weightx = 1.0;
317            add(lblDistance = new JLabel(), gc);
318            lblDistance.setBackground(Color.WHITE);
319            lblDistance.setOpaque(true);
320            lblDistance.setBorder(BorderFactory.createEmptyBorder(2,2,2,2));
321        }
322
323        @Override
324        protected void refresh() {
325            HistoryOsmPrimitive p = getPrimitive();
326            HistoryOsmPrimitive opposite = getOppositePrimitive();
327            if (!(p instanceof HistoryNode)) return;
328            if (!(opposite instanceof HistoryNode)) return;
329            HistoryNode node = (HistoryNode) p;
330            HistoryNode oppositeNode = (HistoryNode) opposite;
331
332            LatLon coord = node.getCoords();
333            LatLon oppositeCoord = oppositeNode.getCoords();
334
335            // update distance
336            //
337            if (coord != null && oppositeCoord != null) {
338                double distance = coord.greatCircleDistance(oppositeCoord);
339                if (distance > 0) {
340                    lblDistance.setBackground(BGCOLOR_DIFFERENCE);
341                } else {
342                    lblDistance.setBackground(Color.WHITE);
343                }
344                lblDistance.setText(NavigatableComponent.getDistText(distance));
345            } else {
346                lblDistance.setBackground(coord != oppositeCoord ? BGCOLOR_DIFFERENCE : Color.WHITE);
347                lblDistance.setText(tr("(none)"));
348            }
349        }
350    }
351}