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

Last change on this file since 13130 was 13130, checked in by Don-vip, 6 years ago

fix #15572 - use ImageProvider attach API for all JOSM actions to ensure proper icon size everywhere

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