1 | // License: GPL. For details, see LICENSE file.
|
---|
2 | package org.openstreetmap.josm.tools;
|
---|
3 |
|
---|
4 | import java.io.IOException;
|
---|
5 | import java.io.InputStreamReader;
|
---|
6 | import java.io.Reader;
|
---|
7 | import java.nio.charset.StandardCharsets;
|
---|
8 | import java.util.regex.Pattern;
|
---|
9 |
|
---|
10 | import javax.script.Invocable;
|
---|
11 | import javax.script.ScriptEngine;
|
---|
12 | import javax.script.ScriptEngineManager;
|
---|
13 | import javax.script.ScriptException;
|
---|
14 |
|
---|
15 | /**
|
---|
16 | * Uses <a href="https://github.com/tyrasd/overpass-turbo/">Overpass Turbo</a> query wizard code
|
---|
17 | * to build an Overpass QL from a {@link org.openstreetmap.josm.actions.search.SearchAction} like query.
|
---|
18 | *
|
---|
19 | * Requires a JavaScript {@link ScriptEngine}.
|
---|
20 | * @since 8744
|
---|
21 | */
|
---|
22 | public final class OverpassTurboQueryWizard {
|
---|
23 |
|
---|
24 | private static OverpassTurboQueryWizard instance;
|
---|
25 | private final ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript");
|
---|
26 |
|
---|
27 | /**
|
---|
28 | * An exception to indicate a failed parse.
|
---|
29 | */
|
---|
30 | public static class ParseException extends RuntimeException {
|
---|
31 | }
|
---|
32 |
|
---|
33 | /**
|
---|
34 | * Replies the unique instance of this class.
|
---|
35 | *
|
---|
36 | * @return the unique instance of this class
|
---|
37 | */
|
---|
38 | public static synchronized OverpassTurboQueryWizard getInstance() {
|
---|
39 | if (instance == null) {
|
---|
40 | instance = new OverpassTurboQueryWizard();
|
---|
41 | }
|
---|
42 | return instance;
|
---|
43 | }
|
---|
44 |
|
---|
45 | private OverpassTurboQueryWizard() {
|
---|
46 | // overpass-turbo is MIT Licensed
|
---|
47 |
|
---|
48 | try (final Reader reader = new InputStreamReader(
|
---|
49 | getClass().getResourceAsStream("/data/overpass-turbo-ffs.js"), StandardCharsets.UTF_8)) {
|
---|
50 | engine.eval("var console = {log: function(){}};");
|
---|
51 | engine.eval(reader);
|
---|
52 | engine.eval("var construct_query = turbo.ffs().construct_query;");
|
---|
53 | } catch (ScriptException | IOException ex) {
|
---|
54 | throw new RuntimeException("Failed to initialize OverpassTurboQueryWizard", ex);
|
---|
55 | }
|
---|
56 | }
|
---|
57 |
|
---|
58 | /**
|
---|
59 | * Builds an Overpass QL from a {@link org.openstreetmap.josm.actions.search.SearchAction} like query.
|
---|
60 | * @param search the {@link org.openstreetmap.josm.actions.search.SearchAction} like query
|
---|
61 | * @return an Overpass QL query
|
---|
62 | * @throws ParseException when the parsing fails
|
---|
63 | */
|
---|
64 | public String constructQuery(String search) throws ParseException {
|
---|
65 | try {
|
---|
66 | final Object result = ((Invocable) engine).invokeFunction("construct_query", search);
|
---|
67 | if (result == Boolean.FALSE) {
|
---|
68 | throw new ParseException();
|
---|
69 | }
|
---|
70 | String query = (String) result;
|
---|
71 | query = Pattern.compile("^.*\\[out:json\\]", Pattern.DOTALL).matcher(query).replaceFirst("");
|
---|
72 | query = Pattern.compile("^out.*", Pattern.MULTILINE).matcher(query).replaceAll("out meta;");
|
---|
73 | query = query.replace("({{bbox}})", "");
|
---|
74 | return query;
|
---|
75 | } catch (NoSuchMethodException e) {
|
---|
76 | throw new IllegalStateException();
|
---|
77 | } catch (ScriptException e) {
|
---|
78 | throw new RuntimeException("Failed to execute OverpassTurboQueryWizard", e);
|
---|
79 | }
|
---|
80 | }
|
---|
81 |
|
---|
82 | }
|
---|