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