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.InputStream; 007 008import javax.xml.stream.XMLStreamConstants; 009import javax.xml.stream.XMLStreamException; 010 011import org.openstreetmap.josm.data.Bounds; 012import org.openstreetmap.josm.data.DataSource; 013import org.openstreetmap.josm.data.osm.DataSet; 014import org.openstreetmap.josm.gui.progress.ProgressMonitor; 015import org.openstreetmap.josm.tools.Utils; 016 017/** 018 * Read content from an Overpass server. 019 * 020 * @since 8744 021 */ 022public class OverpassDownloadReader extends BoundingBoxDownloader { 023 024 final String overpassServer; 025 final String overpassQuery; 026 027 /** 028 * Constructs a new {@code OverpassDownloadReader}. 029 * 030 * @param downloadArea The area to download 031 * @param overpassServer The Overpass server to use 032 * @param overpassQuery The Overpass query 033 */ 034 public OverpassDownloadReader(Bounds downloadArea, String overpassServer, String overpassQuery) { 035 super(downloadArea); 036 this.overpassServer = overpassServer; 037 this.overpassQuery = overpassQuery.trim(); 038 } 039 040 @Override 041 protected String getBaseUrl() { 042 return overpassServer; 043 } 044 045 @Override 046 protected String getRequestForBbox(double lon1, double lat1, double lon2, double lat2) { 047 if (overpassQuery.isEmpty()) 048 return super.getRequestForBbox(lon1, lat1, lon2, lat2); 049 else { 050 String realQuery = completeOverpassQuery(overpassQuery); 051 return "interpreter?data=" + Utils.encodeUrl(realQuery) 052 + "&bbox=" + lon1 + ',' + lat1 + ',' + lon2 + ',' + lat2; 053 } 054 } 055 056 private static String completeOverpassQuery(String query) { 057 int firstColon = query.indexOf(';'); 058 if (firstColon == -1) { 059 return "[bbox];" + query; 060 } 061 int bboxPos = query.indexOf("[bbox"); 062 if (bboxPos > -1 && bboxPos < firstColon) { 063 return query; 064 } 065 066 int bracketCount = 0; 067 int pos = 0; 068 for (; pos < firstColon; ++pos) { 069 if (query.charAt(pos) == '[') 070 ++bracketCount; 071 else if (query.charAt(pos) == ']') 072 --bracketCount; 073 else if (bracketCount == 0) { 074 if (!Character.isWhitespace(query.charAt(pos))) 075 break; 076 } 077 } 078 079 if (pos < firstColon) { 080 // We start with a statement, not with declarations 081 return "[bbox];" + query; 082 } 083 084 // We start with declarations. Add just one more declaration in this case. 085 return "[bbox]" + query; 086 } 087 088 @Override 089 protected InputStream getInputStreamRaw(String urlStr, ProgressMonitor progressMonitor, String reason, 090 boolean uncompressAccordingToContentDisposition) throws OsmTransferException { 091 try { 092 return super.getInputStreamRaw(urlStr, progressMonitor, reason, uncompressAccordingToContentDisposition); 093 } catch (OsmApiException ex) { 094 final String errorIndicator = "Error</strong>: "; 095 if (ex.getMessage() != null && ex.getMessage().contains(errorIndicator)) { 096 final String errorPlusRest = ex.getMessage().split(errorIndicator)[1]; 097 if (errorPlusRest != null) { 098 final String error = errorPlusRest.split("</")[0]; 099 ex.setErrorHeader(error); 100 } 101 } 102 throw ex; 103 } 104 } 105 106 @Override 107 protected String getTaskName() { 108 return tr("Contacting Server..."); 109 } 110 111 @Override 112 protected DataSet parseDataSet(InputStream source, ProgressMonitor progressMonitor) throws IllegalDataException { 113 return new OsmReader() { 114 @Override 115 protected void parseUnknown(boolean printWarning) throws XMLStreamException { 116 if ("remark".equals(parser.getLocalName())) { 117 if (parser.getEventType() == XMLStreamConstants.START_ELEMENT) { 118 final String text = parser.getElementText(); 119 if (text.contains("runtime error")) { 120 throw new XMLStreamException(text); 121 } 122 } 123 } 124 super.parseUnknown(printWarning); 125 } 126 }.doParseDataSet(source, progressMonitor); 127 } 128 129 @Override 130 public DataSet parseOsm(ProgressMonitor progressMonitor) throws OsmTransferException { 131 132 DataSet ds = super.parseOsm(progressMonitor); 133 134 // add bounds if necessary (note that Overpass API does not return bounds in the response XML) 135 if (ds != null && ds.dataSources.isEmpty()) { 136 if (crosses180th) { 137 Bounds bounds = new Bounds(lat1, lon1, lat2, 180.0); 138 DataSource src = new DataSource(bounds, getBaseUrl()); 139 ds.dataSources.add(src); 140 141 bounds = new Bounds(lat1, -180.0, lat2, lon2); 142 src = new DataSource(bounds, getBaseUrl()); 143 ds.dataSources.add(src); 144 } else { 145 Bounds bounds = new Bounds(lat1, lon1, lat2, lon2); 146 DataSource src = new DataSource(bounds, getBaseUrl()); 147 ds.dataSources.add(src); 148 } 149 } 150 151 return ds; 152 } 153}