source: josm/trunk/src/org/openstreetmap/josm/gui/download/OverpassDownloadSource.java@ 12880

Last change on this file since 12880 was 12880, checked in by simon04, 7 years ago

see #15057, see #15264 - Rename OverpassQueryList to UserQueryList

This allows to use it also in the wikipedia plugin without name confusion.

File size: 14.8 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.download;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.BorderLayout;
7import java.awt.Dimension;
8import java.awt.GridBagLayout;
9import java.awt.event.ActionEvent;
10import java.awt.event.FocusEvent;
11import java.awt.event.FocusListener;
12import java.util.Collection;
13import java.util.concurrent.Future;
14import java.util.function.Consumer;
15
16import javax.swing.AbstractAction;
17import javax.swing.Action;
18import javax.swing.Icon;
19import javax.swing.JButton;
20import javax.swing.JLabel;
21import javax.swing.JOptionPane;
22import javax.swing.JPanel;
23import javax.swing.JScrollPane;
24import javax.swing.event.ListSelectionEvent;
25import javax.swing.event.ListSelectionListener;
26import javax.swing.plaf.basic.BasicArrowButton;
27
28import org.openstreetmap.josm.actions.downloadtasks.DownloadOsmTask;
29import org.openstreetmap.josm.actions.downloadtasks.PostDownloadHandler;
30import org.openstreetmap.josm.data.Bounds;
31import org.openstreetmap.josm.data.preferences.AbstractProperty;
32import org.openstreetmap.josm.data.preferences.BooleanProperty;
33import org.openstreetmap.josm.data.preferences.IntegerProperty;
34import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil;
35import org.openstreetmap.josm.gui.MainApplication;
36import org.openstreetmap.josm.gui.download.DownloadSourceSizingPolicy.AdjustableDownloadSizePolicy;
37import org.openstreetmap.josm.gui.util.GuiHelper;
38import org.openstreetmap.josm.gui.widgets.JosmTextArea;
39import org.openstreetmap.josm.io.OverpassDownloadReader;
40import org.openstreetmap.josm.tools.GBC;
41import org.openstreetmap.josm.tools.ImageProvider;
42
43/**
44 * Class defines the way data is fetched from Overpass API.
45 * @since 12652
46 */
47public class OverpassDownloadSource implements DownloadSource<OverpassDownloadSource.OverpassDownloadData> {
48
49 @Override
50 public AbstractDownloadSourcePanel<OverpassDownloadData> createPanel() {
51 return new OverpassDownloadSourcePanel(this);
52 }
53
54 @Override
55 public void doDownload(OverpassDownloadData data, DownloadSettings settings) {
56 /*
57 * In order to support queries generated by the Overpass Turbo Query Wizard tool
58 * which do not require the area to be specified.
59 */
60 Bounds area = settings.getDownloadBounds().orElse(new Bounds(0, 0, 0, 0));
61 DownloadOsmTask task = new DownloadOsmTask();
62 task.setZoomAfterDownload(settings.zoomToData());
63 Future<?> future = task.download(
64 new OverpassDownloadReader(area, OverpassDownloadReader.OVERPASS_SERVER.get(), data.getQuery()),
65 settings.asNewLayer(), area, null);
66 MainApplication.worker.submit(new PostDownloadHandler(task, future, data.getErrorReporter()));
67 }
68
69 @Override
70 public String getLabel() {
71 return tr("Download from Overpass API");
72 }
73
74 @Override
75 public boolean onlyExpert() {
76 return true;
77 }
78
79 /**
80 * The GUI representation of the Overpass download source.
81 * @since 12652
82 */
83 public static class OverpassDownloadSourcePanel extends AbstractDownloadSourcePanel<OverpassDownloadData> {
84
85 private static final String SIMPLE_NAME = "overpassdownloadpanel";
86 private static final AbstractProperty<Integer> PANEL_SIZE_PROPERTY =
87 new IntegerProperty(TAB_SPLIT_NAMESPACE + SIMPLE_NAME, 150).cached();
88 private static final BooleanProperty OVERPASS_QUERY_LIST_OPENED =
89 new BooleanProperty("download.overpass.query-list.opened", false);
90 private static final String ACTION_IMG_SUBDIR = "dialogs";
91
92 private final JosmTextArea overpassQuery;
93 private final UserQueryList overpassQueryList;
94
95 /**
96 * Create a new {@link OverpassDownloadSourcePanel}
97 * @param ds The download source to create the panel for
98 */
99 public OverpassDownloadSourcePanel(OverpassDownloadSource ds) {
100 super(ds);
101 setLayout(new BorderLayout());
102
103 String tooltip = tr("Build an Overpass query using the Overpass Turbo Query Wizard tool");
104 Action queryWizardAction = new AbstractAction() {
105 @Override
106 public void actionPerformed(ActionEvent e) {
107 new OverpassQueryWizardDialog(OverpassDownloadSourcePanel.this).showDialog();
108 }
109 };
110
111 JButton openQueryWizard = new JButton(tr("Query Wizard"));
112 openQueryWizard.setToolTipText(tooltip);
113 openQueryWizard.addActionListener(queryWizardAction);
114
115 // CHECKSTYLE.OFF: LineLength
116 this.overpassQuery = new JosmTextArea(
117 "/*\n" +
118 tr("Place your Overpass query below or generate one using the Overpass Turbo Query Wizard")
119 + "\n*/",
120 8, 80);
121 // CHECKSTYLE.ON: LineLength
122 this.overpassQuery.setFont(GuiHelper.getMonospacedFont(overpassQuery));
123 this.overpassQuery.addFocusListener(new FocusListener() {
124 @Override
125 public void focusGained(FocusEvent e) {
126 overpassQuery.selectAll();
127 }
128
129 @Override
130 public void focusLost(FocusEvent e) {
131 // ignored
132 }
133 });
134
135
136 this.overpassQueryList = new UserQueryList(this, this.overpassQuery, "download.overpass.query");
137 this.overpassQueryList.setPreferredSize(new Dimension(350, 300));
138
139 EditSnippetAction edit = new EditSnippetAction();
140 RemoveSnippetAction remove = new RemoveSnippetAction();
141 this.overpassQueryList.addSelectionListener(edit);
142 this.overpassQueryList.addSelectionListener(remove);
143
144 JPanel listPanel = new JPanel(new GridBagLayout());
145 listPanel.add(new JLabel(tr("Your saved queries:")), GBC.eol().insets(2).anchor(GBC.CENTER));
146 listPanel.add(this.overpassQueryList, GBC.eol().fill(GBC.BOTH));
147 listPanel.add(new JButton(new AddSnippetAction()), GBC.std().fill(GBC.HORIZONTAL));
148 listPanel.add(new JButton(edit), GBC.std().fill(GBC.HORIZONTAL));
149 listPanel.add(new JButton(remove), GBC.std().fill(GBC.HORIZONTAL));
150 listPanel.setVisible(OVERPASS_QUERY_LIST_OPENED.get());
151
152 JScrollPane scrollPane = new JScrollPane(overpassQuery);
153 BasicArrowButton arrowButton = new BasicArrowButton(listPanel.isVisible()
154 ? BasicArrowButton.EAST
155 : BasicArrowButton.WEST);
156 arrowButton.setToolTipText(tr("Show/hide Overpass snippet list"));
157 arrowButton.addActionListener(e -> {
158 if (listPanel.isVisible()) {
159 listPanel.setVisible(false);
160 arrowButton.setDirection(BasicArrowButton.WEST);
161 OVERPASS_QUERY_LIST_OPENED.put(Boolean.FALSE);
162 } else {
163 listPanel.setVisible(true);
164 arrowButton.setDirection(BasicArrowButton.EAST);
165 OVERPASS_QUERY_LIST_OPENED.put(Boolean.TRUE);
166 }
167 });
168
169 JPanel innerPanel = new JPanel(new BorderLayout());
170 innerPanel.add(scrollPane, BorderLayout.CENTER);
171 innerPanel.add(arrowButton, BorderLayout.EAST);
172
173 JPanel leftPanel = new JPanel(new GridBagLayout());
174 leftPanel.add(new JLabel(tr("Overpass query:")), GBC.eol().insets(5, 1, 5, 1).anchor(GBC.NORTHWEST));
175 leftPanel.add(new JLabel(), GBC.eol().fill(GBC.VERTICAL));
176 leftPanel.add(openQueryWizard, GBC.eol().anchor(GBC.CENTER));
177 leftPanel.add(new JLabel(), GBC.eol().fill(GBC.VERTICAL));
178
179 add(leftPanel, BorderLayout.WEST);
180 add(innerPanel, BorderLayout.CENTER);
181 add(listPanel, BorderLayout.EAST);
182
183 setMinimumSize(new Dimension(450, 240));
184 }
185
186 @Override
187 public OverpassDownloadData getData() {
188 String query = overpassQuery.getText();
189 /*
190 * A callback that is passed to PostDownloadReporter that is called once the download task
191 * has finished. According to the number of errors happened, their type we decide whether we
192 * want to save the last query in OverpassQueryList.
193 */
194 Consumer<Collection<Object>> errorReporter = errors -> {
195
196 boolean onlyNoDataError = errors.size() == 1 &&
197 errors.contains("No data found in this area.");
198
199 if (errors.isEmpty() || onlyNoDataError) {
200 overpassQueryList.saveHistoricItem(query);
201 }
202 };
203
204 return new OverpassDownloadData(query, errorReporter);
205 }
206
207 @Override
208 public void rememberSettings() {
209 // nothing
210 }
211
212 @Override
213 public void restoreSettings() {
214 // nothing
215 }
216
217 @Override
218 public boolean checkDownload(DownloadSettings settings) {
219 String query = getData().getQuery();
220
221 /*
222 * Absence of the selected area can be justified only if the overpass query
223 * is not restricted to bbox.
224 */
225 if (!settings.getDownloadBounds().isPresent() && query.contains("{{bbox}}")) {
226 JOptionPane.showMessageDialog(
227 this.getParent(),
228 tr("Please select a download area first."),
229 tr("Error"),
230 JOptionPane.ERROR_MESSAGE
231 );
232 return false;
233 }
234
235 /*
236 * Check for an empty query. User might want to download everything, if so validation is passed,
237 * otherwise return false.
238 */
239 if (query.matches("(/\\*(\\*[^/]|[^\\*/])*\\*/|\\s)*")) {
240 boolean doFix = ConditionalOptionPaneUtil.showConfirmationDialog(
241 "download.overpass.fix.emptytoall",
242 this,
243 tr("You entered an empty query. Do you want to download all data in this area instead?"),
244 tr("Download all data?"),
245 JOptionPane.YES_NO_OPTION,
246 JOptionPane.QUESTION_MESSAGE,
247 JOptionPane.YES_OPTION);
248 if (doFix) {
249 String repairedQuery = "[out:xml]; \n"
250 + query + "\n"
251 + "(\n"
252 + " node({{bbox}});\n"
253 + "<;\n"
254 + ");\n"
255 + "(._;>;);"
256 + "out meta;";
257 this.overpassQuery.setText(repairedQuery);
258 } else {
259 return false;
260 }
261 }
262
263 return true;
264 }
265
266 /**
267 * Sets query to the query text field.
268 * @param query The query to set.
269 */
270 public void setOverpassQuery(String query) {
271 this.overpassQuery.setText(query);
272 }
273
274 @Override
275 public Icon getIcon() {
276 return ImageProvider.get("download-overpass");
277 }
278
279 @Override
280 public String getSimpleName() {
281 return SIMPLE_NAME;
282 }
283
284 @Override
285 public DownloadSourceSizingPolicy getSizingPolicy() {
286 return new AdjustableDownloadSizePolicy(PANEL_SIZE_PROPERTY);
287 }
288
289 /**
290 * Action that delegates snippet creation to {@link UserQueryList#createNewItem()}.
291 */
292 private class AddSnippetAction extends AbstractAction {
293
294 /**
295 * Constructs a new {@code AddSnippetAction}.
296 */
297 AddSnippetAction() {
298 super();
299 putValue(SMALL_ICON, ImageProvider.get(ACTION_IMG_SUBDIR, "add"));
300 putValue(SHORT_DESCRIPTION, tr("Add new snippet"));
301 }
302
303 @Override
304 public void actionPerformed(ActionEvent e) {
305 overpassQueryList.createNewItem();
306 }
307 }
308
309 /**
310 * Action that delegates snippet removal to {@link UserQueryList#removeSelectedItem()}.
311 */
312 private class RemoveSnippetAction extends AbstractAction implements ListSelectionListener {
313
314 /**
315 * Constructs a new {@code RemoveSnippetAction}.
316 */
317 RemoveSnippetAction() {
318 super();
319 putValue(SMALL_ICON, ImageProvider.get(ACTION_IMG_SUBDIR, "delete"));
320 putValue(SHORT_DESCRIPTION, tr("Delete selected snippet"));
321 checkEnabled();
322 }
323
324 @Override
325 public void actionPerformed(ActionEvent e) {
326 overpassQueryList.removeSelectedItem();
327 }
328
329 /**
330 * Disables the action if no items are selected.
331 */
332 void checkEnabled() {
333 setEnabled(overpassQueryList.getSelectedItem().isPresent());
334 }
335
336 @Override
337 public void valueChanged(ListSelectionEvent e) {
338 checkEnabled();
339 }
340 }
341
342 /**
343 * Action that delegates snippet edit to {@link UserQueryList#editSelectedItem()}.
344 */
345 private class EditSnippetAction extends AbstractAction implements ListSelectionListener {
346
347 /**
348 * Constructs a new {@code EditSnippetAction}.
349 */
350 EditSnippetAction() {
351 super();
352 putValue(SMALL_ICON, ImageProvider.get(ACTION_IMG_SUBDIR, "edit"));
353 putValue(SHORT_DESCRIPTION, tr("Edit selected snippet"));
354 checkEnabled();
355 }
356
357 @Override
358 public void actionPerformed(ActionEvent e) {
359 overpassQueryList.editSelectedItem();
360 }
361
362 /**
363 * Disables the action if no items are selected.
364 */
365 void checkEnabled() {
366 setEnabled(overpassQueryList.getSelectedItem().isPresent());
367 }
368
369 @Override
370 public void valueChanged(ListSelectionEvent e) {
371 checkEnabled();
372 }
373 }
374 }
375
376 /**
377 * Encapsulates data that is required to preform download from Overpass API.
378 */
379 static class OverpassDownloadData {
380 private final String query;
381 private final Consumer<Collection<Object>> errorReporter;
382
383 OverpassDownloadData(String query, Consumer<Collection<Object>> errorReporter) {
384 this.query = query;
385 this.errorReporter = errorReporter;
386 }
387
388 String getQuery() {
389 return this.query;
390 }
391
392 Consumer<Collection<Object>> getErrorReporter() {
393 return this.errorReporter;
394 }
395 }
396}
Note: See TracBrowser for help on using the repository browser.