Ignore:
Timestamp:
2008-11-19T02:25:08+01:00 (15 years ago)
Author:
framm
Message:
  • added a new option to the GPX Layer menu ("download along track") which downloads OSM data along the GPX track. This is still highly experimental and not ready for prime time.
File:
1 edited

Legend:

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

    r999 r1086  
    1212import java.awt.GridBagLayout;
    1313import java.awt.Point;
     14import java.awt.Rectangle;
    1415import java.awt.event.ActionEvent;
    1516import java.awt.event.ActionListener;
     17import java.awt.geom.Area;
     18import java.awt.geom.Rectangle2D;
    1619import java.io.BufferedReader;
    1720import java.io.File;
     
    2831import java.util.LinkedList;
    2932import java.util.Date;
     33import java.util.List;
    3034import java.text.DateFormat;
    3135import java.text.DecimalFormat;
     
    3943import javax.swing.JFileChooser;
    4044import javax.swing.JLabel;
     45import javax.swing.JList;
    4146import javax.swing.JMenuItem;
    4247import javax.swing.JOptionPane;
     
    5156import org.openstreetmap.josm.actions.SaveAction;
    5257import org.openstreetmap.josm.actions.SaveAsAction;
     58import org.openstreetmap.josm.actions.downloadtasks.DownloadOsmTask;
    5359import org.openstreetmap.josm.data.coor.EastNorth;
    5460import org.openstreetmap.josm.data.gpx.GpxData;
     
    6369import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
    6470import org.openstreetmap.josm.gui.dialogs.LayerListPopup;
     71import org.openstreetmap.josm.gui.download.DownloadDialog.DownloadTask;
    6572import org.openstreetmap.josm.gui.layer.markerlayer.AudioMarker;
    6673import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer;
     
    266273                        markersFromNamedTrackpoints,
    267274                        new JMenuItem(new ConvertToDataLayerAction()),
     275            new JMenuItem(new DownloadAlongTrackAction()),
    268276                        new JSeparator(),
    269277                        new JMenuItem(new RenameLayerAction(associatedFile, this)),
     
    731739        }
    732740
     741    /**
     742     * Action that issues a series of download requests to the API, following the GPX track.
     743     *
     744     * @author fred
     745     */
     746    public class DownloadAlongTrackAction extends AbstractAction {
     747        public DownloadAlongTrackAction() {
     748            super(tr("Download from OSM along this track"), ImageProvider.get("downloadalongtrack"));
     749        }
     750        public void actionPerformed(ActionEvent e) {
     751            JPanel msg = new JPanel(new GridBagLayout());
     752            JList buffer = new JList(new String[] { "50 metres", "500 metres", "5000 metres" });
     753            JList maxRect = new JList(new String[] { "1 sq km", "5 sq km", "10 sq km", "20 sq km" });
     754           
     755            msg.add(new JLabel(tr("Download everything within:")), GBC.eol());
     756            msg.add(buffer, GBC.eol());
     757            msg.add(new JLabel(tr("Maximum area per request:")), GBC.eol());
     758            msg.add(maxRect, GBC.eol());
     759           
     760            if (JOptionPane.showConfirmDialog(Main.parent, msg,
     761                tr("Download from OSM along this track"),
     762                JOptionPane.OK_CANCEL_OPTION) == JOptionPane.CANCEL_OPTION) {
     763                return;
     764           
     765            }
     766
     767            /*
     768             * Find the average latitude for the data we're contemplating, so we can
     769             * know how many metres per degree of longitude we have.
     770             */
     771            double latsum = 0;
     772            int latcnt = 0;
     773           
     774            for (GpxTrack trk : data.tracks) {
     775                for (Collection<WayPoint> segment : trk.trackSegs) {
     776                    for (WayPoint p : segment) {
     777                        latsum += p.latlon.lat();
     778                        latcnt ++;
     779                    }
     780                }
     781            }
     782           
     783            double avglat = latsum / latcnt;
     784            double scale = Math.cos(Math.toRadians(avglat));
     785
     786            /*
     787             * Compute buffer zone extents and maximum bounding box size. Note how the
     788             * maximum we ever offer is a bbox area of 0.002, while the API theoretically
     789             * supports 0.25, but as soon as you touch any built-up area, that kind of
     790             * bounding box will download forever and then stop because it has more than
     791             * 50k nodes.
     792             */
     793            double buffer_y;
     794            double buffer_x;
     795            switch(buffer.getSelectedIndex()) {
     796            case 0: buffer_y = .0005; break;
     797            case 1: buffer_y = .005; break;
     798            default: buffer_y = .05;
     799            }
     800            buffer_x = buffer_y / scale;
     801           
     802            double max_area;
     803            switch(maxRect.getSelectedIndex()) {
     804            case 0: max_area = 0.0001 / scale; break;
     805            case 1: max_area = 0.0005 / scale; break;
     806            case 2: max_area = 0.001 / scale; break;
     807            default: max_area = 0.002 / scale;
     808            }
     809
     810            Area a = new Area();
     811            Rectangle2D r = new Rectangle2D.Double();
     812           
     813            /*
     814             * Collect the combined area of all gpx points plus buffer zones around them.
     815             * This is rather inefficient (may take 20 seconds and more for large tracks);
     816             * maybe it could be improved by disregarding points that lie really close to
     817             * the previous point.
     818             */
     819            for (GpxTrack trk : data.tracks) {
     820                for (Collection<WayPoint> segment : trk.trackSegs) {
     821                    for (WayPoint p : segment) {
     822                        // we add a buffer around the point.
     823                        r.setRect(p.latlon.lon()-buffer_x, p.latlon.lat()-buffer_y, 2*buffer_x, 2*buffer_y);
     824                        a.add(new Area(r));
     825                    }
     826                }
     827            }
     828           
     829            /*
     830             * Area "a" now contains the hull that we would like to download data for.
     831             * however we can only download rectangles, so the following is an attemt at
     832             * finding a number of rectangles to download.
     833             *
     834             * The idea is simply: Start out with the full bounding box. If it is too large,
     835             * then split it in half and repeat recursively for each half until you arrive
     836             * at something small enough to download. The algorithm is improved
     837             * by always using the intersection between the rectangle and the actual desired
     838             * area. For example, if you have a track that goes like this:
     839             * +----+
     840             * |   /|
     841             * |  / |
     842             * | /  |
     843             * |/   |
     844             * +----+
     845             * then we would first look at downloading the whole rectangle (assume it's too big),
     846             * after that we split it in half (upper and lower half), but we do *not* request the
     847             * full upper and lower rectangle, only the part of the upper/lower rectangle that
     848             * actually has something in it.
     849             */
     850           
     851            List<Rectangle2D> toDownload = new ArrayList<Rectangle2D>();
     852           
     853            addToDownload(a, a.getBounds(), toDownload, max_area);
     854           
     855            msg = new JPanel(new GridBagLayout());
     856                       
     857            msg.add(new JLabel(tr("<html>This action will require {0} individual<br>download requests. Do you wish<br>to continue?</html>",
     858                toDownload.size())), GBC.eol());
     859           
     860            if (JOptionPane.showConfirmDialog(Main.parent, msg,
     861                tr("Download from OSM along this track"),
     862                JOptionPane.OK_CANCEL_OPTION) == JOptionPane.CANCEL_OPTION) {
     863                return;
     864            }
     865           
     866            // FIXME: DownloadTask's "please wait" dialog should display the number of
     867            // downloads left, and "cancel" needs to be honoured. An error along the way
     868            // should abort the whole process.
     869            DownloadTask osmTask = new DownloadOsmTask();
     870            for (Rectangle2D td : toDownload) {
     871               osmTask.download(null, td.getMinY(), td.getMinX(), td.getMaxY(), td.getMaxX());
     872            }
     873        }
     874    }
     875   
     876    private static void addToDownload(Area a, Rectangle2D r, Collection<Rectangle2D> results, double max_area) {
     877        Area tmp = new Area(r);
     878        // intersect with sought-after area
     879        tmp.intersect(a);
     880        if (tmp.isEmpty()) return;
     881        Rectangle2D bounds = tmp.getBounds2D();
     882        if (bounds.getWidth() * bounds.getHeight() > max_area) {
     883            // the rectangle gets too large; split it and make recursive call.
     884            Rectangle2D r1;
     885            Rectangle2D r2;
     886            if (bounds.getWidth() > bounds.getHeight()) {
     887                // rectangles that are wider than high are split into a left and right half,
     888                r1 = new Rectangle2D.Double(bounds.getX(), bounds.getY(), bounds.getWidth()/2, bounds.getHeight());
     889                r2 = new Rectangle2D.Double(bounds.getX()+bounds.getWidth()/2, bounds.getY(), bounds.getWidth()/2, bounds.getHeight());
     890            } else {
     891                // others into a top and bottom half.
     892                r1 = new Rectangle2D.Double(bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight()/2);
     893                r2 = new Rectangle2D.Double(bounds.getX(), bounds.getY()+bounds.getHeight()/2, bounds.getWidth(), bounds.getHeight()/2);
     894            }
     895            addToDownload(a, r1, results, max_area);
     896            addToDownload(a, r2, results, max_area);
     897        } else {
     898            results.add(bounds);
     899        }
     900    }
     901   
    733902        /**
    734903         * Makes a new marker layer derived from this GpxLayer containing at least one
Note: See TracChangeset for help on using the changeset viewer.