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 static final Color BGCOLOR_DIFFERENCE = new Color(255, 197, 197); 032 033 /** the model */ 034 private transient 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 * Constructs a new {@code CoordinateInfoViewer}. 103 * @param model the model. Must not be null. 104 * @throws IllegalArgumentException if model is null 105 */ 106 public CoordinateInfoViewer(HistoryBrowserModel model) { 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 transient HistoryBrowserModel model; 174 private PointInTimeType role; 175 176 protected LatLon coord; 177 protected LatLon oppositeCoord; 178 179 protected HistoryOsmPrimitive getPrimitive() { 180 if (model == null || role == null) 181 return null; 182 return model.getPointInTime(role); 183 } 184 185 protected HistoryOsmPrimitive getOppositePrimitive() { 186 if (model == null || role == null) 187 return null; 188 return model.getPointInTime(role.opposite()); 189 } 190 191 protected void build() { 192 setLayout(new GridBagLayout()); 193 setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY)); 194 GridBagConstraints gc = new GridBagConstraints(); 195 196 // -------- 197 gc.gridx = 0; 198 gc.gridy = 0; 199 gc.fill = GridBagConstraints.NONE; 200 gc.weightx = 0.0; 201 gc.insets = new Insets(5, 5, 5, 5); 202 gc.anchor = GridBagConstraints.NORTHWEST; 203 add(new JLabel(tr("Latitude: ")), gc); 204 205 // -------- 206 gc.gridx = 1; 207 gc.gridy = 0; 208 gc.fill = GridBagConstraints.HORIZONTAL; 209 gc.weightx = 1.0; 210 add(lblLat = new JLabel(), gc); 211 lblLat.setBackground(Color.WHITE); 212 lblLat.setOpaque(true); 213 lblLat.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2)); 214 215 // -------- 216 gc.gridx = 0; 217 gc.gridy = 1; 218 gc.fill = GridBagConstraints.NONE; 219 gc.weightx = 0.0; 220 gc.anchor = GridBagConstraints.NORTHWEST; 221 add(new JLabel(tr("Longitude: ")), gc); 222 223 // -------- 224 gc.gridx = 1; 225 gc.gridy = 1; 226 gc.fill = GridBagConstraints.HORIZONTAL; 227 gc.weightx = 1.0; 228 add(lblLon = new JLabel(), gc); 229 lblLon.setBackground(Color.WHITE); 230 lblLon.setOpaque(true); 231 lblLon.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2)); 232 233 // fill the remaining space 234 gc.gridx = 0; 235 gc.gridy = 2; 236 gc.gridwidth = 2; 237 gc.fill = GridBagConstraints.BOTH; 238 gc.weightx = 1.0; 239 gc.weighty = 1.0; 240 add(new JPanel(), gc); 241 } 242 243 /** 244 * 245 * @param model a model 246 * @param role the role for this viewer. 247 */ 248 LatLonViewer(HistoryBrowserModel model, PointInTimeType role) { 249 build(); 250 this.model = model; 251 this.role = role; 252 } 253 254 protected final boolean prepareRefresh() { 255 HistoryOsmPrimitive p = getPrimitive(); 256 HistoryOsmPrimitive opposite = getOppositePrimitive(); 257 if (!(p instanceof HistoryNode)) return false; 258 if (!(opposite instanceof HistoryNode)) return false; 259 HistoryNode node = (HistoryNode) p; 260 HistoryNode oppositeNode = (HistoryNode) opposite; 261 262 coord = node.getCoords(); 263 oppositeCoord = oppositeNode.getCoords(); 264 return true; 265 } 266 267 protected void refresh() { 268 if (!prepareRefresh()) return; 269 270 // display the coordinates 271 lblLat.setText(coord != null ? coord.latToString(CoordinateFormat.DECIMAL_DEGREES) : tr("(none)")); 272 lblLon.setText(coord != null ? coord.lonToString(CoordinateFormat.DECIMAL_DEGREES) : tr("(none)")); 273 274 // update background color to reflect differences in the coordinates 275 if (coord == oppositeCoord || 276 (coord != null && oppositeCoord != null && coord.lat() == oppositeCoord.lat())) { 277 lblLat.setBackground(Color.WHITE); 278 } else { 279 lblLat.setBackground(BGCOLOR_DIFFERENCE); 280 } 281 if (coord == oppositeCoord || 282 (coord != null && oppositeCoord != null && coord.lon() == oppositeCoord.lon())) { 283 lblLon.setBackground(Color.WHITE); 284 } else { 285 lblLon.setBackground(BGCOLOR_DIFFERENCE); 286 } 287 } 288 289 @Override 290 public void update(Observable o, Object arg) { 291 refresh(); 292 } 293 } 294 295 private static class DistanceViewer extends LatLonViewer { 296 297 private JLabel lblDistance; 298 299 DistanceViewer(HistoryBrowserModel model) { 300 super(model, PointInTimeType.REFERENCE_POINT_IN_TIME); 301 } 302 303 @Override 304 protected void build() { 305 setLayout(new GridBagLayout()); 306 setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY)); 307 GridBagConstraints gc = new GridBagConstraints(); 308 309 // -------- 310 gc.gridx = 0; 311 gc.gridy = 0; 312 gc.fill = GridBagConstraints.NONE; 313 gc.weightx = 0.0; 314 gc.insets = new Insets(5, 5, 5, 5); 315 gc.anchor = GridBagConstraints.NORTHWEST; 316 add(new JLabel(tr("Distance: ")), gc); 317 318 // -------- 319 gc.gridx = 1; 320 gc.gridy = 0; 321 gc.fill = GridBagConstraints.HORIZONTAL; 322 gc.weightx = 1.0; 323 add(lblDistance = new JLabel(), gc); 324 lblDistance.setBackground(Color.WHITE); 325 lblDistance.setOpaque(true); 326 lblDistance.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2)); 327 } 328 329 @Override 330 protected void refresh() { 331 if (!prepareRefresh()) return; 332 333 // update distance 334 // 335 if (coord != null && oppositeCoord != null) { 336 double distance = coord.greatCircleDistance(oppositeCoord); 337 if (distance > 0) { 338 lblDistance.setBackground(BGCOLOR_DIFFERENCE); 339 } else { 340 lblDistance.setBackground(Color.WHITE); 341 } 342 lblDistance.setText(NavigatableComponent.getDistText(distance)); 343 } else { 344 lblDistance.setBackground(coord != oppositeCoord ? BGCOLOR_DIFFERENCE : Color.WHITE); 345 lblDistance.setText(tr("(none)")); 346 } 347 } 348 } 349}