Ticket #11428: 0001-Add-Overpass-download-dialog-to-core-provide-Overpas.patch
| File 0001-Add-Overpass-download-dialog-to-core-provide-Overpas.patch, 17.6 KB (added by , 10 years ago) | 
|---|
- 
      new file 2001From 420b0f011976a6daa6065d79b16f40cdbf7324e4 Mon Sep 17 00:00:00 2001 From: Simon Legner <Simon.Legner@gmail.com> Date: Thu, 16 Apr 2015 18:15:53 +0200 Subject: [PATCH] Add Overpass download dialog to core, provide Overpass Turbo wizard --- images/download-overpass.png | Bin 0 -> 1516 bytes .../josm/actions/OverpassDownloadAction.java | 224 +++++++++++++++++++++ .../josm/actions/OverpassTurboQueryWizard.java | 91 +++++++++ src/org/openstreetmap/josm/gui/MainMenu.java | 4 + 4 files changed, 319 insertions(+) create mode 100644 images/download-overpass.png create mode 100644 src/org/openstreetmap/josm/actions/OverpassDownloadAction.java create mode 100644 src/org/openstreetmap/josm/actions/OverpassTurboQueryWizard.java diff --git a/images/download-overpass.png b/images/download-overpass.png new file mode 100644 index 0000000000000000000000000000000000000000..179567687ba96428c80763cd34e115454fba04f5 GIT binary patch literal 1516 zcmV<I1rz#-P)<h;3K|Lk000e1NJLTq000;O000;W1^@s6;CDUv00006VoOIv0RI60 z0RN!9r;`8x010qNS#tmY3ljhU3ljkVnw%H_000McNliru-vki~7AEnVC!7EP1$jwC zK~zY`m6l&@6xS8Te|L8F|IVy;*BkG8Sv$7Z4#wDP92197CuyL6LR(d7<5r3)UQk3; z5tK@46jf@=dlf3Rl3)7J@K&i+ksn$mD9}(y06`#XWQ>iSVy|O^|1Mtd&d$v4+&p-3 za6_VS-_DuQ`R={veBXBkU%kA51;~4e7iEhEyat>Wh*61C_`A4eeOUqJD~cz9I$#Ho zx|eheSrUk}K%52sD^UJ0BwK?I1JF>*KMLFdrhp=_7ijxO;8qnlRXbTDkpG+br1&rZ zJ|F^I0Xl#(PyyZt)`7iC7mbU+0`L*=%8T;RP9AbS^*+?U0ngXsXMo3n_FCMz|HIS^ z07J6%?2v4o`o2ti9tJ=R!Vl)*U=TWM&E5|9f#>dR0uV#8rE5sG8bh-6&krnge{lTx zaX-R&&$7+_wbi_`_k)Z4r)eAEt{tqbtfK20E|-gig+-Fd1e23fWHN2!p1Z(O&p4fL zz2Jn)@k=he-#mKss0pCn4`5fzhqK+;p25Lq6e%UScTTa-C~;0ocK`ec*RPL}&Gs-q zKaZ-axZQ4w#UjG{V}7k3pzC0h;yIP-y)kj)5HS3OCVM^JeVI(VuTrrQLQs9IgY{&R zY}sUSagju#fy<XKW7{@UQ&WV)VJyp{v3?u7tKVi+T%ftF$?s9!`yMz&4o+8HU1am- zT3T9|@CS&DjL;a5(|G<oPfkzc^?I3`o5L^+gb);qMY6`T%(!lVUt-y2cWoMdn*hg- z9aAcHWm_Py!I_e=G1^i}jNknh^~4EeG>WXRr}3ZvVuw@V%)d{wuylv<Km8Hy?O6)0 zRZ1V6C7)kw1`<GG6CfCLHHSi)ttbKn1pFI7mSy5<Xh6ndOsCUKr_&INqHW)fq9_O< zkli~eX%U1Y@rWwA7IOAKzds6;Hvw$v>P)3lj&0jGj`ILYDj)<%364W5nc6&|8I58^ zBS=MH4h#^9#;WO5V`fVagp{3$hNRE7EmoG7Hr37J@lwsrA{G{C&gE#%<zO|>^5`i3 zx&X^-3)r@e&4%6;^96KW_o^;;=awFLz3#qveZ5*Pmnj-Wa<jLnR;v^W1zf#ZDlfl` zSXx5l^UQtk02kKQ_@DL>#o!uEO=*_(906OWE)=1oy+dtoZfXT?e||-FWsk0hDVB<y zKX;Dx^>t34IYlg9$Ly!G%)I(bW;LBl=`^NkqG=j?x_gP19$_WF!p-{2c;_N0wn8G2 zKvmU&Ej^G@rb3|*h4nRd_3a|8hcU`!OtZ|Hk&{@-IPcwE0;S5t<QT!Y!ulv!6^l}( zz+~(SkJ)|b{wR@141s9c$^(IL9EV-mex7{d8~A-8zVodE_&gyFWgjPCOJ4fv&)73C zNK11&PxpV9%9@`a{qQHuJ$((2@bOaS2<>ehNGXxE+M8PRi&u~QK9xwk=28TX<B*%l zk=m9*-kf4zZkBfjpF-Dl#>Q@t$+U6%_8oTa>>?*`GInr;)F(S>80p_2tmDXu@yX%0 z{&M2T{bJYK)BnV^upVS??l!@YhIA^tzPQM6G>Q`l;P-jy-`&TDSFV!n?cw9=*GUfF zrR2Ly{!kNr-B~VOxysCRZgF~Q@-T4nezDU5%0`)ZJce6Uu`H95?HK}Y6_49Z#kQH5 z$<dwd-tg2n*D+J8G`zW&REI{{Gyw=lqVbx<f3Bv9#>T2;S(j{E45+F~sklrqq%mO> zkd8yyFi_oY?kwMBJa>!qa2{`4mHFRjjGr0X!b#IKuL9Hm?+1_6{5T4DfLLt^qJROc z*8UfD{T^-4UmFh3o-Drm@vl~{0S-_F?3!1v*8KcazQ(gfJm|xaZ1H~`Yy1rGfhYUb Sc(Ew}0000<MNUMnLSTX<pybT}
