[6380] | 1 | // License: GPL. For details, see LICENSE file.
|
---|
[444] | 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;
|
---|
[9509] | 8 | import java.net.SocketException;
|
---|
[7531] | 9 | import java.util.List;
|
---|
[444] | 10 |
|
---|
[2327] | 11 | import org.openstreetmap.josm.data.Bounds;
|
---|
[7575] | 12 | import org.openstreetmap.josm.data.DataSource;
|
---|
[1811] | 13 | import org.openstreetmap.josm.data.gpx.GpxData;
|
---|
[7531] | 14 | import org.openstreetmap.josm.data.notes.Note;
|
---|
[444] | 15 | import org.openstreetmap.josm.data.osm.DataSet;
|
---|
[1811] | 16 | import org.openstreetmap.josm.gui.progress.ProgressMonitor;
|
---|
[6474] | 17 | import org.openstreetmap.josm.tools.CheckParameterUtil;
|
---|
[11746] | 18 | import org.openstreetmap.josm.tools.JosmRuntimeException;
|
---|
[12620] | 19 | import org.openstreetmap.josm.tools.Logging;
|
---|
[444] | 20 | import org.xml.sax.SAXException;
|
---|
| 21 |
|
---|
[6244] | 22 | /**
|
---|
| 23 | * Read content from OSM server for a given bounding box
|
---|
| 24 | * @since 627
|
---|
| 25 | */
|
---|
[444] | 26 | public class BoundingBoxDownloader extends OsmServerReader {
|
---|
| 27 |
|
---|
[1169] | 28 | /**
|
---|
| 29 | * The boundings of the desired map data.
|
---|
| 30 | */
|
---|
[5097] | 31 | protected final double lat1;
|
---|
| 32 | protected final double lon1;
|
---|
| 33 | protected final double lat2;
|
---|
| 34 | protected final double lon2;
|
---|
| 35 | protected final boolean crosses180th;
|
---|
[444] | 36 |
|
---|
[6244] | 37 | /**
|
---|
| 38 | * Constructs a new {@code BoundingBoxDownloader}.
|
---|
| 39 | * @param downloadArea The area to download
|
---|
| 40 | */
|
---|
[2327] | 41 | public BoundingBoxDownloader(Bounds downloadArea) {
|
---|
[6474] | 42 | CheckParameterUtil.ensureParameterNotNull(downloadArea, "downloadArea");
|
---|
[6203] | 43 | this.lat1 = downloadArea.getMinLat();
|
---|
| 44 | this.lon1 = downloadArea.getMinLon();
|
---|
| 45 | this.lat2 = downloadArea.getMaxLat();
|
---|
| 46 | this.lon2 = downloadArea.getMaxLon();
|
---|
[4580] | 47 | this.crosses180th = downloadArea.crosses180thMeridian();
|
---|
[1169] | 48 | }
|
---|
[444] | 49 |
|
---|
[7575] | 50 | private GpxData downloadRawGps(Bounds b, ProgressMonitor progressMonitor) throws IOException, OsmTransferException, SAXException {
|
---|
[4580] | 51 | boolean done = false;
|
---|
| 52 | GpxData result = null;
|
---|
[8846] | 53 | String url = "trackpoints?bbox="+b.getMinLon()+','+b.getMinLat()+','+b.getMaxLon()+','+b.getMaxLat()+"&page=";
|
---|
[9509] | 54 | for (int i = 0; !done && !isCanceled(); ++i) {
|
---|
[8345] | 55 | progressMonitor.subTask(tr("Downloading points {0} to {1}...", i * 5000, (i + 1) * 5000));
|
---|
[7033] | 56 | try (InputStream in = getInputStream(url+i, progressMonitor.createSubTaskMonitor(1, true))) {
|
---|
| 57 | if (in == null) {
|
---|
| 58 | break;
|
---|
| 59 | }
|
---|
| 60 | progressMonitor.setTicks(0);
|
---|
| 61 | GpxReader reader = new GpxReader(in);
|
---|
| 62 | gpxParsedProperly = reader.parse(false);
|
---|
| 63 | GpxData currentGpx = reader.getGpxData();
|
---|
| 64 | if (result == null) {
|
---|
| 65 | result = currentGpx;
|
---|
| 66 | } else if (currentGpx.hasTrackPoints()) {
|
---|
| 67 | result.mergeFrom(currentGpx);
|
---|
[8510] | 68 | } else {
|
---|
[7033] | 69 | done = true;
|
---|
| 70 | }
|
---|
[10253] | 71 | } catch (OsmApiException ex) {
|
---|
| 72 | throw ex; // this avoids infinite loop in case of API error such as bad request (ex: bbox too large, see #12853)
|
---|
[9509] | 73 | } catch (OsmTransferException | SocketException ex) {
|
---|
| 74 | if (isCanceled()) {
|
---|
| 75 | final OsmTransferCanceledException canceledException = new OsmTransferCanceledException("Operation canceled");
|
---|
| 76 | canceledException.initCause(ex);
|
---|
[12620] | 77 | Logging.warn(canceledException);
|
---|
[9509] | 78 | }
|
---|
[4580] | 79 | }
|
---|
| 80 | activeConnection = null;
|
---|
| 81 | }
|
---|
[7033] | 82 | if (result != null) {
|
---|
| 83 | result.fromServer = true;
|
---|
[7575] | 84 | result.dataSources.add(new DataSource(b, "OpenStreetMap server"));
|
---|
[7033] | 85 | }
|
---|
[4580] | 86 | return result;
|
---|
| 87 | }
|
---|
[6070] | 88 |
|
---|
[4521] | 89 | @Override
|
---|
| 90 | public GpxData parseRawGps(ProgressMonitor progressMonitor) throws OsmTransferException {
|
---|
[1811] | 91 | progressMonitor.beginTask("", 1);
|
---|
[1169] | 92 | try {
|
---|
[8787] | 93 | progressMonitor.indeterminateSubTask(getTaskName());
|
---|
[4580] | 94 | if (crosses180th) {
|
---|
| 95 | // API 0.6 does not support requests crossing the 180th meridian, so make two requests
|
---|
[7575] | 96 | GpxData result = downloadRawGps(new Bounds(lat1, lon1, lat2, 180.0), progressMonitor);
|
---|
[11397] | 97 | if (result != null)
|
---|
| 98 | result.mergeFrom(downloadRawGps(new Bounds(lat1, -180.0, lat2, lon2), progressMonitor));
|
---|
[4580] | 99 | return result;
|
---|
| 100 | } else {
|
---|
| 101 | // Simple request
|
---|
[7575] | 102 | return downloadRawGps(new Bounds(lat1, lon1, lat2, lon2), progressMonitor);
|
---|
[1169] | 103 | }
|
---|
| 104 | } catch (IllegalArgumentException e) {
|
---|
| 105 | // caused by HttpUrlConnection in case of illegal stuff in the response
|
---|
| 106 | if (cancel)
|
---|
| 107 | return null;
|
---|
[4521] | 108 | throw new OsmTransferException("Illegal characters within the HTTP-header response.", e);
|
---|
[1169] | 109 | } catch (IOException e) {
|
---|
| 110 | if (cancel)
|
---|
| 111 | return null;
|
---|
[4521] | 112 | throw new OsmTransferException(e);
|
---|
[1169] | 113 | } catch (SAXException e) {
|
---|
[4521] | 114 | throw new OsmTransferException(e);
|
---|
[1835] | 115 | } catch (OsmTransferException e) {
|
---|
| 116 | throw e;
|
---|
[11746] | 117 | } catch (JosmRuntimeException | IllegalStateException e) {
|
---|
[1169] | 118 | if (cancel)
|
---|
| 119 | return null;
|
---|
[2676] | 120 | throw e;
|
---|
[1811] | 121 | } finally {
|
---|
| 122 | progressMonitor.finishTask();
|
---|
[1169] | 123 | }
|
---|
| 124 | }
|
---|
[444] | 125 |
|
---|
[8787] | 126 | /**
|
---|
| 127 | * Returns the name of the download task to be displayed in the {@link ProgressMonitor}.
|
---|
[8929] | 128 | * @return task name
|
---|
[8787] | 129 | */
|
---|
| 130 | protected String getTaskName() {
|
---|
| 131 | return tr("Contacting OSM Server...");
|
---|
| 132 | }
|
---|
| 133 |
|
---|
| 134 | /**
|
---|
| 135 | * Builds the request part for the bounding box.
|
---|
[8929] | 136 | * @param lon1 left
|
---|
| 137 | * @param lat1 bottom
|
---|
| 138 | * @param lon2 right
|
---|
| 139 | * @param lat2 top
|
---|
| 140 | * @return "map?bbox=left,bottom,right,top"
|
---|
[8787] | 141 | */
|
---|
[5097] | 142 | protected String getRequestForBbox(double lon1, double lat1, double lon2, double lat2) {
|
---|
[8846] | 143 | return "map?bbox=" + lon1 + ',' + lat1 + ',' + lon2 + ',' + lat2;
|
---|
[5097] | 144 | }
|
---|
| 145 |
|
---|
[8788] | 146 | /**
|
---|
| 147 | * Parse the given input source and return the dataset.
|
---|
[8929] | 148 | * @param source input stream
|
---|
| 149 | * @param progressMonitor progress monitor
|
---|
| 150 | * @return dataset
|
---|
[8926] | 151 | * @throws IllegalDataException if an error was found while parsing the OSM data
|
---|
[8788] | 152 | *
|
---|
| 153 | * @see OsmReader#parseDataSet(InputStream, ProgressMonitor)
|
---|
| 154 | */
|
---|
| 155 | protected DataSet parseDataSet(InputStream source, ProgressMonitor progressMonitor) throws IllegalDataException {
|
---|
| 156 | return OsmReader.parseDataSet(source, progressMonitor);
|
---|
| 157 | }
|
---|
| 158 |
|
---|
[1670] | 159 | @Override
|
---|
[1811] | 160 | public DataSet parseOsm(ProgressMonitor progressMonitor) throws OsmTransferException {
|
---|
[8787] | 161 | progressMonitor.beginTask(getTaskName(), 10);
|
---|
[1169] | 162 | try {
|
---|
[4580] | 163 | DataSet ds = null;
|
---|
[1811] | 164 | progressMonitor.indeterminateSubTask(null);
|
---|
[4580] | 165 | if (crosses180th) {
|
---|
| 166 | // API 0.6 does not support requests crossing the 180th meridian, so make two requests
|
---|
[7033] | 167 | DataSet ds2 = null;
|
---|
[4580] | 168 |
|
---|
[8540] | 169 | try (InputStream in = getInputStream(getRequestForBbox(lon1, lat1, 180.0, lat2),
|
---|
| 170 | progressMonitor.createSubTaskMonitor(9, false))) {
|
---|
[7033] | 171 | if (in == null)
|
---|
| 172 | return null;
|
---|
[8788] | 173 | ds = parseDataSet(in, progressMonitor.createSubTaskMonitor(1, false));
|
---|
[7033] | 174 | }
|
---|
| 175 |
|
---|
[8540] | 176 | try (InputStream in = getInputStream(getRequestForBbox(-180.0, lat1, lon2, lat2),
|
---|
| 177 | progressMonitor.createSubTaskMonitor(9, false))) {
|
---|
[7033] | 178 | if (in == null)
|
---|
| 179 | return null;
|
---|
[8788] | 180 | ds2 = parseDataSet(in, progressMonitor.createSubTaskMonitor(1, false));
|
---|
[7033] | 181 | }
|
---|
[4580] | 182 | if (ds2 == null)
|
---|
| 183 | return null;
|
---|
| 184 | ds.mergeFrom(ds2);
|
---|
[6070] | 185 |
|
---|
[4580] | 186 | } else {
|
---|
| 187 | // Simple request
|
---|
[8540] | 188 | try (InputStream in = getInputStream(getRequestForBbox(lon1, lat1, lon2, lat2),
|
---|
| 189 | progressMonitor.createSubTaskMonitor(9, false))) {
|
---|
[7033] | 190 | if (in == null)
|
---|
| 191 | return null;
|
---|
[8788] | 192 | ds = parseDataSet(in, progressMonitor.createSubTaskMonitor(1, false));
|
---|
[7033] | 193 | }
|
---|
[4580] | 194 | }
|
---|
| 195 | return ds;
|
---|
[8510] | 196 | } catch (OsmTransferException e) {
|
---|
[1169] | 197 | throw e;
|
---|
[10212] | 198 | } catch (IllegalDataException | IOException e) {
|
---|
[1670] | 199 | throw new OsmTransferException(e);
|
---|
[1811] | 200 | } finally {
|
---|
| 201 | progressMonitor.finishTask();
|
---|
[1879] | 202 | activeConnection = null;
|
---|
[1169] | 203 | }
|
---|
| 204 | }
|
---|
[7531] | 205 |
|
---|
| 206 | @Override
|
---|
[10632] | 207 | public List<Note> parseNotes(int noteLimit, int daysClosed, ProgressMonitor progressMonitor) throws OsmTransferException {
|
---|
[8995] | 208 | progressMonitor.beginTask(tr("Downloading notes"));
|
---|
[8218] | 209 | CheckParameterUtil.ensureThat(noteLimit > 0, "Requested note limit is less than 1.");
|
---|
[8230] | 210 | // see result_limit in https://github.com/openstreetmap/openstreetmap-website/blob/master/app/controllers/notes_controller.rb
|
---|
[11100] | 211 | CheckParameterUtil.ensureThat(noteLimit <= 10_000, "Requested note limit is over API hard limit of 10000.");
|
---|
[8319] | 212 | CheckParameterUtil.ensureThat(daysClosed >= -1, "Requested note limit is less than -1.");
|
---|
[8846] | 213 | String url = "notes?limit=" + noteLimit + "&closed=" + daysClosed + "&bbox=" + lon1 + ',' + lat1 + ',' + lon2 + ',' + lat2;
|
---|
[7531] | 214 | try {
|
---|
| 215 | InputStream is = getInputStream(url, progressMonitor.createSubTaskMonitor(1, false));
|
---|
| 216 | NoteReader reader = new NoteReader(is);
|
---|
[8216] | 217 | final List<Note> notes = reader.parse();
|
---|
| 218 | if (notes.size() == noteLimit) {
|
---|
[8217] | 219 | throw new MoreNotesException(notes, noteLimit);
|
---|
[8216] | 220 | }
|
---|
| 221 | return notes;
|
---|
[9509] | 222 | } catch (IOException | SAXException e) {
|
---|
[7531] | 223 | throw new OsmTransferException(e);
|
---|
| 224 | } finally {
|
---|
| 225 | progressMonitor.finishTask();
|
---|
| 226 | }
|
---|
| 227 | }
|
---|
| 228 |
|
---|
[8216] | 229 | /**
|
---|
| 230 | * Indicates that the number of fetched notes equals the specified limit. Thus there might be more notes to download.
|
---|
| 231 | */
|
---|
[8510] | 232 | public static class MoreNotesException extends RuntimeException {
|
---|
[8216] | 233 | /**
|
---|
[8217] | 234 | * The downloaded notes
|
---|
| 235 | */
|
---|
[8308] | 236 | public final transient List<Note> notes;
|
---|
[8217] | 237 | /**
|
---|
[8216] | 238 | * The download limit sent to the server.
|
---|
| 239 | */
|
---|
| 240 | public final int limit;
|
---|
| 241 |
|
---|
[10194] | 242 | /**
|
---|
| 243 | * Constructs a {@code MoreNotesException}.
|
---|
| 244 | * @param notes downloaded notes
|
---|
| 245 | * @param limit download limit sent to the server
|
---|
| 246 | */
|
---|
[8217] | 247 | public MoreNotesException(List<Note> notes, int limit) {
|
---|
| 248 | this.notes = notes;
|
---|
[8216] | 249 | this.limit = limit;
|
---|
| 250 | }
|
---|
| 251 | }
|
---|
[444] | 252 | }
|
---|