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; 008import java.util.List; 009 010import org.openstreetmap.josm.Main; 011import org.openstreetmap.josm.data.Bounds; 012import org.openstreetmap.josm.data.DataSource; 013import org.openstreetmap.josm.data.gpx.GpxData; 014import org.openstreetmap.josm.data.notes.Note; 015import org.openstreetmap.josm.data.osm.DataSet; 016import org.openstreetmap.josm.gui.progress.ProgressMonitor; 017import org.openstreetmap.josm.tools.CheckParameterUtil; 018import org.xml.sax.SAXException; 019 020/** 021 * Read content from OSM server for a given bounding box 022 * @since 627 023 */ 024public class BoundingBoxDownloader extends OsmServerReader { 025 026 /** 027 * The boundings of the desired map data. 028 */ 029 protected final double lat1; 030 protected final double lon1; 031 protected final double lat2; 032 protected final double lon2; 033 protected final boolean crosses180th; 034 035 /** 036 * Constructs a new {@code BoundingBoxDownloader}. 037 * @param downloadArea The area to download 038 */ 039 public BoundingBoxDownloader(Bounds downloadArea) { 040 CheckParameterUtil.ensureParameterNotNull(downloadArea, "downloadArea"); 041 this.lat1 = downloadArea.getMinLat(); 042 this.lon1 = downloadArea.getMinLon(); 043 this.lat2 = downloadArea.getMaxLat(); 044 this.lon2 = downloadArea.getMaxLon(); 045 this.crosses180th = downloadArea.crosses180thMeridian(); 046 } 047 048 private GpxData downloadRawGps(Bounds b, ProgressMonitor progressMonitor) throws IOException, OsmTransferException, SAXException { 049 boolean done = false; 050 GpxData result = null; 051 String url = "trackpoints?bbox="+b.getMinLon()+","+b.getMinLat()+","+b.getMaxLon()+","+b.getMaxLat()+"&page="; 052 for (int i = 0;!done;++i) { 053 progressMonitor.subTask(tr("Downloading points {0} to {1}...", i * 5000, ((i + 1) * 5000))); 054 try (InputStream in = getInputStream(url+i, progressMonitor.createSubTaskMonitor(1, true))) { 055 if (in == null) { 056 break; 057 } 058 progressMonitor.setTicks(0); 059 GpxReader reader = new GpxReader(in); 060 gpxParsedProperly = reader.parse(false); 061 GpxData currentGpx = reader.getGpxData(); 062 if (result == null) { 063 result = currentGpx; 064 } else if (currentGpx.hasTrackPoints()) { 065 result.mergeFrom(currentGpx); 066 } else{ 067 done = true; 068 } 069 } 070 activeConnection = null; 071 } 072 if (result != null) { 073 result.fromServer = true; 074 result.dataSources.add(new DataSource(b, "OpenStreetMap server")); 075 } 076 return result; 077 } 078 079 @Override 080 public GpxData parseRawGps(ProgressMonitor progressMonitor) throws OsmTransferException { 081 progressMonitor.beginTask("", 1); 082 try { 083 progressMonitor.indeterminateSubTask(tr("Contacting OSM Server...")); 084 if (crosses180th) { 085 // API 0.6 does not support requests crossing the 180th meridian, so make two requests 086 GpxData result = downloadRawGps(new Bounds(lat1, lon1, lat2, 180.0), progressMonitor); 087 result.mergeFrom(downloadRawGps(new Bounds(lat1, -180.0, lat2, lon2), progressMonitor)); 088 return result; 089 } else { 090 // Simple request 091 return downloadRawGps(new Bounds(lat1, lon1, lat2, lon2), progressMonitor); 092 } 093 } catch (IllegalArgumentException e) { 094 // caused by HttpUrlConnection in case of illegal stuff in the response 095 if (cancel) 096 return null; 097 throw new OsmTransferException("Illegal characters within the HTTP-header response.", e); 098 } catch (IOException e) { 099 if (cancel) 100 return null; 101 throw new OsmTransferException(e); 102 } catch (SAXException e) { 103 throw new OsmTransferException(e); 104 } catch (OsmTransferException e) { 105 throw e; 106 } catch (RuntimeException e) { 107 if (cancel) 108 return null; 109 throw e; 110 } finally { 111 progressMonitor.finishTask(); 112 } 113 } 114 115 protected String getRequestForBbox(double lon1, double lat1, double lon2, double lat2) { 116 return "map?bbox=" + lon1 + "," + lat1 + "," + lon2 + "," + lat2; 117 } 118 119 @Override 120 public DataSet parseOsm(ProgressMonitor progressMonitor) throws OsmTransferException { 121 progressMonitor.beginTask(tr("Contacting OSM Server..."), 10); 122 try { 123 DataSet ds = null; 124 progressMonitor.indeterminateSubTask(null); 125 if (crosses180th) { 126 // API 0.6 does not support requests crossing the 180th meridian, so make two requests 127 DataSet ds2 = null; 128 129 try (InputStream in = getInputStream(getRequestForBbox(lon1, lat1, 180.0, lat2), progressMonitor.createSubTaskMonitor(9, false))) { 130 if (in == null) 131 return null; 132 ds = OsmReader.parseDataSet(in, progressMonitor.createSubTaskMonitor(1, false)); 133 } 134 135 try (InputStream in = getInputStream(getRequestForBbox(-180.0, lat1, lon2, lat2), progressMonitor.createSubTaskMonitor(9, false))) { 136 if (in == null) 137 return null; 138 ds2 = OsmReader.parseDataSet(in, progressMonitor.createSubTaskMonitor(1, false)); 139 } 140 if (ds2 == null) 141 return null; 142 ds.mergeFrom(ds2); 143 144 } else { 145 // Simple request 146 try (InputStream in = getInputStream(getRequestForBbox(lon1, lat1, lon2, lat2), progressMonitor.createSubTaskMonitor(9, false))) { 147 if (in == null) 148 return null; 149 ds = OsmReader.parseDataSet(in, progressMonitor.createSubTaskMonitor(1, false)); 150 } 151 } 152 return ds; 153 } catch(OsmTransferException e) { 154 throw e; 155 } catch (Exception e) { 156 throw new OsmTransferException(e); 157 } finally { 158 progressMonitor.finishTask(); 159 activeConnection = null; 160 } 161 } 162 163 @Override 164 public List<Note> parseNotes(Integer noteLimit, Integer daysClosed, ProgressMonitor progressMonitor) throws OsmTransferException { 165 progressMonitor.beginTask("Downloading notes"); 166 noteLimit = checkNoteLimit(noteLimit); 167 daysClosed = checkDaysClosed(daysClosed); 168 String url = new StringBuilder() 169 .append("notes?limit=") 170 .append(noteLimit) 171 .append("&closed=") 172 .append(daysClosed) 173 .append("&bbox=") 174 .append(lon1) 175 .append(",").append(lat1) 176 .append(",").append(lon2) 177 .append(",").append(lat2) 178 .toString(); 179 try { 180 InputStream is = getInputStream(url, progressMonitor.createSubTaskMonitor(1, false)); 181 NoteReader reader = new NoteReader(is); 182 return reader.parse(); 183 } catch (IOException e) { 184 throw new OsmTransferException(e); 185 } catch (SAXException e) { 186 throw new OsmTransferException(e); 187 } finally { 188 progressMonitor.finishTask(); 189 } 190 } 191 192 private Integer checkNoteLimit(Integer limit) { 193 if (limit == null) { 194 limit = Main.pref.getInteger("osm.notes.downloadLimit", 1000); 195 } 196 if (limit > 10000) { 197 Main.error("Requested note limit is over API hard limit of 10000. Reducing to 10000."); 198 limit = 10000; 199 } 200 if (limit < 1) { 201 Main.error("Requested note limit is less than 1. Setting to 1."); 202 limit = 1; 203 } 204 Main.debug("returning note limit: " + limit); 205 return limit; 206 } 207 208 private Integer checkDaysClosed(Integer days) { 209 if (days == null) { 210 days = Main.pref.getInteger("osm.notes.daysClosed", 1); 211 } 212 if (days < -1) { 213 Main.error("Requested days closed must be greater than -1"); 214 days = -1; 215 } 216 Main.debug("returning days closed: " + days); 217 return days; 218 } 219 220}