Ticket #12255: GeoImageLayer_ImageDialog.patch
File GeoImageLayer_ImageDialog.patch, 19.9 KB (added by , 10 years ago) |
---|
-
src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java
21 21 import java.beans.PropertyChangeListener; 22 22 import java.io.File; 23 23 import java.io.IOException; 24 import java.text.ParseException;25 24 import java.util.ArrayList; 26 25 import java.util.Arrays; 27 import java.util.Calendar;28 26 import java.util.Collection; 29 27 import java.util.Collections; 30 import java.util.GregorianCalendar;31 28 import java.util.HashSet; 32 29 import java.util.LinkedHashSet; 33 30 import java.util.LinkedList; 34 31 import java.util.List; 35 32 import java.util.Set; 36 import java.util.TimeZone;37 33 import java.util.concurrent.ExecutorService; 38 34 import java.util.concurrent.Executors; 39 35 … … 67 63 import org.openstreetmap.josm.gui.layer.Layer; 68 64 import org.openstreetmap.josm.gui.util.GuiHelper; 69 65 import org.openstreetmap.josm.io.JpgImporter; 70 import org.openstreetmap.josm.tools.ExifReader;71 66 import org.openstreetmap.josm.tools.ImageProvider; 72 67 import org.openstreetmap.josm.tools.Utils; 73 68 74 import com.drew.imaging.jpeg.JpegMetadataReader;75 import com.drew.lang.CompoundException;76 import com.drew.metadata.Directory;77 import com.drew.metadata.Metadata;78 import com.drew.metadata.MetadataException;79 import com.drew.metadata.exif.ExifIFD0Directory;80 import com.drew.metadata.exif.GpsDirectory;81 82 69 /** 83 70 * Layer displaying geottaged pictures. 84 71 */ … … 157 144 progressMonitor.subTask(tr("Reading {0}...", f.getName())); 158 145 progressMonitor.worked(1); 159 146 160 ImageEntry e = new ImageEntry(); 161 162 // Changed to silently cope with no time info in exif. One case 163 // of person having time that couldn't be parsed, but valid GPS info 164 165 try { 166 e.setExifTime(ExifReader.readTime(f)); 167 } catch (ParseException ex) { 168 e.setExifTime(null); 169 } 170 e.setFile(f); 171 extractExif(e); 147 ImageEntry e = new ImageEntry(f); 148 e.extractExif(); 172 149 data.add(e); 173 150 } 174 151 layer = new GeoImageLayer(data, gpxLayer); … … 593 570 } 594 571 } 595 572 596 /**597 * Extract GPS metadata from image EXIF598 *599 * If successful, fills in the LatLon and EastNorth attributes of passed in image600 */601 private static void extractExif(ImageEntry e) {602 603 Metadata metadata;604 Directory dirExif;605 GpsDirectory dirGps;606 607 try {608 metadata = JpegMetadataReader.readMetadata(e.getFile());609 dirExif = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);610 dirGps = metadata.getFirstDirectoryOfType(GpsDirectory.class);611 } catch (CompoundException | IOException p) {612 e.setExifCoor(null);613 e.setPos(null);614 return;615 }616 617 try {618 if (dirExif != null) {619 int orientation = dirExif.getInt(ExifIFD0Directory.TAG_ORIENTATION);620 e.setExifOrientation(orientation);621 }622 } catch (MetadataException ex) {623 Main.debug(ex.getMessage());624 }625 626 if (dirGps == null) {627 e.setExifCoor(null);628 e.setPos(null);629 return;630 }631 632 try {633 double speed = dirGps.getDouble(GpsDirectory.TAG_SPEED);634 String speedRef = dirGps.getString(GpsDirectory.TAG_SPEED_REF);635 if ("M".equalsIgnoreCase(speedRef)) {636 // miles per hour637 speed *= 1.609344;638 } else if ("N".equalsIgnoreCase(speedRef)) {639 // knots == nautical miles per hour640 speed *= 1.852;641 }642 // default is K (km/h)643 e.setSpeed(speed);644 } catch (Exception ex) {645 Main.debug(ex.getMessage());646 }647 648 try {649 double ele = dirGps.getDouble(GpsDirectory.TAG_ALTITUDE);650 int d = dirGps.getInt(GpsDirectory.TAG_ALTITUDE_REF);651 if (d == 1) {652 ele *= -1;653 }654 e.setElevation(ele);655 } catch (MetadataException ex) {656 Main.debug(ex.getMessage());657 }658 659 try {660 LatLon latlon = ExifReader.readLatLon(dirGps);661 e.setExifCoor(latlon);662 e.setPos(e.getExifCoor());663 664 } catch (Exception ex) { // (other exceptions, e.g. #5271)665 Main.error("Error reading EXIF from file: "+ex);666 e.setExifCoor(null);667 e.setPos(null);668 }669 670 try {671 Double direction = ExifReader.readDirection(dirGps);672 if (direction != null) {673 e.setExifImgDir(direction.doubleValue());674 }675 } catch (Exception ex) { // (CompoundException and other exceptions, e.g. #5271)676 Main.debug(ex.getMessage());677 }678 679 // Time and date. We can have these cases:680 // 1) GPS_TIME_STAMP not set -> date/time will be null681 // 2) GPS_DATE_STAMP not set -> use EXIF date or set to default682 // 3) GPS_TIME_STAMP and GPS_DATE_STAMP are set683 int[] timeStampComps = dirGps.getIntArray(GpsDirectory.TAG_TIME_STAMP);684 if (timeStampComps != null) {685 int gpsHour = timeStampComps[0];686 int gpsMin = timeStampComps[1];687 int gpsSec = timeStampComps[2];688 Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("UTC"));689 690 // We have the time. Next step is to check if the GPS date stamp is set.691 // dirGps.getString() always succeeds, but the return value might be null.692 String dateStampStr = dirGps.getString(GpsDirectory.TAG_DATE_STAMP);693 if (dateStampStr != null && dateStampStr.matches("^\\d+:\\d+:\\d+$")) {694 String[] dateStampComps = dateStampStr.split(":");695 cal.set(Calendar.YEAR, Integer.parseInt(dateStampComps[0]));696 cal.set(Calendar.MONTH, Integer.parseInt(dateStampComps[1]) - 1);697 cal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(dateStampComps[2]));698 } else {699 // No GPS date stamp in EXIF data. Copy it from EXIF time.700 // Date is not set if EXIF time is not available.701 if (e.hasExifTime()) {702 // Time not set yet, so we can copy everything, not just date.703 cal.setTime(e.getExifTime());704 }705 }706 707 cal.set(Calendar.HOUR_OF_DAY, gpsHour);708 cal.set(Calendar.MINUTE, gpsMin);709 cal.set(Calendar.SECOND, gpsSec);710 711 e.setExifGpsTime(cal.getTime());712 }713 }714 715 573 public void showNextPhoto() { 716 574 if (data != null && !data.isEmpty()) { 717 575 currentPhoto++; -
src/org/openstreetmap/josm/gui/layer/geoimage/ImageEntry.java
3 3 4 4 import java.awt.Image; 5 5 import java.io.File; 6 import java.io.IOException; 7 import java.text.ParseException; 8 import java.util.Calendar; 6 9 import java.util.Date; 10 import java.util.GregorianCalendar; 11 import java.util.TimeZone; 7 12 13 import org.openstreetmap.josm.Main; 8 14 import org.openstreetmap.josm.data.coor.CachedLatLon; 9 15 import org.openstreetmap.josm.data.coor.LatLon; 16 import org.openstreetmap.josm.tools.ExifReader; 10 17 18 import com.drew.imaging.jpeg.JpegMetadataReader; 19 import com.drew.lang.CompoundException; 20 import com.drew.metadata.Directory; 21 import com.drew.metadata.Metadata; 22 import com.drew.metadata.MetadataException; 23 import com.drew.metadata.exif.ExifIFD0Directory; 24 import com.drew.metadata.exif.GpsDirectory; 25 11 26 /** 12 27 * Stores info about each image 13 28 */ … … 50 65 ImageEntry tmp; 51 66 52 67 /** 68 * Constructs a new {@code ImageEntry}. 69 */ 70 public ImageEntry() {} 71 72 /** 73 * Constructs a new {@code ImageEntry}. 74 * @param file Path to image file on disk 75 */ 76 public ImageEntry(File file) { 77 setFile(file); 78 } 79 80 /** 53 81 * getter methods that refer to the temporary value 54 82 */ 55 83 public CachedLatLon getPos() { … … 85 113 return (tmp != null && tmp.gpsTime != null) || gpsTime != null; 86 114 } 87 115 116 public Double getExifImgDir() { 117 if (tmp != null) 118 return tmp.exifImgDir; 119 return exifImgDir; 120 } 121 88 122 /** 89 123 * other getter methods 90 124 */ … … 137 171 return exifCoor; 138 172 } 139 173 140 public Double getExifImgDir() {141 return exifImgDir;142 }143 144 174 public boolean hasThumbnail() { 145 175 return thumbnail != null; 146 176 } 147 177 178 public Image getThumbnail() { 179 return thumbnail; 180 } 181 148 182 /** 149 183 * setter methods 150 184 */ … … 153 187 } 154 188 155 189 public void setPos(LatLon pos) { 156 this.pos = new CachedLatLon(pos); 190 if (pos == null) { 191 this.pos = null; 192 } else { 193 this.pos = new CachedLatLon(pos); 194 } 157 195 } 158 196 159 197 public void setSpeed(Double speed) { … … 193 231 this.exifCoor = exifCoor; 194 232 } 195 233 196 public void setExifImgDir( double exifDir) {234 public void setExifImgDir(Double exifDir) { 197 235 this.exifImgDir = exifDir; 198 236 } 199 237 238 //public void setExifImgDir(double exifDir) { 239 // this.exifImgDir = exifDir; 240 //} 241 200 242 @Override 201 243 public ImageEntry clone() { 202 244 Object c; … … 230 272 } 231 273 232 274 /** 233 * Copy the values from the temporary variable to the main instance. 275 * Get temporary variable that is used for real time parameter 276 * adjustments. The temporary variable is created if it does not exist 277 * yet. Use applyTmp() or discardTmp() if the temporary variable is not 278 * needed anymore. 234 279 */ 280 public ImageEntry getTmp() { 281 if (tmp == null) { 282 tmp = clone(); 283 tmp.tmp = null; 284 } 285 return tmp; 286 } 287 288 /** 289 * Copy the values from the temporary variable to the main instance. The 290 * temporary variable is deleted. 291 */ 235 292 public void applyTmp() { 236 293 if (tmp != null) { 237 294 pos = tmp.pos; … … 238 295 speed = tmp.speed; 239 296 elevation = tmp.elevation; 240 297 gpsTime = tmp.gpsTime; 298 exifImgDir = tmp.exifImgDir; 241 299 tmp = null; 242 300 } 243 301 } 244 302 245 303 /** 304 * Delete the temporary variable. Temporary modifications are lost. 305 */ 306 public void discardTmp() { 307 tmp = null; 308 } 309 310 /** 246 311 * If it has been tagged i.e. matched to a gpx track or retrieved lat/lon from exif 247 312 */ 248 313 public boolean isTagged() { … … 287 352 public boolean hasNewGpsData() { 288 353 return isNewGpsData; 289 354 } 355 356 /** 357 * Extract GPS metadata from image EXIF. Has no effect if the image file is not set 358 * 359 * If successful, fills in the LatLon, speed, elevation, image direction, and other attributes 360 */ 361 public void extractExif() { 362 363 Metadata metadata; 364 Directory dirExif; 365 GpsDirectory dirGps; 366 367 if (file == null) { 368 return; 369 } 370 371 try { 372 metadata = JpegMetadataReader.readMetadata(file); 373 dirExif = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class); 374 dirGps = metadata.getFirstDirectoryOfType(GpsDirectory.class); 375 } catch (CompoundException | IOException p) { 376 setExifCoor(null); 377 setPos(null); 378 return; 379 } 380 381 try { 382 if (dirExif != null) { 383 int orientation = dirExif.getInt(ExifIFD0Directory.TAG_ORIENTATION); 384 setExifOrientation(orientation); 385 } 386 } catch (MetadataException ex) { 387 Main.debug(ex.getMessage()); 388 } 389 390 if (dirGps == null) { 391 setExifCoor(null); 392 setPos(null); 393 return; 394 } 395 396 try { 397 double speed = dirGps.getDouble(GpsDirectory.TAG_SPEED); 398 String speedRef = dirGps.getString(GpsDirectory.TAG_SPEED_REF); 399 if ("M".equalsIgnoreCase(speedRef)) { 400 // miles per hour 401 speed *= 1.609344; 402 } else if ("N".equalsIgnoreCase(speedRef)) { 403 // knots == nautical miles per hour 404 speed *= 1.852; 405 } 406 // default is K (km/h) 407 setSpeed(speed); 408 } catch (Exception ex) { 409 Main.debug(ex.getMessage()); 410 } 411 412 try { 413 double ele = dirGps.getDouble(GpsDirectory.TAG_ALTITUDE); 414 int d = dirGps.getInt(GpsDirectory.TAG_ALTITUDE_REF); 415 if (d == 1) { 416 ele *= -1; 417 } 418 setElevation(ele); 419 } catch (MetadataException ex) { 420 Main.debug(ex.getMessage()); 421 } 422 423 try { 424 LatLon latlon = ExifReader.readLatLon(dirGps); 425 setExifCoor(latlon); 426 setPos(getExifCoor()); 427 428 } catch (Exception ex) { // (other exceptions, e.g. #5271) 429 Main.error("Error reading EXIF from file: "+ex); 430 setExifCoor(null); 431 setPos(null); 432 } 433 434 try { 435 Double direction = ExifReader.readDirection(dirGps); 436 if (direction != null) { 437 setExifImgDir(direction); 438 } 439 } catch (Exception ex) { // (CompoundException and other exceptions, e.g. #5271) 440 Main.debug(ex.getMessage()); 441 } 442 443 // Changed to silently cope with no time info in exif. One case 444 // of person having time that couldn't be parsed, but valid GPS info 445 try { 446 setExifTime(ExifReader.readTime(file)); 447 } catch (ParseException ex) { 448 setExifTime(null); 449 } 450 451 // Time and date. We can have these cases: 452 // 1) GPS_TIME_STAMP not set -> date/time will be null 453 // 2) GPS_DATE_STAMP not set -> use EXIF date or set to default 454 // 3) GPS_TIME_STAMP and GPS_DATE_STAMP are set 455 int[] timeStampComps = dirGps.getIntArray(GpsDirectory.TAG_TIME_STAMP); 456 if (timeStampComps != null) { 457 int gpsHour = timeStampComps[0]; 458 int gpsMin = timeStampComps[1]; 459 int gpsSec = timeStampComps[2]; 460 Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("UTC")); 461 462 // We have the time. Next step is to check if the GPS date stamp is set. 463 // dirGps.getString() always succeeds, but the return value might be null. 464 String dateStampStr = dirGps.getString(GpsDirectory.TAG_DATE_STAMP); 465 if (dateStampStr != null && dateStampStr.matches("^\\d+:\\d+:\\d+$")) { 466 String[] dateStampComps = dateStampStr.split(":"); 467 cal.set(Calendar.YEAR, Integer.parseInt(dateStampComps[0])); 468 cal.set(Calendar.MONTH, Integer.parseInt(dateStampComps[1]) - 1); 469 cal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(dateStampComps[2])); 470 } else { 471 // No GPS date stamp in EXIF data. Copy it from EXIF time. 472 // Date is not set if EXIF time is not available. 473 if (hasExifTime()) { 474 // Time not set yet, so we can copy everything, not just date. 475 cal.setTime(getExifTime()); 476 } 477 } 478 479 cal.set(Calendar.HOUR_OF_DAY, gpsHour); 480 cal.set(Calendar.MINUTE, gpsMin); 481 cal.set(Calendar.SECOND, gpsSec); 482 483 setExifGpsTime(cal.getTime()); 484 } 485 } 290 486 } -
src/org/openstreetmap/josm/gui/layer/geoimage/ImageViewerDialog.java
69 69 private JButton btnNext; 70 70 private JButton btnPrevious; 71 71 private JButton btnCollapse; 72 private JToggleButton tbCentre; 72 73 73 74 private ImageViewerDialog() { 74 75 super(tr("Geotagged Images"), "geoimage", tr("Display geotagged images"), Shortcut.registerShortcut("tools:geotagged", … … 149 150 "geoimage:last", tr("Geoimage: {0}", tr("Show last Image")), KeyEvent.VK_END, Shortcut.DIRECT) 150 151 ); 151 152 152 JToggleButtontbCentre = new JToggleButton(new ImageAction(COMMAND_CENTERVIEW,153 tbCentre = new JToggleButton(new ImageAction(COMMAND_CENTERVIEW, 153 154 ImageProvider.get("dialogs", "centreview"), tr("Center view"))); 154 155 tbCentre.setPreferredSize(buttonDim); 155 156 … … 275 276 getInstance().btnNext.setEnabled(value); 276 277 } 277 278 279 /** 280 * Enables (or disables) the "Center view" button. 281 * @param value {@code true} to enable the button, {@code false} otherwise 282 * @return the old enabled value. Can be used to restore the original enable state 283 */ 284 public static boolean setCentreEnabled(boolean value) { 285 final ImageViewerDialog instance = getInstance(); 286 boolean wasEnabled; 287 synchronized (instance) { 288 wasEnabled = instance.tbCentre.isEnabled(); 289 instance.tbCentre.setEnabled(value); 290 if (value) { 291 boolean selected = instance.tbCentre.isSelected(); 292 instance.centerView = selected; 293 if (selected) { 294 // Trigger map centering in case the image position was 295 // modified while the center view was disabled. 296 if (Main.isDisplayingMapView() 297 && instance.currentEntry != null 298 && instance.currentEntry.getPos() != null) { 299 Main.map.mapView.zoomTo(instance.currentEntry.getPos()); 300 } 301 } 302 } else { 303 instance.centerView = false; 304 } 305 } 306 return wasEnabled; 307 } 308 278 309 private transient GeoImageLayer currentLayer; 279 310 private transient ImageEntry currentEntry; 280 311 … … 303 334 setTitle(tr("Geotagged Images") + (entry.getFile() != null ? " - " + entry.getFile().getName() : "")); 304 335 StringBuilder osd = new StringBuilder(entry.getFile() != null ? entry.getFile().getName() : ""); 305 336 if (entry.getElevation() != null) { 306 osd.append(tr("\nAltitude: {0} m", entry.getElevation().longValue()));337 osd.append(tr("\nAltitude: {0} m", Math.round(entry.getElevation()))); 307 338 } 308 339 if (entry.getSpeed() != null) { 309 340 osd.append(tr("\nSpeed: {0} km/h", Math.round(entry.getSpeed()))); -
src/org/openstreetmap/josm/io/session/GeoImageSessionImporter.java
79 79 Double.parseDouble(attrElem.getAttribute("lon")))); 80 80 break; 81 81 case "exif-image-direction": 82 entry.setExifImgDir(Double. parseDouble(attrElem.getTextContent()));82 entry.setExifImgDir(Double.valueOf(attrElem.getTextContent())); 83 83 break; 84 84 case "is-new-gps-data": 85 85 if (Boolean.parseBoolean(attrElem.getTextContent())) {