[josm-dev] JOSM patch for auto-sync of image timestamps
A Morris
aledmorris2 at gmail.com
Sat Apr 12 13:03:37 BST 2008
Here is a patch that adds an 'auto sync' option to the context menu of
the image layer. (No more taking a photo of a clock and typing in the
displayed time)
It works by trying all possible offsets, and choosing the one that
minimises the the sum of speeds at the time of the image.
It works pretty well for me, so please try it out and send feedback to the list.
Cheers, Aled.
-------------------
Index: src/org/openstreetmap/josm/gui/layer/GeoImageLayer.java
===================================================================
--- src/org/openstreetmap/josm/gui/layer/GeoImageLayer.java (revision 599)
+++ src/org/openstreetmap/josm/gui/layer/GeoImageLayer.java (working copy)
@@ -29,6 +29,8 @@
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
+import java.util.Hashtable;
+import java.util.Iterator;
import java.util.LinkedList;
import javax.swing.BorderFactory;
@@ -425,11 +427,19 @@
Main.map.repaint();
}
});
+ JMenuItem autoSync = new JMenuItem(tr("Auto-sync clock"),
ImageProvider.get("clock"));
+ autoSync.addActionListener(new ActionListener(){
+ public void actionPerformed(ActionEvent e) {
+ autoSync();
+ Main.map.repaint();
+ }
+ });
return new Component[]{
new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)),
new JMenuItem(new LayerListDialog.DeleteLayerAction(this)),
new JSeparator(),
sync,
+ autoSync,
new JSeparator(),
new JMenuItem(new RenameLayerAction(null, this)),
new JSeparator(),
@@ -502,6 +512,96 @@
}
}
}
+
+ // automatically sync photos to gps trail, by exploiting the likely
+ // probability that photos are taken while stationary.
+ private void autoSync() {
+ long gpsOffsetMillis = 1000 * 3600 *
Long.parseLong(Main.pref.get("tagimages.gpstimezone", "0"));
+
+ // go through the gps log, working out the speed at each point.
+ Hashtable<Long, Double> speedTable = new Hashtable<Long, Double>();
+ long firstGpsTimeSecs = 0, lastGpsTimeSecs = 0, firstImageTimeSecs
= 0, lastImageTimeSecs = 0;
+
+ long previousTimeMillis = Long.MIN_VALUE;
+ long previousTimeSecs = Long.MIN_VALUE;
+ EastNorth previousPos = null;
+ Iterator<TimedPoint> iter = gps.iterator();
+ while (iter.hasNext()) {
+ TimedPoint gpsPoint = iter.next();
+ long timeMillis = gpsOffsetMillis + gpsPoint.time.getTime();
+ long timeSecs = timeMillis / 1000;
+
+ if (previousPos != null) {
+ // the 100000 is just a fudge factor to get the distance into some
+ // reasonably sized units. We don't care about getting the distance
+ // into "real" units as we are only using it to find the minimum
+ // sum of speeds.
+ double distanceDiff = 100000 *
Math.sqrt(gpsPoint.pos.distance(previousPos));
+
+ long timeDiffMillis = timeMillis - previousTimeMillis;
+ if (timeDiffMillis <= 0) continue;
+ double speed = 1000 * (distanceDiff / timeDiffMillis);
+
+ // Fill in small gaps in the GPS trail
+ if (timeDiffMillis <= 60000) {
+ for (long t = previousTimeSecs + 1; t < timeSecs; t++) {
+ speedTable.put(t, speed);
+ }
+ }
+ speedTable.put(timeSecs, speed);
+
+ lastGpsTimeSecs = timeSecs;
+ } else {
+ firstGpsTimeSecs = timeSecs;
+ }
+ previousTimeMillis = timeMillis;
+ previousTimeSecs = timeSecs;
+ previousPos = gpsPoint.pos;
+ }
+
+ firstImageTimeSecs = data.get(0).time.getTime() / 1000;
+ lastImageTimeSecs = data.get(data.size() - 1).time.getTime() / 1000;
+
+ int startOffset = (int) (firstGpsTimeSecs - lastImageTimeSecs - 4000);
+ int endOffset = (int) (lastGpsTimeSecs - firstImageTimeSecs + 4000);
+
+ if (startOffset > endOffset) return;
+
+ int bestOffset = 0;
+ int bestMatchCount = 0;
+ double bestSpeedSum = Double.MAX_VALUE;
+ int previousMatchCount = 0;
+ for (int offset = startOffset; offset <= endOffset + endOffset; offset++) {
+ double speedSum = 0;
+ int matchCount = 0;
+ for (int i = 0; i < data.size(); i++) {
+ ImageEntry ie = data.get(i);
+ long imageTimeSecs = ie.time.getTime() / 1000;
+ long imageCandidateTime = offset + imageTimeSecs;
+ if (speedTable.containsKey(imageCandidateTime)) {
+ double speed = speedTable.get(imageCandidateTime);
+ speedSum += speed;
+ matchCount += 1;
+ }
+ }
+ //System.out.println("Offset: " + offset + " speedsum=" + speedSum
+ " matchcount=" + matchCount);
+
+ if (matchCount > bestMatchCount ||
+ (matchCount > 0 && matchCount == bestMatchCount && speedSum <
bestSpeedSum)) {
+ bestMatchCount = matchCount;
+ bestSpeedSum = speedSum;
+ bestOffset = offset;
+ }
+ if (matchCount == 0 || matchCount < previousMatchCount) offset += 300;
+ previousMatchCount = matchCount;
+ }
+
+ //System.out.println("Best offset = " + bestOffset);
+
+ delta = bestOffset * 1000;
+ Main.pref.put("tagimages.delta", "" + delta);
+ calculatePosition();
+ }
private static Icon loadScaledImage(File f, int maxSize) {
Image img = new ImageIcon(f.getPath()).getImage();
More information about the josm-dev
mailing list