Changeset 9741 in josm


Ignore:
Timestamp:
2016-02-04T23:32:36+01:00 (9 years ago)
Author:
simon04
Message:

see #12486 - Gpx/image correlation: factor out classes Timezone, Offset

Location:
trunk
Files:
2 edited

Legend:

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

    r9727 r9741  
    3535import java.util.Hashtable;
    3636import java.util.List;
     37import java.util.Objects;
    3738import java.util.TimeZone;
    3839import java.util.zip.GZIPInputStream;
     
    9495
    9596    private final transient GeoImageLayer yLayer;
    96     private double timezone;
    97     private long delta;
     97    private Timezone timezone;
     98    private Offset delta;
    9899
    99100    /**
     
    122123            // Parse values again, to display an error if the format is not recognized
    123124            try {
    124                 timezone = parseTimezone(tfTimezone.getText().trim());
     125                timezone = Timezone.parseTimezone(tfTimezone.getText().trim());
    125126            } catch (ParseException e) {
    126127                JOptionPane.showMessageDialog(Main.parent, e.getMessage(),
     
    130131
    131132            try {
    132                 delta = parseOffset(tfOffset.getText().trim());
     133                delta = Offset.parseOffset(tfOffset.getText().trim());
    133134            } catch (ParseException e) {
    134135                JOptionPane.showMessageDialog(Main.parent, e.getMessage(),
     
    168169                break;
    169170            case DONE:
    170                 Main.pref.put("geoimage.timezone", formatTimezone(timezone));
    171                 Main.pref.put("geoimage.delta", Long.toString(delta * 1000));
     171                Main.pref.put("geoimage.timezone", timezone.formatTimezone());
     172                Main.pref.put("geoimage.delta", delta.formatOffset());
    172173                Main.pref.put("geoimage.showThumbs", yLayer.useThumbs);
    173174
     
    410411
    411412                String tzDesc = new StringBuilder(tzStr).append(" (")
    412                 .append(formatTimezone(tz.getRawOffset() / 3600000.0))
     413                .append(new Timezone(tz.getRawOffset() / 3600000.0).formatTimezone())
    413414                .append(')').toString();
    414415                vtTimezones.add(tzDesc);
     
    428429
    429430            cbTimezones.setSelectedItem(new StringBuilder(defaultTz.getID()).append(" (")
    430                     .append(formatTimezone(defaultTz.getRawOffset() / 3600000.0))
     431                    .append(new Timezone(defaultTz.getRawOffset() / 3600000.0).formatTimezone())
    431432                    .append(')').toString());
    432433
     
    551552
    552553                Main.pref.put("geoimage.timezoneid", tzId);
    553                 tfOffset.setText(Long.toString(delta / 1000));
     554                tfOffset.setText(Offset.milliseconds(delta).formatOffset());
    554555                tfTimezone.setText(tzValue);
    555556
     
    609610        }
    610611        try {
    611             timezone = parseTimezone(prefTimezone);
     612            timezone = Timezone.parseTimezone(prefTimezone);
    612613        } catch (ParseException e) {
    613             timezone = 0;
     614            timezone = Timezone.ZERO;
    614615        }
    615616
    616617        tfTimezone = new JosmTextField(10);
    617         tfTimezone.setText(formatTimezone(timezone));
     618        tfTimezone.setText(timezone.formatTimezone());
    618619
    619620        try {
    620             delta = parseOffset(Main.pref.get("geoimage.delta", "0"));
     621            delta = Offset.parseOffset(Main.pref.get("geoimage.delta", "0"));
    621622        } catch (ParseException e) {
    622             delta = 0;
    623         }
    624         delta = delta / 1000;  // milliseconds -> seconds
     623            delta = Offset.ZERO;
     624        }
    625625
    626626        tfOffset = new JosmTextField(10);
    627         tfOffset.setText(Long.toString(delta));
     627        tfOffset.setText(delta.formatOffset());
    628628
    629629        JButton buttonViewGpsPhoto = new JButton(tr("<html>Use photo of an accurate clock,<br>"
     
    813813        private String statusText() {
    814814            try {
    815                 timezone = parseTimezone(tfTimezone.getText().trim());
    816                 delta = parseOffset(tfOffset.getText().trim());
     815                timezone = Timezone.parseTimezone(tfTimezone.getText().trim());
     816                delta = Offset.parseOffset(tfOffset.getText().trim());
    817817            } catch (ParseException e) {
    818818                return e.getMessage();
     
    839839                return tr("No gpx selected");
    840840
    841             final long offset_ms = ((long) (timezone * 3600) + delta) * 1000; // in milliseconds
     841            final long offset_ms = ((long) (timezone.getHours() * 3600 * 1000)) + delta.getMilliseconds(); // in milliseconds
    842842            lastNumMatched = matchGpxTrack(dateImgLst, selGpx.data, offset_ms);
    843843
     
    869869        public void actionPerformed(ActionEvent arg0) {
    870870
    871             long diff = delta + Math.round(timezone*60*60);
     871            long diff = delta.getSeconds() + Math.round(timezone.getHours() * 60 * 60);
    872872
    873873            double diffInH = (double) diff/(60*60);    // hours
     
    934934
    935935                    try {
    936                         timezone = parseTimezone(zone);
     936                        timezone = Timezone.parseTimezone(zone);
    937937                    } catch (ParseException pe) {
    938938                        throw new RuntimeException(pe);
    939939                    }
    940                     delta = sldMinutes.getValue()*60 + sldSeconds.getValue();
     940                    delta = Offset.seconds(sldMinutes.getValue() * 60 + sldSeconds.getValue() + 24 * 60 * 60L * dayOffset); // add the day offset
    941941
    942942                    tfTimezone.getDocument().removeDocumentListener(statusBarUpdater);
    943943                    tfOffset.getDocument().removeDocumentListener(statusBarUpdater);
    944944
    945                     tfTimezone.setText(formatTimezone(timezone));
    946                     tfOffset.setText(Long.toString(delta + 24*60*60L*dayOffset));    // add the day offset to the offset field
     945                    tfTimezone.setText(timezone.formatTimezone());
     946                    tfOffset.setText(delta.formatOffset());
    947947
    948948                    tfTimezone.getDocument().addDocumentListener(statusBarUpdater);
     
    10091009     * @param imgs the images to correlate
    10101010     * @param gpx the gpx track to correlate to
    1011      * @return a pair of timezone (in hours) and offset (in seconds)
     1011     * @return a pair of timezone and offset
    10121012     * @throws IndexOutOfBoundsException when there are no images
    10131013     * @throws NoGpxTimestamps when the gpx track does not contain a timestamp
    10141014     */
    1015     static Pair<Double, Long> autoGuess(List<ImageEntry> imgs, GpxData gpx) throws IndexOutOfBoundsException, NoGpxTimestamps {
     1015    static Pair<Timezone, Offset> autoGuess(List<ImageEntry> imgs, GpxData gpx) throws IndexOutOfBoundsException, NoGpxTimestamps {
    10161016
    10171017        // Init variables
     
    10561056        final double timezone = (double) Math.round(tz * 2) / 2; // hours, rounded to one decimal place
    10571057        final long delta = Math.round(diff - timezone * 60 * 60); // seconds
    1058         return Pair.create(timezone, delta);
     1058        return Pair.create(new Timezone(timezone), Offset.seconds(delta));
    10591059    }
    10601060
     
    10711071
    10721072            try {
    1073                 final Pair<Double, Long> r = autoGuess(imgs, gpx);
     1073                final Pair<Timezone, Offset> r = autoGuess(imgs, gpx);
    10741074                timezone = r.a;
    10751075                delta = r.b;
     
    10891089            tfOffset.getDocument().removeDocumentListener(statusBarUpdater);
    10901090
    1091             tfTimezone.setText(formatTimezone(timezone));
    1092             tfOffset.setText(Long.toString(delta));
     1091            tfTimezone.setText(timezone.formatTimezone());
     1092            tfOffset.setText(delta.formatOffset());
    10931093            tfOffset.requestFocus();
    10941094
     
    13181318    }
    13191319
    1320     static String formatTimezone(double timezone) {
    1321         StringBuilder ret = new StringBuilder();
    1322 
    1323         if (timezone < 0) {
    1324             ret.append('-');
    1325             timezone = -timezone;
    1326         } else {
    1327             ret.append('+');
    1328         }
    1329         ret.append((long) timezone).append(':');
    1330         int minutes = (int) ((timezone % 1) * 60);
    1331         if (minutes < 10) {
    1332             ret.append('0');
    1333         }
    1334         ret.append(minutes);
    1335 
    1336         return ret.toString();
    1337     }
    1338 
    1339     static double parseTimezone(String timezone) throws ParseException {
    1340 
    1341         if (timezone.isEmpty())
    1342             return 0;
    1343 
    1344         String error = tr("Error while parsing timezone.\nExpected format: {0}", "+H:MM");
    1345 
    1346         char sgnTimezone = '+';
    1347         StringBuilder hTimezone = new StringBuilder();
    1348         StringBuilder mTimezone = new StringBuilder();
    1349         int state = 1; // 1=start/sign, 2=hours, 3=minutes.
    1350         for (int i = 0; i < timezone.length(); i++) {
    1351             char c = timezone.charAt(i);
    1352             switch (c) {
    1353             case ' ' :
    1354                 if (state != 2 || hTimezone.length() != 0)
    1355                     throw new ParseException(error, i);
    1356                 break;
    1357             case '+' :
    1358             case '-' :
    1359                 if (state == 1) {
    1360                     sgnTimezone = c;
    1361                     state = 2;
    1362                 } else
    1363                     throw new ParseException(error, i);
    1364                 break;
    1365             case ':' :
    1366             case '.' :
    1367                 if (state == 2) {
    1368                     state = 3;
    1369                 } else
    1370                     throw new ParseException(error, i);
    1371                 break;
    1372             case '0' : case '1' : case '2' : case '3' : case '4' :
    1373             case '5' : case '6' : case '7' : case '8' : case '9' :
    1374                 switch(state) {
    1375                 case 1 :
    1376                 case 2 :
    1377                     state = 2;
    1378                     hTimezone.append(c);
    1379                     break;
    1380                 case 3 :
    1381                     mTimezone.append(c);
    1382                     break;
    1383                 default :
    1384                     throw new ParseException(error, i);
    1385                 }
    1386                 break;
    1387             default :
    1388                 throw new ParseException(error, i);
    1389             }
    1390         }
    1391 
    1392         int h = 0;
    1393         int m = 0;
    1394         try {
    1395             h = Integer.parseInt(hTimezone.toString());
    1396             if (mTimezone.length() > 0) {
    1397                 m = Integer.parseInt(mTimezone.toString());
    1398             }
    1399         } catch (NumberFormatException nfe) {
    1400             // Invalid timezone
    1401             throw new ParseException(error, 0);
    1402         }
    1403 
    1404         if (h > 12 || m > 59)
    1405             throw new ParseException(error, 0);
    1406         else
    1407             return (h + m / 60.0) * (sgnTimezone == '-' ? -1 : 1);
    1408     }
    1409 
    1410     static long parseOffset(String offset) throws ParseException {
    1411         String error = tr("Error while parsing offset.\nExpected format: {0}", "number");
    1412 
    1413         if (!offset.isEmpty()) {
     1320    static final class Timezone {
     1321
     1322        static final Timezone ZERO = new Timezone(0.0);
     1323        private final double timezone;
     1324
     1325        Timezone(double hours) {
     1326            this.timezone = hours;
     1327        }
     1328
     1329        public double getHours() {
     1330            return timezone;
     1331        }
     1332
     1333        String formatTimezone() {
     1334            StringBuilder ret = new StringBuilder();
     1335
     1336            double timezone = this.timezone;
     1337            if (timezone < 0) {
     1338                ret.append('-');
     1339                timezone = -timezone;
     1340            } else {
     1341                ret.append('+');
     1342            }
     1343            ret.append((long) timezone).append(':');
     1344            int minutes = (int) ((timezone % 1) * 60);
     1345            if (minutes < 10) {
     1346                ret.append('0');
     1347            }
     1348            ret.append(minutes);
     1349
     1350            return ret.toString();
     1351        }
     1352
     1353        static Timezone parseTimezone(String timezone) throws ParseException {
     1354
     1355            if (timezone.isEmpty())
     1356                return ZERO;
     1357
     1358            String error = tr("Error while parsing timezone.\nExpected format: {0}", "+H:MM");
     1359
     1360            char sgnTimezone = '+';
     1361            StringBuilder hTimezone = new StringBuilder();
     1362            StringBuilder mTimezone = new StringBuilder();
     1363            int state = 1; // 1=start/sign, 2=hours, 3=minutes.
     1364            for (int i = 0; i < timezone.length(); i++) {
     1365                char c = timezone.charAt(i);
     1366                switch (c) {
     1367                    case ' ':
     1368                        if (state != 2 || hTimezone.length() != 0)
     1369                            throw new ParseException(error, i);
     1370                        break;
     1371                    case '+':
     1372                    case '-':
     1373                        if (state == 1) {
     1374                            sgnTimezone = c;
     1375                            state = 2;
     1376                        } else
     1377                            throw new ParseException(error, i);
     1378                        break;
     1379                    case ':':
     1380                    case '.':
     1381                        if (state == 2) {
     1382                            state = 3;
     1383                        } else
     1384                            throw new ParseException(error, i);
     1385                        break;
     1386                    case '0':
     1387                    case '1':
     1388                    case '2':
     1389                    case '3':
     1390                    case '4':
     1391                    case '5':
     1392                    case '6':
     1393                    case '7':
     1394                    case '8':
     1395                    case '9':
     1396                        switch (state) {
     1397                            case 1:
     1398                            case 2:
     1399                                state = 2;
     1400                                hTimezone.append(c);
     1401                                break;
     1402                            case 3:
     1403                                mTimezone.append(c);
     1404                                break;
     1405                            default:
     1406                                throw new ParseException(error, i);
     1407                        }
     1408                        break;
     1409                    default:
     1410                        throw new ParseException(error, i);
     1411                }
     1412            }
     1413
     1414            int h = 0;
     1415            int m = 0;
    14141416            try {
    1415                 if (offset.startsWith("+")) {
    1416                     offset = offset.substring(1);
    1417                 }
    1418                 return Long.parseLong(offset);
     1417                h = Integer.parseInt(hTimezone.toString());
     1418                if (mTimezone.length() > 0) {
     1419                    m = Integer.parseInt(mTimezone.toString());
     1420                }
    14191421            } catch (NumberFormatException nfe) {
     1422                // Invalid timezone
    14201423                throw new ParseException(error, 0);
    14211424            }
    1422         } else {
    1423             return 0;
     1425
     1426            if (h > 12 || m > 59)
     1427                throw new ParseException(error, 0);
     1428            else
     1429                return new Timezone((h + m / 60.0) * (sgnTimezone == '-' ? -1 : 1));
     1430        }
     1431
     1432        @Override
     1433        public boolean equals(Object o) {
     1434            if (this == o) return true;
     1435            if (!(o instanceof Timezone)) return false;
     1436            Timezone timezone1 = (Timezone) o;
     1437            return Double.compare(timezone1.timezone, timezone) == 0;
     1438        }
     1439
     1440        @Override
     1441        public int hashCode() {
     1442            return Objects.hash(timezone);
     1443        }
     1444    }
     1445
     1446    static final class Offset {
     1447
     1448        static final Offset ZERO = new Offset(0);
     1449        private final long milliseconds;
     1450
     1451        private Offset(long milliseconds) {
     1452            this.milliseconds = milliseconds;
     1453        }
     1454
     1455        static Offset milliseconds(long milliseconds) {
     1456            return new Offset(milliseconds);
     1457        }
     1458
     1459        static Offset seconds(long seconds) {
     1460            return new Offset(1000 * seconds);
     1461        }
     1462
     1463        long getMilliseconds() {
     1464            return milliseconds;
     1465        }
     1466
     1467        long getSeconds() {
     1468            return milliseconds / 1000;
     1469        }
     1470
     1471        String formatOffset() {
     1472            return Long.toString(milliseconds / 1000);
     1473        }
     1474
     1475        static Offset parseOffset(String offset) throws ParseException {
     1476            String error = tr("Error while parsing offset.\nExpected format: {0}", "number");
     1477
     1478            if (!offset.isEmpty()) {
     1479                try {
     1480                    if (offset.startsWith("+")) {
     1481                        offset = offset.substring(1);
     1482                    }
     1483                    return Offset.seconds(Long.parseLong(offset));
     1484                } catch (NumberFormatException nfe) {
     1485                    throw new ParseException(error, 0);
     1486                }
     1487            } else {
     1488                return Offset.ZERO;
     1489            }
     1490        }
     1491
     1492        @Override
     1493        public boolean equals(Object o) {
     1494            if (this == o) return true;
     1495            if (!(o instanceof Offset)) return false;
     1496            Offset offset = (Offset) o;
     1497            return milliseconds == offset.milliseconds;
     1498        }
     1499
     1500        @Override
     1501        public int hashCode() {
     1502            return Objects.hash(milliseconds);
    14241503        }
    14251504    }
  • trunk/test/unit/org/openstreetmap/josm/gui/layer/geoimage/CorrelateGpxWithImagesTest.java

    r9727 r9741  
    7373        i0.setExifTime(DateUtils.fromString("2016:01:03 11:59:54")); // 4 sec before start of GPX
    7474        i0.createTmp();
    75         assertEquals(Pair.create(0.0, -4L), CorrelateGpxWithImages.autoGuess(Collections.singletonList(i0), gpx));
     75        assertEquals(Pair.create(CorrelateGpxWithImages.Timezone.ZERO, CorrelateGpxWithImages.Offset.seconds(-4)),
     76                CorrelateGpxWithImages.autoGuess(Collections.singletonList(i0), gpx));
    7677    }
    7778
    7879    @Test
    7980    public void testFormatTimezone() throws Exception {
    80         assertEquals("+1:00", CorrelateGpxWithImages.formatTimezone(1));
    81         assertEquals("+6:30", CorrelateGpxWithImages.formatTimezone(6.5));
    82         assertEquals("-6:30", CorrelateGpxWithImages.formatTimezone(-6.5));
    83         assertEquals("+3:08", CorrelateGpxWithImages.formatTimezone(Math.PI));
    84         assertEquals("+2:43", CorrelateGpxWithImages.formatTimezone(Math.E));
     81        assertEquals("+1:00", new CorrelateGpxWithImages.Timezone(1).formatTimezone());
     82        assertEquals("+6:30", new CorrelateGpxWithImages.Timezone(6.5).formatTimezone());
     83        assertEquals("-6:30", new CorrelateGpxWithImages.Timezone(-6.5).formatTimezone());
     84        assertEquals("+3:08", new CorrelateGpxWithImages.Timezone(Math.PI).formatTimezone());
     85        assertEquals("+2:43", new CorrelateGpxWithImages.Timezone(Math.E).formatTimezone());
    8586    }
    8687
    8788    @Test
    8889    public void testParseTimezone() throws ParseException {
    89         assertEquals(1, CorrelateGpxWithImages.parseTimezone("+01:00"), 1e-3);
    90         assertEquals(1, CorrelateGpxWithImages.parseTimezone("+1:00"), 1e-3);
    91         assertEquals(1.5, CorrelateGpxWithImages.parseTimezone("+01:30"), 1e-3);
    92         assertEquals(11.5, CorrelateGpxWithImages.parseTimezone("+11:30"), 1e-3);
     90        assertEquals(1, CorrelateGpxWithImages.Timezone.parseTimezone("+01:00").getHours(), 1e-3);
     91        assertEquals(1, CorrelateGpxWithImages.Timezone.parseTimezone("+1:00").getHours(), 1e-3);
     92        assertEquals(1.5, CorrelateGpxWithImages.Timezone.parseTimezone("+01:30").getHours(), 1e-3);
     93        assertEquals(11.5, CorrelateGpxWithImages.Timezone.parseTimezone("+11:30").getHours(), 1e-3);
     94    }
     95
     96    @Test
     97    public void testFormatOffest() throws ParseException {
     98        assertEquals("0", CorrelateGpxWithImages.Offset.seconds(0).formatOffset());
     99        assertEquals("123", CorrelateGpxWithImages.Offset.seconds(123).formatOffset());
     100        assertEquals("-4242", CorrelateGpxWithImages.Offset.seconds(-4242).formatOffset());
    93101    }
    94102
    95103    @Test
    96104    public void testParseOffest() throws ParseException {
    97         assertEquals(0, CorrelateGpxWithImages.parseOffset("0"));
    98         assertEquals(4242L, CorrelateGpxWithImages.parseOffset("4242"));
    99         assertEquals(-4242L, CorrelateGpxWithImages.parseOffset("-4242"));
    100         assertEquals(0L, CorrelateGpxWithImages.parseOffset("-0"));
     105        assertEquals(0, CorrelateGpxWithImages.Offset.parseOffset("0").getSeconds());
     106        assertEquals(4242L, CorrelateGpxWithImages.Offset.parseOffset("4242").getSeconds());
     107        assertEquals(-4242L, CorrelateGpxWithImages.Offset.parseOffset("-4242").getSeconds());
     108        assertEquals(0L, CorrelateGpxWithImages.Offset.parseOffset("-0").getSeconds());
    101109    }
    102110}
Note: See TracChangeset for help on using the changeset viewer.