commit 1bd6c58c8c6d6d733d8a8051c22826620eb353d1
Author: Simon Legner <Simon.Legner@gmail.com>
Date: 2020-02-29 23:51:15 +0100
fix #18164 - Migrate OverpassTurboQueryWizard to overpass-wizard-server
diff --git a/src/org/openstreetmap/josm/gui/download/OverpassQueryWizardDialog.java b/src/org/openstreetmap/josm/gui/download/OverpassQueryWizardDialog.java
index 891c991c1..5de809442 100644
|
a
|
b
|
|
| 5 | 5 | |
| 6 | 6 | import java.awt.GridBagLayout; |
| 7 | 7 | import java.awt.event.ActionEvent; |
| | 8 | import java.io.IOException; |
| 8 | 9 | import java.util.ArrayList; |
| 9 | 10 | import java.util.Arrays; |
| 10 | 11 | import java.util.Collections; |
| … |
… |
private void saveHistory() {
|
| 143 | 144 | private Optional<String> tryParseSearchTerm(String searchTerm) { |
| 144 | 145 | try { |
| 145 | 146 | return Optional.of(overpassQueryBuilder.constructQuery(searchTerm)); |
| 146 | | } catch (UncheckedParseException | IllegalStateException ex) { |
| | 147 | } catch (IOException | UncheckedParseException ex) { |
| 147 | 148 | Logging.error(ex); |
| 148 | 149 | JOptionPane.showMessageDialog( |
| 149 | 150 | dsPanel.getParent(), |
diff --git a/src/org/openstreetmap/josm/tools/OverpassTurboQueryWizard.java b/src/org/openstreetmap/josm/tools/OverpassTurboQueryWizard.java
index fc4c340f8..4f7b17f1d 100644
|
a
|
b
|
|
| 2 | 2 | package org.openstreetmap.josm.tools; |
| 3 | 3 | |
| 4 | 4 | import java.io.IOException; |
| 5 | | import java.io.Reader; |
| | 5 | import java.net.URL; |
| 6 | 6 | |
| 7 | | import javax.script.Invocable; |
| 8 | | import javax.script.ScriptEngine; |
| 9 | | import javax.script.ScriptException; |
| 10 | | |
| 11 | | import org.openstreetmap.josm.io.CachedFile; |
| 12 | 7 | import org.openstreetmap.josm.spi.preferences.Config; |
| 13 | 8 | |
| 14 | 9 | /** |
| 15 | 10 | * Uses <a href="https://github.com/tyrasd/overpass-wizard/">Overpass Turbo query wizard</a> code (MIT Licensed) |
| 16 | 11 | * to build an Overpass QL from a {@link org.openstreetmap.josm.actions.search.SearchAction} like query. |
| 17 | 12 | * |
| 18 | | * Requires a JavaScript {@link ScriptEngine}. |
| | 13 | * This involves a HTTP request to an overpass-wizard-server. |
| 19 | 14 | * @since 8744 |
| 20 | 15 | */ |
| 21 | 16 | public final class OverpassTurboQueryWizard { |
| 22 | 17 | |
| 23 | 18 | private static OverpassTurboQueryWizard instance; |
| 24 | | private final ScriptEngine engine = Utils.getJavaScriptEngine(); |
| 25 | 19 | |
| 26 | 20 | /** |
| 27 | 21 | * Replies the unique instance of this class. |
| … |
… |
public static synchronized OverpassTurboQueryWizard getInstance() {
|
| 35 | 29 | return instance; |
| 36 | 30 | } |
| 37 | 31 | |
| 38 | | private OverpassTurboQueryWizard() { |
| 39 | | try (CachedFile file = new CachedFile("resource://data/overpass-wizard.js"); |
| 40 | | Reader reader = file.getContentReader()) { |
| 41 | | if (engine != null) { |
| 42 | | engine.eval("var console = {error: " + Logging.class.getCanonicalName() + ".warn};"); |
| 43 | | engine.eval("var global = {};"); |
| 44 | | engine.eval(reader); |
| 45 | | engine.eval("var overpassWizardJOSM = function(query) {" + |
| 46 | | " return overpassWizard(query, {" + |
| 47 | | " comment: false," + |
| 48 | | " timeout: " + Config.getPref().getInt("overpass.wizard.timeout", 90) + "," + |
| 49 | | " outputFormat: 'xml'," + |
| 50 | | " outputMode: 'recursive_meta'" + |
| 51 | | " });" + |
| 52 | | "}"); |
| 53 | | } |
| 54 | | } catch (ScriptException | IOException ex) { |
| 55 | | throw new IllegalStateException("Failed to initialize OverpassTurboQueryWizard", ex); |
| 56 | | } |
| 57 | | } |
| 58 | | |
| 59 | 32 | /** |
| 60 | 33 | * Builds an Overpass QL from a {@link org.openstreetmap.josm.actions.search.SearchAction} like query. |
| 61 | 34 | * @param search the {@link org.openstreetmap.josm.actions.search.SearchAction} like query |
| 62 | 35 | * @return an Overpass QL query |
| 63 | 36 | * @throws UncheckedParseException when the parsing fails |
| | 37 | * @throws IOException in case of I/O error |
| 64 | 38 | */ |
| 65 | | public String constructQuery(String search) { |
| 66 | | if (engine == null) { |
| 67 | | throw new IllegalStateException("Failed to retrieve JavaScript engine"); |
| 68 | | } |
| 69 | | try { |
| 70 | | final Object result = ((Invocable) engine).invokeFunction("overpassWizardJOSM", search); |
| 71 | | if (Boolean.FALSE.equals(result)) { |
| 72 | | throw new UncheckedParseException(); |
| 73 | | } |
| 74 | | return (String) result; |
| 75 | | } catch (NoSuchMethodException e) { |
| 76 | | throw new IllegalStateException(e); |
| 77 | | } catch (ScriptException e) { |
| 78 | | throw new UncheckedParseException("Failed to execute OverpassTurboQueryWizard", e); |
| | 39 | public String constructQuery(String search) throws UncheckedParseException, IOException { |
| | 40 | String url = Config.getPref().get("overpass.wizard.server", "https://overpass-wizard.josm.eu/overpass-wizard/") |
| | 41 | + "?search=" + Utils.encodeUrl(search) |
| | 42 | + "&comment=false" |
| | 43 | + "&timeout=" + Config.getPref().getInt("overpass.wizard.timeout", 90) |
| | 44 | + "&outputFormat=xml" |
| | 45 | + "&outputMode=recursive_meta"; |
| | 46 | final String query = HttpClient.create(new URL(url)) |
| | 47 | .connect() |
| | 48 | .fetchContent(); |
| | 49 | if ("false".equals(query)) { |
| | 50 | throw new UncheckedParseException(); |
| 79 | 51 | } |
| | 52 | return query; |
| 80 | 53 | } |
| 81 | 54 | } |
diff --git a/test/unit/org/openstreetmap/josm/io/OverpassDownloadReaderTest.java b/test/unit/org/openstreetmap/josm/io/OverpassDownloadReaderTest.java
index 8c2d6831e..fe96ccca0 100644
|
a
|
b
|
|
| 10 | 10 | import static org.junit.Assert.assertNull; |
| 11 | 11 | import static org.junit.Assert.assertTrue; |
| 12 | 12 | |
| | 13 | import java.io.IOException; |
| 13 | 14 | import java.io.StringReader; |
| 14 | 15 | import java.time.LocalDateTime; |
| 15 | 16 | import java.util.regex.Matcher; |
| … |
… |
public void setUp() {
|
| 57 | 58 | NameFinder.NOMINATIM_URL_PROP.put("http://localhost:" + wireMockRule.port() + NOMINATIM_URL_PATH); |
| 58 | 59 | } |
| 59 | 60 | |
| 60 | | private String getExpandedQuery(String search) { |
| | 61 | private String getExpandedQuery(String search) throws IOException { |
| 61 | 62 | final String query = OverpassTurboQueryWizard.getInstance().constructQuery(search); |
| 62 | 63 | final String request = new OverpassDownloadReader(new Bounds(1, 2, 3, 4), null, query) |
| 63 | 64 | .getRequestForBbox(1, 2, 3, 4) |
| … |
… |
private String getExpandedQuery(String search) {
|
| 69 | 70 | * Tests evaluating the extended query feature {@code bbox}. |
| 70 | 71 | */ |
| 71 | 72 | @Test |
| 72 | | public void testBbox() { |
| | 73 | public void testBbox() throws IOException { |
| 73 | 74 | final String query = getExpandedQuery("amenity=drinking_water"); |
| 74 | 75 | assertEquals("" + |
| 75 | 76 | "[out:xml][timeout:90][bbox:2.0,1.0,4.0,3.0];\n" + |
| … |
… |
public void testDate() {
|
| 118 | 119 | * Tests evaluating the extended query feature {@code date} through {@code newer:} operator. |
| 119 | 120 | */ |
| 120 | 121 | @Test |
| 121 | | public void testDateNewer() { |
| | 122 | public void testDateNewer() throws IOException { |
| 122 | 123 | final String query = getExpandedQuery("type:node and newer:3minutes"); |
| 123 | 124 | String statement = query.substring(query.indexOf("node(newer:\"") + 12, query.lastIndexOf("\");")); |
| 124 | 125 | assertNotNull(DateUtils.fromString(statement)); |
| … |
… |
public void testDateNewer() {
|
| 128 | 129 | * Tests evaluating the extended query feature {@code geocodeArea}. |
| 129 | 130 | */ |
| 130 | 131 | @Test |
| 131 | | public void testGeocodeArea() { |
| | 132 | public void testGeocodeArea() throws IOException { |
| 132 | 133 | stubNominatim("London"); |
| 133 | 134 | final String query = getExpandedQuery("amenity=drinking_water in London"); |
| 134 | 135 | assertEquals("" + |
diff --git a/test/unit/org/openstreetmap/josm/tools/OverpassTurboQueryWizardTest.java b/test/unit/org/openstreetmap/josm/tools/OverpassTurboQueryWizardTest.java
index 90a3a2a65..899ae0b7e 100644
|
a
|
b
|
|
| 3 | 3 | |
| 4 | 4 | import static org.junit.Assert.assertEquals; |
| 5 | 5 | |
| | 6 | import java.io.IOException; |
| | 7 | |
| 6 | 8 | import org.junit.Ignore; |
| 7 | 9 | import org.junit.Rule; |
| 8 | 10 | import org.junit.Test; |
| … |
… |
|
| 25 | 27 | * Test {@code key=value}. |
| 26 | 28 | */ |
| 27 | 29 | @Test |
| 28 | | public void testKeyValue() { |
| | 30 | public void testKeyValue() throws IOException { |
| 29 | 31 | final String query = OverpassTurboQueryWizard.getInstance().constructQuery("amenity=drinking_water"); |
| 30 | 32 | assertEquals("" + |
| 31 | 33 | "[out:xml][timeout:90][bbox:{{bbox}}];\n" + |
| … |
… |
public void testKeyValue() {
|
| 40 | 42 | * Test {@code key!=value}. |
| 41 | 43 | */ |
| 42 | 44 | @Test |
| 43 | | public void testKeyNotValue() { |
| | 45 | public void testKeyNotValue() throws IOException { |
| 44 | 46 | final String query = OverpassTurboQueryWizard.getInstance().constructQuery("amenity!=drinking_water"); |
| 45 | 47 | assertEquals("" + |
| 46 | 48 | "[out:xml][timeout:90][bbox:{{bbox}}];\n" + |
| … |
… |
public void testKeyNotValue() {
|
| 55 | 57 | * Test {@code foo=bar and baz=42}. |
| 56 | 58 | */ |
| 57 | 59 | @Test |
| 58 | | public void testBooleanAnd() { |
| | 60 | public void testBooleanAnd() throws IOException { |
| 59 | 61 | final String expected = "" + |
| 60 | 62 | "[out:xml][timeout:90][bbox:{{bbox}}];\n" + |
| 61 | 63 | "(\n" + |
| … |
… |
public void testBooleanAnd() {
|
| 72 | 74 | * Test {@code foo=bar or baz=42}. |
| 73 | 75 | */ |
| 74 | 76 | @Test |
| 75 | | public void testBooleanOr() { |
| | 77 | public void testBooleanOr() throws IOException { |
| 76 | 78 | final String expected = "" + |
| 77 | 79 | "[out:xml][timeout:90][bbox:{{bbox}}];\n" + |
| 78 | 80 | "(\n" + |
| … |
… |
public void testBooleanOr() {
|
| 90 | 92 | * Test {@code (foo=* or bar=*) and (asd=* or fasd=*)}. |
| 91 | 93 | */ |
| 92 | 94 | @Test |
| 93 | | public void testBoolean() { |
| | 95 | public void testBoolean() throws IOException { |
| 94 | 96 | final String query = OverpassTurboQueryWizard.getInstance().constructQuery("(foo=* or bar=*) and (asd=* or fasd=*)"); |
| 95 | 97 | assertEquals("" + |
| 96 | 98 | "[out:xml][timeout:90][bbox:{{bbox}}];\n" + |
| … |
… |
public void testBoolean() {
|
| 108 | 110 | * Test {@code foo=bar and (type:node or type:way)}. |
| 109 | 111 | */ |
| 110 | 112 | @Test |
| 111 | | public void testType() { |
| | 113 | public void testType() throws IOException { |
| 112 | 114 | final String query = OverpassTurboQueryWizard.getInstance().constructQuery("foo=bar and (type:node or type:way)"); |
| 113 | 115 | assertEquals("" + |
| 114 | 116 | "[out:xml][timeout:90][bbox:{{bbox}}];\n" + |
| … |
… |
public void testType() {
|
| 124 | 126 | * Test {@code user:foo or uid:42}. |
| 125 | 127 | */ |
| 126 | 128 | @Test |
| 127 | | public void testUser() { |
| | 129 | public void testUser() throws IOException { |
| 128 | 130 | final String query = OverpassTurboQueryWizard.getInstance().constructQuery("user:foo or uid:42"); |
| 129 | 131 | assertEquals("" + |
| 130 | 132 | "[out:xml][timeout:90][bbox:{{bbox}}];\n" + |
| … |
… |
public void testUser() {
|
| 140 | 142 | * Test {@code foo=bar and (type:node or type:way)}. |
| 141 | 143 | */ |
| 142 | 144 | @Test |
| 143 | | public void testEmpty() { |
| | 145 | public void testEmpty() throws IOException { |
| 144 | 146 | final String query = OverpassTurboQueryWizard.getInstance().constructQuery("foo='' and type:way"); |
| 145 | 147 | assertEquals("" + |
| 146 | 148 | "[out:xml][timeout:90][bbox:{{bbox}}];\n" + |
| … |
… |
public void testEmpty() {
|
| 155 | 157 | * Test geocodeArea. |
| 156 | 158 | */ |
| 157 | 159 | @Test |
| 158 | | public void testInArea() { |
| | 160 | public void testInArea() throws IOException { |
| 159 | 161 | final String query = OverpassTurboQueryWizard.getInstance().constructQuery("foo=bar in Josmland"); |
| 160 | 162 | assertEquals("" + |
| 161 | 163 | "[out:xml][timeout:90];\n" + |
| … |
… |
public void testInArea() {
|
| 172 | 174 | */ |
| 173 | 175 | @Test |
| 174 | 176 | @Ignore("preset handling not implemented") |
| 175 | | public void testPreset() { |
| | 177 | public void testPreset() throws IOException { |
| 176 | 178 | final String query = OverpassTurboQueryWizard.getInstance().constructQuery("Hospital"); |
| 177 | 179 | assertEquals("" + |
| 178 | 180 | "[out:xml][timeout:90];\n" + |
| … |
… |
public void testPreset() {
|
| 187 | 189 | * Test erroneous value. |
| 188 | 190 | */ |
| 189 | 191 | @Test(expected = UncheckedParseException.class) |
| 190 | | public void testErroneous() { |
| | 192 | public void testErroneous() throws IOException { |
| 191 | 193 | OverpassTurboQueryWizard.getInstance().constructQuery("foo"); |
| 192 | 194 | } |
| 193 | 195 | } |