- Timestamp:
- 2021-07-18T06:58:54+02:00 (4 years ago)
- Location:
- trunk
- Files:
-
- 3 added
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/data/gpx/GpxImageCorrelation.java
r18034 r18061 3 3 4 4 import java.util.ArrayList; 5 import java.util.Collection; 5 6 import java.util.List; 6 7 import java.util.concurrent.TimeUnit; 7 8 9 import org.openstreetmap.josm.data.coor.LatLon; 10 import org.openstreetmap.josm.data.projection.Projection; 11 import org.openstreetmap.josm.data.projection.ProjectionRegistry; 8 12 import org.openstreetmap.josm.spi.preferences.Config; 9 13 import org.openstreetmap.josm.tools.Logging; 10 14 import org.openstreetmap.josm.tools.Pair; 15 import org.openstreetmap.josm.tools.Utils; 11 16 12 17 /** … … 25 30 * @param images images to match 26 31 * @param selectedGpx selected GPX data 27 * @param offset offset 28 * @param forceTags force tagging of all photos, otherwise prefs are used 32 * @param settings correlation settings 29 33 * @return number of matched points 30 34 */ 31 public static int matchGpxTrack(List<? extends GpxImageEntry> images, GpxData selectedGpx, long offset, boolean forceTags) {35 public static int matchGpxTrack(List<? extends GpxImageEntry> images, GpxData selectedGpx, GpxImageCorrelationSettings settings) { 32 36 int ret = 0; 33 34 long prevWpTime = 0;35 WayPoint prevWp = null;36 37 List<List<List<WayPoint>>> trks = new ArrayList<>();38 39 for (IGpxTrack trk : selectedGpx.tracks) {40 List<List<WayPoint>> segs = new ArrayList<>();41 for (IGpxTrackSegment seg : trk.getSegments()) {42 List<WayPoint> wps = new ArrayList<>(seg.getWayPoints());43 if (!wps.isEmpty()) {44 //remove waypoints at the beginning of the track/segment without timestamps45 int wp;46 for (wp = 0; wp < wps.size(); wp++) {47 if (wps.get(wp).hasDate()) {48 break;49 }50 }51 if (wp == 0) {52 segs.add(wps);53 } else if (wp < wps.size()) {54 segs.add(wps.subList(wp, wps.size()));55 }56 }57 }58 //sort segments by first waypoint59 if (!segs.isEmpty()) {60 segs.sort((o1, o2) -> {61 if (o1.isEmpty() || o2.isEmpty())62 return 0;63 return o1.get(0).compareTo(o2.get(0));64 });65 trks.add(segs);66 }67 }68 //sort tracks by first waypoint of first segment69 trks.sort((o1, o2) -> {70 if (o1.isEmpty() || o1.get(0).isEmpty()71 || o2.isEmpty() || o2.get(0).isEmpty())72 return 0;73 return o1.get(0).get(0).compareTo(o2.get(0).get(0));74 });75 37 76 38 boolean trkInt, trkTag, segInt, segTag; 77 39 int trkTime, trkDist, trkTagTime, segTime, segDist, segTagTime; 78 40 79 if (forceTags) { //temporary option to override advanced settings and activate all possible interpolations / tagging methods 41 if (settings.isForceTags()) { 42 // temporary option to override advanced settings and activate all possible interpolations / tagging methods 80 43 trkInt = trkTag = segInt = segTag = true; 81 44 trkTime = trkDist = trkTagTime = segTime = segDist = segTagTime = Integer.MAX_VALUE; … … 102 65 Config.getPref().getInt("geoimage.seg.tag.time.val", 2) : Integer.MAX_VALUE; 103 66 } 67 68 final GpxImageDirectionPositionSettings dirpos = settings.getDirectionPositionSettings(); 69 final long offset = settings.getOffset(); 70 104 71 boolean isFirst = true; 105 106 for (int t = 0; t < trks.size(); t++) { 107 List<List<WayPoint>> segs = trks.get(t); 108 for (int s = 0; s < segs.size(); s++) { 109 List<WayPoint> wps = segs.get(s); 72 long prevWpTime = 0; 73 WayPoint prevWp = null; 74 75 for (List<List<WayPoint>> segs : loadTracks(selectedGpx.tracks)) { 76 boolean firstSegment = true; 77 for (List<WayPoint> wps : segs) { 110 78 for (int i = 0; i < wps.size(); i++) { 111 WayPoint curWp = wps.get(i); 79 final WayPoint curWp = wps.get(i); 112 80 // Interpolate timestamps in the segment, if one or more waypoints miss them 113 81 if (!curWp.hasDate()) { … … 142 110 int tagTime = 0; 143 111 if (i == 0) { 144 if (s == 0) { //First segment of the track, so apply settings for tracks 112 if (firstSegment) { 113 // First segment of the track, so apply settings for tracks 114 firstSegment = false; 145 115 if (!trkInt || isFirst || prevWp == null || 146 116 Math.abs(curWpTime - prevWpTime) > TimeUnit.MINUTES.toMillis(trkTime) || … … 152 122 } 153 123 } 154 } else { //Apply settings for segments 124 } else { 125 // Apply settings for segments 155 126 if (!segInt || prevWp == null || 156 127 Math.abs(curWpTime - prevWpTime) > TimeUnit.MINUTES.toMillis(segTime) || … … 163 134 } 164 135 } 165 ret += matchPoints(images, prevWp, prevWpTime, curWp, curWpTime, offset, interpolate, tagTime, false); 136 ret += matchPoints(images, prevWp, prevWpTime, curWp, curWpTime, offset, interpolate, tagTime, false, dirpos); 166 137 prevWp = curWp; 167 138 prevWpTime = curWpTime; … … 170 141 } 171 142 if (trkTag && prevWp != null) { 172 ret += matchPoints(images, prevWp, prevWpTime, prevWp, prevWpTime, offset, false, trkTagTime, true); 143 ret += matchPoints(images, prevWp, prevWpTime, prevWp, prevWpTime, offset, false, trkTagTime, true, dirpos); 173 144 } 174 145 return ret; 146 } 147 148 static List<List<List<WayPoint>>> loadTracks(Collection<IGpxTrack> tracks) { 149 List<List<List<WayPoint>>> trks = new ArrayList<>(); 150 for (IGpxTrack trk : tracks) { 151 List<List<WayPoint>> segs = new ArrayList<>(); 152 for (IGpxTrackSegment seg : trk.getSegments()) { 153 List<WayPoint> wps = new ArrayList<>(seg.getWayPoints()); 154 if (!wps.isEmpty()) { 155 //remove waypoints at the beginning of the track/segment without timestamps 156 int wp; 157 for (wp = 0; wp < wps.size(); wp++) { 158 if (wps.get(wp).hasDate()) { 159 break; 160 } 161 } 162 if (wp == 0) { 163 segs.add(wps); 164 } else if (wp < wps.size()) { 165 segs.add(wps.subList(wp, wps.size())); 166 } 167 } 168 } 169 //sort segments by first waypoint 170 if (!segs.isEmpty()) { 171 segs.sort((o1, o2) -> { 172 if (o1.isEmpty() || o2.isEmpty()) 173 return 0; 174 return o1.get(0).compareTo(o2.get(0)); 175 }); 176 trks.add(segs); 177 } 178 } 179 //sort tracks by first waypoint of first segment 180 trks.sort((o1, o2) -> { 181 if (o1.isEmpty() || o1.get(0).isEmpty() 182 || o2.isEmpty() || o2.get(0).isEmpty()) 183 return 0; 184 return o1.get(0).get(0).compareTo(o2.get(0).get(0)); 185 }); 186 return trks; 175 187 } 176 188 … … 190 202 191 203 private static int matchPoints(List<? extends GpxImageEntry> images, WayPoint prevWp, long prevWpTime, WayPoint curWp, long curWpTime, 192 long offset, boolean interpolate, int tagTime, boolean isLast) { 204 long offset, boolean interpolate, int tagTime, boolean isLast, GpxImageDirectionPositionSettings dirpos) { 193 205 194 206 int ret = 0; … … 218 230 } 219 231 220 Double curElevation = getElevation(curWp); 232 final Double curElevation = getElevation(curWp); 221 233 222 234 if (!interpolate || isLast) { … … 250 262 // previous track point assuming a constant speed in between 251 263 while (i >= 0) { 252 GpxImageEntry curImg = images.get(i); 253 GpxImageEntry curTmp = curImg.getTmp(); 264 final GpxImageEntry curImg = images.get(i); 265 final GpxImageEntry curTmp = curImg.getTmp(); 254 266 final long imgTime = curImg.getExifInstant().toEpochMilli(); 255 267 if (imgTime < prevWpTime) { … … 258 270 if (!curTmp.hasNewGpsData()) { 259 271 // The values of timeDiff are between 0 and 1, it is not seconds but a dimensionless variable 260 double timeDiff = (double) (imgTime - prevWpTime) / Math.abs(curWpTime - prevWpTime); 261 curTmp.setPos(prevWp.getCoor().interpolate(curWp.getCoor(), timeDiff)); 272 final double timeDiff = (double) (imgTime - prevWpTime) / Math.abs(curWpTime - prevWpTime); 273 final boolean shiftXY = dirpos.getShiftImageX() != 0d || dirpos.getShiftImageY() != 0d; 274 final LatLon prevCoor = prevWp.getCoor(); 275 final LatLon curCoor = curWp.getCoor(); 276 LatLon position = prevCoor.interpolate(curCoor, timeDiff); 277 if (shiftXY || dirpos.isSetImageDirection()) { 278 double direction = prevCoor.bearing(curCoor); 279 if (dirpos.isSetImageDirection()) { 280 curTmp.setExifImgDir((Utils.toDegrees(direction) + dirpos.getImageDirectionAngleOffset()) % 360d); 281 direction = Utils.toRadians(curTmp.getExifImgDir()); 282 } 283 if (shiftXY) { 284 final Projection proj = ProjectionRegistry.getProjection(); 285 final double offsetX = dirpos.getShiftImageX(); 286 final double offsetY = dirpos.getShiftImageY(); 287 final double r = Math.sqrt(offsetX * offsetX + offsetY * offsetY); 288 final double orientation = (direction + LatLon.ZERO.bearing(new LatLon(offsetX, offsetY))) % (2 * Math.PI); 289 position = proj.eastNorth2latlon(proj.latlon2eastNorth(position) 290 .add(r * Math.sin(orientation), r * Math.cos(orientation))); 291 } 292 } 293 curTmp.setPos(position); 262 294 curTmp.setSpeed(speed); 263 295 if (curElevation != null && prevElevation != null) { 264 curTmp.setElevation(prevElevation + (curElevation - prevElevation) * timeDiff); 296 curTmp.setElevation(prevElevation + (curElevation - prevElevation) * timeDiff + dirpos.getElevationShift()); 265 297 } 266 298 curTmp.setGpsTime(curImg.getExifInstant().minusMillis(offset)); -
trunk/src/org/openstreetmap/josm/gui/layer/geoimage/CorrelateGpxWithImages.java
r18059 r18061 42 42 import javax.swing.MutableComboBoxModel; 43 43 import javax.swing.SwingConstants; 44 import javax.swing.event.ChangeEvent; 45 import javax.swing.event.ChangeListener; 44 46 import javax.swing.event.DocumentEvent; 45 47 import javax.swing.event.DocumentListener; 46 48 49 import org.openstreetmap.josm.actions.ExpertToggleAction; 50 import org.openstreetmap.josm.actions.ExpertToggleAction.ExpertModeChangeListener; 47 51 import org.openstreetmap.josm.data.gpx.GpxData; 48 52 import org.openstreetmap.josm.data.gpx.GpxImageCorrelation; 53 import org.openstreetmap.josm.data.gpx.GpxImageCorrelationSettings; 49 54 import org.openstreetmap.josm.data.gpx.GpxImageEntry; 50 55 import org.openstreetmap.josm.data.gpx.GpxTimeOffset; … … 77 82 * @since 2566 78 83 */ 79 public class CorrelateGpxWithImages extends AbstractAction implements Destroyable { 84 public class CorrelateGpxWithImages extends AbstractAction implements ExpertModeChangeListener, Destroyable { 80 85 81 86 private static MutableComboBoxModel<GpxDataWrapper> gpxModel; … … 94 99 new ImageProvider("dialogs/geoimage/gpx2img").getResource().attachImageIcon(this, true); 95 100 this.yLayer = layer; 101 ExpertToggleAction.addExpertModeChangeListener(this); 96 102 } 97 103 … … 237 243 private JCheckBox cbShowThumbs; 238 244 private JLabel statusBarText; 245 private JSeparator sepDirectionPosition; 246 private ImageDirectionPositionPanel pDirectionPosition; 239 247 240 248 // remember the last number of matched photos … … 556 564 panelTf.add(cbShowThumbs, gbc); 557 565 566 gbc = GBC.eol().fill(GBC.HORIZONTAL).insets(0, 12, 0, 0); 567 sepDirectionPosition = new JSeparator(SwingConstants.HORIZONTAL); 568 panelTf.add(sepDirectionPosition, gbc); 569 570 gbc = GBC.eol(); 571 gbc.gridwidth = 3; 572 pDirectionPosition = ImageDirectionPositionPanel.forGpxTrace(); 573 panelTf.add(pDirectionPosition, gbc); 574 575 expertChanged(ExpertToggleAction.isExpert()); 576 558 577 final JPanel statusBar = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); 559 578 statusBar.setBorder(BorderFactory.createLoweredBevelBorder()); … … 563 582 564 583 RepaintTheMapListener repaintTheMap = new RepaintTheMapListener(); 584 pDirectionPosition.addFocusListenerOnComponent(repaintTheMap); 565 585 tfTimezone.addFocusListener(repaintTheMap); 566 586 tfOffset.addFocusListener(repaintTheMap); … … 570 590 cbExifImg.addItemListener(statusBarUpdaterWithRepaint); 571 591 cbTaggedImg.addItemListener(statusBarUpdaterWithRepaint); 592 pDirectionPosition.addChangeListenerOnComponents(statusBarUpdaterWithRepaint); 572 593 573 594 statusBarUpdater.matchAndUpdateStatusBar(); … … 596 617 } 597 618 619 @Override 620 public void expertChanged(boolean isExpert) { 621 if (sepDirectionPosition != null) { 622 sepDirectionPosition.setVisible(isExpert); 623 } 624 if (pDirectionPosition != null) { 625 pDirectionPosition.setVisible(isExpert); 626 } 627 if (syncDialog != null) { 628 syncDialog.pack(); 629 } 630 } 631 598 632 private static void removeDuplicates(File file) { 599 633 for (int i = gpxModel.getSize() - 1; i >= 0; i--) { … … 613 647 private final transient StatusBarUpdater statusBarUpdaterWithRepaint = new StatusBarUpdater(true); 614 648 615 private class StatusBarUpdater implements DocumentListener, ItemListener, ActionListener { 649 private class StatusBarUpdater implements DocumentListener, ItemListener, ChangeListener, ActionListener { 616 650 private final boolean doRepaint; 617 651 … … 637 671 @Override 638 672 public void itemStateChanged(ItemEvent e) { 673 matchAndUpdateStatusBar(); 674 } 675 676 @Override 677 public void stateChanged(ChangeEvent e) { 639 678 matchAndUpdateStatusBar(); 640 679 } … … 681 720 682 721 final long offsetMs = ((long) (timezone.getHours() * TimeUnit.HOURS.toMillis(1))) + delta.getMilliseconds(); // in milliseconds 683 lastNumMatched = GpxImageCorrelation.matchGpxTrack(dateImgLst, selGpx.data, offsetMs, forceTags); 722 lastNumMatched = GpxImageCorrelation.matchGpxTrack(dateImgLst, selGpx.data, 723 pDirectionPosition.isVisible() ? 724 new GpxImageCorrelationSettings(offsetMs, forceTags, pDirectionPosition.getSettings()) : 725 new GpxImageCorrelationSettings(offsetMs, forceTags)); 684 726 685 727 return trn("<html>Matched <b>{0}</b> of <b>{1}</b> photo to GPX track.</html>", … … 869 911 @Override 870 912 public void destroy() { 913 ExpertToggleAction.removeExpertModeChangeListener(this); 871 914 if (cbGpx != null) { 872 915 // Force the JCombobox to remove its eventListener from the static GpxDataWrapper … … 874 917 cbGpx = null; 875 918 } 919 920 outerPanel = null; 921 tfTimezone = null; 922 tfOffset = null; 923 cbExifImg = null; 924 cbTaggedImg = null; 925 cbShowThumbs = null; 926 statusBarText = null; 927 sepDirectionPosition = null; 928 pDirectionPosition = null; 929 876 930 closeDialog(); 877 931 } -
trunk/src/org/openstreetmap/josm/gui/widgets/DefaultTextComponentValidator.java
r10073 r18061 15 15 /** 16 16 * Constructs a new {@code DefaultTextComponentValidator}. 17 * @param tc he text component. Must not be null. 17 * @param tc the text component. Must not be null. 18 18 * @param validFeedback text displayed for valid feedback 19 19 * @param invalidFeedback text displayed for invalid feedback -
trunk/test/unit/org/openstreetmap/josm/data/gpx/GpxImageCorrelationTest.java
r17888 r18061 115 115 @Test 116 116 void testMatchGpxTrack1() { 117 assertEquals(7, GpxImageCorrelation.matchGpxTrack(images, gpx, 0, false));117 assertEquals(7, GpxImageCorrelation.matchGpxTrack(images, gpx, new GpxImageCorrelationSettings(0, false))); 118 118 assertEquals(null, ib.getPos()); 119 119 assertEquals(new CachedLatLon(47.19286847859621, 8.79732714034617), i0.getPos()); // start of track … … 167 167 s.putBoolean("geoimage.seg.int", false); 168 168 169 assertEquals(4, GpxImageCorrelation.matchGpxTrack(images, gpx, 0, false));169 assertEquals(4, GpxImageCorrelation.matchGpxTrack(images, gpx, new GpxImageCorrelationSettings(0, false))); 170 170 assertEquals(null, ib.getPos()); 171 171 assertEquals(null, i0.getPos()); … … 195 195 s.putBoolean("geoimage.seg.int", false); 196 196 197 assertEquals(6, GpxImageCorrelation.matchGpxTrack(images, gpx, 0, false));197 assertEquals(6, GpxImageCorrelation.matchGpxTrack(images, gpx, new GpxImageCorrelationSettings(0, false))); 198 198 assertEquals(null, ib.getPos()); 199 199 assertEquals(new CachedLatLon(47.19286847859621, 8.79732714034617), i0.getPos()); … … 217 217 @Test 218 218 void testMatchGpxTrack4() { 219 assertEquals(9, GpxImageCorrelation.matchGpxTrack(images, gpx, 0, true));219 assertEquals(9, GpxImageCorrelation.matchGpxTrack(images, gpx, new GpxImageCorrelationSettings(0, true))); 220 220 assertEquals(new CachedLatLon(47.19286847859621, 8.79732714034617), ib.getPos()); 221 221 assertEquals(new CachedLatLon(47.19286847859621, 8.79732714034617), i0.getPos()); … … 254 254 s.putBoolean("geoimage.seg.int.dist", false); 255 255 256 assertEquals(9, GpxImageCorrelation.matchGpxTrack(images, gpx, 0, false));256 assertEquals(9, GpxImageCorrelation.matchGpxTrack(images, gpx, new GpxImageCorrelationSettings(0, false))); 257 257 assertEquals(new CachedLatLon(47.19286847859621, 8.79732714034617), ib.getPos()); 258 258 assertEquals(new CachedLatLon(47.19286847859621, 8.79732714034617), i0.getPos()); … … 288 288 s.putBoolean("geoimage.seg.int", false); 289 289 290 assertEquals(4, GpxImageCorrelation.matchGpxTrack(images, gpx, 0, false));290 assertEquals(4, GpxImageCorrelation.matchGpxTrack(images, gpx, new GpxImageCorrelationSettings(0, false))); 291 291 } 292 292 … … 307 307 s.putBoolean("geoimage.seg.int", false); 308 308 309 assertEquals(6, GpxImageCorrelation.matchGpxTrack(images, gpx, 0, false));309 assertEquals(6, GpxImageCorrelation.matchGpxTrack(images, gpx, new GpxImageCorrelationSettings(0, false))); 310 310 } 311 311 … … 326 326 s.putBoolean("geoimage.seg.int", false); 327 327 328 assertEquals(4, GpxImageCorrelation.matchGpxTrack(images, gpx, 0, false));328 assertEquals(4, GpxImageCorrelation.matchGpxTrack(images, gpx, new GpxImageCorrelationSettings(0, false))); 329 329 } 330 330 … … 345 345 s.putBoolean("geoimage.seg.int", false); 346 346 347 assertEquals(6, GpxImageCorrelation.matchGpxTrack(images, gpx, 0, false));347 assertEquals(6, GpxImageCorrelation.matchGpxTrack(images, gpx, new GpxImageCorrelationSettings(0, false))); 348 348 } 349 349
Note:
See TracChangeset
for help on using the changeset viewer.