Changeset 6392 in josm


Ignore:
Timestamp:
2013-11-18T03:05:56+01:00 (10 years ago)
Author:
Don-vip
Message:

fix #9274 - GeoImageLayer functionality enhancements (patch by holgerosm)

Location:
trunk/src/org/openstreetmap/josm
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/gui/layer/geoimage/CorrelateGpxWithImages.java

    r6318 r6392  
    12091209                    curImg.tmp.setElevation(curElevation);
    12101210                    curImg.tmp.setGpsTime(new Date(curImg.getExifTime().getTime() - offset));
     1211                    curImg.flagNewGpsData();
    12111212                    ret++;
    12121213                }
     
    12381239                }
    12391240                curImg.tmp.setGpsTime(new Date(curImg.getExifTime().getTime() - offset));
     1241                curImg.flagNewGpsData();
    12401242
    12411243                ret++;
  • trunk/src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java

    r6316 r6392  
    2323import java.util.ArrayList;
    2424import java.util.Arrays;
     25import java.util.Calendar;
    2526import java.util.Collection;
    2627import java.util.Collections;
     28import java.util.Date;
     29import java.util.GregorianCalendar;
    2730import java.util.HashSet;
    2831import java.util.LinkedHashSet;
     
    3033import java.util.List;
    3134import java.util.Set;
     35import java.util.TimeZone;
    3236
    3337import javax.swing.Action;
     
    270274     */
    271275    public GeoImageLayer(final List<ImageEntry> data, GpxLayer gpxLayer) {
    272         super(tr("Geotagged Images"));
    273 
     276        this(data, gpxLayer, null, false);
     277    }
     278
     279    /**
     280     * Constructs a new {@code GeoImageLayer}.
     281     * @param data The list of images to display
     282     * @param gpxLayer The associated GPX layer
     283     * @param name Layer name
     284     * @since 6392
     285     */
     286    public GeoImageLayer(final List<ImageEntry> data, GpxLayer gpxLayer, final String name) {
     287        this(data, gpxLayer, name, false);
     288    }
     289
     290    /**
     291     * Constructs a new {@code GeoImageLayer}.
     292     * @param data The list of images to display
     293     * @param gpxLayer The associated GPX layer
     294     * @param useThumbs Thumbnail display flag
     295     * @since 6392
     296     */
     297    public GeoImageLayer(final List<ImageEntry> data, GpxLayer gpxLayer, boolean useThumbs) {
     298        this(data, gpxLayer, null, useThumbs);
     299    }
     300
     301    /**
     302     * Constructs a new {@code GeoImageLayer}.
     303     * @param data The list of images to display
     304     * @param gpxLayer The associated GPX layer
     305     * @param name Layer name
     306     * @param useThumbs Thumbnail display flag
     307     * @since 6392
     308     */
     309    public GeoImageLayer(final List<ImageEntry> data, GpxLayer gpxLayer, final String name, boolean useThumbs) {
     310        super(name != null ? name : tr("Geotagged Images"));
    274311        Collections.sort(data);
    275312        this.data = data;
    276313        this.gpxLayer = gpxLayer;
     314        this.useThumbs = useThumbs;
    277315    }
    278316
     
    403441        Rectangle clip = g.getClipBounds();
    404442        if (useThumbs) {
     443            if (!thumbsLoaded) {
     444                loadThumbs();
     445            }
     446
    405447            if (null == offscreenBuffer || offscreenBuffer.getWidth() != width  // reuse the old buffer if possible
    406448                    || offscreenBuffer.getHeight() != height) {
     
    458500                Point p = mv.getPoint(e.getPos());
    459501
    460                 if (e.thumbnail != null) {
     502                if (useThumbs && e.thumbnail != null) {
    461503                    Dimension d = scaledDimension(e.thumbnail);
    462504                    g.setColor(new Color(128, 0, 0, 122));
     
    572614            // Do nothing
    573615        }
     616
     617        // Time and date. We can have these cases:
     618        // 1) GPS_TIME_STAMP not set -> date/time will be null
     619        // 2) GPS_DATE_STAMP not set -> use EXIF date or set to default
     620        // 3) GPS_TIME_STAMP and GPS_DATE_STAMP are set
     621        int[] timeStampComps = dirGps.getIntArray(GpsDirectory.TAG_GPS_TIME_STAMP);
     622        if (timeStampComps != null) {
     623            int gpsHour = timeStampComps[0];
     624            int gpsMin = timeStampComps[1];
     625            int gpsSec = timeStampComps[2];
     626            Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
     627
     628            // We have the time. Next step is to check if the GPS date stamp is set.
     629            // dirGps.getString() always succeeds, but the return value might be null.
     630            String dateStampStr = dirGps.getString(GpsDirectory.TAG_GPS_DATE_STAMP);
     631            if (dateStampStr != null && dateStampStr.matches("^\\d+:\\d+:\\d+$")) {
     632                String[] dateStampComps = dateStampStr.split(":");
     633                cal.set(Calendar.YEAR, Integer.parseInt(dateStampComps[0]));
     634                cal.set(Calendar.MONTH, Integer.parseInt(dateStampComps[1]) - 1);
     635                cal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(dateStampComps[2]));
     636            }
     637            else {
     638                // No GPS date stamp in EXIF data.  Copy it from EXIF time.
     639                // Date is not set if EXIF time is not available.
     640                Date exifTime = e.getExifTime();
     641                if (exifTime != null) {
     642                    // Time not set yet, so we can copy everything, not just date.
     643                    cal.setTime(exifTime);
     644                }
     645            }
     646
     647            cal.set(Calendar.HOUR_OF_DAY, gpsHour);
     648            cal.set(Calendar.MINUTE, gpsMin);
     649            cal.set(Calendar.SECOND, gpsSec);
     650
     651            e.setExifGpsTime(cal.getTime());
     652        }
    574653    }
    575654
     
    668747    }
    669748
     749    /**
     750     * Removes a photo from the list of images by index.
     751     * @param idx Image index
     752     * @since 6392
     753     */
     754    public void removePhotoByIdx(int idx) {
     755        if (idx >= 0 && data != null && idx < data.size()) {
     756            data.remove(idx);
     757        }
     758    }
     759
     760    /**
     761     * Returns the image that matches the position of the mouse event.
     762     * @param evt Mouse event
     763     * @return Image at mouse position, or {@code null} if there is no image at the mouse position
     764     * @since 6392
     765     */
     766    public ImageEntry getPhotoUnderMouse(MouseEvent evt) {
     767        if (data != null) {
     768            for (int idx = data.size() - 1; idx >= 0; --idx) {
     769                ImageEntry img = data.get(idx);
     770                if (img.getPos() == null) {
     771                    continue;
     772                }
     773                Point p = Main.map.mapView.getPoint(img.getPos());
     774                Rectangle r;
     775                if (useThumbs && img.thumbnail != null) {
     776                    Dimension d = scaledDimension(img.thumbnail);
     777                    r = new Rectangle(p.x - d.width / 2, p.y - d.height / 2, d.width, d.height);
     778                } else {
     779                    r = new Rectangle(p.x - icon.getIconWidth() / 2,
     780                                      p.y - icon.getIconHeight() / 2,
     781                                      icon.getIconWidth(),
     782                                      icon.getIconHeight());
     783                }
     784                if (r.contains(evt.getPoint())) {
     785                    return img;
     786                }
     787            }
     788        }
     789        return null;
     790    }
     791
     792    /**
     793     * Clears the currentPhoto, i.e. remove select marker, and optionally repaint.
     794     * @param repaint Repaint flag
     795     * @since 6392
     796     */
     797    public void clearCurrentPhoto(boolean repaint) {
     798        currentPhoto = -1;
     799        if (repaint) {
     800            updateBufferAndRepaint();
     801        }
     802    }
     803
     804    /**
     805     * Clears the currentPhoto of the other GeoImageLayer's. Otherwise there could be multiple selected photos.
     806     */
     807    private void clearOtherCurrentPhotos() {
     808        for (GeoImageLayer layer:
     809                 Main.map.mapView.getLayersOfType(GeoImageLayer.class)) {
     810            if (layer != this) {
     811                layer.clearCurrentPhoto(false);
     812            }
     813        }
     814    }
     815
     816    private static List<MapMode> supportedMapModes = null;
     817
     818    /**
     819     * Registers a map mode for which the functionality of this layer should be available.
     820     * @param mapMode Map mode to be registered
     821     * @since 6392
     822     */
     823    public static void registerSupportedMapMode(MapMode mapMode) {
     824        if (supportedMapModes == null) {
     825            supportedMapModes = new ArrayList<MapMode>();
     826        }
     827        supportedMapModes.add(mapMode);
     828    }
     829
     830    /**
     831     * Determines if the functionality of this layer is available in
     832     * the specified map mode.  SelectAction is supported by default,
     833     * other map modes can be registered.
     834     * @param mapMode Map mode to be checked
     835     * @return {@code true} if the map mode is supported,
     836     *         {@code false} otherwise
     837     */
     838    private static final boolean isSupportedMapMode(MapMode mapMode) {
     839        if (mapMode instanceof SelectAction) return true;
     840        if (supportedMapModes != null) {
     841            for (MapMode supmmode: supportedMapModes) {
     842                if (mapMode == supmmode) {
     843                    return true;
     844                }
     845            }
     846        }
     847        return false;
     848    }
     849
    670850    private MouseAdapter mouseAdapter = null;
    671851    private MapModeChangeListener mapModeListener = null;
     
    675855        mouseAdapter = new MouseAdapter() {
    676856            private final boolean isMapModeOk() {
    677                 return Main.map.mapMode == null || Main.map.mapMode instanceof SelectAction;
     857                return Main.map.mapMode == null || isSupportedMapMode(Main.map.mapMode);
    678858            }
    679859            @Override public void mousePressed(MouseEvent e) {
     
    699879                    Point p = Main.map.mapView.getPoint(e.getPos());
    700880                    Rectangle r;
    701                     if (e.thumbnail != null) {
     881                    if (useThumbs && e.thumbnail != null) {
    702882                        Dimension d = scaledDimension(e.thumbnail);
    703883                        r = new Rectangle(p.x - d.width / 2, p.y - d.height / 2, d.width, d.height);
     
    709889                    }
    710890                    if (r.contains(ev.getPoint())) {
     891                        clearOtherCurrentPhotos();
    711892                        currentPhoto = i;
    712893                        ImageViewerDialog.showImage(GeoImageLayer.this, e);
     
    721902            @Override
    722903            public void mapModeChange(MapMode oldMapMode, MapMode newMapMode) {
    723                 if (newMapMode == null || (newMapMode instanceof org.openstreetmap.josm.actions.mapmode.SelectAction)) {
     904                if (newMapMode == null || isSupportedMapMode(newMapMode)) {
    724905                    Main.map.mapView.addMouseListener(mouseAdapter);
    725906                } else {
     
    816997        showPreviousPhoto();
    817998    }
     999
     1000    /**
     1001     * Returns the current thumbnail display status.
     1002     * {@code true}: thumbnails are displayed, {@code false}: an icon is displayed instead of thumbnails.
     1003     * @return Current thumbnail display status
     1004     * @since 6392
     1005     */
     1006    public boolean isUseThumbs() {
     1007        return useThumbs;
     1008    }
     1009
     1010    /**
     1011     * Enables or disables the display of thumbnails.  Does not update the display.
     1012     * @param useThumbs New thumbnail display status
     1013     * @since 6392
     1014     */
     1015    public void setUseThumbs(boolean useThumbs) {
     1016        this.useThumbs = useThumbs;
     1017        if (useThumbs && !thumbsLoaded) {
     1018            loadThumbs();
     1019        }
     1020    }
    8181021}
  • trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ImageEntry.java

    r6296 r6392  
    1818    private Double exifImgDir;
    1919    private Date exifTime;
     20    /**
     21     * Flag isNewGpsData indicates that the GPS data of the image is new or has changed.
     22     * GPS data includes the position, speed, elevation, time (e.g. as extracted from the GPS track).
     23     * The flag can used to decide for which image file the EXIF GPS data is (re-)written.
     24     */
     25    private boolean isNewGpsData = false;
     26    /** Temporary source of GPS time if not correlated with GPX track. */
     27    private Date exifGpsTime = null;
    2028    Image thumbnail;
    2129
    22     /** The following values are computed from the correlation with the gpx track */
     30    /**
     31     * The following values are computed from the correlation with the gpx track
     32     * or extracted from the image EXIF data.
     33     */
    2334    private CachedLatLon pos;
    2435    /** Speed in kilometer per second */
     
    7586        return exifTime;
    7687    }
     88   
     89    /**
     90     * Returns the EXIF GPS time.
     91     * @return the EXIF GPS time
     92     * @since 6392
     93     */
     94    public final Date getExifGpsTime() {
     95        return exifGpsTime;
     96    }
     97   
    7798    public LatLon getExifCoor() {
    7899        return exifCoor;
     
    110131        this.exifTime = exifTime;
    111132    }
     133   
     134    /**
     135     * Sets the EXIF GPS time.
     136     * @param exifGpsTime the EXIF GPS time
     137     * @since 6392
     138     */
     139    public final void setExifGpsTime(Date exifGpsTime) {
     140        this.exifGpsTime = exifGpsTime;
     141    }
     142   
    112143    public void setGpsTime(Date gpsTime) {
    113144        this.gpsTime = gpsTime;
     
    184215        return result;
    185216    }
     217
     218    /**
     219     * Indicates that the image has new GPS data.
     220     * That flag is used e.g. by the photo_geotagging plugin to decide for which image
     221     * file the EXIF GPS data needs to be (re-)written.
     222     * @since 6392
     223     */
     224    public void flagNewGpsData() {
     225        isNewGpsData = true;
     226        // We need to set the GPS time to tell the system (mainly the photo_geotagging plug-in)
     227        // that the GPS data has changed. Check for existing GPS time and take EXIF time otherwise.
     228        // This can be removed once isNewGpsData is used instead of the GPS time.
     229        if (gpsTime == null) {
     230            Date gpsTime = getExifGpsTime();
     231            if (gpsTime == null) {
     232                gpsTime = getExifTime();
     233                if (gpsTime == null) {
     234                    // Time still not set, take the current time.
     235                    gpsTime = new Date();
     236                }
     237            }
     238            setGpsTime(gpsTime);
     239        }
     240        if (tmp != null && tmp.getGpsTime() == null) {
     241            // tmp.gpsTime overrides gpsTime, so we set it too.
     242            tmp.setGpsTime(getGpsTime());
     243        }
     244    }
     245
     246    /**
     247     * Queries whether the GPS data changed.
     248     * @return {@code true} if GPS data changed, {@code false} otherwise
     249     * @since 6392
     250     */
     251    public boolean hasNewGpsData() {
     252        return isNewGpsData;
     253    }
    186254}
  • trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ImageViewerDialog.java

    r6362 r6392  
    214214
    215215    public void displayImage(GeoImageLayer layer, ImageEntry entry) {
     216        boolean imageChanged;
     217
    216218        synchronized(this) {
    217219            // TODO: pop up image dialog but don't load image again
    218220
     221            imageChanged = currentEntry != entry;
     222
    219223            if (centerView && Main.isDisplayingMapView() && entry != null && entry.getPos() != null) {
    220224                Main.map.mapView.zoomTo(entry.getPos());
     
    226230
    227231        if (entry != null) {
    228             imgDisplay.setImage(entry.getFile(), entry.getExifOrientation());
     232            if (imageChanged) {
     233                // Set only if the image is new to preserve zoom and position if the same image is redisplayed
     234                // (e.g. to update the OSD).
     235                imgDisplay.setImage(entry.getFile(), entry.getExifOrientation());
     236            }
    229237            setTitle("Geotagged Images" + (entry.getFile() != null ? " - " + entry.getFile().getName() : ""));
    230238            StringBuffer osd = new StringBuffer(entry.getFile() != null ? entry.getFile().getName() : "");
     
    302310        return currentEntry != null;
    303311    }
     312
     313    /**
     314     * Returns the currently displayed image.
     315     * @return Currently displayed image or {@code null}
     316     * @since 6392
     317     */
     318    public static ImageEntry getCurrentImage() {
     319        return getInstance().currentEntry;
     320    }
     321
     322    /**
     323     * Returns the layer associated with the image.
     324     * @return Layer associated with the image
     325     * @since 6392
     326     */
     327    public static GeoImageLayer getCurrentLayer() {
     328        return getInstance().currentLayer;
     329    }
    304330}
  • trunk/src/org/openstreetmap/josm/io/session/GeoImageSessionExporter.java

    r6070 r6392  
    6666        layerElem.setAttribute("type", "geoimage");
    6767        layerElem.setAttribute("version", "0.1");
     68        addAttr("show-thumbnails", Boolean.toString(layer.isUseThumbs()), layerElem, support);
    6869
    6970        for (ImageEntry entry : layer.getImages()) {
     
    101102                addAttr("exif-time", Long.toString(entry.getExifTime().getTime()), imgElem, support);
    102103            }
     104            if (entry.getExifGpsTime() != null) {
     105                addAttr("exif-gps-time", Long.toString(entry.getExifGpsTime().getTime()), imgElem, support);
     106            }
    103107            if (entry.getExifCoor() != null) {
    104108                Element posElem = support.createElement("exif-coordinates");
     
    109113            if (entry.getExifImgDir() != null) {
    110114                addAttr("exif-image-direction", entry.getExifImgDir().toString(), imgElem, support);
     115            }
     116            if (entry.hasNewGpsData()) {
     117                addAttr("is-new-gps-data", Boolean.toString(entry.hasNewGpsData()), imgElem, support);
    111118            }
    112119
  • trunk/src/org/openstreetmap/josm/io/session/GeoImageSessionImporter.java

    r6093 r6392  
    3232        List<ImageEntry> entries = new ArrayList<ImageEntry>();
    3333        NodeList imgNodes = elem.getChildNodes();
     34        boolean useThumbs = false;
    3435        for (int i=0; i<imgNodes.getLength(); ++i) {
    3536            Node imgNode = imgNodes.item(i);
     
    5657                                } else if (attrElem.getTagName().equals("gps-time")) {
    5758                                    entry.setGpsTime(new Date(Long.parseLong(attrElem.getTextContent())));
    58                                 } else if (attrElem.getTagName().equals("gps-orientation")) {
     59                                } else if (attrElem.getTagName().equals("exif-orientation")) {
    5960                                    entry.setExifOrientation(Integer.parseInt(attrElem.getTextContent()));
    6061                                } else if (attrElem.getTagName().equals("exif-time")) {
    6162                                    entry.setExifTime(new Date(Long.parseLong(attrElem.getTextContent())));
     63                                } else if (attrElem.getTagName().equals("exif-gps-time")) {
     64                                    entry.setExifGpsTime(new Date(Long.parseLong(attrElem.getTextContent())));
    6265                                } else if (attrElem.getTagName().equals("exif-coordinates")) {
    6366                                    double lat = Double.parseDouble(attrElem.getAttribute("lat"));
     
    6669                                } else if (attrElem.getTagName().equals("exif-image-direction")) {
    6770                                    entry.setExifImgDir(Double.parseDouble(attrElem.getTextContent()));
     71                                } else if (attrElem.getTagName().equals("is-new-gps-data")) {
     72                                    if (Boolean.parseBoolean(attrElem.getTextContent())) {
     73                                        entry.flagNewGpsData();
     74                                    }
    6875                                }
    6976                                // TODO: handle thumbnail loading
     
    7481                    }
    7582                    entries.add(entry);
     83                } else if (imgElem.getTagName().equals("show-thumbnails")) {
     84                    useThumbs = Boolean.parseBoolean(imgElem.getTextContent());
    7685                }
    7786            }
     
    8796        }
    8897
    89         return new GeoImageLayer(entries, gpxLayer);
     98        return new GeoImageLayer(entries, gpxLayer, useThumbs);
    9099    }
    91100
Note: See TracChangeset for help on using the changeset viewer.