source: josm/trunk/src/org/openstreetmap/josm/gui/download/OSMDownloadSource.java@ 16553

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

see #19334 - javadoc fixes + protected constructors for abstract classes

File size: 18.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.marktr;
5import static org.openstreetmap.josm.tools.I18n.tr;
6
7import java.awt.Color;
8import java.awt.Dimension;
9import java.awt.Font;
10import java.awt.GridBagLayout;
11import java.lang.reflect.InvocationTargetException;
12import java.util.ArrayList;
13import java.util.Collection;
14import java.util.Collections;
15import java.util.List;
16import java.util.concurrent.ExecutionException;
17import java.util.concurrent.Future;
18
19import javax.swing.Icon;
20import javax.swing.JCheckBox;
21import javax.swing.JLabel;
22import javax.swing.JOptionPane;
23import javax.swing.JPanel;
24import javax.swing.event.ChangeListener;
25
26import org.openstreetmap.josm.actions.downloadtasks.AbstractDownloadTask;
27import org.openstreetmap.josm.actions.downloadtasks.DownloadGpsTask;
28import org.openstreetmap.josm.actions.downloadtasks.DownloadNotesTask;
29import org.openstreetmap.josm.actions.downloadtasks.DownloadOsmTask;
30import org.openstreetmap.josm.actions.downloadtasks.DownloadParams;
31import org.openstreetmap.josm.actions.downloadtasks.PostDownloadHandler;
32import org.openstreetmap.josm.data.Bounds;
33import org.openstreetmap.josm.data.ProjectionBounds;
34import org.openstreetmap.josm.data.ViewportData;
35import org.openstreetmap.josm.data.gpx.GpxData;
36import org.openstreetmap.josm.data.osm.DataSet;
37import org.openstreetmap.josm.data.osm.NoteData;
38import org.openstreetmap.josm.data.preferences.BooleanProperty;
39import org.openstreetmap.josm.gui.MainApplication;
40import org.openstreetmap.josm.gui.MapFrame;
41import org.openstreetmap.josm.gui.util.GuiHelper;
42import org.openstreetmap.josm.spi.preferences.Config;
43import org.openstreetmap.josm.tools.GBC;
44import org.openstreetmap.josm.tools.ImageProvider;
45import org.openstreetmap.josm.tools.Logging;
46import org.openstreetmap.josm.tools.Pair;
47
48/**
49 * Class defines the way data is fetched from the OSM server.
50 * @since 12652
51 */
52public class OSMDownloadSource implements DownloadSource<List<IDownloadSourceType>> {
53 /**
54 * The simple name for the {@link OSMDownloadSourcePanel}
55 * @since 12706
56 */
57 public static final String SIMPLE_NAME = "osmdownloadpanel";
58
59 /** The possible methods to get data */
60 static final List<IDownloadSourceType> DOWNLOAD_SOURCES = new ArrayList<>();
61 static {
62 // Order is important (determines button order, and what gets zoomed to)
63 DOWNLOAD_SOURCES.add(new OsmDataDownloadType());
64 DOWNLOAD_SOURCES.add(new GpsDataDownloadType());
65 DOWNLOAD_SOURCES.add(new NotesDataDownloadType());
66 }
67
68 @Override
69 public AbstractDownloadSourcePanel<List<IDownloadSourceType>> createPanel(DownloadDialog dialog) {
70 return new OSMDownloadSourcePanel(this, dialog);
71 }
72
73 @Override
74 public void doDownload(List<IDownloadSourceType> data, DownloadSettings settings) {
75 Bounds bbox = settings.getDownloadBounds()
76 .orElseThrow(() -> new IllegalArgumentException("OSM downloads requires bounds"));
77 boolean zoom = settings.zoomToData();
78 boolean newLayer = settings.asNewLayer();
79 final List<Pair<AbstractDownloadTask<?>, Future<?>>> tasks = new ArrayList<>();
80 IDownloadSourceType zoomTask = zoom ? data.stream().findFirst().orElse(null) : null;
81 data.stream().filter(IDownloadSourceType::isEnabled).forEach(type -> {
82 try {
83 AbstractDownloadTask<?> task = type.getDownloadClass().getDeclaredConstructor().newInstance();
84 task.setZoomAfterDownload(type.equals(zoomTask));
85 Future<?> future = task.download(new DownloadParams().withNewLayer(newLayer), bbox, null);
86 MainApplication.worker.submit(new PostDownloadHandler(task, future));
87 if (zoom) {
88 tasks.add(new Pair<AbstractDownloadTask<?>, Future<?>>(task, future));
89 }
90 } catch (InstantiationException | IllegalAccessException | IllegalArgumentException
91 | InvocationTargetException | NoSuchMethodException | SecurityException e) {
92 Logging.error(e);
93 }
94 });
95
96 if (zoom && tasks.size() > 1) {
97 MainApplication.worker.submit(() -> {
98 ProjectionBounds bounds = null;
99 // Wait for completion of download jobs
100 for (Pair<AbstractDownloadTask<?>, Future<?>> p : tasks) {
101 try {
102 p.b.get();
103 ProjectionBounds b = p.a.getDownloadProjectionBounds();
104 if (bounds == null) {
105 bounds = b;
106 } else if (b != null) {
107 bounds.extend(b);
108 }
109 } catch (InterruptedException | ExecutionException ex) {
110 Logging.warn(ex);
111 }
112 }
113 MapFrame map = MainApplication.getMap();
114 // Zoom to the larger download bounds
115 if (map != null && bounds != null) {
116 final ProjectionBounds pb = bounds;
117 GuiHelper.runInEDTAndWait(() -> map.mapView.zoomTo(new ViewportData(pb)));
118 }
119 });
120 }
121 }
122
123 @Override
124 public String getLabel() {
125 return tr("Download from OSM");
126 }
127
128 @Override
129 public boolean onlyExpert() {
130 return false;
131 }
132
133 /**
134 * Returns the possible downloads that JOSM can make in the default Download screen.
135 * @return The possible downloads that JOSM can make in the default Download screen
136 * @since 16503
137 */
138 public static List<IDownloadSourceType> getDownloadTypes() {
139 return Collections.unmodifiableList(DOWNLOAD_SOURCES);
140 }
141
142 /**
143 * Get the instance of a data download type
144 *
145 * @param <T> The type to get
146 * @param typeClazz The class of the type
147 * @return The type instance
148 * @since 16503
149 */
150 public static <T extends IDownloadSourceType> T getDownloadType(Class<T> typeClazz) {
151 return DOWNLOAD_SOURCES.stream().filter(typeClazz::isInstance).map(typeClazz::cast).findFirst().orElse(null);
152 }
153
154 /**
155 * Removes a download source type.
156 * @param type The IDownloadSourceType object to remove
157 * @return {@code true} if this download types contained the specified object
158 * @since 16503
159 */
160 public static boolean removeDownloadType(IDownloadSourceType type) {
161 if (type instanceof OsmDataDownloadType || type instanceof GpsDataDownloadType || type instanceof NotesDataDownloadType) {
162 throw new IllegalArgumentException(type.getClass().getName());
163 }
164 return DOWNLOAD_SOURCES.remove(type);
165 }
166
167 /**
168 * Add a download type to the default JOSM download window
169 *
170 * @param type The initialized type to download
171 * @return {@code true} (as specified by {@link Collection#add}), but it also returns false if the class already has an instance in the list
172 * @since 16503
173 */
174 public static boolean addDownloadType(IDownloadSourceType type) {
175 if (type instanceof OsmDataDownloadType || type instanceof GpsDataDownloadType || type instanceof NotesDataDownloadType) {
176 throw new IllegalArgumentException(type.getClass().getName());
177 } else if (getDownloadType(type.getClass()) != null) {
178 return false;
179 }
180 return DOWNLOAD_SOURCES.add(type);
181 }
182
183 /**
184 * The GUI representation of the OSM download source.
185 * @since 12652
186 */
187 public static class OSMDownloadSourcePanel extends AbstractDownloadSourcePanel<List<IDownloadSourceType>> {
188 private final JLabel sizeCheck = new JLabel();
189
190 /** This is used to keep track of the components for download sources, and to dynamically update/remove them */
191 private final JPanel downloadSourcesPanel;
192
193 private final ChangeListener checkboxChangeListener;
194
195 /**
196 * Label used in front of data types available for download. Made public for reuse in other download dialogs.
197 * @since 16155
198 */
199 public static final String DATA_SOURCES_AND_TYPES = marktr("Data Sources and Types:");
200
201 /**
202 * Creates a new {@link OSMDownloadSourcePanel}.
203 * @param dialog the parent download dialog, as {@code DownloadDialog.getInstance()} might not be initialized yet
204 * @param ds The osm download source the panel is for.
205 * @since 12900
206 */
207 public OSMDownloadSourcePanel(OSMDownloadSource ds, DownloadDialog dialog) {
208 super(ds);
209 setLayout(new GridBagLayout());
210
211 // size check depends on selected data source
212 checkboxChangeListener = e ->
213 dialog.getSelectedDownloadArea().ifPresent(this::updateSizeCheck);
214
215 // adding the download tasks
216 add(new JLabel(tr(DATA_SOURCES_AND_TYPES)), GBC.std().insets(5, 5, 1, 5).anchor(GBC.CENTER));
217 Font labelFont = sizeCheck.getFont();
218 sizeCheck.setFont(labelFont.deriveFont(Font.PLAIN, labelFont.getSize()));
219
220 downloadSourcesPanel = new JPanel();
221 add(downloadSourcesPanel, GBC.eol().anchor(GBC.EAST));
222 updateSources();
223 add(sizeCheck, GBC.eol().anchor(GBC.EAST).insets(5, 5, 5, 2));
224
225 setMinimumSize(new Dimension(450, 115));
226 }
227
228 /**
229 * Update the source list for downloading data
230 */
231 protected void updateSources() {
232 downloadSourcesPanel.removeAll();
233 DOWNLOAD_SOURCES
234 .forEach(obj -> downloadSourcesPanel.add(obj.getCheckBox(checkboxChangeListener), GBC.std().insets(1, 5, 1, 5)));
235 }
236
237 @Override
238 public List<IDownloadSourceType> getData() {
239 return DOWNLOAD_SOURCES;
240 }
241
242 @Override
243 public void rememberSettings() {
244 DOWNLOAD_SOURCES.forEach(type -> type.getBooleanProperty().put(type.getCheckBox().isSelected()));
245 }
246
247 @Override
248 public void restoreSettings() {
249 updateSources();
250 DOWNLOAD_SOURCES.forEach(type -> type.getCheckBox().setSelected(type.isEnabled()));
251 }
252
253 @Override
254 public void setVisible(boolean aFlag) {
255 super.setVisible(aFlag);
256 updateSources();
257 }
258
259 @Override
260 public boolean checkDownload(DownloadSettings settings) {
261 /*
262 * It is mandatory to specify the area to download from OSM.
263 */
264 if (!settings.getDownloadBounds().isPresent()) {
265 JOptionPane.showMessageDialog(
266 this.getParent(),
267 tr("Please select a download area first."),
268 tr("Error"),
269 JOptionPane.ERROR_MESSAGE
270 );
271
272 return false;
273 }
274
275 /*
276 * Checks if the user selected the type of data to download. At least one the following
277 * must be chosen : raw osm data, gpx data, notes.
278 * If none of those are selected, then the corresponding dialog is shown to inform the user.
279 */
280 if (DOWNLOAD_SOURCES.stream().noneMatch(IDownloadSourceType::isEnabled)) {
281 JOptionPane.showMessageDialog(
282 this.getParent(),
283 tr("Please select at least one download source."),
284 tr("Error"),
285 JOptionPane.ERROR_MESSAGE
286 );
287
288 return false;
289 }
290
291 this.rememberSettings();
292
293 return true;
294 }
295
296 /**
297 * Replies true if the user selected to download OSM data
298 *
299 * @return true if the user selected to download OSM data
300 * @deprecated since 16503 -- use {@code getDownloadType(OsmDataDownloadType.class).getCheckBox().isSelected()}
301 */
302 @Deprecated
303 public boolean isDownloadOsmData() {
304 return getDownloadType(OsmDataDownloadType.class).getCheckBox().isSelected();
305 }
306
307 /**
308 * Replies true if the user selected to download GPX data
309 *
310 * @return true if the user selected to download GPX data
311 * @deprecated since 16503 -- use {@code getDownloadType(GpsDataDownloadType.class).getCheckBox().isSelected()}
312 */
313 @Deprecated
314 public boolean isDownloadGpxData() {
315 return getDownloadType(GpsDataDownloadType.class).getCheckBox().isSelected();
316 }
317
318 /**
319 * Replies true if user selected to download notes
320 *
321 * @return true if user selected to download notes
322 * @deprecated since 16503 -- use {@code getDownloadType(NotesDataDownloadType.class).getCheckBox().isSelected()}
323 */
324 @Deprecated
325 public boolean isDownloadNotes() {
326 return getDownloadType(NotesDataDownloadType.class).getCheckBox().isSelected();
327 }
328
329 @Override
330 public Icon getIcon() {
331 return ImageProvider.get("download");
332 }
333
334 @Override
335 public void boundingBoxChanged(Bounds bbox) {
336 updateSizeCheck(bbox);
337 }
338
339 @Override
340 public String getSimpleName() {
341 return SIMPLE_NAME;
342 }
343
344 private void updateSizeCheck(Bounds bbox) {
345 if (bbox == null) {
346 sizeCheck.setText(tr("No area selected yet"));
347 sizeCheck.setForeground(Color.darkGray);
348 return;
349 }
350
351 displaySizeCheckResult(DOWNLOAD_SOURCES.stream()
352 .anyMatch(type -> type.isDownloadAreaTooLarge(bbox)));
353 }
354
355 private void displaySizeCheckResult(boolean isAreaTooLarge) {
356 if (isAreaTooLarge) {
357 sizeCheck.setText(tr("Download area too large; will probably be rejected by server"));
358 sizeCheck.setForeground(Color.red);
359 } else {
360 sizeCheck.setText(tr("Download area ok, size probably acceptable to server"));
361 sizeCheck.setForeground(Color.darkGray);
362 }
363 }
364 }
365
366 /**
367 * Encapsulates data that is required to download from the OSM server.
368 */
369 static class OSMDownloadData {
370
371 private final List<IDownloadSourceType> downloadPossibilities;
372
373 /**
374 * Constructs a new {@code OSMDownloadData}.
375 * @param downloadPossibilities A list of DataDownloadTypes (instantiated, with
376 * options set)
377 */
378 OSMDownloadData(List<IDownloadSourceType> downloadPossibilities) {
379 this.downloadPossibilities = downloadPossibilities;
380 }
381
382 /**
383 * Returns the download possibilities.
384 * @return A list of DataDownloadTypes (instantiated, with options set)
385 */
386 public List<IDownloadSourceType> getDownloadPossibilities() {
387 return downloadPossibilities;
388 }
389 }
390
391 private static class OsmDataDownloadType implements IDownloadSourceType {
392 static final BooleanProperty IS_ENABLED = new BooleanProperty("download.osm.data", true);
393 JCheckBox cbDownloadOsmData;
394
395 @Override
396 public JCheckBox getCheckBox(ChangeListener checkboxChangeListener) {
397 if (cbDownloadOsmData == null) {
398 cbDownloadOsmData = new JCheckBox(tr("OpenStreetMap data"), true);
399 cbDownloadOsmData.setToolTipText(tr("Select to download OSM data in the selected download area."));
400 cbDownloadOsmData.getModel().addChangeListener(checkboxChangeListener);
401 }
402 if (checkboxChangeListener != null) {
403 cbDownloadOsmData.getModel().addChangeListener(checkboxChangeListener);
404 }
405 return cbDownloadOsmData;
406 }
407
408 @Override
409 public Class<? extends AbstractDownloadTask<DataSet>> getDownloadClass() {
410 return DownloadOsmTask.class;
411 }
412
413 @Override
414 public BooleanProperty getBooleanProperty() {
415 return IS_ENABLED;
416 }
417
418 @Override
419 public boolean isDownloadAreaTooLarge(Bounds bound) {
420 // see max_request_area in
421 // https://github.com/openstreetmap/openstreetmap-website/blob/master/config/example.application.yml
422 return bound.getArea() > Config.getPref().getDouble("osm-server.max-request-area", 0.25);
423 }
424 }
425
426 private static class GpsDataDownloadType implements IDownloadSourceType {
427 static final BooleanProperty IS_ENABLED = new BooleanProperty("download.osm.gps", false);
428 private JCheckBox cbDownloadGpxData;
429
430 @Override
431 public JCheckBox getCheckBox(ChangeListener checkboxChangeListener) {
432 if (cbDownloadGpxData == null) {
433 cbDownloadGpxData = new JCheckBox(tr("Raw GPS data"));
434 cbDownloadGpxData.setToolTipText(tr("Select to download GPS traces in the selected download area."));
435 }
436 if (checkboxChangeListener != null) {
437 cbDownloadGpxData.getModel().addChangeListener(checkboxChangeListener);
438 }
439
440 return cbDownloadGpxData;
441 }
442
443 @Override
444 public Class<? extends AbstractDownloadTask<GpxData>> getDownloadClass() {
445 return DownloadGpsTask.class;
446 }
447
448 @Override
449 public BooleanProperty getBooleanProperty() {
450 return IS_ENABLED;
451 }
452
453 @Override
454 public boolean isDownloadAreaTooLarge(Bounds bound) {
455 return false;
456 }
457 }
458
459 private static class NotesDataDownloadType implements IDownloadSourceType {
460 static final BooleanProperty IS_ENABLED = new BooleanProperty("download.osm.notes", false);
461 private JCheckBox cbDownloadNotes;
462
463 @Override
464 public JCheckBox getCheckBox(ChangeListener checkboxChangeListener) {
465 if (cbDownloadNotes == null) {
466 cbDownloadNotes = new JCheckBox(tr("Notes"));
467 cbDownloadNotes.setToolTipText(tr("Select to download notes in the selected download area."));
468 }
469 if (checkboxChangeListener != null) {
470 cbDownloadNotes.getModel().addChangeListener(checkboxChangeListener);
471 }
472
473 return cbDownloadNotes;
474 }
475
476 @Override
477 public Class<? extends AbstractDownloadTask<NoteData>> getDownloadClass() {
478 return DownloadNotesTask.class;
479 }
480
481 @Override
482 public BooleanProperty getBooleanProperty() {
483 return IS_ENABLED;
484 }
485
486 @Override
487 public boolean isDownloadAreaTooLarge(Bounds bound) {
488 // see max_note_request_area in
489 // https://github.com/openstreetmap/openstreetmap-website/blob/master/config/example.application.yml
490 return bound.getArea() > Config.getPref().getDouble("osm-server.max-request-area-notes", 25);
491 }
492 }
493}
Note: See TracBrowser for help on using the repository browser.