001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.plugins.streetside; 003 004import java.text.SimpleDateFormat; 005import java.util.Calendar; 006import java.util.Date; 007import java.util.Locale; 008 009import org.openstreetmap.josm.data.coor.LatLon; 010import org.openstreetmap.josm.plugins.streetside.utils.StreetsideProperties; 011 012/** 013 * Abstract superclass for all image objects. At the moment there are just 2, 014 * {@link StreetsideImportedImage} and {@link StreetsideImage}. 015 * 016 * @author nokutu 017 * 018 */ 019public abstract class StreetsideAbstractImage implements Comparable<StreetsideAbstractImage> { 020 /** 021 * If two values for field cd differ by less than EPSILON both values are 022 * considered equal. 023 */ 024 private static final float EPSILON = 1e-5f; 025 026 protected String id; 027 028 /** The time the image was captured, in Epoch format. */ 029 protected long cd; 030 /** Sequence of pictures containing this object. */ 031 private StreetsideSequence sequence; 032 033 /** Position of the picture. */ 034 protected LatLon latLon; 035 /** Direction of the picture. */ 036 protected double he; 037 /** Temporal position of the picture until it is uploaded. */ 038 private LatLon tempLatLon; 039 /** 040 * When the object is being dragged in the map, the temporal position is stored 041 * here. 042 */ 043 private LatLon movingLatLon; 044 /** Temporal direction of the picture until it is uploaded */ 045 private double tempHe; 046 /** 047 * When the object direction is being moved in the map, the temporal direction 048 * is stored here 049 */ 050 protected double movingHe; 051 /** Whether the image must be drown in the map or not */ 052 private boolean visible; 053 054 /** 055 * Creates a new object in the given position and with the given direction. 056 * {@link LatLon} 057 * 058 * @param id - the Streetside image id 059 * 060 * @param latLon 061 * The latitude and longitude of the image. 062 * @param he 063 * The direction of the picture (0 means north im Mapillary 064 * camera direction is not yet supported in the Streetside plugin). 065 */ 066 protected StreetsideAbstractImage(final String id, final LatLon latLon, final double he) { 067 this.id = id; 068 this.latLon = latLon; 069 tempLatLon = this.latLon; 070 movingLatLon = this.latLon; 071 this.he = he; 072 tempHe = he; 073 movingHe = he; 074 visible = true; 075 } 076 077 /** 078 * Creates a new object with the given id. 079 * 080 * @param id - the image id (All images require ids in Streetside) 081 */ 082 protected StreetsideAbstractImage(final String id) { 083 this.id = id; 084 085 visible = true; 086 } 087 088 /** 089 * @return the id 090 */ 091 public String getId() { 092 return id; 093 } 094 095 /** 096 * @param id 097 * the id to set 098 */ 099 public void setId(String id) { 100 this.id = id; 101 } 102 103 /** 104 * Returns the original direction towards the image has been taken. 105 * 106 * @return The direction of the image (0 means north and goes clockwise). 107 */ 108 public double getHe() { 109 return he; 110 } 111 112 /** 113 * Returns the Epoch time when the image was captured. 114 * 115 * @return The long containing the Epoch time when the image was captured. 116 */ 117 public long getCd() { 118 return cd; 119 } 120 121 /** 122 * Returns the date the picture was taken in DMY format. 123 * 124 * @return A String object containing the date when the picture was taken. 125 */ 126 public String getDate() { 127 final StringBuilder format = new StringBuilder(26); 128 format.append("m/d/YYYY"); 129 if (StreetsideProperties.DISPLAY_HOUR.get()) { 130 if (StreetsideProperties.TIME_FORMAT_24.get()) { 131 format.append(" - HH:mm:ss"); 132 } else { 133 format.append(" - h:mm:ss a"); 134 } 135 } 136 return getDate(format.toString()); 137 } 138 139 /** 140 * Returns the date the picture was taken in the given format. 141 * 142 * @param format 143 * Format of the date. See {@link SimpleDateFormat}. 144 * @return A String containing the date the picture was taken using the given 145 * format. 146 * @throws NullPointerException 147 * if parameter format is <code>null</code> 148 */ 149 public String getDate(String format) { 150 final Date date = new Date(getCd()); 151 final SimpleDateFormat formatter = new SimpleDateFormat(format, Locale.US); 152 formatter.setTimeZone(Calendar.getInstance().getTimeZone()); 153 return formatter.format(date); 154 } 155 156 /** 157 * Returns a LatLon object containing the original coordinates of the object. 158 * 159 * @return The LatLon object with the position of the object. 160 */ 161 public LatLon getLatLon() { 162 return latLon; 163 } 164 165 /** 166 * Returns the direction towards the image has been taken. 167 * 168 * @return The direction of the image (0 means north and goes clockwise). 169 */ 170 public double getMovingHe() { 171 return movingHe; 172 } 173 174 /** 175 * Returns a LatLon object containing the current coordinates of the object. 176 * When you are dragging the image this changes. 177 * 178 * @return The LatLon object with the position of the object. 179 */ 180 public LatLon getMovingLatLon() { 181 return movingLatLon; 182 } 183 184 /** 185 * Returns the sequence which contains this image. Never null. 186 * 187 * @return The StreetsideSequence object that contains this StreetsideImage. 188 */ 189 190 public StreetsideSequence getSequence() { 191 synchronized (this) { 192 if (sequence == null) { 193 sequence = new StreetsideSequence(); 194 sequence.add(this); 195 } 196 return sequence; 197 } 198 } 199 200 /** 201 * Returns the last fixed direction of the object. 202 * 203 * @return The last fixed direction of the object. 0 means north. 204 */ 205 public double getTempHe() { 206 return tempHe; 207 } 208 209 /** 210 * Returns the last fixed coordinates of the object. 211 * 212 * @return A LatLon object containing. 213 */ 214 public LatLon getTempLatLon() { 215 return tempLatLon; 216 } 217 218 /** 219 * Returns whether the object has been modified or not. 220 * 221 * @return true if the object has been modified; false otherwise. 222 */ 223 public boolean isModified() { 224 return !getMovingLatLon().equals(latLon) || Math.abs(getMovingHe() - cd) > EPSILON; 225 } 226 227 /** 228 * Returns whether the image is visible on the map or not. 229 * 230 * @return True if the image is visible; false otherwise. 231 */ 232 public boolean isVisible() { 233 return visible; 234 } 235 236 /** 237 * Moves the image temporally to another position 238 * 239 * @param x 240 * The movement of the image in longitude units. 241 * @param y 242 * The movement of the image in latitude units. 243 */ 244 public void move(final double x, final double y) { 245 movingLatLon = new LatLon(tempLatLon.getY() + y, tempLatLon.getX() + x); 246 } 247 248 /** 249 * If the StreetsideImage belongs to a StreetsideSequence, returns the next 250 * image in the sequence. 251 * 252 * @return The following StreetsideImage, or null if there is none. 253 */ 254 public StreetsideAbstractImage next() { 255 synchronized (this) { 256 return getSequence().next(this); 257 } 258 } 259 260 /** 261 * If the StreetsideImage belongs to a StreetsideSequence, returns the previous 262 * image in the sequence. 263 * 264 * @return The previous StreetsideImage, or null if there is none. 265 */ 266 public StreetsideAbstractImage previous() { 267 synchronized (this) { 268 return getSequence().previous(this); 269 } 270 } 271 272 public void setHe(final double he) { 273 this.he = he; 274 } 275 276 /** 277 * Sets the Epoch time when the picture was captured. 278 * 279 * @param cd 280 * Epoch time when the image was captured. 281 */ 282 public synchronized void setCd(final long cd) { 283 this.cd = cd; 284 } 285 286 public void setLatLon(final LatLon latLon) { 287 if (latLon != null) { 288 this.latLon = latLon; 289 } 290 } 291 292 /** 293 * Sets the StreetsideSequence object which contains the StreetsideImage. 294 * 295 * @param sequence 296 * The StreetsideSequence that contains the StreetsideImage. 297 * @throws IllegalArgumentException 298 * if the image is not already part of the 299 * {@link StreetsideSequence}. Call 300 * {@link StreetsideSequence#add(StreetsideAbstractImage)} first. 301 */ 302 public void setSequence(final StreetsideSequence sequence) { 303 synchronized (this) { 304 if (sequence != null && !sequence.getImages().contains(this)) { 305 throw new IllegalArgumentException(); 306 } 307 this.sequence = sequence; 308 } 309 } 310 311 /** 312 * Set's whether the image should be visible on the map or not. 313 * 314 * @param visible 315 * true if the image is set to be visible; false otherwise. 316 */ 317 public void setVisible(final boolean visible) { 318 this.visible = visible; 319 } 320 321 /** 322 * Called when the mouse button is released, meaning that the picture has 323 * stopped being dragged, so the temporal values are saved. 324 */ 325 public void stopMoving() { 326 tempLatLon = movingLatLon; 327 tempHe = movingHe; 328 } 329 330 /** 331 * Turns the image direction. 332 * 333 * @param ca 334 * The angle the image is moving. 335 */ 336 public void turn(final double ca) { 337 movingHe = tempHe + ca; 338 } 339}