Changeset 17548 in josm for trunk/src/org/openstreetmap
- Timestamp:
- 2021-02-28T17:08:41+01:00 (4 years ago)
- Location:
- trunk/src/org/openstreetmap/josm
- Files:
-
- 1 added
- 8 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/actions/ExtensionFileFilter.java
r16643 r17548 23 23 import org.openstreetmap.josm.gui.io.importexport.GeoJSONImporter; 24 24 import org.openstreetmap.josm.gui.io.importexport.GpxImporter; 25 import org.openstreetmap.josm.gui.io.importexport. JpgImporter;25 import org.openstreetmap.josm.gui.io.importexport.ImageImporter; 26 26 import org.openstreetmap.josm.gui.io.importexport.NMEAImporter; 27 27 import org.openstreetmap.josm.gui.io.importexport.NoteImporter; … … 70 70 RtkLibImporter.class, 71 71 NoteImporter.class, 72 JpgImporter.class,72 ImageImporter.class, 73 73 WMSLayerImporter.class, 74 74 AllFormatsImporter.class, -
trunk/src/org/openstreetmap/josm/data/gpx/GpxImageEntry.java
r17459 r17548 2 2 package org.openstreetmap.josm.data.gpx; 3 3 4 import static org.openstreetmap.josm.tools.I18n.tr; 5 4 6 import java.io.File; 5 import java.io.IOException;6 7 import java.util.Date; 7 8 import java.util.List; … … 18 19 19 20 import com.drew.imaging.jpeg.JpegMetadataReader; 20 import com.drew.lang.CompoundException; 21 import com.drew.imaging.png.PngMetadataReader; 22 import com.drew.imaging.tiff.TiffMetadataReader; 21 23 import com.drew.metadata.Directory; 22 24 import com.drew.metadata.Metadata; … … 582 584 } 583 585 586 String fn = file.getName(); 587 584 588 try { 585 metadata = JpegMetadataReader.readMetadata(file); 586 } catch (CompoundException | IOException ex) { 587 Logging.error(ex); 588 setExifTime(null); 589 setExifCoor(null); 590 setPos(null); 591 return; 589 // try to parse metadata according to extension 590 String ext = fn.substring(fn.lastIndexOf(".") + 1).toLowerCase(); 591 switch (ext) { 592 case "jpg": 593 case "jpeg": 594 metadata = JpegMetadataReader.readMetadata(file); 595 break; 596 case "tif": 597 case "tiff": 598 metadata = TiffMetadataReader.readMetadata(file); 599 break; 600 case "png": 601 metadata = PngMetadataReader.readMetadata(file); 602 break; 603 default: 604 throw new NoMetadataReaderWarning(ext); 605 } 606 } catch (Exception topException) { 607 //try other formats (e.g. JPEG file with .png extension) 608 try { 609 metadata = JpegMetadataReader.readMetadata(file); 610 } catch (Exception ex1) { 611 try { 612 metadata = TiffMetadataReader.readMetadata(file); 613 } catch (Exception ex2) { 614 try { 615 metadata = PngMetadataReader.readMetadata(file); 616 } catch (Exception ex3) { 617 618 Logging.warn(topException); 619 Logging.info(tr("Can''t parse metadata for file \"{0}\". Using last modified date as timestamp.", fn)); 620 setExifTime(new Date(file.lastModified())); 621 setExifCoor(null); 622 setPos(null); 623 return; 624 } 625 } 626 } 592 627 } 593 628 594 629 // Changed to silently cope with no time info in exif. One case 595 630 // of person having time that couldn't be parsed, but valid GPS info 631 Date time = null; 596 632 try { 597 setExifTime(ExifReader.readTime(metadata));633 time = ExifReader.readTime(metadata); 598 634 } catch (JosmRuntimeException | IllegalArgumentException | IllegalStateException ex) { 599 635 Logging.warn(ex); 600 setExifTime(null); 601 } 636 } 637 638 if (time == null) { 639 Logging.info(tr("No EXIF time in file \"{0}\". Using last modified date as timestamp.", fn)); 640 time = new Date(file.lastModified()); //use lastModified time if no EXIF time present 641 } 642 setExifTime(time); 602 643 603 644 final Directory dir = metadata.getFirstDirectoryOfType(JpegDirectory.class); … … 623 664 } 624 665 625 if (dirGps == null ) {666 if (dirGps == null || dirGps.getTagCount() <= 1) { 626 667 setExifCoor(null); 627 668 setPos(null); … … 658 699 } 659 700 701 private static class NoMetadataReaderWarning extends Exception { 702 NoMetadataReaderWarning(String ext) { 703 super("No metadata reader for format *." + ext); 704 } 705 } 706 660 707 private static <T> void ifNotNull(T value, Consumer<T> setter) { 661 708 if (value != null) { -
trunk/src/org/openstreetmap/josm/gui/io/importexport/JpgImporter.java
r17534 r17548 2 2 package org.openstreetmap.josm.gui.io.importexport; 3 3 4 import static org.openstreetmap.josm.tools.I18n.tr;5 6 import java.io.File;7 import java.io.IOException;8 import java.util.ArrayList;9 import java.util.Arrays;10 import java.util.EnumSet;11 import java.util.HashSet;12 import java.util.List;13 import java.util.Set;14 import java.util.regex.Matcher;15 import java.util.regex.Pattern;16 17 import org.openstreetmap.josm.actions.ExtensionFileFilter;18 import org.openstreetmap.josm.gui.layer.GpxLayer;19 import org.openstreetmap.josm.gui.layer.geoimage.GeoImageLayer;20 import org.openstreetmap.josm.gui.progress.ProgressMonitor;21 import org.openstreetmap.josm.io.CachedFile;22 import org.openstreetmap.josm.io.IllegalDataException;23 24 4 /** 25 * File importer allowing to import geotagged images (*.jpg files). 26 * 27 */ 28 public class JpgImporter extends FileImporter { 29 /** Check if the filename starts with a borked path ({@link java.io.File#File} drops consecutive {@code /} characters). */ 30 private static final Pattern URL_START_BAD = Pattern.compile("^(https?:/)([^/].*)$"); 31 /** Check for the beginning of a "good" url */ 32 private static final Pattern URL_START_GOOD = Pattern.compile("^https?://.*$"); 33 34 private GpxLayer gpx; 5 * File importer allowing to import geotagged images 6 * @deprecated use {@link ImageImporter} instead 7 */ 8 @Deprecated 9 public class JpgImporter extends ImageImporter { 35 10 36 11 /** 37 * The default file filter (only *.jpg files). 38 */ 39 public static final ExtensionFileFilter FILE_FILTER = new ExtensionFileFilter( 40 "jpg,jpeg", "jpg", tr("Image Files") + " (*.jpg)"); 41 42 /** 43 * An alternate file filter that also includes folders. 44 * @since 5438 45 */ 46 public static final ExtensionFileFilter FILE_FILTER_WITH_FOLDERS = new ExtensionFileFilter( 47 "jpg,jpeg", "jpg", tr("Image Files") + " (*.jpg, "+ tr("folder")+')'); 48 49 /** 50 * Constructs a new {@code JpgImporter}. 51 */ 12 * Constructs a new {@code JpgImporter}. 13 */ 52 14 public JpgImporter() { 53 this(false);15 super(false); 54 16 } 55 17 56 18 /** 57 58 59 60 19 * Constructs a new {@code JpgImporter} with folders selection, if wanted. 20 * @param includeFolders If true, includes folders in the file filter 21 * @since 5438 22 */ 61 23 public JpgImporter(boolean includeFolders) { 62 super(includeFolders ? FILE_FILTER_WITH_FOLDERS : FILE_FILTER); 63 } 64 65 /** 66 * Constructs a new {@code JpgImporter} for the given GPX layer. Folders selection is allowed. 67 * @param gpx The GPX layer 68 */ 69 public JpgImporter(GpxLayer gpx) { 70 this(true); 71 this.gpx = gpx; 72 } 73 74 @Override 75 public boolean acceptFile(File pathname) { 76 return super.acceptFile(pathname) || pathname.isDirectory(); 77 } 78 79 @Override 80 public void importData(List<File> sel, ProgressMonitor progressMonitor) throws IOException, IllegalDataException { 81 progressMonitor.beginTask(tr("Looking for image files"), 1); 82 try { 83 List<File> files = new ArrayList<>(); 84 Set<String> visitedDirs = new HashSet<>(); 85 addRecursiveFiles(this.options, files, visitedDirs, sel, progressMonitor.createSubTaskMonitor(1, true)); 86 87 if (progressMonitor.isCanceled()) 88 return; 89 90 if (files.isEmpty()) 91 throw new IOException(tr("No image files found.")); 92 93 GeoImageLayer.create(files, gpx); 94 } finally { 95 progressMonitor.finishTask(); 96 } 97 } 98 99 static void addRecursiveFiles(List<File> files, Set<String> visitedDirs, List<File> sel, ProgressMonitor progressMonitor) 100 throws IOException { 101 addRecursiveFiles(EnumSet.noneOf(Options.class), files, visitedDirs, sel, progressMonitor); 102 } 103 104 static void addRecursiveFiles(Set<Options> options, List<File> files, Set<String> visitedDirs, List<File> sel, 105 ProgressMonitor progressMonitor) throws IOException { 106 107 if (progressMonitor.isCanceled()) 108 return; 109 110 progressMonitor.beginTask(null, sel.size()); 111 try { 112 for (File f : sel) { 113 if (f.isDirectory()) { 114 if (visitedDirs.add(f.getCanonicalPath())) { // Do not loop over symlinks 115 File[] dirFiles = f.listFiles(); // Can be null for some strange directories (like lost+found) 116 if (dirFiles != null) { 117 addRecursiveFiles(options, files, visitedDirs, Arrays.asList(dirFiles), 118 progressMonitor.createSubTaskMonitor(1, true)); 119 } 120 } else { 121 progressMonitor.worked(1); 122 } 123 } else { 124 /* Check if the path is a web path, and if so, ensure that it is "correct" */ 125 final String path = f.getPath(); 126 Matcher matcherBad = URL_START_BAD.matcher(path); 127 final String realPath; 128 if (matcherBad.matches()) { 129 realPath = matcherBad.replaceFirst(matcherBad.group(1) + "/" + matcherBad.group(2)); 130 } else { 131 realPath = path; 132 } 133 if (URL_START_GOOD.matcher(realPath).matches() && FILE_FILTER.accept(f) 134 && options.contains(Options.ALLOW_WEB_RESOURCES)) { 135 try (CachedFile cachedFile = new CachedFile(realPath)) { 136 files.add(cachedFile.getFile()); 137 } 138 } else if (FILE_FILTER.accept(f)) { 139 files.add(f); 140 } 141 progressMonitor.worked(1); 142 } 143 } 144 } finally { 145 progressMonitor.finishTask(); 146 } 147 } 148 149 @Override 150 public boolean isBatchImporter() { 151 return true; 152 } 153 154 /** 155 * Needs to be the last, to avoid problems. 156 */ 157 @Override 158 public double getPriority() { 159 return -1000; 24 super(includeFolders); 160 25 } 161 26 } -
trunk/src/org/openstreetmap/josm/gui/layer/geoimage/CorrelateGpxWithImages.java
r16438 r17548 79 79 import org.openstreetmap.josm.gui.MainApplication; 80 80 import org.openstreetmap.josm.gui.io.importexport.GpxImporter; 81 import org.openstreetmap.josm.gui.io.importexport. JpgImporter;81 import org.openstreetmap.josm.gui.io.importexport.ImageImporter; 82 82 import org.openstreetmap.josm.gui.io.importexport.NMEAImporter; 83 83 import org.openstreetmap.josm.gui.io.importexport.RtkLibImporter; … … 718 718 openButton.addActionListener(ae -> { 719 719 AbstractFileChooser fc = DiskAccessAction.createAndOpenFileChooser(true, false, null, 720 JpgImporter.FILE_FILTER_WITH_FOLDERS, JFileChooser.FILES_ONLY, "geoimage.lastdirectory");720 ImageImporter.FILE_FILTER_WITH_FOLDERS, JFileChooser.FILES_ONLY, "geoimage.lastdirectory"); 721 721 if (fc == null) 722 722 return; -
trunk/src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java
r17459 r17548 56 56 import org.openstreetmap.josm.gui.dialogs.LayerListDialog; 57 57 import org.openstreetmap.josm.gui.dialogs.LayerListPopup; 58 import org.openstreetmap.josm.gui.io.importexport. JpgImporter;58 import org.openstreetmap.josm.gui.io.importexport.ImageImporter; 59 59 import org.openstreetmap.josm.gui.layer.AbstractModifiableLayer; 60 60 import org.openstreetmap.josm.gui.layer.GpxLayer; … … 313 313 } 314 314 315 File[] children = f.listFiles( JpgImporter.FILE_FILTER_WITH_FOLDERS);315 File[] children = f.listFiles(ImageImporter.FILE_FILTER_WITH_FOLDERS); 316 316 if (children != null) { 317 317 progressMonitor.subTask(tr("Scanning directory {0}", f.getPath())); -
trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ImageDisplay.java
r16284 r17548 14 14 import java.awt.Rectangle; 15 15 import java.awt.RenderingHints; 16 import java.awt.Toolkit;17 16 import java.awt.event.MouseEvent; 18 17 import java.awt.event.MouseListener; … … 25 24 import java.awt.image.ImageObserver; 26 25 import java.io.File; 27 26 import java.io.IOException; 27 28 import javax.imageio.ImageIO; 28 29 import javax.swing.JComponent; 29 30 import javax.swing.SwingUtilities; … … 244 245 if (!(entry.getWidth() > 0 && entry.getHeight() > 0)) { 245 246 synchronized (entry) { 246 img.getWidth(this); 247 img.getHeight(this); 247 int width = img.getWidth(this); 248 int height = img.getHeight(this); 249 250 if (!(entry.getWidth() > 0 && entry.getHeight() > 0) && width > 0 && height > 0) { 251 // dimensions not in metadata but already present in image, so observer won't be called 252 entry.setWidth(width); 253 entry.setHeight(height); 254 entry.notifyAll(); 255 } 248 256 249 257 long now = System.currentTimeMillis(); … … 280 288 @Override 281 289 public void run() { 282 Image img = Toolkit.getDefaultToolkit().createImage(file.getPath()); 283 if (!updateImageEntry(img)) 284 return; 285 286 int width = entry.getWidth(); 287 int height = entry.getHeight(); 288 289 if (mayFitMemory(((long) width)*height*4*2)) { 290 Logging.info(tr("Loading {0}", file.getPath())); 291 tracker.addImage(img, 1); 292 293 // Wait for the end of loading 294 while (!tracker.checkID(1, true)) { 290 Image img; 291 try { 292 img = ImageIO.read(file); 293 294 if (!updateImageEntry(img)) 295 return; 296 297 int width = entry.getWidth(); 298 int height = entry.getHeight(); 299 300 if (mayFitMemory(((long) width)*height*4*2)) { 301 Logging.info(tr("Loading {0}", file.getPath())); 302 tracker.addImage(img, 1); 303 304 // Wait for the end of loading 305 while (!tracker.checkID(1, true)) { 306 if (this.entry != ImageDisplay.this.entry) { 307 // The file has changed 308 tracker.removeImage(img); 309 return; 310 } 311 try { 312 Thread.sleep(5); 313 } catch (InterruptedException e) { 314 Logging.trace(e); 315 Logging.warn("InterruptedException in {0} while loading image {1}", 316 getClass().getSimpleName(), file.getPath()); 317 Thread.currentThread().interrupt(); 318 } 319 } 320 if (tracker.isErrorID(1)) { 321 Logging.warn("Abort loading of {0} since tracker errored with 1", file); 322 // the tracker catches OutOfMemory conditions 323 tracker.removeImage(img); 324 img = null; 325 } else { 326 tracker.removeImage(img); 327 } 328 } else { 329 Logging.warn("Abort loading of {0} since it might not fit into memory", file); 330 img = null; 331 } 332 333 synchronized (ImageDisplay.this) { 295 334 if (this.entry != ImageDisplay.this.entry) { 296 335 // The file has changed 297 tracker.removeImage(img);298 336 return; 299 337 } 300 try { 301 Thread.sleep(5); 302 } catch (InterruptedException e) { 303 Logging.trace(e); 304 Logging.warn("InterruptedException in {0} while loading image {1}", 305 getClass().getSimpleName(), file.getPath()); 306 Thread.currentThread().interrupt(); 338 339 if (img != null) { 340 boolean switchedDim = false; 341 if (ExifReader.orientationNeedsCorrection(entry.getExifOrientation())) { 342 if (ExifReader.orientationSwitchesDimensions(entry.getExifOrientation())) { 343 width = img.getHeight(null); 344 height = img.getWidth(null); 345 switchedDim = true; 346 } 347 final BufferedImage rot = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); 348 final AffineTransform xform = ExifReader.getRestoreOrientationTransform( 349 entry.getExifOrientation(), 350 img.getWidth(null), 351 img.getHeight(null)); 352 final Graphics2D g = rot.createGraphics(); 353 g.drawImage(img, xform, null); 354 g.dispose(); 355 img = rot; 356 } 357 358 ImageDisplay.this.image = img; 359 visibleRect = new VisRect(0, 0, width, height); 360 361 Logging.debug("Loaded {0} with dimensions {1}x{2} memoryTaken={3}m exifOrientationSwitchedDimension={4}", 362 file.getPath(), width, height, width*height*4/1024/1024, switchedDim); 307 363 } 308 } 309 if (tracker.isErrorID(1)) { 310 Logging.warn("Abort loading of {0} since tracker errored with 1", file); 311 // the tracker catches OutOfMemory conditions 312 tracker.removeImage(img); 313 img = null; 314 } else { 315 tracker.removeImage(img); 316 } 317 } else { 318 Logging.warn("Abort loading of {0} since it might not fit into memory", file); 319 img = null; 320 } 321 322 synchronized (ImageDisplay.this) { 323 if (this.entry != ImageDisplay.this.entry) { 324 // The file has changed 325 return; 326 } 327 328 if (img != null) { 329 boolean switchedDim = false; 330 if (ExifReader.orientationNeedsCorrection(entry.getExifOrientation())) { 331 if (ExifReader.orientationSwitchesDimensions(entry.getExifOrientation())) { 332 width = img.getHeight(null); 333 height = img.getWidth(null); 334 switchedDim = true; 335 } 336 final BufferedImage rot = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); 337 final AffineTransform xform = ExifReader.getRestoreOrientationTransform( 338 entry.getExifOrientation(), 339 img.getWidth(null), 340 img.getHeight(null)); 341 final Graphics2D g = rot.createGraphics(); 342 g.drawImage(img, xform, null); 343 g.dispose(); 344 img = rot; 345 } 346 347 ImageDisplay.this.image = img; 348 visibleRect = new VisRect(0, 0, width, height); 349 350 Logging.debug("Loaded {0} with dimensions {1}x{2} memoryTaken={3}m exifOrientationSwitchedDimension={4}", 351 file.getPath(), width, height, width*height*4/1024/1024, switchedDim); 352 } 353 354 selectedRect = null; 355 errorLoading = (img == null); 356 } 357 ImageDisplay.this.repaint(); 364 365 selectedRect = null; 366 errorLoading = (img == null); 367 } 368 ImageDisplay.this.repaint(); 369 } catch (IOException ex) { 370 Logging.error(ex); 371 } 358 372 } 359 373 } -
trunk/src/org/openstreetmap/josm/gui/layer/gpx/ImportImagesAction.java
r17333 r17548 16 16 import org.openstreetmap.josm.gui.HelpAwareOptionPane; 17 17 import org.openstreetmap.josm.gui.MainApplication; 18 import org.openstreetmap.josm.gui.io.importexport. JpgImporter;18 import org.openstreetmap.josm.gui.io.importexport.ImageImporter; 19 19 import org.openstreetmap.josm.gui.layer.GpxLayer; 20 20 import org.openstreetmap.josm.gui.progress.NullProgressMonitor; … … 69 69 return; 70 70 } 71 JpgImporter importer = new JpgImporter(layer);71 ImageImporter importer = new ImageImporter(layer); 72 72 AbstractFileChooser fc = new FileChooserManager(true, "geoimage.lastdirectory", Config.getPref().get("lastDirectory")). 73 73 createFileChooser(true, null, importer.filter, JFileChooser.FILES_AND_DIRECTORIES).openFileChooser(); -
trunk/src/org/openstreetmap/josm/tools/ExifReader.java
r15219 r17548 178 178 */ 179 179 public static LatLon readLatLon(GpsDirectory dirGps) throws MetadataException { 180 if (dirGps != null ) {180 if (dirGps != null && dirGps.getTagCount() > 1) { 181 181 double lat = readAxis(dirGps, GpsDirectory.TAG_LATITUDE, GpsDirectory.TAG_LATITUDE_REF, 'S'); 182 182 double lon = readAxis(dirGps, GpsDirectory.TAG_LONGITUDE, GpsDirectory.TAG_LONGITUDE_REF, 'W');
Note:
See TracChangeset
for help on using the changeset viewer.