| 1 | // License: GPL. For details, see LICENSE file.
|
|---|
| 2 | package org.openstreetmap.josm.io;
|
|---|
| 3 |
|
|---|
| 4 | import static org.openstreetmap.josm.tools.I18n.tr;
|
|---|
| 5 |
|
|---|
| 6 | import java.io.IOException;
|
|---|
| 7 | import java.io.InputStream;
|
|---|
| 8 | import java.util.List;
|
|---|
| 9 |
|
|---|
| 10 | import org.openstreetmap.josm.data.Bounds;
|
|---|
| 11 | import org.openstreetmap.josm.data.DataSource;
|
|---|
| 12 | import org.openstreetmap.josm.data.gpx.GpxData;
|
|---|
| 13 | import org.openstreetmap.josm.data.notes.Note;
|
|---|
| 14 | import org.openstreetmap.josm.data.osm.DataSet;
|
|---|
| 15 | import org.openstreetmap.josm.gui.progress.ProgressMonitor;
|
|---|
| 16 | import org.openstreetmap.josm.tools.CheckParameterUtil;
|
|---|
| 17 | import org.xml.sax.SAXException;
|
|---|
| 18 |
|
|---|
| 19 | /**
|
|---|
| 20 | * Read content from OSM server for a given bounding box
|
|---|
| 21 | * @since 627
|
|---|
| 22 | */
|
|---|
| 23 | public class BoundingBoxDownloader extends OsmServerReader {
|
|---|
| 24 |
|
|---|
| 25 | /**
|
|---|
| 26 | * The boundings of the desired map data.
|
|---|
| 27 | */
|
|---|
| 28 | protected final double lat1;
|
|---|
| 29 | protected final double lon1;
|
|---|
| 30 | protected final double lat2;
|
|---|
| 31 | protected final double lon2;
|
|---|
| 32 | protected final boolean crosses180th;
|
|---|
| 33 |
|
|---|
| 34 | /**
|
|---|
| 35 | * Constructs a new {@code BoundingBoxDownloader}.
|
|---|
| 36 | * @param downloadArea The area to download
|
|---|
| 37 | */
|
|---|
| 38 | public BoundingBoxDownloader(Bounds downloadArea) {
|
|---|
| 39 | CheckParameterUtil.ensureParameterNotNull(downloadArea, "downloadArea");
|
|---|
| 40 | this.lat1 = downloadArea.getMinLat();
|
|---|
| 41 | this.lon1 = downloadArea.getMinLon();
|
|---|
| 42 | this.lat2 = downloadArea.getMaxLat();
|
|---|
| 43 | this.lon2 = downloadArea.getMaxLon();
|
|---|
| 44 | this.crosses180th = downloadArea.crosses180thMeridian();
|
|---|
| 45 | }
|
|---|
| 46 |
|
|---|
| 47 | private GpxData downloadRawGps(Bounds b, ProgressMonitor progressMonitor) throws IOException, OsmTransferException, SAXException {
|
|---|
| 48 | boolean done = false;
|
|---|
| 49 | GpxData result = null;
|
|---|
| 50 | String url = "trackpoints?bbox="+b.getMinLon()+","+b.getMinLat()+","+b.getMaxLon()+","+b.getMaxLat()+"&page=";
|
|---|
| 51 | for (int i = 0;!done;++i) {
|
|---|
| 52 | progressMonitor.subTask(tr("Downloading points {0} to {1}...", i * 5000, ((i + 1) * 5000)));
|
|---|
| 53 | try (InputStream in = getInputStream(url+i, progressMonitor.createSubTaskMonitor(1, true))) {
|
|---|
| 54 | if (in == null) {
|
|---|
| 55 | break;
|
|---|
| 56 | }
|
|---|
| 57 | progressMonitor.setTicks(0);
|
|---|
| 58 | GpxReader reader = new GpxReader(in);
|
|---|
| 59 | gpxParsedProperly = reader.parse(false);
|
|---|
| 60 | GpxData currentGpx = reader.getGpxData();
|
|---|
| 61 | if (result == null) {
|
|---|
| 62 | result = currentGpx;
|
|---|
| 63 | } else if (currentGpx.hasTrackPoints()) {
|
|---|
| 64 | result.mergeFrom(currentGpx);
|
|---|
| 65 | } else{
|
|---|
| 66 | done = true;
|
|---|
| 67 | }
|
|---|
| 68 | }
|
|---|
| 69 | activeConnection = null;
|
|---|
| 70 | }
|
|---|
| 71 | if (result != null) {
|
|---|
| 72 | result.fromServer = true;
|
|---|
| 73 | result.dataSources.add(new DataSource(b, "OpenStreetMap server"));
|
|---|
| 74 | }
|
|---|
| 75 | return result;
|
|---|
| 76 | }
|
|---|
| 77 |
|
|---|
| 78 | @Override
|
|---|
| 79 | public GpxData parseRawGps(ProgressMonitor progressMonitor) throws OsmTransferException {
|
|---|
| 80 | progressMonitor.beginTask("", 1);
|
|---|
| 81 | try {
|
|---|
| 82 | progressMonitor.indeterminateSubTask(tr("Contacting OSM Server..."));
|
|---|
| 83 | if (crosses180th) {
|
|---|
| 84 | // API 0.6 does not support requests crossing the 180th meridian, so make two requests
|
|---|
| 85 | GpxData result = downloadRawGps(new Bounds(lat1, lon1, lat2, 180.0), progressMonitor);
|
|---|
| 86 | result.mergeFrom(downloadRawGps(new Bounds(lat1, -180.0, lat2, lon2), progressMonitor));
|
|---|
| 87 | return result;
|
|---|
| 88 | } else {
|
|---|
| 89 | // Simple request
|
|---|
| 90 | return downloadRawGps(new Bounds(lat1, lon1, lat2, lon2), progressMonitor);
|
|---|
| 91 | }
|
|---|
| 92 | } catch (IllegalArgumentException e) {
|
|---|
| 93 | // caused by HttpUrlConnection in case of illegal stuff in the response
|
|---|
| 94 | if (cancel)
|
|---|
| 95 | return null;
|
|---|
| 96 | throw new OsmTransferException("Illegal characters within the HTTP-header response.", e);
|
|---|
| 97 | } catch (IOException e) {
|
|---|
| 98 | if (cancel)
|
|---|
| 99 | return null;
|
|---|
| 100 | throw new OsmTransferException(e);
|
|---|
| 101 | } catch (SAXException e) {
|
|---|
| 102 | throw new OsmTransferException(e);
|
|---|
| 103 | } catch (OsmTransferException e) {
|
|---|
| 104 | throw e;
|
|---|
| 105 | } catch (RuntimeException e) {
|
|---|
| 106 | if (cancel)
|
|---|
| 107 | return null;
|
|---|
| 108 | throw e;
|
|---|
| 109 | } finally {
|
|---|
| 110 | progressMonitor.finishTask();
|
|---|
| 111 | }
|
|---|
| 112 | }
|
|---|
| 113 |
|
|---|
| 114 | protected String getRequestForBbox(double lon1, double lat1, double lon2, double lat2) {
|
|---|
| 115 | return "map?bbox=" + lon1 + "," + lat1 + "," + lon2 + "," + lat2;
|
|---|
| 116 | }
|
|---|
| 117 |
|
|---|
| 118 | @Override
|
|---|
| 119 | public DataSet parseOsm(ProgressMonitor progressMonitor) throws OsmTransferException {
|
|---|
| 120 | progressMonitor.beginTask(tr("Contacting OSM Server..."), 10);
|
|---|
| 121 | try {
|
|---|
| 122 | DataSet ds = null;
|
|---|
| 123 | progressMonitor.indeterminateSubTask(null);
|
|---|
| 124 | if (crosses180th) {
|
|---|
| 125 | // API 0.6 does not support requests crossing the 180th meridian, so make two requests
|
|---|
| 126 | DataSet ds2 = null;
|
|---|
| 127 |
|
|---|
| 128 | try (InputStream in = getInputStream(getRequestForBbox(lon1, lat1, 180.0, lat2), progressMonitor.createSubTaskMonitor(9, false))) {
|
|---|
| 129 | if (in == null)
|
|---|
| 130 | return null;
|
|---|
| 131 | ds = OsmReader.parseDataSet(in, progressMonitor.createSubTaskMonitor(1, false));
|
|---|
| 132 | }
|
|---|
| 133 |
|
|---|
| 134 | try (InputStream in = getInputStream(getRequestForBbox(-180.0, lat1, lon2, lat2), progressMonitor.createSubTaskMonitor(9, false))) {
|
|---|
| 135 | if (in == null)
|
|---|
| 136 | return null;
|
|---|
| 137 | ds2 = OsmReader.parseDataSet(in, progressMonitor.createSubTaskMonitor(1, false));
|
|---|
| 138 | }
|
|---|
| 139 | if (ds2 == null)
|
|---|
| 140 | return null;
|
|---|
| 141 | ds.mergeFrom(ds2);
|
|---|
| 142 |
|
|---|
| 143 | } else {
|
|---|
| 144 | // Simple request
|
|---|
| 145 | try (InputStream in = getInputStream(getRequestForBbox(lon1, lat1, lon2, lat2), progressMonitor.createSubTaskMonitor(9, false))) {
|
|---|
| 146 | if (in == null)
|
|---|
| 147 | return null;
|
|---|
| 148 | ds = OsmReader.parseDataSet(in, progressMonitor.createSubTaskMonitor(1, false));
|
|---|
| 149 | }
|
|---|
| 150 | }
|
|---|
| 151 | return ds;
|
|---|
| 152 | } catch(OsmTransferException e) {
|
|---|
| 153 | throw e;
|
|---|
| 154 | } catch (Exception e) {
|
|---|
| 155 | throw new OsmTransferException(e);
|
|---|
| 156 | } finally {
|
|---|
| 157 | progressMonitor.finishTask();
|
|---|
| 158 | activeConnection = null;
|
|---|
| 159 | }
|
|---|
| 160 | }
|
|---|
| 161 |
|
|---|
| 162 | @Override
|
|---|
| 163 | public List<Note> parseNotes(int noteLimit, int daysClosed, ProgressMonitor progressMonitor) throws OsmTransferException, MoreNotesException {
|
|---|
| 164 | progressMonitor.beginTask("Downloading notes");
|
|---|
| 165 | CheckParameterUtil.ensureThat(noteLimit > 0, "Requested note limit is less than 1.");
|
|---|
| 166 | // see result_limit in https://github.com/openstreetmap/openstreetmap-website/blob/master/app/controllers/notes_controller.rb
|
|---|
| 167 | CheckParameterUtil.ensureThat(noteLimit <= 10000, "Requested note limit is over API hard limit of 10000.");
|
|---|
| 168 | CheckParameterUtil.ensureThat(daysClosed >= 0, "Requested note limit is less than 0.");
|
|---|
| 169 | String url = "notes?limit=" + noteLimit + "&closed=" + daysClosed + "&bbox=" + lon1 + "," + lat1 + "," + lon2 + "," + lat2;
|
|---|
| 170 | try {
|
|---|
| 171 | InputStream is = getInputStream(url, progressMonitor.createSubTaskMonitor(1, false));
|
|---|
| 172 | NoteReader reader = new NoteReader(is);
|
|---|
| 173 | final List<Note> notes = reader.parse();
|
|---|
| 174 | if (notes.size() == noteLimit) {
|
|---|
| 175 | throw new MoreNotesException(notes, noteLimit);
|
|---|
| 176 | }
|
|---|
| 177 | return notes;
|
|---|
| 178 | } catch (IOException e) {
|
|---|
| 179 | throw new OsmTransferException(e);
|
|---|
| 180 | } catch (SAXException e) {
|
|---|
| 181 | throw new OsmTransferException(e);
|
|---|
| 182 | } finally {
|
|---|
| 183 | progressMonitor.finishTask();
|
|---|
| 184 | }
|
|---|
| 185 | }
|
|---|
| 186 |
|
|---|
| 187 | /**
|
|---|
| 188 | * Indicates that the number of fetched notes equals the specified limit. Thus there might be more notes to download.
|
|---|
| 189 | */
|
|---|
| 190 | public static class MoreNotesException extends RuntimeException{
|
|---|
| 191 | /**
|
|---|
| 192 | * The downloaded notes
|
|---|
| 193 | */
|
|---|
| 194 | public final List<Note> notes;
|
|---|
| 195 | /**
|
|---|
| 196 | * The download limit sent to the server.
|
|---|
| 197 | */
|
|---|
| 198 | public final int limit;
|
|---|
| 199 |
|
|---|
| 200 | public MoreNotesException(List<Note> notes, int limit) {
|
|---|
| 201 | this.notes = notes;
|
|---|
| 202 | this.limit = limit;
|
|---|
| 203 | }
|
|---|
| 204 | }
|
|---|
| 205 |
|
|---|
| 206 | }
|
|---|