Changeset 12714 in josm
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/io/OverpassDownloadReader.java
r12620 r12714 7 7 import java.io.InputStream; 8 8 import java.nio.charset.StandardCharsets; 9 import java.time.Duration; 10 import java.time.LocalDateTime; 11 import java.time.Period; 12 import java.time.ZoneOffset; 9 13 import java.util.EnumMap; 14 import java.util.Locale; 10 15 import java.util.Map; 11 16 import java.util.NoSuchElementException; … … 21 26 import org.openstreetmap.josm.data.Bounds; 22 27 import org.openstreetmap.josm.data.DataSource; 28 import org.openstreetmap.josm.data.coor.LatLon; 29 import org.openstreetmap.josm.data.osm.BBox; 23 30 import org.openstreetmap.josm.data.osm.DataSet; 24 31 import org.openstreetmap.josm.data.osm.OsmPrimitiveType; 25 32 import org.openstreetmap.josm.data.osm.PrimitiveId; 26 33 import org.openstreetmap.josm.gui.progress.ProgressMonitor; 34 import org.openstreetmap.josm.io.NameFinder.SearchResult; 27 35 import org.openstreetmap.josm.tools.HttpClient; 28 36 import org.openstreetmap.josm.tools.Logging; … … 146 154 return super.getRequestForBbox(lon1, lat1, lon2, lat2); 147 155 else { 148 final String query = this.overpassQuery.replace("{{bbox}}", lat1 + "," + lon1 + "," + lat2 + "," + lon2); 156 final String query = this.overpassQuery 157 .replace("{{bbox}}", bbox(lon1, lat1, lon2, lat2)) 158 .replace("{{center}}", center(lon1, lat1, lon2, lat2)); 149 159 final String expandedOverpassQuery = expandExtendedQueries(query); 150 160 return "interpreter" + DATA_PREFIX + Utils.encodeUrl(expandedOverpassQuery); … … 160 170 static String expandExtendedQueries(String query) { 161 171 final StringBuffer sb = new StringBuffer(); 162 final Matcher matcher = Pattern.compile("\\{\\{(geocodeArea):([^}]+)\\}\\}").matcher(query); 172 final Matcher matcher = Pattern.compile("\\{\\{(date|geocodeArea|geocodeBbox|geocodeCoords|geocodeId):([^}]+)\\}\\}").matcher(query); 163 173 while (matcher.find()) { 164 174 try { 165 175 switch (matcher.group(1)) { 176 case "date": 177 matcher.appendReplacement(sb, date(matcher.group(2), LocalDateTime.now())); 178 break; 166 179 case "geocodeArea": 167 180 matcher.appendReplacement(sb, geocodeArea(matcher.group(2))); 168 181 break; 182 case "geocodeBbox": 183 matcher.appendReplacement(sb, geocodeBbox(matcher.group(2))); 184 break; 185 case "geocodeCoords": 186 matcher.appendReplacement(sb, geocodeCoords(matcher.group(2))); 187 break; 188 case "geocodeId": 189 matcher.appendReplacement(sb, geocodeId(matcher.group(2))); 190 break; 169 191 default: 170 192 Logging.warn("Unsupported syntax: " + matcher.group(1)); 171 193 } 172 } catch (UncheckedParseException ex) { 194 } catch (UncheckedParseException | IOException | NoSuchElementException | IndexOutOfBoundsException ex) { 173 195 final String msg = tr("Failed to evaluate {0}", matcher.group()); 174 196 Logging.log(Logging.LEVEL_WARN, msg, ex); … … 180 202 } 181 203 182 private static String geocodeArea(String area) { 204 static String bbox(double lon1, double lat1, double lon2, double lat2) { 205 return lat1 + "," + lon1 + "," + lat2 + "," + lon2; 206 } 207 208 static String center(double lon1, double lat1, double lon2, double lat2) { 209 LatLon c = new BBox(lon1, lat1, lon2, lat2).getCenter(); 210 return c.lat()+ "," + c.lon(); 211 } 212 213 static String date(String humanDuration, LocalDateTime from) { 214 // Convert to ISO 8601. Replace months by X temporarily to avoid conflict with minutes 215 String duration = humanDuration.toLowerCase(Locale.ENGLISH).replace(" ", "") 216 .replaceAll("years?", "Y").replaceAll("months?", "X").replaceAll("weeks?", "W") 217 .replaceAll("days?", "D").replaceAll("hours?", "H").replaceAll("minutes?", "M").replaceAll("seconds?", "S"); 218 Matcher matcher = Pattern.compile( 219 "((?:[0-9]+Y)?(?:[0-9]+X)?(?:[0-9]+W)?)"+ 220 "((?:[0-9]+D)?)" + 221 "((?:[0-9]+H)?(?:[0-9]+M)?(?:[0-9]+(?:[.,][0-9]{0,9})?S)?)?").matcher(duration); 222 boolean javaPer = false; 223 boolean javaDur = false; 224 if (matcher.matches()) { 225 javaPer = matcher.group(1) != null && !matcher.group(1).isEmpty(); 226 javaDur = matcher.group(3) != null && !matcher.group(3).isEmpty(); 227 duration = 'P' + matcher.group(1).replace('X', 'M') + matcher.group(2); 228 if (javaDur) { 229 duration += 'T' + matcher.group(3); 230 } 231 } 232 233 // Duration is now a full ISO 8601 duration string. Unfortunately Java does not allow to parse it entirely. 234 // We must split the "period" (years, months, weeks, days) from the "duration" (days, hours, minutes, seconds). 235 Period p = null; 236 Duration d = null; 237 int idx = duration.indexOf('T'); 238 if (javaPer) { 239 p = Period.parse(javaDur ? duration.substring(0, idx) : duration); 240 } 241 if (javaDur) { 242 d = Duration.parse(javaPer ? 'P' + duration.substring(idx, duration.length()) : duration); 243 } else if (!javaPer) { 244 d = Duration.parse(duration); 245 } 246 247 // Now that period and duration are known, compute the correct date/time 248 LocalDateTime dt = from; 249 if (p != null) { 250 dt = dt.minus(p); 251 } 252 if (d != null) { 253 dt = dt.minus(d); 254 } 255 256 // Returns the date/time formatted in ISO 8601 257 return dt.toInstant(ZoneOffset.UTC).toString(); 258 } 259 260 private static SearchResult searchName(String area) throws IOException { 261 return NameFinder.queryNominatim(area).stream().filter( 262 x -> !OsmPrimitiveType.NODE.equals(x.getOsmId().getType())).iterator().next(); 263 } 264 265 static String geocodeArea(String area) throws IOException { 183 266 // Offsets defined in https://wiki.openstreetmap.org/wiki/Overpass_API/Overpass_QL#By_element_id 184 267 final EnumMap<OsmPrimitiveType, Long> idOffset = new EnumMap<>(OsmPrimitiveType.class); … … 186 269 idOffset.put(OsmPrimitiveType.WAY, 2_400_000_000L); 187 270 idOffset.put(OsmPrimitiveType.RELATION, 3_600_000_000L); 188 try { 189 final PrimitiveId osmId = NameFinder.queryNominatim(area).stream().filter( 190 x -> !OsmPrimitiveType.NODE.equals(x.getOsmId().getType())).iterator().next().getOsmId(); 191 return String.format("area(%d)", osmId.getUniqueId() + idOffset.get(osmId.getType())); 192 } catch (IOException | NoSuchElementException | IndexOutOfBoundsException ex) { 193 throw new UncheckedParseException(ex); 194 } 271 final PrimitiveId osmId = searchName(area).getOsmId(); 272 return String.format("area(%d)", osmId.getUniqueId() + idOffset.get(osmId.getType())); 273 } 274 275 static String geocodeBbox(String area) throws IOException { 276 Bounds bounds = searchName(area).getBounds(); 277 return bounds.getMinLat() + "," + bounds.getMinLon() + "," + bounds.getMaxLat() + "," + bounds.getMaxLon(); 278 } 279 280 static String geocodeCoords(String area) throws IOException { 281 SearchResult result = searchName(area); 282 return result.getLat() + "," + result.getLon(); 283 } 284 285 static String geocodeId(String area) throws IOException { 286 PrimitiveId osmId = searchName(area).getOsmId(); 287 return String.format("%s(%d)", osmId.getType().getAPIName(), osmId.getUniqueId()); 195 288 } 196 289 -
trunk/test/unit/org/openstreetmap/josm/io/OverpassDownloadReaderTest.java
r12557 r12714 7 7 import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; 8 8 import static org.junit.Assert.assertEquals; 9 import static org.junit.Assert.assertNotNull; 9 10 import static org.junit.Assert.assertTrue; 10 11 12 import java.time.LocalDateTime; 11 13 import java.util.regex.Matcher; 12 14 … … 20 22 import org.openstreetmap.josm.tools.OverpassTurboQueryWizard; 21 23 import org.openstreetmap.josm.tools.Utils; 24 import org.openstreetmap.josm.tools.date.DateUtils; 22 25 23 26 import com.github.tomakehurst.wiremock.junit.WireMockRule; … … 87 90 88 91 /** 92 * Tests evaluating the extended query feature {@code date}. 93 */ 94 @Test 95 public void testDate() { 96 LocalDateTime from = LocalDateTime.of(2017, 7, 14, 2, 40); 97 assertEquals("2016-07-14T02:40:00Z", OverpassDownloadReader.date("1 year", from)); 98 assertEquals("2007-07-14T02:40:00Z", OverpassDownloadReader.date("10years", from)); 99 assertEquals("2017-06-14T02:40:00Z", OverpassDownloadReader.date("1 month", from)); 100 assertEquals("2016-09-14T02:40:00Z", OverpassDownloadReader.date("10months", from)); 101 assertEquals("2017-07-07T02:40:00Z", OverpassDownloadReader.date("1 week", from)); 102 assertEquals("2017-05-05T02:40:00Z", OverpassDownloadReader.date("10weeks", from)); 103 assertEquals("2017-07-13T02:40:00Z", OverpassDownloadReader.date("1 day", from)); 104 assertEquals("2017-07-04T02:40:00Z", OverpassDownloadReader.date("10days", from)); 105 assertEquals("2017-07-14T01:40:00Z", OverpassDownloadReader.date("1 hour", from)); 106 assertEquals("2017-07-13T16:40:00Z", OverpassDownloadReader.date("10hours", from)); 107 assertEquals("2017-07-14T02:39:00Z", OverpassDownloadReader.date("1 minute", from)); 108 assertEquals("2017-07-14T02:30:00Z", OverpassDownloadReader.date("10minutes", from)); 109 assertEquals("2017-07-14T02:39:59Z", OverpassDownloadReader.date("1 second", from)); 110 assertEquals("2017-07-14T02:39:50Z", OverpassDownloadReader.date("10seconds", from)); 111 112 assertEquals("2016-07-13T02:40:00Z", OverpassDownloadReader.date("1 year 1 day", from)); 113 assertEquals("2016-07-14T02:38:20Z", OverpassDownloadReader.date("1 year 100 seconds", from)); 114 assertEquals("2017-07-13T02:38:20Z", OverpassDownloadReader.date("1 day 100 seconds", from)); 115 } 116 117 /** 118 * Tests evaluating the extended query feature {@code date} through {@code newer:} operator. 119 */ 120 @Test 121 public void testDateNewer() { 122 final String query = getExpandedQuery("type:node and newer:3minutes"); 123 String statement = query.substring(query.indexOf("node(newer:\"") + 12, query.lastIndexOf("\");")); 124 assertNotNull(DateUtils.fromString(statement)); 125 } 126 127 /** 89 128 * Tests evaluating the extended query feature {@code geocodeArea}. 90 129 */
Note:
See TracChangeset
for help on using the changeset viewer.