Index: trunk/test/unit/org/openstreetmap/josm/data/gpx/GpxImageCorrelationTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/data/gpx/GpxImageCorrelationTest.java	(revision 14205)
+++ trunk/test/unit/org/openstreetmap/josm/data/gpx/GpxImageCorrelationTest.java	(revision 14205)
@@ -0,0 +1,269 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data.gpx;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.openstreetmap.josm.TestUtils;
+import org.openstreetmap.josm.data.coor.CachedLatLon;
+import org.openstreetmap.josm.io.GpxReaderTest;
+import org.openstreetmap.josm.spi.preferences.Config;
+import org.openstreetmap.josm.spi.preferences.IPreferences;
+import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.tools.date.DateUtils;
+import org.openstreetmap.josm.tools.date.DateUtilsTest;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+
+/**
+ * Unit tests of {@link GpxImageCorrelation} class.
+ */
+public class GpxImageCorrelationTest {
+
+    /**
+     * Setup test.
+     */
+    @Rule
+    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
+    public JOSMTestRules test = new JOSMTestRules();
+
+    /**
+     * Setup test.
+     */
+    @BeforeClass
+    public static void setUp() {
+        DateUtilsTest.setTimeZone(DateUtils.UTC);
+    }
+
+    /**
+     * Tests matching of images to a GPX track.
+     * @throws Exception if the track cannot be parsed
+     */
+    @Test
+    public void testMatchGpxTrack() throws Exception {
+        IPreferences s = Config.getPref();
+        final GpxData gpx = GpxReaderTest.parseGpxData(TestUtils.getTestDataRoot() + "ImageCorrelationTest.gpx");
+        assertEquals(5, gpx.tracks.size());
+        assertEquals(1, gpx.tracks.iterator().next().getSegments().size());
+        assertEquals(128, gpx.tracks.iterator().next().getSegments().iterator().next().getWayPoints().size());
+
+        final GpxImageEntry ib = new GpxImageEntry();
+        ib.setExifTime(DateUtils.fromString("2016:01:03 11:54:58")); // 5 minutes before start of GPX
+        ib.createTmp();
+        final GpxImageEntry i0 = new GpxImageEntry();
+        i0.setExifTime(DateUtils.fromString("2016:01:03 11:59:54")); // 4 sec before start of GPX
+        i0.createTmp();
+        final GpxImageEntry i1 = new GpxImageEntry();
+        i1.setExifTime(DateUtils.fromString("2016:01:03 12:04:01"));
+        i1.createTmp();
+        final GpxImageEntry i2 = new GpxImageEntry();
+        i2.setExifTime(DateUtils.fromString("2016:01:03 12:04:57"));
+        i2.createTmp();
+        final GpxImageEntry i3 = new GpxImageEntry();
+        i3.setExifTime(DateUtils.fromString("2016:01:03 12:05:05"));
+        i3.createTmp();
+        final GpxImageEntry i4 = new GpxImageEntry(); //Image close to two points with elevation, but without time
+        i4.setExifTime(DateUtils.fromString("2016:01:03 12:05:20"));
+        i4.createTmp();
+        final GpxImageEntry i5 = new GpxImageEntry(); //between two tracks, closer to first
+        i5.setExifTime(DateUtils.fromString("2016:01:03 12:07:00"));
+        i5.createTmp();
+        final GpxImageEntry i6 = new GpxImageEntry(); //between two tracks, closer to second (more than 1 minute from any track)
+        i6.setExifTime(DateUtils.fromString("2016:01:03 12:07:45"));
+        i6.createTmp();
+
+        List<GpxImageEntry> images = Arrays.asList(ib, i0, i1, i2, i3, i4, i5, i6);
+
+        // TEST #1: default settings
+        // tag images within 2 minutes to tracks/segments, interpolate between segments only
+        assertEquals(7, GpxImageCorrelation.matchGpxTrack(images, gpx, 0, false));
+        assertEquals(null, ib.getPos());
+        assertEquals(new CachedLatLon(47.19286847859621, 8.79732714034617), i0.getPos()); // start of track
+        assertEquals(new CachedLatLon(47.196979885920882, 8.79541271366179), i1.getPos()); // exact match
+        assertEquals(new CachedLatLon(47.197319911792874, 8.792139580473304), i3.getPos()); // exact match
+        assertEquals(new CachedLatLon((47.197131179273129 + 47.197186248376966) / 2, (8.792974585667253 + 8.792809881269932) / 2),
+                i2.getPos()); // interpolated
+        assertEquals(new CachedLatLon(47.197568312311816, 8.790292849679897),
+                i4.getPos()); // interpolated between points without timestamp
+        assertEquals(new CachedLatLon(47.19819249585271, 8.78536943346262),
+                i5.getPos()); // tagged to last WP of first track, because closer and within 2 min (default setting)
+        assertEquals(new CachedLatLon(47.20138901844621, 8.774476982653141),
+                i6.getPos()); // tagged to first WP of second track, because closer and within 2 min (default setting)
+        assertFalse(ib.hasNewGpsData());
+        assertTrue(i0.hasNewGpsData() && i1.hasNewGpsData() && i2.hasNewGpsData() && i3.hasNewGpsData()
+                && i4.hasNewGpsData() && i5.hasNewGpsData() && i6.hasNewGpsData());
+        // First waypoint has no speed in matchGpxTrack(). Speed is calculated
+        // and not taken from GPX track.
+        assertEquals(null, ib.getSpeed());
+        assertEquals(null, i0.getSpeed());
+        assertEquals(Double.valueOf(11.675317966018756), i1.getSpeed(), 0.000001);
+        assertEquals(Double.valueOf(24.992418392716967), i2.getSpeed(), 0.000001);
+        assertEquals(Double.valueOf(27.307968754679223), i3.getSpeed(), 0.000001);
+        assertEquals(null, ib.getElevation());
+        assertEquals(null, i0.getElevation());
+        assertEquals(Double.valueOf(489.29), i1.getElevation(), 0.000001);
+        assertEquals(Double.valueOf((490.40 + 489.75) / 2), i2.getElevation(), 0.000001);
+        assertEquals(Double.valueOf(486.368333333), i3.getElevation(), 0.000001);
+        // interpolated elevation between trackpoints with interpolated timestamps
+        assertEquals(Double.valueOf(475.393978719), i4.getElevation(), 0.000001);
+        assertEquals(null, i5.getElevation());
+        assertEquals(null, i6.getElevation());
+
+        assertEquals(null, ib.getGpsTime());
+        assertEquals(DateUtils.fromString("2016:01:03 11:59:54"), i0.getGpsTime()); // original time is kept
+        assertEquals(DateUtils.fromString("2016:01:03 12:04:01"), i1.getGpsTime());
+        assertEquals(DateUtils.fromString("2016:01:03 12:04:57"), i2.getGpsTime());
+        assertEquals(DateUtils.fromString("2016:01:03 12:05:05"), i3.getGpsTime());
+
+        clearTmp(images);
+
+        // TEST #2: Disable all interpolation and tagging close to tracks. Only i1-i4 are tagged
+
+        s.putBoolean("geoimage.trk.tag", false);
+        s.putBoolean("geoimage.trk.int", false);
+        s.putBoolean("geoimage.seg.tag", false);
+        s.putBoolean("geoimage.seg.int", false);
+
+        assertEquals(4, GpxImageCorrelation.matchGpxTrack(images, gpx, 0, false));
+        assertEquals(null, ib.getPos());
+        assertEquals(null, i0.getPos());
+        assertEquals(new CachedLatLon(47.196979885920882, 8.79541271366179), i1.getPos());
+        assertEquals(new CachedLatLon((47.197131179273129 + 47.197186248376966) / 2, (8.792974585667253 + 8.792809881269932) / 2), i2.getPos());
+        assertEquals(new CachedLatLon(47.197319911792874, 8.792139580473304), i3.getPos());
+        assertEquals(new CachedLatLon(47.197568312311816, 8.790292849679897), i4.getPos());
+        assertEquals(null, i5.getPos());
+        assertEquals(null, i6.getPos());
+
+        clearTmp(images);
+
+        // TEST #3: Disable all interpolation and allow tagging within 1 minute of a track. i0-i5 are tagged.
+        // i6 will not be tagged, because it's 68 seconds away from the next waypoint in either direction
+
+        s.putBoolean("geoimage.trk.tag", true);
+        s.putBoolean("geoimage.trk.tag.time", true);
+        s.putInt("geoimage.trk.tag.time.val", 1);
+
+        s.putBoolean("geoimage.trk.int", false);
+        s.putBoolean("geoimage.seg.tag", false);
+        s.putBoolean("geoimage.seg.int", false);
+
+        assertEquals(6, GpxImageCorrelation.matchGpxTrack(images, gpx, 0, false));
+        assertEquals(null, ib.getPos());
+        assertEquals(new CachedLatLon(47.19286847859621, 8.79732714034617), i0.getPos());
+        assertEquals(new CachedLatLon(47.196979885920882, 8.79541271366179), i1.getPos());
+        assertEquals(new CachedLatLon((47.197131179273129 + 47.197186248376966) / 2, (8.792974585667253 + 8.792809881269932) / 2), i2.getPos());
+        assertEquals(new CachedLatLon(47.197319911792874, 8.792139580473304), i3.getPos());
+        assertEquals(new CachedLatLon(47.197568312311816, 8.790292849679897), i4.getPos());
+        assertEquals(new CachedLatLon(47.19819249585271, 8.78536943346262), i5.getPos());
+        assertEquals(null, i6.getPos());
+
+        clearTmp(images);
+
+        // TEST #4: Force tagging (parameter forceTags=true, no change of configuration). All images will be tagged
+        // i5-i6 will now be interpolated, therefore it will have an elevation and different coordinates than in tests above
+
+        assertEquals(8, GpxImageCorrelation.matchGpxTrack(images, gpx, 0, true));
+        assertEquals(new CachedLatLon(47.19286847859621, 8.79732714034617), ib.getPos());
+        assertEquals(new CachedLatLon(47.19286847859621, 8.79732714034617), i0.getPos());
+        assertEquals(new CachedLatLon(47.196979885920882, 8.79541271366179), i1.getPos());
+        assertEquals(new CachedLatLon((47.197131179273129 + 47.197186248376966) / 2, (8.792974585667253 + 8.792809881269932) / 2), i2.getPos());
+        assertEquals(new CachedLatLon(47.197319911792874, 8.792139580473304), i3.getPos());
+        assertEquals(new CachedLatLon(47.197568312311816, 8.790292849679897), i4.getPos());
+        assertEquals(new CachedLatLon(47.198845306804905, 8.783144918860685), i5.getPos()); // interpolated between tracks
+        assertEquals(new CachedLatLon(47.19985828931693, 8.77969308585768), i6.getPos()); // different values than in tests #1 and #3!
+
+        assertEquals(Double.valueOf(447.894014085), i5.getElevation(), 0.000001);
+        assertEquals(Double.valueOf(437.395070423), i6.getElevation(), 0.000001);
+
+        clearTmp(images);
+
+        // TEST #5: Force tagging (parameter forceTags=false, but configuration changed).
+        // Results same as #4
+
+        s.putBoolean("geoimage.trk.tag", true);
+        s.putBoolean("geoimage.trk.tag.time", false);
+        s.putBoolean("geoimage.trk.int", true);
+        s.putBoolean("geoimage.trk.int.time", false);
+        s.putBoolean("geoimage.trk.int.dist", false);
+        s.putBoolean("geoimage.seg.tag", false);
+        s.putBoolean("geoimage.seg.int", false);
+        s.putBoolean("geoimage.seg.tag.time", false);
+        s.putBoolean("geoimage.seg.int", true);
+        s.putBoolean("geoimage.seg.int.time", false);
+        s.putBoolean("geoimage.seg.int.dist", false);
+
+        assertEquals(8, GpxImageCorrelation.matchGpxTrack(images, gpx, 0, false));
+        assertEquals(new CachedLatLon(47.19286847859621, 8.79732714034617), ib.getPos());
+        assertEquals(new CachedLatLon(47.19286847859621, 8.79732714034617), i0.getPos());
+        assertEquals(new CachedLatLon(47.196979885920882, 8.79541271366179), i1.getPos());
+        assertEquals(new CachedLatLon((47.197131179273129 + 47.197186248376966) / 2, (8.792974585667253 + 8.792809881269932) / 2), i2.getPos());
+        assertEquals(new CachedLatLon(47.197319911792874, 8.792139580473304), i3.getPos());
+        assertEquals(new CachedLatLon(47.197568312311816, 8.790292849679897), i4.getPos());
+        assertEquals(new CachedLatLon(47.198845306804905, 8.783144918860685), i5.getPos());
+        assertEquals(new CachedLatLon(47.19985828931693, 8.77969308585768), i6.getPos());
+
+        assertEquals(Double.valueOf(447.894014085), i5.getElevation(), 0.000001);
+        assertEquals(Double.valueOf(437.395070423), i6.getElevation(), 0.000001);
+
+        clearTmp(images);
+
+        // TEST #6: Disable tagging but allow interpolation when tracks are less than 500m apart. i0-i4 are tagged.
+        // i5-i6 will not be tagged, because the tracks are 897m apart.
+        // not checking all the coordinates again, did that 5 times already, just the number of matched images
+
+        s.putBoolean("geoimage.trk.tag", false);
+        s.putBoolean("geoimage.trk.int", true);
+        s.putBoolean("geoimage.trk.int.time", false);
+        s.putBoolean("geoimage.trk.int.dist", true);
+        s.putInt("geoimage.trk.int.dist.val", 500);
+        s.putBoolean("geoimage.seg.tag", false);
+        s.putBoolean("geoimage.seg.int", false);
+
+        assertEquals(4, GpxImageCorrelation.matchGpxTrack(images, gpx, 0, false));
+        clearTmp(images);
+
+        // TEST #7: Disable tagging but allow interpolation when tracks are less than 1000m apart. i0-i6 are tagged.
+        // i5-i6 will be tagged, because the tracks are 897m apart.
+
+        s.putInt("geoimage.trk.int.dist.val", 1000);
+
+        assertEquals(6, GpxImageCorrelation.matchGpxTrack(images, gpx, 0, false));
+        clearTmp(images);
+
+        // TEST #8: Disable tagging but allow interpolation when tracks are less than 2 min apart. i0-i4 are tagged.
+        // i5-i6 will not be tagged, because the tracks are 2.5min apart.
+
+        s.putBoolean("geoimage.trk.tag", false);
+        s.putBoolean("geoimage.trk.int", true);
+        s.putBoolean("geoimage.trk.int.time", true);
+        s.putInt("geoimage.trk.int.time.val", 2);
+        s.putBoolean("geoimage.trk.int.dist", false);
+        s.putBoolean("geoimage.seg.tag", false);
+        s.putBoolean("geoimage.seg.int", false);
+
+        assertEquals(4, GpxImageCorrelation.matchGpxTrack(images, gpx, 0, false));
+        clearTmp(images);
+
+        // TEST #9: Disable tagging but allow interpolation when tracks are less than 3 min apart. i0-i6 are tagged.
+        // i5-i6 will be tagged, because the tracks are 2.5min apart.
+
+        s.putInt("geoimage.trk.int.time.val", 3);
+
+        assertEquals(6, GpxImageCorrelation.matchGpxTrack(images, gpx, 0, false));
+
+    }
+
+    private void clearTmp(List<GpxImageEntry> imgs) {
+        for (GpxImageEntry i : imgs) {
+            i.discardTmp();
+            i.createTmp();
+        }
+    }
+}
Index: trunk/test/unit/org/openstreetmap/josm/data/gpx/GpxOffsetTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/data/gpx/GpxOffsetTest.java	(revision 14205)
+++ trunk/test/unit/org/openstreetmap/josm/data/gpx/GpxOffsetTest.java	(revision 14205)
@@ -0,0 +1,84 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data.gpx;
+
+import static org.junit.Assert.assertEquals;
+
+import java.text.ParseException;
+
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.tools.date.DateUtils;
+import org.openstreetmap.josm.tools.date.DateUtilsTest;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+
+/**
+ * Unit tests of {@link GpxTimeOffset} class.
+ */
+public class GpxOffsetTest {
+
+    /**
+     * Setup test.
+     */
+    @Rule
+    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
+    public JOSMTestRules test = new JOSMTestRules();
+
+    /**
+     * Setup test.
+     */
+    @BeforeClass
+    public static void setUp() {
+        DateUtilsTest.setTimeZone(DateUtils.UTC);
+    }
+
+    /**
+     * Unit test of {@link GpxTimeOffset#formatOffset}.
+     */
+    @Test
+    public void testFormatOffset() {
+        assertEquals("0", GpxTimeOffset.seconds(0).formatOffset());
+        assertEquals("123", GpxTimeOffset.seconds(123).formatOffset());
+        assertEquals("-4242", GpxTimeOffset.seconds(-4242).formatOffset());
+        assertEquals("0.1", GpxTimeOffset.milliseconds(100).formatOffset());
+        assertEquals("0.120", GpxTimeOffset.milliseconds(120).formatOffset());
+        assertEquals("0.123", GpxTimeOffset.milliseconds(123).formatOffset());
+        assertEquals("1.2", GpxTimeOffset.milliseconds(1200).formatOffset());
+        assertEquals("1.234", GpxTimeOffset.milliseconds(1234).formatOffset());
+    }
+
+    /**
+     * Unit test of {@link GpxTimeOffset#parseOffset}.
+     * @throws ParseException in case of parsing error
+     */
+    @Test
+    public void testParseOffest() throws ParseException {
+        assertEquals(0, GpxTimeOffset.parseOffset("0").getSeconds());
+        assertEquals(4242L, GpxTimeOffset.parseOffset("4242").getSeconds());
+        assertEquals(-4242L, GpxTimeOffset.parseOffset("-4242").getSeconds());
+        assertEquals(0L, GpxTimeOffset.parseOffset("-0").getSeconds());
+        assertEquals(100L, GpxTimeOffset.parseOffset("0.1").getMilliseconds());
+        assertEquals(123L, GpxTimeOffset.parseOffset("0.123").getMilliseconds());
+        assertEquals(-42420L, GpxTimeOffset.parseOffset("-42.42").getMilliseconds());
+    }
+
+    /**
+     * Unit test of {@link GpxTimeOffset#splitOutTimezone}.
+     */
+    @Test
+    public void testSplitOutTimezone() {
+        assertEquals("+1:00", GpxTimeOffset.seconds(3602).splitOutTimezone().a.formatTimezone());
+        assertEquals("2", GpxTimeOffset.seconds(3602).splitOutTimezone().b.formatOffset());
+        assertEquals("-7:00", GpxTimeOffset.seconds(-7 * 3600 + 123).splitOutTimezone().a.formatTimezone());
+        assertEquals("123", GpxTimeOffset.seconds(-7 * 3600 + 123).splitOutTimezone().b.formatOffset());
+        assertEquals(1, GpxTimeOffset.seconds(35 * 3600 + 421).getDayOffset());
+        assertEquals(11 * 3600 + 421, GpxTimeOffset.seconds(35 * 3600 + 421).withoutDayOffset().getSeconds());
+        assertEquals("+11:00", GpxTimeOffset.seconds(35 * 3600 + 421).splitOutTimezone().a.formatTimezone());
+        assertEquals(86400 + 421, GpxTimeOffset.seconds(35 * 3600 + 421).splitOutTimezone().b.getSeconds());
+        assertEquals(421, GpxTimeOffset.seconds(35 * 3600 + 421).withoutDayOffset().splitOutTimezone().b.getSeconds());
+        assertEquals("+1:00", GpxTimeOffset.milliseconds(3602987).splitOutTimezone().a.formatTimezone());
+        assertEquals("2.987", GpxTimeOffset.milliseconds(3602987).splitOutTimezone().b.formatOffset());
+    }
+}
Index: trunk/test/unit/org/openstreetmap/josm/data/gpx/GpxTimezoneTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/data/gpx/GpxTimezoneTest.java	(revision 14205)
+++ trunk/test/unit/org/openstreetmap/josm/data/gpx/GpxTimezoneTest.java	(revision 14205)
@@ -0,0 +1,61 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data.gpx;
+
+import static org.junit.Assert.assertEquals;
+
+import java.text.ParseException;
+
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.tools.date.DateUtils;
+import org.openstreetmap.josm.tools.date.DateUtilsTest;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+
+/**
+ * Unit tests of {@link GpxTimezone} class.
+ */
+public class GpxTimezoneTest {
+
+    /**
+     * Setup test.
+     */
+    @Rule
+    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
+    public JOSMTestRules test = new JOSMTestRules();
+
+    /**
+     * Setup test.
+     */
+    @BeforeClass
+    public static void setUp() {
+        DateUtilsTest.setTimeZone(DateUtils.UTC);
+    }
+
+    /**
+     * Unit test of {@link GpxTimezone#formatTimezone}.
+     */
+    @Test
+    public void testFormatTimezone() {
+        assertEquals("+1:00", new GpxTimezone(1).formatTimezone());
+        assertEquals("+6:30", new GpxTimezone(6.5).formatTimezone());
+        assertEquals("-6:30", new GpxTimezone(-6.5).formatTimezone());
+        assertEquals("+3:08", new GpxTimezone(Math.PI).formatTimezone());
+        assertEquals("+2:43", new GpxTimezone(Math.E).formatTimezone());
+    }
+
+    /**
+     * Unit test of {@link GpxTimezone#parseTimezone}.
+     * @throws ParseException in case of parsing error
+     */
+    @Test
+    public void testParseTimezone() throws ParseException {
+        assertEquals(1, GpxTimezone.parseTimezone("+01:00").getHours(), 1e-3);
+        assertEquals(1, GpxTimezone.parseTimezone("+1:00").getHours(), 1e-3);
+        assertEquals(1.5, GpxTimezone.parseTimezone("+01:30").getHours(), 1e-3);
+        assertEquals(11.5, GpxTimezone.parseTimezone("+11:30").getHours(), 1e-3);
+        assertEquals(-11.5, GpxTimezone.parseTimezone("-11:30").getHours(), 1e-3);
+    }
+}
Index: trunk/test/unit/org/openstreetmap/josm/gui/layer/geoimage/CorrelateGpxWithImagesTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/gui/layer/geoimage/CorrelateGpxWithImagesTest.java	(revision 14202)
+++ trunk/test/unit/org/openstreetmap/josm/gui/layer/geoimage/CorrelateGpxWithImagesTest.java	(revision 14205)
@@ -3,8 +3,5 @@
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
 