- 
      new file src/org/openstreetmap/josm/actions/OverpassDownloadAction.javaliteral 0 HcmV?d00001 diff --git a/src/org/openstreetmap/josm/actions/OverpassDownloadAction.java b/src/org/openstreetmap/josm/actions/OverpassDownloadAction.java new file mode 100644 index 0000000..af63d7d - + 1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.actions; 3 4 import static org.openstreetmap.josm.gui.help.HelpUtil.ht; 5 import static org.openstreetmap.josm.tools.I18n.tr; 6 7 import java.awt.Component; 8 import java.awt.GridBagConstraints; 9 import java.awt.event.ActionEvent; 10 import java.io.UnsupportedEncodingException; 11 import java.net.URLEncoder; 12 import java.util.ArrayList; 13 import java.util.Collections; 14 import java.util.concurrent.Future; 15 16 import javax.swing.*; 17 18 import org.openstreetmap.josm.Main; 19 import org.openstreetmap.josm.actions.downloadtasks.DownloadOsmTask; 20 import org.openstreetmap.josm.actions.downloadtasks.PostDownloadHandler; 21 import org.openstreetmap.josm.data.Bounds; 22 import org.openstreetmap.josm.data.DataSource; 23 import org.openstreetmap.josm.data.osm.DataSet; 24 import org.openstreetmap.josm.gui.HelpAwareOptionPane; 25 import org.openstreetmap.josm.gui.download.DownloadDialog; 26 import org.openstreetmap.josm.gui.progress.ProgressMonitor; 27 import org.openstreetmap.josm.gui.widgets.HistoryComboBox; 28 import org.openstreetmap.josm.io.BoundingBoxDownloader; 29 import org.openstreetmap.josm.io.OsmTransferException; 30 import org.openstreetmap.josm.tools.GBC; 31 import org.openstreetmap.josm.tools.Utils; 32 33 public class OverpassDownloadAction extends JosmAction { 34 35 public OverpassDownloadAction() { 36 super(tr("Download from Overpass API ..."), "download-overpass", tr("Download map data from Overpass API server."), 37 null, true, "overpassdownload/download", true); 38 putValue("help", ht("/Action/OverpassDownload")); 39 } 40 41 @Override 42 public void actionPerformed(ActionEvent e) { 43 OverpassDownloadDialog dialog = OverpassDownloadDialog.getInstance(); 44 dialog.restoreSettings(); 45 dialog.setVisible(true); 46 if (!dialog.isCanceled()) { 47 dialog.rememberSettings(); 48 Bounds area = dialog.getSelectedDownloadArea(); 49 DownloadOsmTask task = new DownloadOsmTask(); 50 Future<?> future = task.download( 51 new OverpassDownloadReader(area, dialog.getOverpassQuery()), 52 dialog.isNewLayerRequired(), area, null); 53 Main.worker.submit(new PostDownloadHandler(task, future)); 54 } 55 } 56 57 static class OverpassDownloadDialog extends DownloadDialog { 58 59 protected HistoryComboBox overpassWizard; 60 protected JTextArea overpassQuery; 61 private static OverpassDownloadDialog instance; 62 static final String OVERPASS_WIZARD_HISTORY_KEY = "download.overpass.wizard"; 63 64 private OverpassDownloadDialog(Component parent) { 65 super(parent); 66 cbDownloadOsmData.setEnabled(false); 67 cbDownloadOsmData.setSelected(false); 68 cbDownloadGpxData.setVisible(false); 69 cbDownloadNotes.setVisible(false); 70 cbStartup.setVisible(false); 71 } 72 73 static public OverpassDownloadDialog getInstance() { 74 if (instance == null) { 75 instance = new OverpassDownloadDialog(Main.parent); 76 } 77 return instance; 78 } 79 80 @Override 81 protected void buildMainPanelAboveDownloadSelections(JPanel pnl) { 82 83 pnl.add(new JLabel(), GBC.eol()); // needed for the invisible checkboxes cbDownloadGpxData, cbDownloadNotes 84 85 final String tooltip = tr("Builds an Overpass query using the Overpass Turbo query wizard"); 86 overpassWizard = new HistoryComboBox(); 87 overpassWizard.setToolTipText(tooltip); 88 final JButton buildQuery = new JButton(tr("Build query")); 89 buildQuery.addActionListener(new AbstractAction() { 90 @Override 91 public void actionPerformed(ActionEvent e) { 92 final String overpassWizardText = overpassWizard.getText(); 93 try { 94 overpassQuery.setText(OverpassTurboQueryWizard.getInstance().constructQuery(overpassWizardText)); 95 } catch (OverpassTurboQueryWizard.ParseException ex) { 96 HelpAwareOptionPane.showOptionDialog( 97 Main.parent, 98 tr("<html>The Overpass wizard could not parse the following query:" 99 + Utils.joinAsHtmlUnorderedList(Collections.singleton(overpassWizardText))), 100 tr("Parse error"), 101 JOptionPane.ERROR_MESSAGE, 102 null 103 ); 104 } 105 } 106 }); 107 buildQuery.setToolTipText(tooltip); 108 pnl.add(buildQuery, GBC.std().insets(5, 5, 5, 5)); 109 pnl.add(overpassWizard, GBC.eol().fill()); 110 111 overpassQuery = new JTextArea("[timeout:15];", 8, 80); 112 JScrollPane scrollPane = new JScrollPane(overpassQuery); 113 pnl.add(new JLabel(tr("Overpass query: ")), GBC.std().insets(5, 5, 5, 5)); 114 GridBagConstraints gbc = GBC.eol().fill(); 115 gbc.ipady = 200; 116 pnl.add(scrollPane, gbc); 117 } 118 119 public String getOverpassQuery() { 120 return overpassQuery.getText(); 121 } 122 123 @Override 124 public void restoreSettings() { 125 super.restoreSettings(); 126 overpassWizard.setPossibleItems( 127 Main.pref.getCollection(OVERPASS_WIZARD_HISTORY_KEY, new ArrayList<String>())); 128 } 129 130 @Override 131 public void rememberSettings() { 132 super.rememberSettings(); 133 overpassWizard.addCurrentItemToHistory(); 134 Main.pref.putCollection(OVERPASS_WIZARD_HISTORY_KEY, overpassWizard.getHistory()); 135 } 136 137 } 138 139 static class OverpassDownloadReader extends BoundingBoxDownloader { 140 141 final String overpassQuery; 142 143 public OverpassDownloadReader(Bounds downloadArea, String overpassQuery) { 144 super(downloadArea); 145 this.overpassQuery = overpassQuery.trim(); 146 } 147 148 @Override 149 protected String getBaseUrl() { 150 return "https://overpass-api.de/api/"; 151 } 152 153 @Override 154 protected String getRequestForBbox(double lon1, double lat1, double lon2, double lat2) { 155 if (overpassQuery.isEmpty()) 156 return super.getRequestForBbox(lon1, lat1, lon2, lat2); 157 else { 158 String realQuery = completeOverpassQuery(overpassQuery); 159 try { 160 return "interpreter?data=" + URLEncoder.encode(realQuery, "UTF-8") + "&bbox=" + lon1 + "," + lat1 + "," + lon2 + "," + lat2; 161 } catch (UnsupportedEncodingException e) { 162 throw new IllegalStateException(); 163 } 164 } 165 } 166 167 private String completeOverpassQuery(String query) { 168 int firstColon = query.indexOf(";"); 169 if (firstColon == -1) { 170 return "[bbox];" + query; 171 } 172 int bboxPos = query.indexOf("[bbox"); 173 if (bboxPos > -1 && bboxPos < firstColon) { 174 return query; 175 } 176 177 int bracketCount = 0; 178 int pos = 0; 179 for (; pos < firstColon; ++pos) { 180 if (query.charAt(pos) == '[') 181 ++bracketCount; 182 else if (query.charAt(pos) == '[') 183 --bracketCount; 184 else if (bracketCount == 0) { 185 if (!Character.isWhitespace(query.charAt(pos))) 186 break; 187 } 188 } 189 190 if (pos < firstColon) { 191 // We start with a statement, not with declarations 192 return "[bbox];" + query; 193 } 194 195 // We start with declarations. Add just one more declaration in this case. 196 return "[bbox]" + query; 197 } 198 199 @Override 200 public DataSet parseOsm(ProgressMonitor progressMonitor) throws OsmTransferException { 201 202 DataSet ds = super.parseOsm(progressMonitor); 203 204 // add bounds if necessary (note that Overpass API does not return bounds in the response XML) 205 if (ds != null && ds.dataSources.isEmpty()) { 206 if (crosses180th) { 207 Bounds bounds = new Bounds(lat1, lon1, lat2, 180.0); 208 DataSource src = new DataSource(bounds, getBaseUrl()); 209 ds.dataSources.add(src); 210 211 bounds = new Bounds(lat1, -180.0, lat2, lon2); 212 src = new DataSource(bounds, getBaseUrl()); 213 ds.dataSources.add(src); 214 } else { 215 Bounds bounds = new Bounds(lat1, lon1, lat2, lon2); 216 DataSource src = new DataSource(bounds, getBaseUrl()); 217 ds.dataSources.add(src); 218 } 219 } 220 221 return ds; 222 } 223 } 224 } 
- 
      new file src/org/openstreetmap/josm/actions/OverpassTurboQueryWizard.javadiff --git a/src/org/openstreetmap/josm/actions/OverpassTurboQueryWizard.java b/src/org/openstreetmap/josm/actions/OverpassTurboQueryWizard.java new file mode 100644 index 0000000..22b440e - + 1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.actions; 3 4 import org.openstreetmap.josm.io.CachedFile; 5 6 import javax.script.Invocable; 7 import javax.script.ScriptEngine; 8 import javax.script.ScriptEngineManager; 9 import javax.script.ScriptException; 10 import java.io.IOException; 11 import java.io.InputStreamReader; 12 import java.io.Reader; 13 import java.nio.charset.StandardCharsets; 14 import java.util.regex.Pattern; 15 16 /** 17 * Uses <a href="https://github.com/tyrasd/overpass-turbo/">Overpass Turbo</a> query wizard code 18 * to build an Overpass QL from a {@link org.openstreetmap.josm.actions.search.SearchAction} like query. 19 * 20 * Requires a JavaScript {@link ScriptEngine}. 21 */ 22 public 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 try { 47 engine.eval("var console = {log: function(){}};"); 48 final String baseUrl = "https://raw.githubusercontent.com/tyrasd/overpass-turbo/b0ef5ebbdd353c1bc7e45da527cab03498b4cbef/"; 49 // lodash is MIT Licensed 50 initEngine(baseUrl + "/libs/lodash/lodash-2.4.1.js"); 51 // overpass-turbo is MIT Licensed 52 initEngine(baseUrl + "/js/ffs.js"); 53 initEngine(baseUrl + "/js/ffs/free.js"); 54 initEngine(baseUrl + "/js/ffs/parser.js"); 55 engine.eval("var construct_query = turbo.ffs().construct_query;"); 56 } catch (ScriptException | IOException ex) { 57 throw new RuntimeException("Failed to initialize OverpassTurboQueryWizard", ex); 58 } 59 } 60 61 private void initEngine(String url) throws ScriptException, IOException { 62 try (Reader reader = new InputStreamReader(new CachedFile(url).getInputStream(), StandardCharsets.UTF_8)) { 63 engine.eval(reader); 64 } 65 } 66 67 /** 68 * Builds an Overpass QL from a {@link org.openstreetmap.josm.actions.search.SearchAction} like query. 69 * @param search the {@link org.openstreetmap.josm.actions.search.SearchAction} like query 70 * @return an Overpass QL query 71 * @throws ParseException when the parsing fails 72 */ 73 public String constructQuery(String search) throws ParseException { 74 try { 75 final Object result = ((Invocable) engine).invokeFunction("construct_query", search); 76 if (result == Boolean.FALSE) { 77 throw new ParseException(); 78 } 79 String query = (String) result; 80 query = Pattern.compile("^.*\\[out:json\\]", Pattern.DOTALL).matcher(query).replaceFirst(""); 81 query = Pattern.compile("^out.*", Pattern.MULTILINE).matcher(query).replaceAll("out meta;"); 82 query = query.replace("({{bbox}})", ""); 83 return query; 84 } catch (NoSuchMethodException e) { 85 throw new IllegalStateException(); 86 } catch (ScriptException e) { 87 throw new RuntimeException("Failed to execute OverpassTurboQueryWizard", e); 88 } 89 } 90 91 } 
- 
      src/org/openstreetmap/josm/gui/MainMenu.javadiff --git a/src/org/openstreetmap/josm/gui/MainMenu.java b/src/org/openstreetmap/josm/gui/MainMenu.java index 62cce67..06186b0 100644 a b import org.openstreetmap.josm.actions.OpenFileAction; 80 80 import org.openstreetmap.josm.actions.OpenLocationAction; 81 81 import org.openstreetmap.josm.actions.OrthogonalizeAction; 82 82 import org.openstreetmap.josm.actions.OrthogonalizeAction.Undo; 83 import org.openstreetmap.josm.actions.OverpassDownloadAction; 83 84 import org.openstreetmap.josm.actions.PasteAction; 84 85 import org.openstreetmap.josm.actions.PasteTagsAction; 85 86 import org.openstreetmap.josm.actions.PreferenceToggleAction; … … public class MainMenu extends JMenuBar { 165 166 public final GpxExportAction gpxExport = new GpxExportAction(); 166 167 /** File / Download from OSM... **/ 167 168 public final DownloadAction download = new DownloadAction(); 169 /** File / Download from Overpass API... **/ 170 public final OverpassDownloadAction overpassDownload = new OverpassDownloadAction(); 168 171 /** File / Download object... **/ 169 172 public final DownloadPrimitiveAction downloadPrimitive = new DownloadPrimitiveAction(); 170 173 /** File / Download notes in current view **/ … … public class MainMenu extends JMenuBar { 638 641 add(fileMenu, gpxExport, true); 639 642 fileMenu.addSeparator(); 640 643 add(fileMenu, download); 644 add(fileMenu, overpassDownload); 641 645 add(fileMenu, downloadPrimitive); 642 646 add(fileMenu, searchNotes); 643 647 add(fileMenu, downloadNotesInView); 

