Changeset 18592 in josm for trunk/src/org/openstreetmap
- Timestamp:
- 2022-11-09T23:33:07+01:00 (2 years ago)
- Location:
- trunk/src/org/openstreetmap/josm
- Files:
-
- 3 added
- 6 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/data/gpx/GpxImageEntry.java
r18315 r18592 1 1 // License: GPL. For details, see LICENSE file. 2 2 package org.openstreetmap.josm.data.gpx; 3 4 import static org.openstreetmap.josm.tools.I18n.tr;5 3 6 4 import java.awt.Dimension; … … 8 6 import java.io.File; 9 7 import java.io.IOException; 8 import java.io.InputStream; 9 import java.net.URI; 10 import java.nio.file.Files; 10 11 import java.time.Instant; 11 12 import java.util.Date; 12 13 import java.util.List; 13 import java.util.Locale;14 import java.util.Map;15 14 import java.util.Objects; 16 import java.util.function.Consumer;17 import java.util.stream.Stream;18 15 19 16 import javax.imageio.IIOParam; … … 21 18 import org.openstreetmap.josm.data.IQuadBucketType; 22 19 import org.openstreetmap.josm.data.coor.CachedLatLon; 20 import org.openstreetmap.josm.data.coor.ILatLon; 23 21 import org.openstreetmap.josm.data.coor.LatLon; 24 22 import org.openstreetmap.josm.data.imagery.street_level.Projections; 25 23 import org.openstreetmap.josm.data.osm.BBox; 26 import org.openstreetmap.josm.tools.ExifReader; 27 import org.openstreetmap.josm.tools.JosmRuntimeException; 28 import org.openstreetmap.josm.tools.Logging; 29 30 import com.drew.imaging.jpeg.JpegMetadataReader; 31 import com.drew.imaging.jpeg.JpegProcessingException; 32 import com.drew.imaging.png.PngMetadataReader; 33 import com.drew.imaging.png.PngProcessingException; 34 import com.drew.imaging.tiff.TiffMetadataReader; 35 import com.drew.imaging.tiff.TiffProcessingException; 36 import com.drew.metadata.Directory; 37 import com.drew.metadata.Metadata; 38 import com.drew.metadata.MetadataException; 39 import com.drew.metadata.exif.ExifIFD0Directory; 40 import com.drew.metadata.exif.GpsDirectory; 41 import com.drew.metadata.iptc.IptcDirectory; 42 import com.drew.metadata.jpeg.JpegDirectory; 43 import com.drew.metadata.xmp.XmpDirectory; 24 import org.openstreetmap.josm.gui.layer.geoimage.ImageMetadata; 44 25 45 26 /** … … 47 28 * @since 14205 (extracted from gui.layer.geoimage.ImageEntry) 48 29 */ 49 public class GpxImageEntry implements Comparable<GpxImageEntry>, IQuadBucketType { 30 public class GpxImageEntry implements Comparable<GpxImageEntry>, IQuadBucketType, ImageMetadata { 50 31 private File file; 51 32 private Integer exifOrientation; … … 128 109 } 129 110 111 @Override 112 public URI getImageURI() { 113 return this.getFile().toURI(); 114 } 115 130 116 /** 131 117 * Returns width of the image this GpxImageEntry represents. … … 133 119 * @since 13220 134 120 */ 121 @Override 135 122 public int getWidth() { 136 123 return width; … … 142 129 * @since 13220 143 130 */ 131 @Override 144 132 public int getHeight() { 145 133 return height; … … 151 139 * @return the position value 152 140 */ 141 @Override 153 142 public CachedLatLon getPos() { 154 143 if (tmp != null) … … 162 151 * @return the speed value 163 152 */ 153 @Override 164 154 public Double getSpeed() { 165 155 if (tmp != null) … … 173 163 * @return the elevation value 174 164 */ 165 @Override 175 166 public Double getElevation() { 176 167 if (tmp != null) … … 197 188 * @return the GPS time value 198 189 */ 190 @Override 199 191 public Instant getGpsInstant() { 200 192 return tmp != null ? tmp.gpsTime : gpsTime; … … 206 198 * @since 6450 207 199 */ 200 @Override 208 201 public boolean hasGpsTime() { 209 202 return (tmp != null && tmp.gpsTime != null) || gpsTime != null; … … 222 215 * @return a display name for this entry 223 216 */ 217 @Override 224 218 public String getDisplayName() { 225 219 return file == null ? "" : file.getName(); … … 230 224 * @return EXIF orientation 231 225 */ 226 @Override 232 227 public Integer getExifOrientation() { 233 228 return exifOrientation != null ? exifOrientation : 1; … … 239 234 * @since 17715 240 235 */ 236 @Override 241 237 public Instant getExifInstant() { 242 238 return exifTime; … … 248 244 * @since 6450 249 245 */ 246 @Override 250 247 public boolean hasExifTime() { 251 248 return exifTime != null; … … 268 265 * @since 17715 269 266 */ 267 @Override 270 268 public Instant getExifGpsInstant() { 271 269 return exifGpsTime; … … 277 275 * @since 6450 278 276 */ 277 @Override 279 278 public boolean hasExifGpsTime() { 280 279 return exifGpsTime != null; … … 287 286 } 288 287 288 @Override 289 289 public LatLon getExifCoor() { 290 290 return exifCoor; 291 291 } 292 292 293 @Override 293 294 public Double getExifImgDir() { 294 295 if (tmp != null) … … 297 298 } 298 299 300 @Override 301 public Instant getLastModified() { 302 return Instant.ofEpochMilli(this.getFile().lastModified()); 303 } 304 299 305 /** 300 306 * Sets the width of this GpxImageEntry. … … 302 308 * @since 13220 303 309 */ 310 @Override 304 311 public void setWidth(int width) { 305 312 this.width = width; … … 311 318 * @since 13220 312 319 */ 320 @Override 313 321 public void setHeight(int height) { 314 322 this.height = height; … … 331 339 } 332 340 341 @Override 342 public void setPos(ILatLon pos) { 343 if (pos instanceof CachedLatLon) { 344 this.setPos((CachedLatLon) pos); 345 } else if (pos instanceof LatLon) { 346 this.setPos((LatLon) pos); 347 } else if (pos != null) { 348 this.setPos(new LatLon(pos)); 349 } else { 350 this.setPos(null); 351 } 352 } 353 333 354 /** 334 355 * Sets the speed. 335 356 * @param speed speed 336 357 */ 358 @Override 337 359 public void setSpeed(Double speed) { 338 360 this.speed = speed; … … 343 365 * @param elevation elevation 344 366 */ 367 @Override 345 368 public void setElevation(Double elevation) { 346 369 this.elevation = elevation; … … 359 382 * @param exifOrientation EXIF orientation 360 383 */ 384 @Override 361 385 public void setExifOrientation(Integer exifOrientation) { 362 386 this.exifOrientation = exifOrientation; … … 368 392 * @since 17715 369 393 */ 394 @Override 370 395 public void setExifTime(Instant exifTime) { 371 396 this.exifTime = exifTime; … … 377 402 * @since 17715 378 403 */ 404 @Override 379 405 public void setExifGpsTime(Instant exifGpsTime) { 380 406 this.exifGpsTime = exifGpsTime; … … 386 412 * @since 17715 387 413 */ 414 @Override 388 415 public void setGpsTime(Instant gpsTime) { 389 416 this.gpsTime = gpsTime; … … 394 421 } 395 422 423 @Override 424 public void setExifCoor(ILatLon exifCoor) { 425 if (exifCoor instanceof LatLon) { 426 this.exifCoor = (LatLon) exifCoor; 427 } else if (exifCoor != null) { 428 this.exifCoor = new LatLon(exifCoor); 429 } else { 430 this.exifCoor = null; 431 } 432 } 433 434 @Override 396 435 public void setExifImgDir(Double exifDir) { 397 436 this.exifImgDir = exifDir; … … 403 442 * @since 15219 404 443 */ 444 @Override 405 445 public void setIptcCaption(String iptcCaption) { 406 446 this.iptcCaption = iptcCaption; … … 412 452 * @since 15219 413 453 */ 454 @Override 414 455 public void setIptcHeadline(String iptcHeadline) { 415 456 this.iptcHeadline = iptcHeadline; … … 421 462 * @since 15219 422 463 */ 464 @Override 423 465 public void setIptcKeywords(List<String> iptcKeywords) { 424 466 this.iptcKeywords = iptcKeywords; … … 430 472 * @since 15219 431 473 */ 474 @Override 432 475 public void setIptcObjectName(String iptcObjectName) { 433 476 this.iptcObjectName = iptcObjectName; … … 439 482 * @since 15219 440 483 */ 484 @Override 441 485 public String getIptcCaption() { 442 486 return iptcCaption; … … 448 492 * @since 15219 449 493 */ 494 @Override 450 495 public String getIptcHeadline() { 451 496 return iptcHeadline; … … 457 502 * @since 15219 458 503 */ 504 @Override 459 505 public List<String> getIptcKeywords() { 460 506 return iptcKeywords; … … 466 512 * @since 15219 467 513 */ 514 @Override 468 515 public String getIptcObjectName() { 469 516 return iptcObjectName; … … 643 690 * @since 9270 644 691 */ 692 @Override 645 693 public void extractExif() { 646 647 Metadata metadata; 648 649 if (file == null) { 650 return; 651 } 652 653 String fn = file.getName(); 654 655 try { 656 // try to parse metadata according to extension 657 String ext = fn.substring(fn.lastIndexOf('.') + 1).toLowerCase(Locale.US); 658 switch (ext) { 659 case "jpg": 660 case "jpeg": 661 metadata = JpegMetadataReader.readMetadata(file); 662 break; 663 case "tif": 664 case "tiff": 665 metadata = TiffMetadataReader.readMetadata(file); 666 break; 667 case "png": 668 metadata = PngMetadataReader.readMetadata(file); 669 break; 670 default: 671 throw new NoMetadataReaderWarning(ext); 672 } 673 } catch (JpegProcessingException | TiffProcessingException | PngProcessingException | IOException 674 | NoMetadataReaderWarning topException) { 675 //try other formats (e.g. JPEG file with .png extension) 676 try { 677 metadata = JpegMetadataReader.readMetadata(file); 678 } catch (JpegProcessingException | IOException ex1) { 679 try { 680 metadata = TiffMetadataReader.readMetadata(file); 681 } catch (TiffProcessingException | IOException ex2) { 682 try { 683 metadata = PngMetadataReader.readMetadata(file); 684 } catch (PngProcessingException | IOException ex3) { 685 Logging.warn(topException); 686 Logging.info(tr("Can''t parse metadata for file \"{0}\". Using last modified date as timestamp.", fn)); 687 setExifTime(Instant.ofEpochMilli(file.lastModified())); 688 setExifCoor(null); 689 setPos(null); 690 return; 691 } 692 } 693 } 694 } 695 696 IptcDirectory dirIptc = metadata.getFirstDirectoryOfType(IptcDirectory.class); 697 if (dirIptc != null) { 698 ifNotNull(ExifReader.readCaption(dirIptc), this::setIptcCaption); 699 ifNotNull(ExifReader.readHeadline(dirIptc), this::setIptcHeadline); 700 ifNotNull(ExifReader.readKeywords(dirIptc), this::setIptcKeywords); 701 ifNotNull(ExifReader.readObjectName(dirIptc), this::setIptcObjectName); 702 } 703 704 for (XmpDirectory xmpDirectory : metadata.getDirectoriesOfType(XmpDirectory.class)) { 705 Map<String, String> properties = xmpDirectory.getXmpProperties(); 706 final String projectionType = "GPano:ProjectionType"; 707 if (properties.containsKey(projectionType)) { 708 Stream.of(Projections.values()).filter(p -> p.name().equalsIgnoreCase(properties.get(projectionType))) 709 .findFirst().ifPresent(projection -> this.cameraProjection = projection); 710 break; 711 } 712 } 713 714 // Changed to silently cope with no time info in exif. One case 715 // of person having time that couldn't be parsed, but valid GPS info 716 Instant time = null; 717 try { 718 time = ExifReader.readInstant(metadata); 719 } catch (JosmRuntimeException | IllegalArgumentException | IllegalStateException ex) { 720 Logging.warn(ex); 721 } 722 723 if (time == null) { 724 Logging.info(tr("No EXIF time in file \"{0}\". Using last modified date as timestamp.", fn)); 725 time = Instant.ofEpochMilli(file.lastModified()); //use lastModified time if no EXIF time present 726 } 727 setExifTime(time); 728 729 final Directory dir = metadata.getFirstDirectoryOfType(JpegDirectory.class); 730 final Directory dirExif = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class); 731 final GpsDirectory dirGps = metadata.getFirstDirectoryOfType(GpsDirectory.class); 732 733 try { 734 if (dirExif != null && dirExif.containsTag(ExifIFD0Directory.TAG_ORIENTATION)) { 735 setExifOrientation(dirExif.getInt(ExifIFD0Directory.TAG_ORIENTATION)); 736 } 737 } catch (MetadataException ex) { 738 Logging.debug(ex); 739 } 740 741 try { 742 if (dir != null && dir.containsTag(JpegDirectory.TAG_IMAGE_WIDTH) && dir.containsTag(JpegDirectory.TAG_IMAGE_HEIGHT)) { 743 // there are cases where these do not match width and height stored in dirExif 744 setWidth(dir.getInt(JpegDirectory.TAG_IMAGE_WIDTH)); 745 setHeight(dir.getInt(JpegDirectory.TAG_IMAGE_HEIGHT)); 746 } 747 } catch (MetadataException ex) { 748 Logging.debug(ex); 749 } 750 751 if (dirGps == null || dirGps.getTagCount() <= 1) { 752 setExifCoor(null); 753 setPos(null); 754 return; 755 } 756 757 ifNotNull(ExifReader.readSpeed(dirGps), this::setSpeed); 758 ifNotNull(ExifReader.readElevation(dirGps), this::setElevation); 759 760 try { 761 setExifCoor(ExifReader.readLatLon(dirGps)); 762 setPos(getExifCoor()); 763 } catch (MetadataException | IndexOutOfBoundsException ex) { // (other exceptions, e.g. #5271) 764 Logging.error("Error reading EXIF from file: " + ex); 765 setExifCoor(null); 766 setPos(null); 767 } 768 769 try { 770 ifNotNull(ExifReader.readDirection(dirGps), this::setExifImgDir); 771 } catch (IndexOutOfBoundsException ex) { // (other exceptions, e.g. #5271) 772 Logging.debug(ex); 773 } 774 775 ifNotNull(dirGps.getGpsDate(), d -> setExifGpsTime(d.toInstant())); 694 ImageMetadata.super.extractExif(); 695 } 696 697 @Override 698 public InputStream getInputStream() throws IOException { 699 return Files.newInputStream(this.getFile().toPath()); 776 700 } 777 701 … … 787 711 } 788 712 789 private static class NoMetadataReaderWarning extends Exception {790 NoMetadataReaderWarning(String ext) {791 super("No metadata reader for format *." + ext);792 }793 }794 795 private static <T> void ifNotNull(T value, Consumer<T> setter) {796 if (value != null) {797 setter.accept(value);798 }799 }800 801 713 /** 802 714 * Get the projection type for this entry … … 804 716 * @since 18246 805 717 */ 718 @Override 806 719 public Projections getProjectionType() { 807 720 return this.cameraProjection; 721 } 722 723 @Override 724 public void setProjectionType(Projections newProjection) { 725 this.cameraProjection = newProjection; 808 726 } 809 727 -
trunk/src/org/openstreetmap/josm/data/imagery/street_level/IImageEntry.java
r18427 r18592 1 1 // License: GPL. For details, see LICENSE file. 2 2 package org.openstreetmap.josm.data.imagery.street_level; 3 4 import static org.openstreetmap.josm.tools.I18n.tr; 3 5 4 6 import java.awt.Dimension; … … 13 15 14 16 import org.openstreetmap.josm.data.coor.ILatLon; 17 import org.openstreetmap.josm.gui.layer.geoimage.ImageMetadata; 18 import org.openstreetmap.josm.gui.layer.geoimage.ImageUtils; 15 19 import org.openstreetmap.josm.gui.layer.geoimage.ImageViewerDialog; 20 import org.openstreetmap.josm.tools.ExifReader; 21 import org.openstreetmap.josm.tools.ImageProvider; 22 import org.openstreetmap.josm.tools.Logging; 16 23 17 24 /** … … 138 145 * @throws IOException if any I/O error occurs 139 146 */ 140 BufferedImage read(Dimension target) throws IOException; 147 default BufferedImage read(Dimension target) throws IOException { 148 URI imageUrl = getImageURI(); 149 Logging.info(tr("Loading {0}", imageUrl)); 150 BufferedImage image = ImageProvider.read(imageUrl.toURL(), false, false, 151 r -> target == null ? r.getDefaultReadParam() : ImageUtils.withSubsampling(r, target)); 152 if (image == null) { 153 Logging.warn("Unable to load {0}", imageUrl); 154 return null; 155 } 156 if (this instanceof ImageMetadata) { 157 ImageMetadata data = (ImageMetadata) this; 158 Logging.debug("Loaded {0} with dimensions {1}x{2} memoryTaken={3}m exifOrientationSwitchedDimension={4}", 159 imageUrl, image.getWidth(), image.getHeight(), image.getWidth() * image.getHeight() * 4 / 1024 / 1024, 160 ExifReader.orientationSwitchesDimensions(data.getExifOrientation())); 161 return ImageUtils.applyExifRotation(image, data.getExifOrientation()); 162 } 163 return image; 164 } 141 165 142 166 /** -
trunk/src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java
r18591 r18592 692 692 }; 693 693 MainApplication.getLayerManager().addActiveLayerChangeListener(activeLayerChangeListener); 694 695 MapFrame map = MainApplication.getMap();696 if (map.getToggleDialog(ImageViewerDialog.class) == null) {697 ImageViewerDialog.createInstance();698 map.addToggleDialog(ImageViewerDialog.getInstance());699 }700 694 } 701 695 -
trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ImageEntry.java
r18591 r18592 2 2 package org.openstreetmap.josm.gui.layer.geoimage; 3 3 4 import static org.openstreetmap.josm.tools.I18n.tr;5 6 4 import java.awt.Dimension; 7 import java.awt.Graphics2D;8 5 import java.awt.Image; 9 import java.awt.geom.AffineTransform;10 6 import java.awt.image.BufferedImage; 11 7 import java.io.File; 12 8 import java.io.IOException; 13 import java.io.UncheckedIOException;14 9 import java.net.MalformedURLException; 15 10 import java.net.URL; 16 11 import java.util.Collections; 17 12 import java.util.Objects; 13 18 14 import javax.imageio.IIOParam; 19 import javax.imageio.ImageReadParam;20 import javax.imageio.ImageReader;21 15 22 16 import org.openstreetmap.josm.data.ImageData; 23 17 import org.openstreetmap.josm.data.gpx.GpxImageEntry; 24 18 import org.openstreetmap.josm.data.imagery.street_level.IImageEntry; 25 import org.openstreetmap.josm.tools.ExifReader;26 import org.openstreetmap.josm.tools.ImageProvider;27 import org.openstreetmap.josm.tools.Logging;28 19 import org.openstreetmap.josm.tools.Utils; 29 20 … … 194 185 @Override 195 186 public BufferedImage read(Dimension target) throws IOException { 196 URL imageUrl = getImageUrl(); 197 Logging.info(tr("Loading {0}", imageUrl)); 198 BufferedImage image = ImageProvider.read(imageUrl, false, false, 199 r -> target == null ? r.getDefaultReadParam() : withSubsampling(r, target)); 200 if (image == null) { 201 Logging.warn("Unable to load {0}", imageUrl); 202 return null; 203 } 204 Logging.debug("Loaded {0} with dimensions {1}x{2} memoryTaken={3}m exifOrientationSwitchedDimension={4}", 205 imageUrl, image.getWidth(), image.getHeight(), image.getWidth() * image.getHeight() * 4 / 1024 / 1024, 206 ExifReader.orientationSwitchesDimensions(getExifOrientation())); 207 return applyExifRotation(image); 187 return IImageEntry.super.read(target); 208 188 } 209 189 … … 211 191 return getFile().toURI().toURL(); 212 192 } 213 214 private static ImageReadParam withSubsampling(ImageReader reader, Dimension target) {215 try {216 ImageReadParam param = reader.getDefaultReadParam();217 Dimension source = new Dimension(reader.getWidth(0), reader.getHeight(0));218 if (source.getWidth() > target.getWidth() || source.getHeight() > target.getHeight()) {219 int subsampling = (int) Math.floor(Math.max(220 source.getWidth() / target.getWidth(),221 source.getHeight() / target.getHeight()));222 param.setSourceSubsampling(subsampling, subsampling, 0, 0);223 }224 return param;225 } catch (IOException e) {226 throw new UncheckedIOException(e);227 }228 }229 230 private BufferedImage applyExifRotation(BufferedImage img) {231 Integer exifOrientation = getExifOrientation();232 if (!ExifReader.orientationNeedsCorrection(exifOrientation)) {233 return img;234 }235 boolean switchesDimensions = ExifReader.orientationSwitchesDimensions(exifOrientation);236 int width = switchesDimensions ? img.getHeight() : img.getWidth();237 int height = switchesDimensions ? img.getWidth() : img.getHeight();238 BufferedImage rotated = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);239 AffineTransform transform = ExifReader.getRestoreOrientationTransform(exifOrientation, img.getWidth(), img.getHeight());240 Graphics2D g = rotated.createGraphics();241 g.drawImage(img, transform, null);242 g.dispose();243 return rotated;244 }245 193 } -
trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ImageViewerDialog.java
r18591 r18592 49 49 import org.openstreetmap.josm.gui.ExtendedDialog; 50 50 import org.openstreetmap.josm.gui.MainApplication; 51 import org.openstreetmap.josm.gui.MapFrame; 51 52 import org.openstreetmap.josm.gui.datatransfer.ClipboardUtils; 52 53 import org.openstreetmap.josm.gui.dialogs.DialogsPanel; … … 113 114 */ 114 115 public static ImageViewerDialog getInstance() { 115 if (dialog == null) 116 throw new AssertionError("a new instance needs to be created first"); 116 MapFrame map = MainApplication.getMap(); 117 synchronized (ImageViewerDialog.class) { 118 if (dialog == null) 119 createInstance(); 120 if (map != null && map.getToggleDialog(ImageViewerDialog.class) == null) { 121 map.addToggleDialog(dialog); 122 } 123 } 117 124 return dialog; 118 125 } … … 241 248 invalidLayers.forEach(this.tabbedEntries::remove); 242 249 addButtonsForImageLayers(); 250 this.layers.invalidate(); 243 251 } 244 252 this.revalidate(); … … 904 912 removedData.removeImageDataUpdateListener(this); 905 913 } 914 // Unfortunately, there will be no way to remove the default null layer. This will be fixed as plugins update. 915 this.tabbedEntries.remove(e.getRemovedLayer()); 906 916 } 907 917 -
trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/ImageMarker.java
r14153 r18592 2 2 package org.openstreetmap.josm.gui.layer.markerlayer; 3 3 4 import java.awt.BorderLayout; 5 import java.awt.Cursor; 6 import java.awt.GraphicsEnvironment; 7 import java.awt.Image; 4 import static org.openstreetmap.josm.tools.I18n.tr; 5 8 6 import java.awt.event.ActionEvent; 7 import java.net.URISyntaxException; 9 8 import java.net.URL; 9 import java.time.Instant; 10 10 import java.util.Collections; 11 import java.util.function.Supplier; 11 12 12 import javax.swing.Icon;13 import javax.swing.ImageIcon;14 import javax.swing.JDialog;15 import javax.swing.JLabel;16 13 import javax.swing.JOptionPane; 17 import javax.swing.JPanel;18 import javax.swing.JScrollPane;19 import javax.swing.JToggleButton;20 import javax.swing.JViewport;21 14 22 15 import org.openstreetmap.josm.data.coor.LatLon; … … 24 17 import org.openstreetmap.josm.data.gpx.GpxLink; 25 18 import org.openstreetmap.josm.data.gpx.WayPoint; 26 import org.openstreetmap.josm.gui.MainApplication; 27 import org.openstreetmap.josm.tools.ImageProvider; 19 import org.openstreetmap.josm.gui.Notification; 20 import org.openstreetmap.josm.gui.layer.geoimage.ImageViewerDialog; 21 import org.openstreetmap.josm.gui.layer.geoimage.RemoteEntry; 22 import org.openstreetmap.josm.tools.Logging; 23 import org.openstreetmap.josm.tools.Utils; 28 24 29 25 /** … … 43 39 } 44 40 45 @Override public void actionPerformed(ActionEvent ev) { 46 final JPanel p = new JPanel(new BorderLayout()); 47 final JScrollPane scroll = new JScrollPane(new JLabel(loadScaledImage(imageUrl, 580))); 48 final JViewport vp = scroll.getViewport(); 49 p.add(scroll, BorderLayout.CENTER); 50 51 final JToggleButton scale = new JToggleButton(ImageProvider.get("misc", "rectangle")); 52 53 JPanel p2 = new JPanel(); 54 p2.add(scale); 55 p.add(p2, BorderLayout.SOUTH); 56 scale.addActionListener(ev1 -> { 57 p.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); 58 if (scale.getModel().isSelected()) { 59 ((JLabel) vp.getView()).setIcon(loadScaledImage(imageUrl, Math.max(vp.getWidth(), vp.getHeight()))); 60 } else { 61 ((JLabel) vp.getView()).setIcon(new ImageIcon(imageUrl)); 62 } 63 p.setCursor(Cursor.getDefaultCursor()); 64 }); 65 scale.setSelected(true); 66 JOptionPane pane = new JOptionPane(p, JOptionPane.PLAIN_MESSAGE); 67 if (!GraphicsEnvironment.isHeadless()) { 68 JDialog dlg = pane.createDialog(MainApplication.getMainFrame(), imageUrl.toString()); 69 dlg.setModal(false); 70 dlg.toFront(); 71 dlg.setVisible(true); 72 } 41 @Override 42 public void actionPerformed(ActionEvent ev) { 43 ImageViewerDialog.getInstance().displayImages(this.parentLayer, Collections.singletonList(getRemoteEntry())); 73 44 } 74 45 75 private static Icon loadScaledImage(URL u, int maxSize) { 76 Image img = new ImageIcon(u).getImage(); 77 int w = img.getWidth(null); 78 int h = img.getHeight(null); 79 if (w > h) { 80 h = (int) Math.round(maxSize*((double) h/w)); 81 w = maxSize; 82 } else { 83 w = (int) Math.round(maxSize*((double) w/h)); 84 h = maxSize; 46 private RemoteEntry getRemoteEntry() { 47 try { 48 final RemoteEntry remoteEntry = new RemoteEntry(this.parentLayer, imageUrl.toURI(), getFirstImage(), getPreviousImage(), 49 getNextImage(), getLastImage()); 50 // First, extract EXIF data 51 remoteEntry.extractExif(); 52 // Then, apply information from this point. This may overwrite details from 53 // the exif, but that will (hopefully) be OK. 54 if (Double.isFinite(this.time)) { 55 remoteEntry.setGpsTime(Instant.ofEpochMilli((long) (this.time * 1000))); 56 } 57 if (this.isLatLonKnown()) { 58 remoteEntry.setPos(this); 59 } 60 if (!Utils.isBlank(this.getText())) { 61 remoteEntry.setDisplayName(this.getText()); 62 } 63 return remoteEntry; 64 } catch (URISyntaxException e) { 65 Logging.trace(e); 66 new Notification(tr("Malformed URI: ", this.imageUrl.toExternalForm())).setIcon(JOptionPane.WARNING_MESSAGE).show(); 85 67 } 86 return new ImageIcon(img.getScaledInstance(w, h, Image.SCALE_SMOOTH)); 68 return null; 69 } 70 71 private Supplier<RemoteEntry> getFirstImage() { 72 for (Marker marker : this.parentLayer.data) { 73 if (marker instanceof ImageMarker) { 74 if (marker == this) { 75 break; 76 } 77 ImageMarker imageMarker = (ImageMarker) marker; 78 return imageMarker::getRemoteEntry; 79 } 80 } 81 return () -> null; 82 } 83 84 private Supplier<RemoteEntry> getPreviousImage() { 85 int index = this.parentLayer.data.indexOf(this); 86 for (int i = index - 1; i >= 0; i--) { 87 Marker marker = this.parentLayer.data.get(i); 88 if (marker instanceof ImageMarker) { 89 ImageMarker imageMarker = (ImageMarker) marker; 90 return imageMarker::getRemoteEntry; 91 } 92 } 93 return () -> null; 94 } 95 96 private Supplier<RemoteEntry> getNextImage() { 97 int index = this.parentLayer.data.indexOf(this); 98 for (int i = index + 1; i < this.parentLayer.data.size(); i++) { 99 Marker marker = this.parentLayer.data.get(i); 100 if (marker instanceof ImageMarker) { 101 ImageMarker imageMarker = (ImageMarker) marker; 102 return imageMarker::getRemoteEntry; 103 } 104 } 105 return () -> null; 106 } 107 108 private Supplier<RemoteEntry> getLastImage() { 109 int index = this.parentLayer.data.indexOf(this); 110 for (int i = this.parentLayer.data.size() - 1; i >= index; i--) { 111 Marker marker = this.parentLayer.data.get(i); 112 if (marker instanceof ImageMarker) { 113 if (marker == this) { 114 break; 115 } 116 ImageMarker imageMarker = (ImageMarker) marker; 117 return imageMarker::getRemoteEntry; 118 } 119 } 120 return () -> null; 87 121 } 88 122
Note:
See TracChangeset
for help on using the changeset viewer.