001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.io;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.io.IOException;
007import java.io.InputStream;
008
009import org.openstreetmap.josm.data.Bounds;
010import org.openstreetmap.josm.data.gpx.GpxData;
011import org.openstreetmap.josm.data.osm.DataSet;
012import org.openstreetmap.josm.gui.progress.ProgressMonitor;
013import org.openstreetmap.josm.tools.CheckParameterUtil;
014import org.openstreetmap.josm.tools.Utils;
015import org.xml.sax.SAXException;
016
017/**
018 * Read content from OSM server for a given bounding box
019 * @since 627
020 */
021public class BoundingBoxDownloader extends OsmServerReader {
022
023    /**
024     * The boundings of the desired map data.
025     */
026    protected final double lat1;
027    protected final double lon1;
028    protected final double lat2;
029    protected final double lon2;
030    protected final boolean crosses180th;
031
032    /**
033     * Constructs a new {@code BoundingBoxDownloader}.
034     * @param downloadArea The area to download
035     */
036    public BoundingBoxDownloader(Bounds downloadArea) {
037        CheckParameterUtil.ensureParameterNotNull(downloadArea, "downloadArea");
038        this.lat1 = downloadArea.getMinLat();
039        this.lon1 = downloadArea.getMinLon();
040        this.lat2 = downloadArea.getMaxLat();
041        this.lon2 = downloadArea.getMaxLon();
042        this.crosses180th = downloadArea.crosses180thMeridian();
043    }
044
045    private GpxData downloadRawGps(String url, ProgressMonitor progressMonitor) throws IOException, OsmTransferException, SAXException {
046        boolean done = false;
047        GpxData result = null;
048        for (int i = 0;!done;++i) {
049            progressMonitor.subTask(tr("Downloading points {0} to {1}...", i * 5000, ((i + 1) * 5000)));
050            InputStream in = getInputStream(url+i, progressMonitor.createSubTaskMonitor(1, true));
051            if (in == null) {
052                break;
053            }
054            progressMonitor.setTicks(0);
055            GpxReader reader = new GpxReader(in);
056            gpxParsedProperly = reader.parse(false);
057            GpxData currentGpx = reader.getGpxData();
058            if (result == null) {
059                result = currentGpx;
060            } else if (currentGpx.hasTrackPoints()) {
061                result.mergeFrom(currentGpx);
062            } else{
063                done = true;
064            }
065            Utils.close(in);
066            activeConnection = null;
067        }
068        result.fromServer = true;
069        return result;
070    }
071
072    @Override
073    public GpxData parseRawGps(ProgressMonitor progressMonitor) throws OsmTransferException {
074        progressMonitor.beginTask("", 1);
075        try {
076            progressMonitor.indeterminateSubTask(tr("Contacting OSM Server..."));
077            if (crosses180th) {
078                // API 0.6 does not support requests crossing the 180th meridian, so make two requests
079                GpxData result = downloadRawGps("trackpoints?bbox="+lon1+","+lat1+",180.0,"+lat2+"&page=", progressMonitor);
080                result.mergeFrom(downloadRawGps("trackpoints?bbox=-180.0,"+lat1+","+lon2+","+lat2+"&page=", progressMonitor));
081                return result;
082            } else {
083                // Simple request
084                return downloadRawGps("trackpoints?bbox="+lon1+","+lat1+","+lon2+","+lat2+"&page=", progressMonitor);
085            }
086        } catch (IllegalArgumentException e) {
087            // caused by HttpUrlConnection in case of illegal stuff in the response
088            if (cancel)
089                return null;
090            throw new OsmTransferException("Illegal characters within the HTTP-header response.", e);
091        } catch (IOException e) {
092            if (cancel)
093                return null;
094            throw new OsmTransferException(e);
095        } catch (SAXException e) {
096            throw new OsmTransferException(e);
097        } catch (OsmTransferException e) {
098            throw e;
099        } catch (RuntimeException e) {
100            if (cancel)
101                return null;
102            throw e;
103        } finally {
104            progressMonitor.finishTask();
105        }
106    }
107
108    protected String getRequestForBbox(double lon1, double lat1, double lon2, double lat2) {
109        return "map?bbox=" + lon1 + "," + lat1 + "," + lon2 + "," + lat2;
110    }
111
112    @Override
113    public DataSet parseOsm(ProgressMonitor progressMonitor) throws OsmTransferException {
114        progressMonitor.beginTask(tr("Contacting OSM Server..."), 10);
115        InputStream in = null;
116        try {
117            DataSet ds = null;
118            progressMonitor.indeterminateSubTask(null);
119            if (crosses180th) {
120                // API 0.6 does not support requests crossing the 180th meridian, so make two requests
121                in = getInputStream(getRequestForBbox(lon1, lat1, 180.0, lat2), progressMonitor.createSubTaskMonitor(9, false));
122                if (in == null)
123                    return null;
124                ds = OsmReader.parseDataSet(in, progressMonitor.createSubTaskMonitor(1, false));
125
126                in = getInputStream(getRequestForBbox(-180.0, lat1, lon2, lat2), progressMonitor.createSubTaskMonitor(9, false));
127                if (in == null)
128                    return null;
129                DataSet ds2 = OsmReader.parseDataSet(in, progressMonitor.createSubTaskMonitor(1, false));
130                if (ds2 == null)
131                    return null;
132                ds.mergeFrom(ds2);
133
134            } else {
135                // Simple request
136                in = getInputStream(getRequestForBbox(lon1, lat1, lon2, lat2), progressMonitor.createSubTaskMonitor(9, false));
137                if (in == null)
138                    return null;
139                ds = OsmReader.parseDataSet(in, progressMonitor.createSubTaskMonitor(1, false));
140            }
141            return ds;
142        } catch(OsmTransferException e) {
143            throw e;
144        } catch (Exception e) {
145            throw new OsmTransferException(e);
146        } finally {
147            progressMonitor.finishTask();
148            Utils.close(in);
149            activeConnection = null;
150        }
151    }
152}