-import java.util.Arrays;
 import java.util.Collections;
 
@@ -12,6 +9,7 @@
 import org.junit.Rule;
 import org.junit.Test;
-import org.openstreetmap.josm.data.coor.CachedLatLon;
 import org.openstreetmap.josm.data.gpx.GpxData;
+import org.openstreetmap.josm.data.gpx.GpxTimeOffset;
+import org.openstreetmap.josm.data.gpx.GpxTimezone;
 import org.openstreetmap.josm.io.GpxReaderTest;
 import org.openstreetmap.josm.testutils.JOSMTestRules;
@@ -43,61 +41,4 @@
 
     /**
-     * Tests matching of images to a GPX track.
-     * @throws Exception if the track cannot be parsed
-     */
-    @Test
-    public void testMatchGpxTrack() throws Exception {
-        final GpxData gpx = GpxReaderTest.parseGpxData("data_nodist/2094047.gpx");
-        assertEquals(4, gpx.tracks.size());
-        assertEquals(1, gpx.tracks.iterator().next().getSegments().size());
-        assertEquals(185, gpx.tracks.iterator().next().getSegments().iterator().next().getWayPoints().size());
-
-        final ImageEntry ib = new ImageEntry();
-        ib.setExifTime(DateUtils.fromString("2016:01:03 11:54:58")); // 5 minutes before start of GPX
-        ib.createTmp();
-        final ImageEntry i0 = new ImageEntry();
-        i0.setExifTime(DateUtils.fromString("2016:01:03 11:59:54")); // 4 sec before start of GPX
-        i0.createTmp();
-        final ImageEntry i1 = new ImageEntry();
-        i1.setExifTime(DateUtils.fromString("2016:01:03 12:04:01"));
-        i1.createTmp();
-        final ImageEntry i2 = new ImageEntry();
-        i2.setExifTime(DateUtils.fromString("2016:01:03 12:04:57"));
-        i2.createTmp();
-        final ImageEntry i3 = new ImageEntry();
-        i3.setExifTime(DateUtils.fromString("2016:01:03 12:05:05"));
-        i3.createTmp();
-
-        assertEquals(4, CorrelateGpxWithImages.matchGpxTrack(Arrays.asList(ib, i0, i1, i2, i3), gpx, 0));
-        assertEquals(new CachedLatLon(47.19286847859621, 8.79732714034617), i0.getPos()); // start of track
-        assertEquals(new CachedLatLon(47.196979885920882, 8.79541271366179), i1.getPos()); // exact match
-        assertEquals(new CachedLatLon(47.197319911792874, 8.792139580473304), i3.getPos()); // exact match
-        assertEquals(new CachedLatLon((47.197131179273129 + 47.197186248376966) / 2, (8.792974585667253 + 8.792809881269932) / 2),
-                i2.getPos()); // interpolated
-        assertFalse(ib.hasNewGpsData());
-        assertTrue(i0.hasNewGpsData());
-        assertTrue(i1.hasNewGpsData());
-        assertTrue(i2.hasNewGpsData());
-        assertTrue(i3.hasNewGpsData());
-        // First waypoint has no speed in matchGpxTrack(). Speed is calculated
-        // and not taken from GPX track.
-        assertEquals(null, ib.getSpeed());
-        assertEquals(null, i0.getSpeed());
-        assertEquals(Double.valueOf(11.675317966018756), i1.getSpeed(), 0.000001);
-        assertEquals(Double.valueOf(24.992418392716967), i2.getSpeed(), 0.000001);
-        assertEquals(Double.valueOf(27.307968754679223), i3.getSpeed(), 0.000001);
-        assertEquals(null, ib.getElevation());
-        assertEquals(Double.valueOf(471.86), i0.getElevation(), 0.000001);
-        assertEquals(Double.valueOf(489.29), i1.getElevation(), 0.000001);
-        assertEquals(Double.valueOf((490.40 + 489.75) / 2), i2.getElevation(), 0.000001);
-        assertEquals(Double.valueOf(486.368333333), i3.getElevation(), 0.000001);
-        assertEquals(null, ib.getGpsTime());
-        assertEquals(DateUtils.fromString("2016:01:03 11:59:54"), i0.getGpsTime()); // original time is kept
-        assertEquals(DateUtils.fromString("2016:01:03 12:04:01"), i1.getGpsTime());
-        assertEquals(DateUtils.fromString("2016:01:03 12:04:57"), i2.getGpsTime());
-        assertEquals(DateUtils.fromString("2016:01:03 12:05:05"), i3.getGpsTime());
-    }
-
-    /**
      * Tests automatic guessing of timezone/offset
      * @throws Exception if an error occurs
@@ -109,5 +50,5 @@
         i0.setExifTime(DateUtils.fromString("2016:01:03 11:59:54")); // 4 sec before start of GPX
         i0.createTmp();
-        assertEquals(Pair.create(Timezone.ZERO, Offset.seconds(-4)),
+        assertEquals(Pair.create(GpxTimezone.ZERO, GpxTimeOffset.seconds(-4)),
                 CorrelateGpxWithImages.autoGuess(Collections.singletonList(i0), gpx));
     }
Index: trunk/test/unit/org/openstreetmap/josm/gui/layer/geoimage/OffsetTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/gui/layer/geoimage/OffsetTest.java	(revision 14202)
+++ 	(revision )
@@ -1,84 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.gui.layer.geoimage;
-
-import static org.junit.Assert.assertEquals;
-
-import java.text.ParseException;
-
-import org.junit.BeforeClass;
-import org.junit.Rule;
-import org.junit.Test;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
-import org.openstreetmap.josm.tools.date.DateUtils;
-import org.openstreetmap.josm.tools.date.DateUtilsTest;
-
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
-/**
- * Unit tests of {@link Offset} class.
- */
-public class OffsetTest {
-
-    /**
-     * Setup test.
-     */
-    @Rule
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules();
-
-    /**
-     * Setup test.
-     */
-    @BeforeClass
-    public static void setUp() {
-        DateUtilsTest.setTimeZone(DateUtils.UTC);
-    }
-
-    /**
-     * Unit test of {@link Offset#formatOffset}.
-     */
-    @Test
-    public void testFormatOffset() {
-        assertEquals("0", Offset.seconds(0).formatOffset());
-        assertEquals("123", Offset.seconds(123).formatOffset());
-        assertEquals("-4242", Offset.seconds(-4242).formatOffset());
-        assertEquals("0.1", Offset.milliseconds(100).formatOffset());
-        assertEquals("0.120", Offset.milliseconds(120).formatOffset());
-        assertEquals("0.123", Offset.milliseconds(123).formatOffset());
-        assertEquals("1.2", Offset.milliseconds(1200).formatOffset());
-        assertEquals("1.234", Offset.milliseconds(1234).formatOffset());
-    }
-
-    /**
-     * Unit test of {@link Offset#parseOffset}.
-     * @throws ParseException in case of parsing error
-     */
-    @Test
-    public void testParseOffest() throws ParseException {
-        assertEquals(0, Offset.parseOffset("0").getSeconds());
-        assertEquals(4242L, Offset.parseOffset("4242").getSeconds());
-        assertEquals(-4242L, Offset.parseOffset("-4242").getSeconds());
-        assertEquals(0L, Offset.parseOffset("-0").getSeconds());
-        assertEquals(100L, Offset.parseOffset("0.1").getMilliseconds());
-        assertEquals(123L, Offset.parseOffset("0.123").getMilliseconds());
-        assertEquals(-42420L, Offset.parseOffset("-42.42").getMilliseconds());
-    }
-
-    /**
-     * Unit test of {@link Offset#splitOutTimezone}.
-     */
-    @Test
-    public void testSplitOutTimezone() {
-        assertEquals("+1:00", Offset.seconds(3602).splitOutTimezone().a.formatTimezone());
-        assertEquals("2", Offset.seconds(3602).splitOutTimezone().b.formatOffset());
-        assertEquals("-7:00", Offset.seconds(-7 * 3600 + 123).splitOutTimezone().a.formatTimezone());
-        assertEquals("123", Offset.seconds(-7 * 3600 + 123).splitOutTimezone().b.formatOffset());
-        assertEquals(1, Offset.seconds(35 * 3600 + 421).getDayOffset());
-        assertEquals(11 * 3600 + 421, Offset.seconds(35 * 3600 + 421).withoutDayOffset().getSeconds());
-        assertEquals("+11:00", Offset.seconds(35 * 3600 + 421).splitOutTimezone().a.formatTimezone());
-        assertEquals(86400 + 421, Offset.seconds(35 * 3600 + 421).splitOutTimezone().b.getSeconds());
-        assertEquals(421, Offset.seconds(35 * 3600 + 421).withoutDayOffset().splitOutTimezone().b.getSeconds());
-        assertEquals("+1:00", Offset.milliseconds(3602987).splitOutTimezone().a.formatTimezone());
-        assertEquals("2.987", Offset.milliseconds(3602987).splitOutTimezone().b.formatOffset());
-    }
-}
Index: trunk/test/unit/org/openstreetmap/josm/gui/layer/geoimage/TimezoneTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/gui/layer/geoimage/TimezoneTest.java	(revision 14202)
+++ 	(revision )
@@ -1,60 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.gui.layer.geoimage;
-
-import static org.junit.Assert.assertEquals;
-
-import java.text.ParseException;
-
-import org.junit.BeforeClass;
-import org.junit.Rule;
-import org.junit.Test;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
-import org.openstreetmap.josm.tools.date.DateUtils;
-import org.openstreetmap.josm.tools.date.DateUtilsTest;
-
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
-/**
- * Unit tests of {@link Timezone} class.
- */
-public class TimezoneTest {
-
-    /**
-     * Setup test.
-     */
-    @Rule
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules();
-
-    /**
-     * Setup test.
-     */
-    @BeforeClass
-    public static void setUp() {
-        DateUtilsTest.setTimeZone(DateUtils.UTC);
-    }
-
-    /**
-     * Unit test of {@link Timezone#formatTimezone}.
-     */
-    @Test
-    public void testFormatTimezone() {
-        assertEquals("+1:00", new Timezone(1).formatTimezone());
-        assertEquals("+6:30", new Timezone(6.5).formatTimezone());
-        assertEquals("-6:30", new Timezone(-6.5).formatTimezone());
-        assertEquals("+3:08", new Timezone(Math.PI).formatTimezone());
-        assertEquals("+2:43", new Timezone(Math.E).formatTimezone());
-    }
-
-    /**
-     * Unit test of {@link Timezone#parseTimezone}.
-     * @throws ParseException in case of parsing error
-     */
-    @Test
-    public void testParseTimezone() throws ParseException {
-        assertEquals(1, Timezone.parseTimezone("+01:00").getHours(), 1e-3);
-        assertEquals(1, Timezone.parseTimezone("+1:00").getHours(), 1e-3);
-        assertEquals(1.5, Timezone.parseTimezone("+01:30").getHours(), 1e-3);
-        assertEquals(11.5, Timezone.parseTimezone("+11:30").getHours(), 1e-3);
-    }
-}
