Modify

Opened 3 years ago

Last modified 3 years ago

#12037 new enhancement

[Alpha patch] Feedback on valid/invalid Overpass Turbo queries

Reported by: Don-vip Owned by: team
Priority: normal Milestone:
Component: Core Version:
Keywords: overpass turbo feedback Cc: simon04

Description (last modified by Don-vip)

In the "Download from Overpass" dialog, the first text field allowing to build Overpass queries does not provide real-time feedback on the validity of the query being typed.

I came up with two solutions, but none of them is good enough:

  • the first one calls Overpass Turbo in JavaScript. It's too slow!
  • the second one calls our Search syntax validator. It's fast, but wrong!

Does anyone have a better idea?

Here's my patch:

  • src/org/openstreetmap/josm/actions/OverpassDownloadAction.java

     
    3333import javax.swing.JPopupMenu;
    3434import javax.swing.JScrollPane;
    3535import javax.swing.plaf.basic.BasicArrowButton;
     36import javax.swing.text.JTextComponent;
    3637
    3738import org.openstreetmap.josm.Main;
    3839import org.openstreetmap.josm.actions.downloadtasks.DownloadOsmTask;
    3940import org.openstreetmap.josm.actions.downloadtasks.PostDownloadHandler;
     41import org.openstreetmap.josm.actions.search.SearchAction.SearchSetting;
     42import org.openstreetmap.josm.actions.search.SearchCompiler;
     43import org.openstreetmap.josm.actions.search.SearchCompiler.ParseError;
    4044import org.openstreetmap.josm.data.Bounds;
    4145import org.openstreetmap.josm.data.preferences.CollectionProperty;
    4246import org.openstreetmap.josm.data.preferences.IntegerProperty;
     
    4448import org.openstreetmap.josm.gui.HelpAwareOptionPane;
    4549import org.openstreetmap.josm.gui.download.DownloadDialog;
    4650import org.openstreetmap.josm.gui.util.GuiHelper;
     51import org.openstreetmap.josm.gui.widgets.AbstractTextComponentValidator;
    4752import org.openstreetmap.josm.gui.widgets.HistoryComboBox;
    4853import org.openstreetmap.josm.gui.widgets.JosmTextArea;
    4954import org.openstreetmap.josm.io.OverpassDownloadReader;
     
    153158            final String tooltip = tr("Builds an Overpass query using the Overpass Turbo query wizard");
    154159            overpassWizard = new HistoryComboBox();
    155160            overpassWizard.setToolTipText(tooltip);
    156             overpassWizard.getEditor().getEditorComponent().addFocusListener(disableActionsFocusListener);
     161            JTextComponent editorComponent = (JTextComponent) overpassWizard.getEditor().getEditorComponent();
     162            editorComponent.addFocusListener(disableActionsFocusListener);
     163            editorComponent.getDocument().addDocumentListener(new AbstractTextComponentValidator(editorComponent) {
     164
     165                @Override
     166                public void validate() {
     167                    if (!isValid()) {
     168                        feedbackInvalid(tr("Invalid Overpass Turbo query"));
     169                    } else {
     170                        feedbackValid(tooltip);
     171                    }
     172                }
     173
     174                @Override
     175                public boolean isValid() {
     176                    // SOLUTION 1: terribly slow
     177                    //return OverpassTurboQueryWizard.getInstance().testQuery(overpassWizard.getText());
     178
     179                    // SOLUTION 2: fast but incorrect
     180                    try {
     181                        SearchSetting ss = new SearchSetting();
     182                        ss.text = overpassWizard.getText();
     183                        SearchCompiler.compile(ss);
     184                        return true;
     185                    } catch (ParseError e) {
     186                        return false;
     187                    }
     188                }
     189            });
    157190            final JButton buildQuery = new JButton(tr("Build query"));
    158191            buildQuery.addActionListener(new AbstractAction() {
    159192                @Override
  • src/org/openstreetmap/josm/tools/OverpassTurboQueryWizard.java

     
    2727    /**
    2828     * An exception to indicate a failed parse.
    2929     */
    30     public static class ParseException extends RuntimeException {
     30    public static class ParseException extends Exception {
     31
     32        /**
     33         * Constructs a new {@code ParseException}.
     34         * @param message the error message
     35         */
     36        public ParseException(String message) {
     37            super(message);
     38        }
    3139    }
    3240
    3341    /**
     
    4452
    4553    private OverpassTurboQueryWizard() {
    4654        // overpass-turbo is MIT Licensed
    47 
    4855        try (final Reader reader = new InputStreamReader(
    4956                getClass().getResourceAsStream("/data/overpass-turbo-ffs.js"), StandardCharsets.UTF_8)) {
    5057            engine.eval("var console = {log: function(){}};");
     
    5562        }
    5663    }
    5764
     65    private Object doConstructQuery(String search) {
     66        try {
     67            return ((Invocable) engine).invokeFunction("construct_query", search);
     68        } catch (NoSuchMethodException e) {
     69            throw new IllegalStateException(e);
     70        } catch (ScriptException e) {
     71            throw new RuntimeException("Failed to execute OverpassTurboQueryWizard", e);
     72        }
     73    }
     74
    5875    /**
    5976     * Builds an Overpass QL from a {@link org.openstreetmap.josm.actions.search.SearchAction} like query.
    6077     * @param search the {@link org.openstreetmap.josm.actions.search.SearchAction} like query
     
    6279     * @throws ParseException when the parsing fails
    6380     */
    6481    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);
     82        final Object result = doConstructQuery(search);
     83        if (Boolean.FALSE.equals(result)) {
     84            throw new ParseException("Cannot parse: " + search);
    7985        }
     86        String query = (String) result;
     87        query = Pattern.compile("^.*\\[out:json\\]", Pattern.DOTALL).matcher(query).replaceFirst("");
     88        query = Pattern.compile("^out.*", Pattern.MULTILINE).matcher(query).replaceAll("out meta;");
     89        query = query.replace("({{bbox}})", "");
     90        return query;
    8091    }
    8192
     93    /**
     94     * Test validity of an Overpass Turbo query.
     95     * @param search the {@link org.openstreetmap.josm.actions.search.SearchAction} like query
     96     * @return {@code true} if the query is valid
     97     */
     98    public boolean testQuery(String search) {
     99        return !Boolean.FALSE.equals(doConstructQuery(search));
     100    }
    82101}
  • test/unit/org/openstreetmap/josm/tools/OverpassTurboQueryWizardTest.java

     
    66import org.junit.BeforeClass;
    77import org.junit.Test;
    88import org.openstreetmap.josm.JOSMFixture;
     9import org.openstreetmap.josm.tools.OverpassTurboQueryWizard.ParseException;
    910
    1011/**
    1112 * Unit tests of {@link OverpassTurboQueryWizard} class.
     
    2324
    2425    /**
    2526     * Test key=value.
     27     * @throws ParseException in case of invalid syntax
    2628     */
    2729    @Test
    28     public void testKeyValue() {
     30    public void testKeyValue() throws ParseException {
    2931        final String query = OverpassTurboQueryWizard.getInstance().constructQuery("amenity=drinking_water");
    3032        assertEquals("" +
    3133                "[timeout:25];\n" +
     
    4446
    4547    /**
    4648     * Test erroneous value.
     49     * @throws ParseException in case of invalid syntax
    4750     */
    48     @Test(expected = OverpassTurboQueryWizard.ParseException.class)
    49     public void testErroneous() {
     51    @Test(expected = ParseException.class)
     52    public void testErroneous() throws ParseException {
    5053        OverpassTurboQueryWizard.getInstance().constructQuery("foo");
    5154    }
    5255}

Attachments (0)

Change History (1)

comment:1 Changed 3 years ago by Don-vip

Description: modified (diff)

Modify Ticket

Change Properties
Set your email in Preferences
Action
as new The owner will remain team.
as The resolution will be set.
to The owner will be changed from team to the specified user.
The owner will change to Don-vip
as duplicate The resolution will be set to duplicate.The specified ticket will be cross-referenced with this ticket
The owner will be changed from team to anonymous.

Add Comment


E-mail address and name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.