001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.layer.geoimage; 003 004import java.awt.Image; 005import java.io.File; 006import java.util.Date; 007 008import org.openstreetmap.josm.data.coor.CachedLatLon; 009import org.openstreetmap.josm.data.coor.LatLon; 010 011/** 012 * Stores info about each image 013 */ 014public final class ImageEntry implements Comparable<ImageEntry>, Cloneable { 015 private File file; 016 private Integer exifOrientation; 017 private LatLon exifCoor; 018 private Double exifImgDir; 019 private Date exifTime; 020 /** 021 * Flag isNewGpsData indicates that the GPS data of the image is new or has changed. 022 * GPS data includes the position, speed, elevation, time (e.g. as extracted from the GPS track). 023 * The flag can used to decide for which image file the EXIF GPS data is (re-)written. 024 */ 025 private boolean isNewGpsData; 026 /** Temporary source of GPS time if not correlated with GPX track. */ 027 private Date exifGpsTime; 028 Image thumbnail; 029 030 /** 031 * The following values are computed from the correlation with the gpx track 032 * or extracted from the image EXIF data. 033 */ 034 private CachedLatLon pos; 035 /** Speed in kilometer per hour */ 036 private Double speed; 037 /** Elevation (altitude) in meters */ 038 private Double elevation; 039 /** The time after correlation with a gpx track */ 040 private Date gpsTime; 041 042 /** 043 * When the correlation dialog is open, we like to show the image position 044 * for the current time offset on the map in real time. 045 * On the other hand, when the user aborts this operation, the old values 046 * should be restored. We have a temprary copy, that overrides 047 * the normal values if it is not null. (This may be not the most elegant 048 * solution for this, but it works.) 049 */ 050 ImageEntry tmp; 051 052 /** 053 * getter methods that refer to the temporary value 054 */ 055 public CachedLatLon getPos() { 056 if (tmp != null) 057 return tmp.pos; 058 return pos; 059 } 060 061 public Double getSpeed() { 062 if (tmp != null) 063 return tmp.speed; 064 return speed; 065 } 066 067 public Double getElevation() { 068 if (tmp != null) 069 return tmp.elevation; 070 return elevation; 071 } 072 073 public Date getGpsTime() { 074 if (tmp != null) 075 return getDefensiveDate(tmp.gpsTime); 076 return getDefensiveDate(gpsTime); 077 } 078 079 /** 080 * Convenient way to determine if this entry has a GPS time, without the cost of building a defensive copy. 081 * @return {@code true} if this entry has a GPS time 082 * @since 6450 083 */ 084 public boolean hasGpsTime() { 085 return (tmp != null && tmp.gpsTime != null) || gpsTime != null; 086 } 087 088 /** 089 * other getter methods 090 */ 091 public File getFile() { 092 return file; 093 } 094 095 public Integer getExifOrientation() { 096 return exifOrientation; 097 } 098 099 public Date getExifTime() { 100 return getDefensiveDate(exifTime); 101 } 102 103 /** 104 * Convenient way to determine if this entry has a EXIF time, without the cost of building a defensive copy. 105 * @return {@code true} if this entry has a EXIF time 106 * @since 6450 107 */ 108 public boolean hasExifTime() { 109 return exifTime != null; 110 } 111 112 /** 113 * Returns the EXIF GPS time. 114 * @return the EXIF GPS time 115 * @since 6392 116 */ 117 public Date getExifGpsTime() { 118 return getDefensiveDate(exifGpsTime); 119 } 120 121 /** 122 * Convenient way to determine if this entry has a EXIF GPS time, without the cost of building a defensive copy. 123 * @return {@code true} if this entry has a EXIF GPS time 124 * @since 6450 125 */ 126 public boolean hasExifGpsTime() { 127 return exifGpsTime != null; 128 } 129 130 private static Date getDefensiveDate(Date date) { 131 if (date == null) 132 return null; 133 return new Date(date.getTime()); 134 } 135 136 public LatLon getExifCoor() { 137 return exifCoor; 138 } 139 140 public Double getExifImgDir() { 141 return exifImgDir; 142 } 143 144 public boolean hasThumbnail() { 145 return thumbnail != null; 146 } 147 148 /** 149 * setter methods 150 */ 151 public void setPos(CachedLatLon pos) { 152 this.pos = pos; 153 } 154 155 public void setPos(LatLon pos) { 156 this.pos = new CachedLatLon(pos); 157 } 158 159 public void setSpeed(Double speed) { 160 this.speed = speed; 161 } 162 163 public void setElevation(Double elevation) { 164 this.elevation = elevation; 165 } 166 167 public void setFile(File file) { 168 this.file = file; 169 } 170 171 public void setExifOrientation(Integer exifOrientation) { 172 this.exifOrientation = exifOrientation; 173 } 174 175 public void setExifTime(Date exifTime) { 176 this.exifTime = getDefensiveDate(exifTime); 177 } 178 179 /** 180 * Sets the EXIF GPS time. 181 * @param exifGpsTime the EXIF GPS time 182 * @since 6392 183 */ 184 public void setExifGpsTime(Date exifGpsTime) { 185 this.exifGpsTime = getDefensiveDate(exifGpsTime); 186 } 187 188 public void setGpsTime(Date gpsTime) { 189 this.gpsTime = getDefensiveDate(gpsTime); 190 } 191 192 public void setExifCoor(LatLon exifCoor) { 193 this.exifCoor = exifCoor; 194 } 195 196 public void setExifImgDir(double exifDir) { 197 this.exifImgDir = exifDir; 198 } 199 200 @Override 201 public ImageEntry clone() { 202 Object c; 203 try { 204 c = super.clone(); 205 } catch (CloneNotSupportedException e) { 206 throw new RuntimeException(e); 207 } 208 return (ImageEntry) c; 209 } 210 211 @Override 212 public int compareTo(ImageEntry image) { 213 if (exifTime != null && image.exifTime != null) 214 return exifTime.compareTo(image.exifTime); 215 else if (exifTime == null && image.exifTime == null) 216 return 0; 217 else if (exifTime == null) 218 return -1; 219 else 220 return 1; 221 } 222 223 /** 224 * Make a fresh copy and save it in the temporary variable. 225 */ 226 public void cleanTmp() { 227 tmp = clone(); 228 tmp.setPos(null); 229 tmp.tmp = null; 230 } 231 232 /** 233 * Copy the values from the temporary variable to the main instance. 234 */ 235 public void applyTmp() { 236 if (tmp != null) { 237 pos = tmp.pos; 238 speed = tmp.speed; 239 elevation = tmp.elevation; 240 gpsTime = tmp.gpsTime; 241 tmp = null; 242 } 243 } 244 245 /** 246 * If it has been tagged i.e. matched to a gpx track or retrieved lat/lon from exif 247 */ 248 public boolean isTagged() { 249 return pos != null; 250 } 251 252 /** 253 * String representation. (only partial info) 254 */ 255 @Override 256 public String toString() { 257 return file.getName()+": "+ 258 "pos = "+pos+" | "+ 259 "exifCoor = "+exifCoor+" | "+ 260 (tmp == null ? " tmp==null" : 261 " [tmp] pos = "+tmp.pos); 262 } 263 264 /** 265 * Indicates that the image has new GPS data. 266 * That flag is set by new GPS data providers. It is used e.g. by the photo_geotagging plugin 267 * to decide for which image file the EXIF GPS data needs to be (re-)written. 268 * @since 6392 269 */ 270 public void flagNewGpsData() { 271 isNewGpsData = true; 272 } 273 274 /** 275 * Remove the flag that indicates new GPS data. 276 * The flag is cleared by a new GPS data consumer. 277 */ 278 public void unflagNewGpsData() { 279 isNewGpsData = false; 280 } 281 282 /** 283 * Queries whether the GPS data changed. 284 * @return {@code true} if GPS data changed, {@code false} otherwise 285 * @since 6392 286 */ 287 public boolean hasNewGpsData() { 288 return isNewGpsData; 289 } 290}