Changeset 18592 in josm for trunk/src/org/openstreetmap


Ignore:
Timestamp:
2022-11-09T23:33:07+01:00 (2 years ago)
Author:
taylor.smock
Message:

Fix #22337: Make window for attached GPX waypoint images resizable

The GPX waypoint images used completely different code paths for
showing images. With this change, the GPX waypoint images are
viewed in ImageViewerDialog, along with GeoTagged Images.

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  
    11// License: GPL. For details, see LICENSE file.
    22package org.openstreetmap.josm.data.gpx;
    3 
    4 import static org.openstreetmap.josm.tools.I18n.tr;
    53
    64import java.awt.Dimension;
     
    86import java.io.File;
    97import java.io.IOException;
     8import java.io.InputStream;
     9import java.net.URI;
     10import java.nio.file.Files;
    1011import java.time.Instant;
    1112import java.util.Date;
    1213import java.util.List;
    13 import java.util.Locale;
    14 import java.util.Map;
    1514import java.util.Objects;
    16 import java.util.function.Consumer;
    17 import java.util.stream.Stream;
    1815
    1916import javax.imageio.IIOParam;
     
    2118import org.openstreetmap.josm.data.IQuadBucketType;
    2219import org.openstreetmap.josm.data.coor.CachedLatLon;
     20import org.openstreetmap.josm.data.coor.ILatLon;
    2321import org.openstreetmap.josm.data.coor.LatLon;
    2422import org.openstreetmap.josm.data.imagery.street_level.Projections;
    2523import 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;
     24import org.openstreetmap.josm.gui.layer.geoimage.ImageMetadata;
    4425
    4526/**
     
    4728 * @since 14205 (extracted from gui.layer.geoimage.ImageEntry)
    4829 */
    49 public class GpxImageEntry implements Comparable<GpxImageEntry>, IQuadBucketType {
     30public class GpxImageEntry implements Comparable<GpxImageEntry>, IQuadBucketType, ImageMetadata {
    5031    private File file;
    5132    private Integer exifOrientation;
     
    128109    }
    129110
     111    @Override
     112    public URI getImageURI() {
     113        return this.getFile().toURI();
     114    }
     115
    130116    /**
    131117     * Returns width of the image this GpxImageEntry represents.
     
    133119     * @since 13220
    134120     */
     121    @Override
    135122    public int getWidth() {
    136123        return width;
     
    142129     * @since 13220
    143130     */
     131    @Override
    144132    public int getHeight() {
    145133        return height;
     
    151139     * @return the position value
    152140     */
     141    @Override
    153142    public CachedLatLon getPos() {
    154143        if (tmp != null)
     
    162151     * @return the speed value
    163152     */
     153    @Override
    164154    public Double getSpeed() {
    165155        if (tmp != null)
     
    173163     * @return the elevation value
    174164     */
     165    @Override
    175166    public Double getElevation() {
    176167        if (tmp != null)
     
    197188     * @return the GPS time value
    198189     */
     190    @Override
    199191    public Instant getGpsInstant() {
    200192        return tmp != null ? tmp.gpsTime : gpsTime;
     
    206198     * @since 6450
    207199     */
     200    @Override
    208201    public boolean hasGpsTime() {
    209202        return (tmp != null && tmp.gpsTime != null) || gpsTime != null;
     
    222215     * @return a display name for this entry
    223216     */
     217    @Override
    224218    public String getDisplayName() {
    225219        return file == null ? "" : file.getName();
     
    230224     * @return EXIF orientation
    231225     */
     226    @Override
    232227    public Integer getExifOrientation() {
    233228        return exifOrientation != null ? exifOrientation : 1;
     
    239234     * @since 17715
    240235     */
     236    @Override
    241237    public Instant getExifInstant() {
    242238        return exifTime;
     
    248244     * @since 6450
    249245     */
     246    @Override
    250247    public boolean hasExifTime() {
    251248        return exifTime != null;
     
    268265     * @since 17715
    269266     */
     267    @Override
    270268    public Instant getExifGpsInstant() {
    271269        return exifGpsTime;
     
    277275     * @since 6450
    278276     */
     277    @Override
    279278    public boolean hasExifGpsTime() {
    280279        return exifGpsTime != null;
     
    287286    }
    288287
     288    @Override
    289289    public LatLon getExifCoor() {
    290290        return exifCoor;
    291291    }
    292292
     293    @Override
    293294    public Double getExifImgDir() {
    294295        if (tmp != null)
     
    297298    }
    298299
     300    @Override
     301    public Instant getLastModified() {
     302        return Instant.ofEpochMilli(this.getFile().lastModified());
     303    }
     304
    299305    /**
    300306     * Sets the width of this GpxImageEntry.
     
    302308     * @since 13220
    303309     */
     310    @Override
    304311    public void setWidth(int width) {
    305312        this.width = width;
     
    311318     * @since 13220
    312319     */
     320    @Override
    313321    public void setHeight(int height) {
    314322        this.height = height;
     
    331339    }
    332340
     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
    333354    /**
    334355     * Sets the speed.
    335356     * @param speed speed
    336357     */
     358    @Override
    337359    public void setSpeed(Double speed) {
    338360        this.speed = speed;
     
    343365     * @param elevation elevation
    344366     */
     367    @Override
    345368    public void setElevation(Double elevation) {
    346369        this.elevation = elevation;
     
    359382     * @param exifOrientation EXIF orientation
    360383     */
     384    @Override
    361385    public void setExifOrientation(Integer exifOrientation) {
    362386        this.exifOrientation = exifOrientation;
     
    368392     * @since 17715
    369393     */
     394    @Override
    370395    public void setExifTime(Instant exifTime) {
    371396        this.exifTime = exifTime;
     
    377402     * @since 17715
    378403     */
     404    @Override
    379405    public void setExifGpsTime(Instant exifGpsTime) {
    380406        this.exifGpsTime = exifGpsTime;
     
    386412     * @since 17715
    387413     */
     414    @Override
    388415    public void setGpsTime(Instant gpsTime) {
    389416        this.gpsTime = gpsTime;
     
    394421    }
    395422
     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
    396435    public void setExifImgDir(Double exifDir) {
    397436        this.exifImgDir = exifDir;
     
    403442     * @since 15219
    404443     */
     444    @Override
    405445    public void setIptcCaption(String iptcCaption) {
    406446        this.iptcCaption = iptcCaption;
     
    412452     * @since 15219
    413453     */
     454    @Override
    414455    public void setIptcHeadline(String iptcHeadline) {
    415456        this.iptcHeadline = iptcHeadline;
     
    421462     * @since 15219
    422463     */
     464    @Override
    423465    public void setIptcKeywords(List<String> iptcKeywords) {
    424466        this.iptcKeywords = iptcKeywords;
     
    430472     * @since 15219
    431473     */
     474    @Override
    432475    public void setIptcObjectName(String iptcObjectName) {
    433476        this.iptcObjectName = iptcObjectName;
     
    439482     * @since 15219
    440483     */
     484    @Override
    441485    public String getIptcCaption() {
    442486        return iptcCaption;
     
    448492     * @since 15219
    449493     */
     494    @Override
    450495    public String getIptcHeadline() {
    451496        return iptcHeadline;
     
    457502     * @since 15219
    458503     */
     504    @Override
    459505    public List<String> getIptcKeywords() {
    460506        return iptcKeywords;
     
    466512     * @since 15219
    467513     */
     514    @Override
    468515    public String getIptcObjectName() {
    469516        return iptcObjectName;
     
    643690     * @since 9270
    644691     */
     692    @Override
    645693    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());
    776700    }
    777701
     
    787711    }
    788712
    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 
    801713    /**
    802714     * Get the projection type for this entry
     
    804716     * @since 18246
    805717     */
     718    @Override
    806719    public Projections getProjectionType() {
    807720        return this.cameraProjection;
     721    }
     722
     723    @Override
     724    public void setProjectionType(Projections newProjection) {
     725        this.cameraProjection = newProjection;
    808726    }
    809727
  • trunk/src/org/openstreetmap/josm/data/imagery/street_level/IImageEntry.java

    r18427 r18592  
    11// License: GPL. For details, see LICENSE file.
    22package org.openstreetmap.josm.data.imagery.street_level;
     3
     4import static org.openstreetmap.josm.tools.I18n.tr;
    35
    46import java.awt.Dimension;
     
    1315
    1416import org.openstreetmap.josm.data.coor.ILatLon;
     17import org.openstreetmap.josm.gui.layer.geoimage.ImageMetadata;
     18import org.openstreetmap.josm.gui.layer.geoimage.ImageUtils;
    1519import org.openstreetmap.josm.gui.layer.geoimage.ImageViewerDialog;
     20import org.openstreetmap.josm.tools.ExifReader;
     21import org.openstreetmap.josm.tools.ImageProvider;
     22import org.openstreetmap.josm.tools.Logging;
    1623
    1724/**
     
    138145     * @throws IOException if any I/O error occurs
    139146     */
    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    }
    141165
    142166    /**
  • trunk/src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java

    r18591 r18592  
    692692        };
    693693        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         }
    700694    }
    701695
  • trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ImageEntry.java

    r18591 r18592  
    22package org.openstreetmap.josm.gui.layer.geoimage;
    33
    4 import static org.openstreetmap.josm.tools.I18n.tr;
    5 
    64import java.awt.Dimension;
    7 import java.awt.Graphics2D;
    85import java.awt.Image;
    9 import java.awt.geom.AffineTransform;
    106import java.awt.image.BufferedImage;
    117import java.io.File;
    128import java.io.IOException;
    13 import java.io.UncheckedIOException;
    149import java.net.MalformedURLException;
    1510import java.net.URL;
    1611import java.util.Collections;
    1712import java.util.Objects;
     13
    1814import javax.imageio.IIOParam;
    19 import javax.imageio.ImageReadParam;
    20 import javax.imageio.ImageReader;
    2115
    2216import org.openstreetmap.josm.data.ImageData;
    2317import org.openstreetmap.josm.data.gpx.GpxImageEntry;
    2418import 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;
    2819import org.openstreetmap.josm.tools.Utils;
    2920
     
    194185    @Override
    195186    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);
    208188    }
    209189
     
    211191        return getFile().toURI().toURL();
    212192    }
    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     }
    245193}
  • trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ImageViewerDialog.java

    r18591 r18592  
    4949import org.openstreetmap.josm.gui.ExtendedDialog;
    5050import org.openstreetmap.josm.gui.MainApplication;
     51import org.openstreetmap.josm.gui.MapFrame;
    5152import org.openstreetmap.josm.gui.datatransfer.ClipboardUtils;
    5253import org.openstreetmap.josm.gui.dialogs.DialogsPanel;
     
    113114     */
    114115    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        }
    117124        return dialog;
    118125    }
     
    241248            invalidLayers.forEach(this.tabbedEntries::remove);
    242249            addButtonsForImageLayers();
     250            this.layers.invalidate();
    243251        }
    244252        this.revalidate();
     
    904912            removedData.removeImageDataUpdateListener(this);
    905913        }
     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());
    906916    }
    907917
  • trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/ImageMarker.java

    r14153 r18592  
    22package org.openstreetmap.josm.gui.layer.markerlayer;
    33
    4 import java.awt.BorderLayout;
    5 import java.awt.Cursor;
    6 import java.awt.GraphicsEnvironment;
    7 import java.awt.Image;
     4import static org.openstreetmap.josm.tools.I18n.tr;
     5
    86import java.awt.event.ActionEvent;
     7import java.net.URISyntaxException;
    98import java.net.URL;
     9import java.time.Instant;
    1010import java.util.Collections;
     11import java.util.function.Supplier;
    1112
    12 import javax.swing.Icon;
    13 import javax.swing.ImageIcon;
    14 import javax.swing.JDialog;
    15 import javax.swing.JLabel;
    1613import javax.swing.JOptionPane;
    17 import javax.swing.JPanel;
    18 import javax.swing.JScrollPane;
    19 import javax.swing.JToggleButton;
    20 import javax.swing.JViewport;
    2114
    2215import org.openstreetmap.josm.data.coor.LatLon;
     
    2417import org.openstreetmap.josm.data.gpx.GpxLink;
    2518import org.openstreetmap.josm.data.gpx.WayPoint;
    26 import org.openstreetmap.josm.gui.MainApplication;
    27 import org.openstreetmap.josm.tools.ImageProvider;
     19import org.openstreetmap.josm.gui.Notification;
     20import org.openstreetmap.josm.gui.layer.geoimage.ImageViewerDialog;
     21import org.openstreetmap.josm.gui.layer.geoimage.RemoteEntry;
     22import org.openstreetmap.josm.tools.Logging;
     23import org.openstreetmap.josm.tools.Utils;
    2824
    2925/**
     
    4339    }
    4440
    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()));
    7344    }
    7445
    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();
    8567        }
    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;
    87121    }
    88122
Note: See TracChangeset for help on using the changeset viewer.