source: josm/trunk/src/org/openstreetmap/josm/io/BoundingBoxDownloader.java@ 14761

Last change on this file since 14761 was 14761, checked in by GerdP, 5 years ago

fix #13195
1) BoundingBoxDownloader: Reduce number of gpx api calls: If a call returned less than 5000 points there is no need to request another page. This improves all routines which download raw GPX data from the server.
2) GpxImporter: Create a new GpxLayer even if the area contains no GPX data. This allows to keep track of the already downloaded boundaries. Without this change the continuosDownload will try agaian and again to download data from this area because the GpxLayer will not contain the information that the corresponding bbox was already downloaded.

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