Changeset 18061 in josm


Ignore:
Timestamp:
2021-07-18T06:58:54+02:00 (3 years ago)
Author:
Don-vip
Message:

see #21131 - Improve GPX image correlation: direction detection and position shift

Location:
trunk
Files:
3 added
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/data/gpx/GpxImageCorrelation.java

    r18034 r18061  
    33
    44import java.util.ArrayList;
     5import java.util.Collection;
    56import java.util.List;
    67import java.util.concurrent.TimeUnit;
    78
     9import org.openstreetmap.josm.data.coor.LatLon;
     10import org.openstreetmap.josm.data.projection.Projection;
     11import org.openstreetmap.josm.data.projection.ProjectionRegistry;
    812import org.openstreetmap.josm.spi.preferences.Config;
    913import org.openstreetmap.josm.tools.Logging;
    1014import org.openstreetmap.josm.tools.Pair;
     15import org.openstreetmap.josm.tools.Utils;
    1116
    1217/**
     
    2530     * @param images images to match
    2631     * @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
    2933     * @return number of matched points
    3034     */
    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) {
    3236        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 timestamps
    45                     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 waypoint
    59             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 segment
    69         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         });
    7537
    7638        boolean trkInt, trkTag, segInt, segTag;
    7739        int trkTime, trkDist, trkTagTime, segTime, segDist, segTagTime;
    7840
    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
    8043            trkInt = trkTag = segInt = segTag = true;
    8144            trkTime = trkDist = trkTagTime = segTime = segDist = segTagTime = Integer.MAX_VALUE;
     
    10265                    Config.getPref().getInt("geoimage.seg.tag.time.val", 2) : Integer.MAX_VALUE;
    10366        }
     67
     68        final GpxImageDirectionPositionSettings dirpos = settings.getDirectionPositionSettings();
     69        final long offset = settings.getOffset();
     70
    10471        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) {
    11078                for (int i = 0; i < wps.size(); i++) {
    111                     WayPoint curWp = wps.get(i);
     79                    final WayPoint curWp = wps.get(i);
    11280                    // Interpolate timestamps in the segment, if one or more waypoints miss them
    11381                    if (!curWp.hasDate()) {
     
    142110                    int tagTime = 0;
    143111                    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;
    145115                            if (!trkInt || isFirst || prevWp == null ||
    146116                                    Math.abs(curWpTime - prevWpTime) > TimeUnit.MINUTES.toMillis(trkTime) ||
     
    152122                                }
    153123                            }
    154                         } else { //Apply settings for segments
     124                        } else {
     125                            // Apply settings for segments
    155126                            if (!segInt || prevWp == null ||
    156127                                    Math.abs(curWpTime - prevWpTime) > TimeUnit.MINUTES.toMillis(segTime) ||
     
    163134                        }
    164135                    }
    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);
    166137                    prevWp = curWp;
    167138                    prevWpTime = curWpTime;
     
    170141        }
    171142        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);
    173144        }
    174145        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;
    175187    }
    176188
     
    190202
    191203    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) {
    193205
    194206        int ret = 0;
     
    218230        }
    219231
    220         Double curElevation = getElevation(curWp);
     232        final Double curElevation = getElevation(curWp);
    221233
    222234        if (!interpolate || isLast) {
     
    250262            // previous track point assuming a constant speed in between
    251263            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();
    254266                final long imgTime = curImg.getExifInstant().toEpochMilli();
    255267                if (imgTime < prevWpTime) {
     
    258270                if (!curTmp.hasNewGpsData()) {
    259271                    // 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);
    262294                    curTmp.setSpeed(speed);
    263295                    if (curElevation != null && prevElevation != null) {
    264                         curTmp.setElevation(prevElevation + (curElevation - prevElevation) * timeDiff);
     296                        curTmp.setElevation(prevElevation + (curElevation - prevElevation) * timeDiff + dirpos.getElevationShift());
    265297                    }
    266298                    curTmp.setGpsTime(curImg.getExifInstant().minusMillis(offset));
  • trunk/src/org/openstreetmap/josm/gui/layer/geoimage/CorrelateGpxWithImages.java

    r18059 r18061  
    4242import javax.swing.MutableComboBoxModel;
    4343import javax.swing.SwingConstants;
     44import javax.swing.event.ChangeEvent;
     45import javax.swing.event.ChangeListener;
    4446import javax.swing.event.DocumentEvent;
    4547import javax.swing.event.DocumentListener;
    4648
     49import org.openstreetmap.josm.actions.ExpertToggleAction;
     50import org.openstreetmap.josm.actions.ExpertToggleAction.ExpertModeChangeListener;
    4751import org.openstreetmap.josm.data.gpx.GpxData;
    4852import org.openstreetmap.josm.data.gpx.GpxImageCorrelation;
     53import org.openstreetmap.josm.data.gpx.GpxImageCorrelationSettings;
    4954import org.openstreetmap.josm.data.gpx.GpxImageEntry;
    5055import org.openstreetmap.josm.data.gpx.GpxTimeOffset;
     
    7782 * @since 2566
    7883 */
    79 public class CorrelateGpxWithImages extends AbstractAction implements Destroyable {
     84public class CorrelateGpxWithImages extends AbstractAction implements ExpertModeChangeListener, Destroyable {
    8085
    8186    private static MutableComboBoxModel<GpxDataWrapper> gpxModel;
     
    9499        new ImageProvider("dialogs/geoimage/gpx2img").getResource().attachImageIcon(this, true);
    95100        this.yLayer = layer;
     101        ExpertToggleAction.addExpertModeChangeListener(this);
    96102    }
    97103
     
    237243    private JCheckBox cbShowThumbs;
    238244    private JLabel statusBarText;
     245    private JSeparator sepDirectionPosition;
     246    private ImageDirectionPositionPanel pDirectionPosition;
    239247
    240248    // remember the last number of matched photos
     
    556564        panelTf.add(cbShowThumbs, gbc);
    557565
     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
    558577        final JPanel statusBar = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0));
    559578        statusBar.setBorder(BorderFactory.createLoweredBevelBorder());
     
    563582
    564583        RepaintTheMapListener repaintTheMap = new RepaintTheMapListener();
     584        pDirectionPosition.addFocusListenerOnComponent(repaintTheMap);
    565585        tfTimezone.addFocusListener(repaintTheMap);
    566586        tfOffset.addFocusListener(repaintTheMap);
     
    570590        cbExifImg.addItemListener(statusBarUpdaterWithRepaint);
    571591        cbTaggedImg.addItemListener(statusBarUpdaterWithRepaint);
     592        pDirectionPosition.addChangeListenerOnComponents(statusBarUpdaterWithRepaint);
    572593
    573594        statusBarUpdater.matchAndUpdateStatusBar();
     
    596617    }
    597618
     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
    598632    private static void removeDuplicates(File file) {
    599633        for (int i = gpxModel.getSize() - 1; i >= 0; i--) {
     
    613647    private final transient StatusBarUpdater statusBarUpdaterWithRepaint = new StatusBarUpdater(true);
    614648
    615     private class StatusBarUpdater implements DocumentListener, ItemListener, ActionListener {
     649    private class StatusBarUpdater implements DocumentListener, ItemListener, ChangeListener, ActionListener {
    616650        private final boolean doRepaint;
    617651
     
    637671        @Override
    638672        public void itemStateChanged(ItemEvent e) {
     673            matchAndUpdateStatusBar();
     674        }
     675
     676        @Override
     677        public void stateChanged(ChangeEvent e) {
    639678            matchAndUpdateStatusBar();
    640679        }
     
    681720
    682721            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));
    684726
    685727            return trn("<html>Matched <b>{0}</b> of <b>{1}</b> photo to GPX track.</html>",
     
    869911    @Override
    870912    public void destroy() {
     913        ExpertToggleAction.removeExpertModeChangeListener(this);
    871914        if (cbGpx != null) {
    872915            // Force the JCombobox to remove its eventListener from the static GpxDataWrapper
     
    874917            cbGpx = null;
    875918        }
     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
    876930        closeDialog();
    877931    }
  • trunk/src/org/openstreetmap/josm/gui/widgets/DefaultTextComponentValidator.java

    r10073 r18061  
    1515    /**
    1616     * 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.
    1818     * @param validFeedback text displayed for valid feedback
    1919     * @param invalidFeedback text displayed for invalid feedback
  • trunk/test/unit/org/openstreetmap/josm/data/gpx/GpxImageCorrelationTest.java

    r17888 r18061  
    115115    @Test
    116116    void testMatchGpxTrack1() {
    117         assertEquals(7, GpxImageCorrelation.matchGpxTrack(images, gpx, 0, false));
     117        assertEquals(7, GpxImageCorrelation.matchGpxTrack(images, gpx, new GpxImageCorrelationSettings(0, false)));
    118118        assertEquals(null, ib.getPos());
    119119        assertEquals(new CachedLatLon(47.19286847859621, 8.79732714034617), i0.getPos()); // start of track
     
    167167        s.putBoolean("geoimage.seg.int", false);
    168168
    169         assertEquals(4, GpxImageCorrelation.matchGpxTrack(images, gpx, 0, false));
     169        assertEquals(4, GpxImageCorrelation.matchGpxTrack(images, gpx, new GpxImageCorrelationSettings(0, false)));
    170170        assertEquals(null, ib.getPos());
    171171        assertEquals(null, i0.getPos());
     
    195195        s.putBoolean("geoimage.seg.int", false);
    196196
    197         assertEquals(6, GpxImageCorrelation.matchGpxTrack(images, gpx, 0, false));
     197        assertEquals(6, GpxImageCorrelation.matchGpxTrack(images, gpx, new GpxImageCorrelationSettings(0, false)));
    198198        assertEquals(null, ib.getPos());
    199199        assertEquals(new CachedLatLon(47.19286847859621, 8.79732714034617), i0.getPos());
     
    217217    @Test
    218218    void testMatchGpxTrack4() {
    219         assertEquals(9, GpxImageCorrelation.matchGpxTrack(images, gpx, 0, true));
     219        assertEquals(9, GpxImageCorrelation.matchGpxTrack(images, gpx, new GpxImageCorrelationSettings(0, true)));
    220220        assertEquals(new CachedLatLon(47.19286847859621, 8.79732714034617), ib.getPos());
    221221        assertEquals(new CachedLatLon(47.19286847859621, 8.79732714034617), i0.getPos());
     
    254254        s.putBoolean("geoimage.seg.int.dist", false);
    255255
    256         assertEquals(9, GpxImageCorrelation.matchGpxTrack(images, gpx, 0, false));
     256        assertEquals(9, GpxImageCorrelation.matchGpxTrack(images, gpx, new GpxImageCorrelationSettings(0, false)));
    257257        assertEquals(new CachedLatLon(47.19286847859621, 8.79732714034617), ib.getPos());
    258258        assertEquals(new CachedLatLon(47.19286847859621, 8.79732714034617), i0.getPos());
     
    288288        s.putBoolean("geoimage.seg.int", false);
    289289
    290         assertEquals(4, GpxImageCorrelation.matchGpxTrack(images, gpx, 0, false));
     290        assertEquals(4, GpxImageCorrelation.matchGpxTrack(images, gpx, new GpxImageCorrelationSettings(0, false)));
    291291    }
    292292
     
    307307        s.putBoolean("geoimage.seg.int", false);
    308308
    309         assertEquals(6, GpxImageCorrelation.matchGpxTrack(images, gpx, 0, false));
     309        assertEquals(6, GpxImageCorrelation.matchGpxTrack(images, gpx, new GpxImageCorrelationSettings(0, false)));
    310310    }
    311311
     
    326326        s.putBoolean("geoimage.seg.int", false);
    327327
    328         assertEquals(4, GpxImageCorrelation.matchGpxTrack(images, gpx, 0, false));
     328        assertEquals(4, GpxImageCorrelation.matchGpxTrack(images, gpx, new GpxImageCorrelationSettings(0, false)));
    329329    }
    330330
     
    345345        s.putBoolean("geoimage.seg.int", false);
    346346
    347         assertEquals(6, GpxImageCorrelation.matchGpxTrack(images, gpx, 0, false));
     347        assertEquals(6, GpxImageCorrelation.matchGpxTrack(images, gpx, new GpxImageCorrelationSettings(0, false)));
    348348    }
    349349
Note: See TracChangeset for help on using the changeset viewer.