Changeset 15045 in josm


Ignore:
Timestamp:
2019-05-04T14:17:27+02:00 (7 months ago)
Author:
Don-vip
Message:

fix #17576 - Fix timezones and DST for image correlation (patch by Bjoeni, modified)

Location:
trunk
Files:
3 edited

Legend:

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

    r14981 r15045  
    2929import java.util.ArrayList;
    3030import java.util.Arrays;
    31 import java.util.Collection;
    3231import java.util.Collections;
    3332import java.util.Comparator;
     
    3736import java.util.List;
    3837import java.util.Objects;
    39 import java.util.Optional;
    4038import java.util.TimeZone;
    4139import java.util.concurrent.TimeUnit;
     
    108106 * This class displays the window to select the GPX file and the offset (timezone + delta).
    109107 * Then it correlates the images of the layer with that GPX file.
     108 * @since 2566
    110109 */
    111110public class CorrelateGpxWithImages extends AbstractAction {
     
    541540     */
    542541    private class SetOffsetActionListener implements ActionListener {
    543 
    544         @Override
    545         public void actionPerformed(ActionEvent arg0) {
     542        JCheckBox ckDst;
     543        ImageDisplay imgDisp;
     544        JLabel lbExifTime;
     545        JosmTextField tfGpsTime;
     546
     547        class TimeZoneItem implements Comparable<TimeZoneItem> {
     548            private final TimeZone tz;
     549            private String rawString;
     550            private String dstString;
     551
     552            TimeZoneItem(TimeZone tz) {
     553                this.tz = tz;
     554            }
     555
     556            public String getFormattedString() {
     557                if (ckDst.isSelected()) {
     558                    return getDstString();
     559                } else {
     560                    return getRawString();
     561                }
     562            }
     563
     564            public String getDstString() {
     565                if (dstString == null) {
     566                    dstString = formatTimezone(tz.getRawOffset() + tz.getDSTSavings());
     567                }
     568                return dstString;
     569            }
     570
     571            public String getRawString() {
     572                if (rawString == null) {
     573                    rawString = formatTimezone(tz.getRawOffset());
     574                }
     575                return rawString;
     576            }
     577
     578            public String getID() {
     579                return tz.getID();
     580            }
     581
     582            @Override
     583            public String toString() {
     584                return getID() + " (" + getFormattedString() + ')';
     585            }
     586
     587            @Override
     588            public int compareTo(TimeZoneItem o) {
     589                return getID().compareTo(o.getID());
     590            }
     591
     592            private String formatTimezone(int offset) {
     593                return new GpxTimezone((double) offset / TimeUnit.HOURS.toMillis(1)).formatTimezone();
     594            }
     595        }
     596
     597        @Override
     598        public void actionPerformed(ActionEvent e) {
    546599            SimpleDateFormat dateFormat = (SimpleDateFormat) DateUtils.getDateTimeFormat(DateFormat.SHORT, DateFormat.MEDIUM);
    547600
     
    552605                    BorderLayout.NORTH);
    553606
    554             ImageDisplay imgDisp = new ImageDisplay();
     607            imgDisp = new ImageDisplay();
    555608            imgDisp.setPreferredSize(new Dimension(300, 225));
    556609            panel.add(imgDisp, BorderLayout.CENTER);
     
    566619            panelTf.add(new JLabel(tr("Photo time (from exif):")), gc);
    567620
    568             JLabel lbExifTime = new JLabel();
     621            lbExifTime = new JLabel();
    569622            gc.gridx = 1;
    570623            gc.weightx = 1.0;
     
    581634            panelTf.add(new JLabel(tr("Gps time (read from the above photo): ")), gc);
    582635
    583             JosmTextField tfGpsTime = new JosmTextField(12);
     636            tfGpsTime = new JosmTextField(12);
    584637            tfGpsTime.setEnabled(false);
    585638            tfGpsTime.setMinimumSize(new Dimension(155, tfGpsTime.getMinimumSize().height));
     
    599652            gc.fill = GridBagConstraints.NONE;
    600653            gc.anchor = GridBagConstraints.WEST;
    601             panelTf.add(new JLabel(tr("I am in the timezone of: ")), gc);
     654            panelTf.add(new JLabel(tr("Photo taken in the timezone of: ")), gc);
     655
     656            ckDst = new JCheckBox(tr("Use daylight saving time (where applicable)"), Config.getPref().getBoolean("geoimage.timezoneid.dst"));
    602657
    603658            String[] tmp = TimeZone.getAvailableIDs();
    604             List<String> vtTimezones = new ArrayList<>(tmp.length);
     659            List<TimeZoneItem> vtTimezones = new ArrayList<>(tmp.length);
     660
     661            String defTzStr = Config.getPref().get("geoimage.timezoneid", "");
     662            if (defTzStr.isEmpty()) {
     663                defTzStr = TimeZone.getDefault().getID();
     664            }
     665            TimeZoneItem defTzItem = null;
    605666
    606667            for (String tzStr : tmp) {
    607                 TimeZone tz = TimeZone.getTimeZone(tzStr);
    608 
    609                 String tzDesc = tzStr + " (" +
    610                         new GpxTimezone(((double) tz.getRawOffset()) / TimeUnit.HOURS.toMillis(1)).formatTimezone() +
    611                         ')';
    612                 vtTimezones.add(tzDesc);
     668                TimeZoneItem tz = new TimeZoneItem(TimeZone.getTimeZone(tzStr));
     669                vtTimezones.add(tz);
     670                if (defTzStr.equals(tzStr)) {
     671                    defTzItem = tz;
     672                }
    613673            }
    614674
    615675            Collections.sort(vtTimezones);
    616676
    617             JosmComboBox<String> cbTimezones = new JosmComboBox<>(vtTimezones.toArray(new String[0]));
    618 
    619             String tzId = Config.getPref().get("geoimage.timezoneid", "");
    620             TimeZone defaultTz;
    621             if (tzId.isEmpty()) {
    622                 defaultTz = TimeZone.getDefault();
    623             } else {
    624                 defaultTz = TimeZone.getTimeZone(tzId);
    625             }
    626 
    627             cbTimezones.setSelectedItem(defaultTz.getID() + " (" +
    628                     new GpxTimezone(((double) defaultTz.getRawOffset()) / TimeUnit.HOURS.toMillis(1)).formatTimezone() +
    629                     ')');
     677            JosmComboBox<TimeZoneItem> cbTimezones = new JosmComboBox<>(vtTimezones.toArray(new TimeZoneItem[0]));
     678
     679            if (defTzItem != null) {
     680                cbTimezones.setSelectedItem(defTzItem);
     681            }
    630682
    631683            gc.gridx = 1;
     
    635687            panelTf.add(cbTimezones, gc);
    636688
     689            gc.gridy = 3;
     690            panelTf.add(ckDst, gc);
     691
     692            ckDst.addActionListener(x -> cbTimezones.repaint());
     693
    637694            panel.add(panelTf, BorderLayout.SOUTH);
    638695
     
    654711                int index = imgList.getSelectedIndex();
    655712                ImageEntry img = yLayer.getImageData().getImages().get(index);
    656                 imgDisp.setImage(img);
    657                 Date date = img.getExifTime();
    658                 if (date != null) {
    659                     DateFormat df = DateUtils.getDateTimeFormat(DateFormat.SHORT, DateFormat.MEDIUM);
    660                     lbExifTime.setText(df.format(date));
    661                     tfGpsTime.setText(df.format(date));
    662                     tfGpsTime.setCaretPosition(tfGpsTime.getText().length());
    663                     tfGpsTime.setEnabled(true);
    664                     tfGpsTime.requestFocus();
    665                 } else {
    666                     lbExifTime.setText(tr("No date"));
    667                     tfGpsTime.setText("");
    668                     tfGpsTime.setEnabled(false);
    669                 }
     713                updateExifComponents(img);
    670714            });
    671715            panelLst.add(new JScrollPane(imgList), BorderLayout.CENTER);
     
    679723                ImageEntry entry = new ImageEntry(fc.getSelectedFile());
    680724                entry.extractExif();
    681                 imgDisp.setImage(entry);
    682 
    683                 Date date = entry.getExifTime();
    684                 if (date != null) {
    685                     lbExifTime.setText(DateUtils.getDateTimeFormat(DateFormat.SHORT, DateFormat.MEDIUM).format(date));
    686                     tfGpsTime.setText(DateUtils.getDateFormat(DateFormat.SHORT).format(date)+' ');
    687                     tfGpsTime.setEnabled(true);
    688                 } else {
    689                     lbExifTime.setText(tr("No date"));
    690                     tfGpsTime.setText("");
    691                     tfGpsTime.setEnabled(false);
    692                 }
     725                updateExifComponents(entry);
    693726            });
    694727            panelLst.add(openButton, BorderLayout.PAGE_END);
     
    711744                try {
    712745                    delta = dateFormat.parse(lbExifTime.getText()).getTime()
    713                     - dateFormat.parse(tfGpsTime.getText()).getTime();
    714                 } catch (ParseException e) {
     746                          - dateFormat.parse(tfGpsTime.getText()).getTime();
     747                } catch (ParseException ex) {
    715748                    JOptionPane.showMessageDialog(MainApplication.getMainFrame(), tr("Error while parsing the date.\n"
    716749                            + "Please use the requested format"),
     
    719752                }
    720753
    721                 String selectedTz = (String) cbTimezones.getSelectedItem();
    722                 int pos = selectedTz.lastIndexOf('(');
    723                 tzId = selectedTz.substring(0, pos - 1);
    724                 String tzValue = selectedTz.substring(pos + 1, selectedTz.length() - 1);
    725 
    726                 Config.getPref().put("geoimage.timezoneid", tzId);
     754                TimeZoneItem selectedTz = (TimeZoneItem) cbTimezones.getSelectedItem();
     755
     756                Config.getPref().put("geoimage.timezoneid", selectedTz.getID());
     757                Config.getPref().putBoolean("geoimage.timezoneid.dst", ckDst.isSelected());
    727758                tfOffset.setText(GpxTimeOffset.milliseconds(delta).formatOffset());
    728                 tfTimezone.setText(tzValue);
     759                tfTimezone.setText(selectedTz.getFormattedString());
    729760
    730761                isOk = true;
     
    733764            statusBarUpdater.updateStatusBar();
    734765            yLayer.updateBufferAndRepaint();
     766        }
     767
     768        void updateExifComponents(ImageEntry img) {
     769            imgDisp.setImage(img);
     770            Date date = img.getExifTime();
     771            if (date != null) {
     772                DateFormat df = DateUtils.getDateTimeFormat(DateFormat.SHORT, DateFormat.MEDIUM);
     773                df.setTimeZone(DateUtils.UTC); // EXIF data does not contain timezone information and is read as UTC
     774                lbExifTime.setText(df.format(date));
     775                tfGpsTime.setText(df.format(date));
     776                tfGpsTime.setCaretPosition(tfGpsTime.getText().length());
     777                tfGpsTime.setEnabled(true);
     778                tfGpsTime.requestFocus();
     779            } else {
     780                lbExifTime.setText(tr("No date"));
     781                tfGpsTime.setText("");
     782                tfGpsTime.setEnabled(false);
     783            }
    735784        }
    736785    }
     
    769818    public void actionPerformed(ActionEvent ae) {
    770819        // Construct the list of loaded GPX tracks
    771         Collection<Layer> layerLst = MainApplication.getLayerManager().getLayers();
    772820        gpxLst.clear();
    773821        GpxDataWrapper defaultItem = null;
    774         for (Layer cur : layerLst) {
    775             if (cur instanceof GpxLayer) {
    776                 GpxLayer curGpx = (GpxLayer) cur;
    777                 GpxDataWrapper gdw = new GpxDataWrapper(curGpx.getName(), curGpx.data, curGpx.data.storageFile);
    778                 gpxLst.add(gdw);
    779                 if (cur == yLayer.gpxLayer) {
    780                     defaultItem = gdw;
    781                 }
     822        for (GpxLayer cur : MainApplication.getLayerManager().getLayersOfType(GpxLayer.class)) {
     823            GpxDataWrapper gdw = new GpxDataWrapper(cur.getName(), cur.data, cur.data.storageFile);
     824            gpxLst.add(gdw);
     825            if (cur == yLayer.gpxLayer) {
     826                defaultItem = gdw;
    782827            }
    783828        }
     
    801846        } else {
    802847            // select first GPX track associated to a file
    803             for (GpxDataWrapper item : gpxLst) {
    804                 if (item.file != null) {
    805                     cbGpx.setSelectedItem(item);
    806                     break;
    807                 }
    808             }
     848            gpxLst.stream().filter(i -> i.file != null).findFirst().ifPresent(cbGpx::setSelectedItem);
    809849        }
    810850        cbGpx.addActionListener(statusBarUpdaterWithRepaint);
     
    818858
    819859        try {
    820             timezone = GpxTimezone.parseTimezone(Optional.ofNullable(Config.getPref().get("geoimage.timezone", "0:00")).orElse("0:00"));
     860            String tz = Config.getPref().get("geoimage.timezone");
     861            if (!tz.isEmpty()) {
     862                timezone = GpxTimezone.parseTimezone(tz);
     863            } else {
     864                timezone = new GpxTimezone(TimeUnit.MILLISECONDS.toMinutes(TimeZone.getDefault().getRawOffset()) / 60.); //hours is double
     865            }
    821866        } catch (ParseException e) {
    822867            timezone = GpxTimezone.ZERO;
     
    837882        tfOffset.setText(delta.formatOffset());
    838883
    839         JButton buttonViewGpsPhoto = new JButton(tr("<html>Use photo of an accurate clock,<br>"
    840                 + "e.g. GPS receiver display</html>"));
     884        JButton buttonViewGpsPhoto = new JButton(tr("<html>Use photo of an accurate clock,<br>e.g. GPS receiver display</html>"));
    841885        buttonViewGpsPhoto.setIcon(ImageProvider.get("clock"));
    842886        buttonViewGpsPhoto.addActionListener(new SetOffsetActionListener());
     
    10551099                return tr("No gpx selected");
    10561100
    1057             final long offsetMs = ((long) (timezone.getHours() * TimeUnit.HOURS.toMillis(-1))) + delta.getMilliseconds(); // in milliseconds
     1101            final long offsetMs = ((long) (timezone.getHours() * TimeUnit.HOURS.toMillis(1))) + delta.getMilliseconds(); // in milliseconds
    10581102            lastNumMatched = GpxImageCorrelation.matchGpxTrack(dateImgLst, selGpx.data, offsetMs, forceTags);
    10591103
  • trunk/src/org/openstreetmap/josm/tools/date/DateUtils.java

    r14434 r15045  
    77import java.time.DateTimeException;
    88import java.time.Instant;
    9 import java.time.ZoneId;
    109import java.time.ZoneOffset;
    1110import java.time.ZonedDateTime;
     
    101100                parsePart2(str, 17),
    102101                0,
    103                 // consider EXIF date in default timezone
    104                 checkLayout(str, "xxxx:xx:xx xx:xx:xx") ? ZoneId.systemDefault() : ZoneOffset.UTC
     102                ZoneOffset.UTC
    105103            );
    106104            if (str.length() == 22 || str.length() == 25) {
     
    123121                parsePart2(str, 17),
    124122                parsePart3(str, 20) * 1_000_000,
    125                 // consider EXIF date in default timezone
    126                 checkLayout(str, "xxxx:xx:xx xx:xx:xx.xxx") ? ZoneId.systemDefault() : ZoneOffset.UTC
     123                ZoneOffset.UTC
    127124            );
    128125            if (str.length() == 29) {
  • trunk/test/unit/org/openstreetmap/josm/tools/date/DateUtilsTest.java

    r11921 r15045  
    7070    @Test
    7171    public void testExifDate() {
    72         setTimeZone(TimeZone.getTimeZone("GMT+8:00")); // parsing is timezone aware
    73         assertEquals(1443038712000L - 8 * 3600 * 1000, DateUtils.fromString("2015:09:23 20:05:12").getTime());
    74         assertEquals(1443038712888L - 8 * 3600 * 1000, DateUtils.fromString("2015:09:23 20:05:12.888").getTime());
     72        assertEquals(1443038712000L, DateUtils.fromString("2015:09:23 20:05:12").getTime());
     73        assertEquals(1443038712888L, DateUtils.fromString("2015:09:23 20:05:12.888").getTime());
    7574    }
    7675
Note: See TracChangeset for help on using the changeset viewer.