Changeset 578 in josm for trunk/src/org/openstreetmap


Ignore:
Timestamp:
2008-03-06T13:51:08+01:00 (16 years ago)
Author:
david
Message:

Allow combination of audio marker creation methods and implement marker creation by inference of time from track for untimed waypoints

Location:
trunk/src/org/openstreetmap/josm/gui
Files:
2 edited

Legend:

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

    r575 r578  
    2525import java.net.UnknownHostException;
    2626import java.util.Iterator;
     27import java.util.Collections;
     28import java.util.Comparator;
    2729
    2830import javax.swing.AbstractAction;
     
    4648import org.openstreetmap.josm.actions.SaveAsAction;
    4749import org.openstreetmap.josm.actions.RenameLayerAction;
     50import org.openstreetmap.josm.data.coor.EastNorth;
    4851import org.openstreetmap.josm.data.gpx.GpxData;
    4952import org.openstreetmap.josm.data.gpx.GpxTrack;
     
    499502                String uri = "file:".concat(wavFile.getAbsolutePath());
    500503            MarkerLayer ml = new MarkerLayer(new GpxData(), tr("Audio markers from {0}", name), associatedFile, me);
    501 
    502             // (a) try explicit waypoints - unless suppressed
     504           
     505            Collection<WayPoint> waypoints = new ArrayList<WayPoint>();
     506            boolean timedMarkersOmitted = false;
     507            boolean untimedMarkersOmitted = false;
     508           
     509            // determine time of first point in track
     510            double firstTime = -1.0;
     511        if (data.tracks != null && ! data.tracks.isEmpty()) {
     512                for (GpxTrack track : data.tracks) {
     513                        if (track.trackSegs == null) continue;
     514                        for (Collection<WayPoint> seg : track.trackSegs) {
     515                                for (WayPoint w : seg) {
     516                                        firstTime = w.time;
     517                                        break;
     518                                }
     519                                if (firstTime >= 0.0) break;
     520                        }
     521                        if (firstTime >= 0.0) break;
     522                }
     523        }
     524        if (firstTime < 0.0) {
     525                        JOptionPane.showMessageDialog(Main.parent, tr("No GPX track available in layer to associate audio with."));
     526                        return;
     527        }
     528           
     529            // (a) try explicit timestamped waypoints - unless suppressed
    503530            if (Main.pref.getBoolean("marker.audiofromexplicitwaypoints", true) &&
    504                 data.waypoints != null &&
    505                 ! data.waypoints.isEmpty())
     531                data.waypoints != null && ! data.waypoints.isEmpty())
    506532            {
    507                 double firstTime = -1.0;
    508533                for (WayPoint w : data.waypoints) {
    509                         if (firstTime < 0.0) firstTime = w.time;
    510                         double offset = w.time - firstTime;
    511                         String name = w.attr.containsKey("name") ? w.getString("name") :
    512                                 w.attr.containsKey("desc") ? w.getString("desc") :
    513                                 AudioMarker.inventName(offset);
    514                                 AudioMarker am = AudioMarker.create(w.latlon,
    515                                                 name, uri, ml, w.time, offset);
    516                                 ml.data.add(am);                       
     534                        if (w.time > firstTime) {
     535                                waypoints.add(w);
     536                        } else if (w.time > 0.0) {
     537                                timedMarkersOmitted = true;
     538                        }
    517539                }
    518540            }
    519541
    520             // (b) use explicitly named track points, again unless suppressed
    521             if (ml.data.isEmpty() &&
    522                 Main.pref.getBoolean("marker.namedtrackpoints") &&
    523                 data.tracks != null &&
    524                 ! data.tracks.isEmpty())
     542            // (b) try explicit waypoints without timestamps - unless suppressed
     543            if (Main.pref.getBoolean("marker.audiofromuntimedwaypoints", true) &&
     544                data.waypoints != null && ! data.waypoints.isEmpty())
    525545            {
    526                 double firstTime = -1.0;
     546                for (WayPoint w : data.waypoints) {
     547                        if (waypoints.contains(w)) { continue; }
     548                        WayPoint wNear = nearestPointOnTrack(w.eastNorth, 10.0e-7 /* about 25m */);
     549                        if (wNear != null) {
     550                                WayPoint wc = new WayPoint(w.latlon);
     551                                wc.time = wNear.time;
     552                                if (w.attr.containsKey("name")) wc.attr.put("name", w.getString("name"));
     553                                waypoints.add(wc);
     554                        } else {
     555                                untimedMarkersOmitted = true;
     556                        }
     557                }
     558            }
     559           
     560            // (c) use explicitly named track points, again unless suppressed
     561            if ((Main.pref.getBoolean("marker.audiofromnamedtrackpoints", Main.pref.getBoolean("marker.namedtrackpoints") /* old name */)) &&
     562                data.tracks != null && ! data.tracks.isEmpty())
     563            {
    527564                for (GpxTrack track : data.tracks) {
     565                        if (track.trackSegs == null) continue;
    528566                        for (Collection<WayPoint> seg : track.trackSegs) {
    529567                                for (WayPoint w : seg) {
    530                                         String name;
    531                                         if (w.attr.containsKey("name"))
    532                                                 name = w.getString("name");
    533                                         else if (w.attr.containsKey("desc"))
    534                                                 name = w.getString("desc");
    535                                         else
    536                                                 continue;
    537                                 if (firstTime < 0.0) firstTime = w.time;
    538                                 double offset = w.time - firstTime;
    539                                         AudioMarker am = AudioMarker.create(w.latlon,
    540                                                         name, uri, ml, w.time, offset);
    541                                         ml.data.add(am);                       
     568                                        if (w.attr.containsKey("name") || w.attr.containsKey("desc")) {
     569                                                waypoints.add(w);
     570                                        }
    542571                                }
    543572                        }
     
    545574            }
    546575
    547             // (c) analyse audio for spoken markers here, in due course
     576            // (d) analyse audio for spoken markers here, in due course
    548577           
    549             // (d) simply add a single marker at the start of the track
    550             if (ml.data.isEmpty() &&
    551                 data.tracks != null &&
    552                     ! data.tracks.isEmpty())
     578            // (e) simply add a single marker at the start of the track
     579            if ((Main.pref.getBoolean("marker.audiofromstart") || waypoints.isEmpty()) &&
     580                data.tracks != null && ! data.tracks.isEmpty())
    553581                {
     582                boolean gotOne = false;
    554583                for (GpxTrack track : data.tracks) {
     584                        if (track.trackSegs == null) continue;
    555585                        for (Collection<WayPoint> seg : track.trackSegs) {
    556586                                for (WayPoint w : seg) {
    557                                 AudioMarker am = AudioMarker.create(w.latlon,
    558                                                         tr("start"), uri, ml, w.time, 0.0);
    559                                         ml.data.add(am);                       
     587                                        WayPoint wStart = new WayPoint(w.latlon);
     588                                        wStart.attr.put("name", "start");
     589                                        wStart.time = w.time;
     590                                        waypoints.add(wStart);
     591                                        gotOne = true;
    560592                                        break;
    561593                                }
    562                                 if (! ml.data.isEmpty()) break;
     594                                if (gotOne) break;
    563595                        }
    564                         if (! ml.data.isEmpty()) break;
     596                        if (gotOne) break;
    565597                }
    566598                }
     599
     600            /* we must have got at least one waypoint now */
    567601           
    568         if (! ml.data.isEmpty()) {
    569                 Main.main.addLayer(ml);
    570         } else {
    571                         JOptionPane.showMessageDialog(Main.parent, tr("Nothing available to associate audio with."));
    572         }
     602            Collections.sort((ArrayList<WayPoint>) waypoints, new Comparator<WayPoint>() {
     603                public int compare(WayPoint a, WayPoint b) {
     604                        return a.time <= b.time ? -1 : 1;
     605                }
     606            });
     607
     608            firstTime = -1.0; /* this time of the first waypoint, not first trackpoint */
     609            for (WayPoint w : waypoints) {
     610                if (firstTime < 0.0) firstTime = w.time;
     611                double offset = w.time - firstTime;
     612                        String name;
     613                        if (w.attr.containsKey("name"))
     614                                name = w.getString("name");
     615                        else if (w.attr.containsKey("desc"))
     616                                name = w.getString("desc");
     617                        else
     618                                name = AudioMarker.inventName(offset);
     619                        AudioMarker am = AudioMarker.create(w.latlon,
     620                                        name, uri, ml, w.time, offset);
     621                        ml.data.add(am);                                       
     622            }
     623            Main.main.addLayer(ml);
     624           
     625            if (timedMarkersOmitted) {
     626                        JOptionPane.showMessageDialog(Main.parent, tr("Some waypoints with timestamps from before the start of the track were omitted."));
     627            }
     628            if (untimedMarkersOmitted) {
     629                        JOptionPane.showMessageDialog(Main.parent, tr("Some waypoints which were too far from the track to sensibly estimate their time were omitted."));
     630            }
     631        }
     632
     633        /**
     634         * Makes a WayPoint at the projection of point P onto the track providing P is
     635         * less than tolerance away from the track
     636
     637         * @param P : the point to determine the projection for
     638         * @param tolerance : must be no further than this from the track
     639         * @return the closest point on the track to P, which may be the
     640         * first or last point if off the end of a segment, or may be null if
     641         * nothing close enough
     642         */
     643        public WayPoint nearestPointOnTrack(EastNorth P, double tolerance) {
     644                /*
     645                 * assume the coordinates of P are xp,yp, and those of a section of track
     646                 * between two trackpoints are R=xr,yr and S=xs,ys. Let N be the projected point.
     647                 *
     648                 * The equation of RS is Ax + By + C = 0 where
     649                 * A = ys - yr
     650                 * B = xr - xs
     651                 * C = - Axr - Byr
     652                 *
     653                 * Also, note that the distance RS^2 is A^2 + B^2
     654                 *
     655                 * If RS^2 == 0.0 ignore the degenerate section of track
     656                 *
     657                 * PN^2 = (Axp + Byp + C)^2 / RS^2
     658                 * that is the distance from P to the line
     659                 *
     660                 * so if PN^2 is less than PNmin^2 (initialized to tolerance) we can reject
     661                 * the line; otherwise...
     662                 * determine if the projected poijnt lies within the bounds of the line:
     663                 * PR^2 - PN^2 <= RS^2 and PS^2 - PN^2 <= RS^2
     664                 *
     665                 * where PR^2 = (xp - xr)^2 + (yp-yr)^2
     666                 * and   PS^2 = (xp - xs)^2 + (yp-ys)^2
     667                 *
     668                 * If so, calculate N as
     669                 * xn = xr + (RN/RS) B
     670                 * yn = y1 + (RN/RS) A
     671                 *
     672                 * where RN = sqrt(PR^2 - PN^2)
     673                 */
     674               
     675                double PNminsq = tolerance * tolerance;
     676                EastNorth bestEN = null;
     677                double bestTime = 0.0;
     678                double px = P.east();
     679                double py = P.north();
     680                double rx = 0.0, ry = 0.0, sx, sy, x, y;
     681                if (data.tracks == null) return null;
     682                for (GpxTrack track : data.tracks) {
     683                        if (track.trackSegs == null) continue;                 
     684                        for (Collection<WayPoint> seg : track.trackSegs) {
     685                                WayPoint R = null;
     686                                for (WayPoint S : seg) {
     687                                        if (R == null) {
     688                                                R = S;
     689                                                rx = R.eastNorth.east();
     690                                                ry = R.eastNorth.north();
     691                                                x = px - rx;
     692                                                y = py - ry;
     693                                                double PRsq = x * x + y * y;
     694                                                if (PRsq < PNminsq) {
     695                                                        PNminsq = PRsq;
     696                                                        bestEN = R.eastNorth;
     697                                                        bestTime = R.time;
     698                                                }
     699                                        } else {
     700                                                sx = S.eastNorth.east();
     701                                                sy = S.eastNorth.north();
     702                                                double A = sy - ry;
     703                                                double B = rx - sx;
     704                                                double C = - A * rx - B * ry;
     705                                                double RSsq = A * A + B * B;
     706                                                if (RSsq == 0.0) continue;
     707                                                double PNsq = A * px + B * py + C;
     708                                                PNsq = PNsq * PNsq / RSsq;
     709                                                if (PNsq < PNminsq) {
     710                                                        x = px - rx;
     711                                                        y = py - ry;
     712                                                        double PRsq = x * x + y * y;
     713                                                        x = px - sx;
     714                                                        y = py - sy;
     715                                                        double PSsq = x * x + y * y;
     716                                                        if (PRsq - PNsq <= RSsq && PSsq - PNsq <= RSsq) {
     717                                                                double RNoverRS = Math.sqrt((PRsq - PNsq)/RSsq);
     718                                                                double nx = rx - RNoverRS * B;
     719                                                                double ny = ry + RNoverRS * A;
     720                                                                bestEN = new EastNorth(nx, ny);
     721                                                                bestTime = R.time + RNoverRS * (S.time - R.time);
     722                                                                PNminsq = PNsq;
     723                                                        }
     724                                                }
     725                                                R = S;
     726                                                rx = sx;
     727                                                ry = sy;
     728                                        }
     729                                }
     730                                if (R != null) {
     731                                        /* if there is only one point in the seg, it will do this twice, but no matter */
     732                                        rx = R.eastNorth.east();
     733                                        ry = R.eastNorth.north();
     734                                        x = px - rx;
     735                                        y = py - ry;
     736                                        double PRsq = x * x + y * y;
     737                                        if (PRsq < PNminsq) {
     738                                                PNminsq = PRsq;
     739                                                bestEN = R.eastNorth;
     740                                                bestTime = R.time;
     741                                        }
     742                                       
     743                                }
     744                        }
     745                }
     746                if (bestEN == null) return null;
     747                WayPoint best = new WayPoint(Main.proj.eastNorth2latlon(bestEN));
     748                best.time = bestTime;
     749                return best;
    573750        }
    574751}
  • trunk/src/org/openstreetmap/josm/gui/preferences/AudioPreference.java

    r573 r578  
    3030        private JCheckBox markerButtonLabels = new JCheckBox(tr("Label audio (and image and web) markers."));
    3131        private JCheckBox markerAudioTraceVisible = new JCheckBox(tr("Display live audio trace."));
    32         private JCheckBox markersNamedTrackpoints = new JCheckBox(tr("Create audio markers from named trackpoints."));
    3332        private JCheckBox makeAutoMarkers = new JCheckBox(tr("Create non-audio markers when reading GPX."));
    34         private JCheckBox audioMarkersFromExplicitWaypoints = new JCheckBox(tr("Import audio uses explicit waypoints."));
     33
     34        // various methods of making markers on import audio
     35        private JCheckBox audioMarkersFromExplicitWaypoints = new JCheckBox(tr("Explicit waypoints with valid timestamps."));
     36        private JCheckBox audioMarkersFromUntimedWaypoints = new JCheckBox(tr("Explicit waypoints with time estimated from track position."));
     37        private JCheckBox audioMarkersFromNamedTrackpoints = new JCheckBox(tr("Named trackpoints."));
     38        private JCheckBox audioMarkersFromStart = new JCheckBox(tr("Start of track (will always do this if no other markers available)."));
    3539
    3640        private JTextField audioLeadIn = new JTextField(8);
     
    5761                                if (!markerAudioTraceVisible.isSelected())
    5862                                        markerAudioTraceVisible.setSelected(false);
    59                                 markerAudioTraceVisible.setEnabled(markerAudioTraceVisible.isSelected());
    6063                        }
    6164                });
     
    6972                                if (!markerButtonLabels.isSelected())
    7073                                        markerButtonLabels.setSelected(false);
    71                                 markerButtonLabels.setEnabled(markerButtonLabels.isSelected());
    7274                        }
    7375                });
     
    7678                gui.audio.add(markerButtonLabels, GBC.eol().insets(0,0,0,0));
    7779               
    78                 // markersNamedTrackpoints
    79                 markersNamedTrackpoints.addActionListener(new ActionListener(){
    80                         public void actionPerformed(ActionEvent e) {
    81                                 if (!markersNamedTrackpoints.isSelected())
    82                                         markersNamedTrackpoints.setSelected(false);
    83                                 markersNamedTrackpoints.setEnabled(markersNamedTrackpoints.isSelected());
    84                         }
    85                 });
    86                 markersNamedTrackpoints.setSelected(Main.pref.getBoolean("marker.namedtrackpoints"));
    87                 markersNamedTrackpoints.setToolTipText(tr("Automatically create audio markers from trackpoints (rather than explicit waypoints) with names or descriptions."));
    88                 gui.audio.add(markersNamedTrackpoints, GBC.eol().insets(0,0,0,0));
    89                
    9080                // makeAutoMarkers
    9181                makeAutoMarkers.addActionListener(new ActionListener(){
     
    9383                                if (!makeAutoMarkers.isSelected())
    9484                                        makeAutoMarkers.setSelected(false);
    95                                 makeAutoMarkers.setEnabled(makeAutoMarkers.isSelected());
    9685                        }
    9786                });
     
    10089                gui.audio.add(makeAutoMarkers, GBC.eol().insets(0,0,0,0));
    10190               
     91                gui.audio.add(new JLabel(tr("When importing audio, make markers from...")), GBC.eol());
     92
    10293                // audioMarkersFromExplicitWaypoints
    10394                audioMarkersFromExplicitWaypoints.addActionListener(new ActionListener(){
     
    10596                                if (!audioMarkersFromExplicitWaypoints.isSelected())
    10697                                        audioMarkersFromExplicitWaypoints.setSelected(false);
    107                                 audioMarkersFromExplicitWaypoints.setEnabled(audioMarkersFromExplicitWaypoints.isSelected());
    10898                        }
    10999                });
    110100                audioMarkersFromExplicitWaypoints.setSelected(Main.pref.getBoolean("marker.audiofromexplicitwaypoints", true));
    111101                audioMarkersFromExplicitWaypoints.setToolTipText(tr("When importing audio, apply it to any waypoints in the GPX layer."));
    112                 gui.audio.add(audioMarkersFromExplicitWaypoints, GBC.eol().insets(0,0,0,0));
     102                gui.audio.add(audioMarkersFromExplicitWaypoints, GBC.eol().insets(10,0,0,0));
     103               
     104                // audioMarkersFromUntimedWaypoints
     105                audioMarkersFromUntimedWaypoints.addActionListener(new ActionListener(){
     106                        public void actionPerformed(ActionEvent e) {
     107                                if (!audioMarkersFromUntimedWaypoints.isSelected())
     108                                        audioMarkersFromUntimedWaypoints.setSelected(false);
     109                        }
     110                });
     111                audioMarkersFromUntimedWaypoints.setSelected(Main.pref.getBoolean("marker.audiofromuntimedwaypoints", true));
     112                audioMarkersFromUntimedWaypoints.setToolTipText(tr("When importing audio, apply it to any waypoints in the GPX layer."));
     113                gui.audio.add(audioMarkersFromUntimedWaypoints, GBC.eol().insets(10,0,0,0));
     114               
     115                // audioMarkersFromNamedTrackpoints
     116                audioMarkersFromNamedTrackpoints.addActionListener(new ActionListener(){
     117                        public void actionPerformed(ActionEvent e) {
     118                                if (!audioMarkersFromNamedTrackpoints.isSelected())
     119                                        audioMarkersFromNamedTrackpoints.setSelected(false);
     120                        }
     121                });
     122                audioMarkersFromNamedTrackpoints.setSelected(Main.pref.getBoolean("marker.audiofromnamedtrackpoints", Main.pref.getBoolean("marker.namedtrackpoints")));
     123                audioMarkersFromNamedTrackpoints.setToolTipText(tr("Automatically create audio markers from trackpoints (rather than explicit waypoints) with names or descriptions."));
     124                gui.audio.add(audioMarkersFromNamedTrackpoints, GBC.eol().insets(10,0,0,0));
     125               
     126                // audioMarkersFromStart
     127                audioMarkersFromStart.addActionListener(new ActionListener(){
     128                        public void actionPerformed(ActionEvent e) {
     129                                if (!audioMarkersFromStart.isSelected())
     130                                        audioMarkersFromStart.setSelected(false);
     131                        }
     132                });
     133                audioMarkersFromStart.setSelected(Main.pref.getBoolean("marker.audiofromstart"));
     134                audioMarkersFromStart.setToolTipText(tr("Automatically create audio markers from trackpoints (rather than explicit waypoints) with names or descriptions."));
     135                gui.audio.add(audioMarkersFromStart, GBC.eol().insets(10,0,0,0));
    113136               
    114137                audioForwardBackAmount.setText(Main.pref.get("audio.forwardbackamount", "10"));
     
    139162                Main.pref.put("marker.traceaudio", markerAudioTraceVisible.isSelected());
    140163                Main.pref.put("marker.buttonlabels", markerButtonLabels.isSelected());
    141                 Main.pref.put("marker.namedtrackpoints", markersNamedTrackpoints.isSelected());
    142164                Main.pref.put("marker.makeautomarkers", makeAutoMarkers.isSelected());
    143165                Main.pref.put("marker.audiofromexplicitwaypoints", audioMarkersFromExplicitWaypoints.isSelected());
     166                Main.pref.put("marker.audiofromuntimedwaypoints", audioMarkersFromUntimedWaypoints.isSelected());
     167                Main.pref.put("marker.audiofromnamedtrackpoints", audioMarkersFromNamedTrackpoints.isSelected());
     168                Main.pref.put("marker.audiofromstart", audioMarkersFromStart.isSelected());
    144169                Main.pref.put("audio.forwardbackamount", audioForwardBackAmount.getText());             
    145170                Main.pref.put("audio.fastfwdmultiplier", audioFastForwardMultiplier.getText());         
Note: See TracChangeset for help on using the changeset viewer.