Changeset 16503 in josm


Ignore:
Timestamp:
2020-05-27T08:03:39+02:00 (4 years ago)
Author:
simon04
Message:

fix #18340 - Allow additional sources of data to be downloaded at the same time as OpenStreetMap data sources (patch by taylor.smock)

Location:
trunk/src/org/openstreetmap/josm/gui/download
Files:
1 added
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/gui/download/OSMDownloadSource.java

    r16174 r16503  
    99import java.awt.Font;
    1010import java.awt.GridBagLayout;
     11import java.lang.reflect.InvocationTargetException;
    1112import java.util.ArrayList;
     13import java.util.Collections;
    1214import java.util.List;
    1315import java.util.concurrent.ExecutionException;
     
    1820import javax.swing.JLabel;
    1921import javax.swing.JOptionPane;
     22import javax.swing.JPanel;
    2023import javax.swing.event.ChangeListener;
    2124
     
    2932import org.openstreetmap.josm.data.ProjectionBounds;
    3033import org.openstreetmap.josm.data.ViewportData;
     34import org.openstreetmap.josm.data.gpx.GpxData;
     35import org.openstreetmap.josm.data.osm.DataSet;
     36import org.openstreetmap.josm.data.osm.NoteData;
    3137import org.openstreetmap.josm.data.preferences.BooleanProperty;
    3238import org.openstreetmap.josm.gui.MainApplication;
     
    4349 * @since 12652
    4450 */
    45 public class OSMDownloadSource implements DownloadSource<OSMDownloadSource.OSMDownloadData> {
     51public class OSMDownloadSource implements DownloadSource<List<IDownloadSourceType>> {
    4652    /**
    4753     * The simple name for the {@link OSMDownloadSourcePanel}
     
    5056    public static final String SIMPLE_NAME = "osmdownloadpanel";
    5157
     58    /** The possible methods to get data */
     59    static final List<IDownloadSourceType> DOWNLOAD_SOURCES = new ArrayList<>();
     60    static {
     61        // Order is important (determines button order, and what gets zoomed to)
     62        DOWNLOAD_SOURCES.add(new OsmDataDownloadType());
     63        DOWNLOAD_SOURCES.add(new GpsDataDownloadType());
     64        DOWNLOAD_SOURCES.add(new NotesDataDownloadType());
     65    }
     66
    5267    @Override
    53     public AbstractDownloadSourcePanel<OSMDownloadData> createPanel(DownloadDialog dialog) {
     68    public AbstractDownloadSourcePanel<List<IDownloadSourceType>> createPanel(DownloadDialog dialog) {
    5469        return new OSMDownloadSourcePanel(this, dialog);
    5570    }
    5671
    5772    @Override
    58     public void doDownload(OSMDownloadData data, DownloadSettings settings) {
     73    public void doDownload(List<IDownloadSourceType> data, DownloadSettings settings) {
    5974        Bounds bbox = settings.getDownloadBounds()
    6075                .orElseThrow(() -> new IllegalArgumentException("OSM downloads requires bounds"));
    6176        boolean zoom = settings.zoomToData();
    6277        boolean newLayer = settings.asNewLayer();
    63         List<Pair<AbstractDownloadTask<?>, Future<?>>> tasks = new ArrayList<>();
    64 
    65         if (data.isDownloadOSMData()) {
    66             DownloadOsmTask task = new DownloadOsmTask();
    67             task.setZoomAfterDownload(zoom && !data.isDownloadGPX() && !data.isDownloadNotes());
    68             Future<?> future = task.download(new DownloadParams().withNewLayer(newLayer), bbox, null);
    69             MainApplication.worker.submit(new PostDownloadHandler(task, future));
    70             if (zoom) {
    71                 tasks.add(new Pair<>(task, future));
    72             }
    73         }
    74 
    75         if (data.isDownloadGPX()) {
    76             DownloadGpsTask task = new DownloadGpsTask();
    77             task.setZoomAfterDownload(zoom && !data.isDownloadOSMData() && !data.isDownloadNotes());
    78             Future<?> future = task.download(new DownloadParams().withNewLayer(newLayer), bbox, null);
    79             MainApplication.worker.submit(new PostDownloadHandler(task, future));
    80             if (zoom) {
    81                 tasks.add(new Pair<>(task, future));
    82             }
    83         }
    84 
    85         if (data.isDownloadNotes()) {
    86             DownloadNotesTask task = new DownloadNotesTask();
    87             task.setZoomAfterDownload(zoom && !data.isDownloadOSMData() && !data.isDownloadGPX());
    88             Future<?> future = task.download(new DownloadParams(), bbox, null);
    89             MainApplication.worker.submit(new PostDownloadHandler(task, future));
    90             if (zoom) {
    91                 tasks.add(new Pair<>(task, future));
    92             }
    93         }
     78        final List<Pair<AbstractDownloadTask<?>, Future<?>>> tasks = new ArrayList<>();
     79        IDownloadSourceType zoomTask = zoom ? data.stream().findFirst().orElse(null) : null;
     80        data.stream().filter(IDownloadSourceType::isEnabled).forEach(type -> {
     81            try {
     82                AbstractDownloadTask<?> task = type.getDownloadClass().getDeclaredConstructor().newInstance();
     83                task.setZoomAfterDownload(type.equals(zoomTask));
     84                Future<?> future = task.download(new DownloadParams().withNewLayer(newLayer), bbox, null);
     85                MainApplication.worker.submit(new PostDownloadHandler(task, future));
     86                if (zoom) {
     87                    tasks.add(new Pair<AbstractDownloadTask<?>, Future<?>>(task, future));
     88                }
     89            } catch (InstantiationException | IllegalAccessException | IllegalArgumentException
     90                    | InvocationTargetException | NoSuchMethodException | SecurityException e) {
     91                Logging.error(e);
     92            }
     93        });
    9494
    9595        if (zoom && tasks.size() > 1) {
     
    131131
    132132    /**
     133     * @return The possible downloads that JOSM can make in the default Download
     134     *         screen
     135     * @since 16503
     136     */
     137    public static List<IDownloadSourceType> getDownloadTypes() {
     138        return Collections.unmodifiableList(DOWNLOAD_SOURCES);
     139    }
     140
     141    /**
     142     * Get the instance of a data download type
     143     *
     144     * @param <T> The type to get
     145     * @param typeClazz The class of the type
     146     * @return The type instance
     147     * @since 16503
     148     */
     149    public static <T extends IDownloadSourceType> T getDownloadType(Class<T> typeClazz) {
     150        return DOWNLOAD_SOURCES.stream().filter(typeClazz::isInstance).map(typeClazz::cast).findFirst().orElse(null);
     151    }
     152
     153    /**
     154     * @param type The IDownloadSourceType object to remove
     155     * @return true See {@link List#remove}, but it also returns false if the
     156     * parameter is a class from JOSM core.
     157     * @since 16503
     158     */
     159    public static boolean removeDownloadType(IDownloadSourceType type) {
     160        boolean modified = false;
     161        if (!(type instanceof OsmDataDownloadType) && !(type instanceof GpsDataDownloadType)
     162                && !(type instanceof NotesDataDownloadType)) {
     163            modified = DOWNLOAD_SOURCES.remove(type);
     164        }
     165        return modified;
     166    }
     167
     168    /**
     169     * Add a download type to the default JOSM download window
     170     *
     171     * @param type The initialized type to download
     172     * @return See {@link List#add}, but it also returns false if the class
     173     * already has an instance in the list or it is a class from JOSM core.
     174     * @since 16503
     175     */
     176    public static boolean addDownloadType(IDownloadSourceType type) {
     177        boolean modified = false;
     178        if (!(type instanceof OsmDataDownloadType) && !(type instanceof GpsDataDownloadType)
     179                && !(type instanceof NotesDataDownloadType)
     180                || DOWNLOAD_SOURCES.stream()
     181                        .noneMatch(possibility -> type.getClass().isInstance(possibility))) {
     182            modified = DOWNLOAD_SOURCES.add(type);
     183        } else {
     184            throw new IllegalArgumentException("There can only be one instance of a class added, and it cannot be a built-in class.");
     185        }
     186        return modified;
     187    }
     188
     189    /**
    133190     * The GUI representation of the OSM download source.
    134191     * @since 12652
    135192     */
    136     public static class OSMDownloadSourcePanel extends AbstractDownloadSourcePanel<OSMDownloadData> {
    137 
    138         private final JCheckBox cbDownloadOsmData;
    139         private final JCheckBox cbDownloadGpxData;
    140         private final JCheckBox cbDownloadNotes;
     193    public static class OSMDownloadSourcePanel extends AbstractDownloadSourcePanel<List<IDownloadSourceType>> {
    141194        private final JLabel sizeCheck = new JLabel();
    142195
    143         private static final BooleanProperty DOWNLOAD_OSM = new BooleanProperty("download.osm.data", true);
    144         private static final BooleanProperty DOWNLOAD_GPS = new BooleanProperty("download.osm.gps", false);
    145         private static final BooleanProperty DOWNLOAD_NOTES = new BooleanProperty("download.osm.notes", false);
     196        /** This is used to keep track of the components for download sources, and to dynamically update/remove them */
     197        private JPanel downloadSourcesPanel;
     198
     199        private ChangeListener checkboxChangeListener;
    146200
    147201        /**
     
    162216
    163217            // size check depends on selected data source
    164             final ChangeListener checkboxChangeListener = e ->
     218            checkboxChangeListener = e ->
    165219                    dialog.getSelectedDownloadArea().ifPresent(this::updateSizeCheck);
    166220
    167221            // adding the download tasks
    168222            add(new JLabel(tr(DATA_SOURCES_AND_TYPES)), GBC.std().insets(5, 5, 1, 5).anchor(GBC.CENTER));
    169             cbDownloadOsmData = new JCheckBox(tr("OpenStreetMap data"), true);
    170             cbDownloadOsmData.setToolTipText(tr("Select to download OSM data in the selected download area."));
    171             cbDownloadOsmData.getModel().addChangeListener(checkboxChangeListener);
    172 
    173             cbDownloadGpxData = new JCheckBox(tr("Raw GPS data"));
    174             cbDownloadGpxData.setToolTipText(tr("Select to download GPS traces in the selected download area."));
    175             cbDownloadGpxData.getModel().addChangeListener(checkboxChangeListener);
    176 
    177             cbDownloadNotes = new JCheckBox(tr("Notes"));
    178             cbDownloadNotes.setToolTipText(tr("Select to download notes in the selected download area."));
    179             cbDownloadNotes.getModel().addChangeListener(checkboxChangeListener);
    180 
    181223            Font labelFont = sizeCheck.getFont();
    182224            sizeCheck.setFont(labelFont.deriveFont(Font.PLAIN, labelFont.getSize()));
    183225
    184             add(cbDownloadOsmData, GBC.std().insets(1, 5, 1, 5));
    185             add(cbDownloadGpxData, GBC.std().insets(1, 5, 1, 5));
    186             add(cbDownloadNotes, GBC.eol().insets(1, 5, 1, 5));
     226            downloadSourcesPanel = new JPanel();
     227            add(downloadSourcesPanel, GBC.eol().anchor(GBC.EAST));
     228            updateSources();
    187229            add(sizeCheck, GBC.eol().anchor(GBC.EAST).insets(5, 5, 5, 2));
    188230
     
    190232        }
    191233
    192         @Override
    193         public OSMDownloadData getData() {
    194             return new OSMDownloadData(
    195                     isDownloadOsmData(),
    196                     isDownloadNotes(),
    197                     isDownloadGpxData());
     234        /**
     235         * Update the source list for downloading data
     236         */
     237        protected void updateSources() {
     238            downloadSourcesPanel.removeAll();
     239            DOWNLOAD_SOURCES
     240                .forEach(obj -> downloadSourcesPanel.add(obj.getCheckBox(checkboxChangeListener), GBC.std().insets(1, 5, 1, 5)));
     241        }
     242
     243        @Override
     244        public List<IDownloadSourceType> getData() {
     245            return DOWNLOAD_SOURCES;
    198246        }
    199247
    200248        @Override
    201249        public void rememberSettings() {
    202             DOWNLOAD_OSM.put(isDownloadOsmData());
    203             DOWNLOAD_GPS.put(isDownloadGpxData());
    204             DOWNLOAD_NOTES.put(isDownloadNotes());
     250            DOWNLOAD_SOURCES.forEach(type -> type.getBooleanProperty().put(type.getCheckBox().isSelected()));
    205251        }
    206252
    207253        @Override
    208254        public void restoreSettings() {
    209             cbDownloadOsmData.setSelected(DOWNLOAD_OSM.get());
    210             cbDownloadGpxData.setSelected(DOWNLOAD_GPS.get());
    211             cbDownloadNotes.setSelected(DOWNLOAD_NOTES.get());
     255            updateSources();
     256            DOWNLOAD_SOURCES.forEach(type -> type.getCheckBox().setSelected(type.isEnabled()));
     257        }
     258
     259        @Override
     260        public void setVisible(boolean aFlag) {
     261            super.setVisible(aFlag);
     262            updateSources();
    212263        }
    213264
     
    233284             * If none of those are selected, then the corresponding dialog is shown to inform the user.
    234285             */
    235             if (!isDownloadOsmData() && !isDownloadGpxData() && !isDownloadNotes()) {
     286            if (DOWNLOAD_SOURCES.stream().noneMatch(IDownloadSourceType::isEnabled)) {
     287                StringBuilder line1 = new StringBuilder("<html>").append(tr("None of"));
     288                StringBuilder line2 = new StringBuilder(tr("Please choose to either download"));
     289
     290                DOWNLOAD_SOURCES.forEach(type -> {
     291                    line1.append(" <strong>").append(type.getCheckBox().getText()).append("</strong> ");
     292                    line2.append(' ').append(type.getCheckBox().getText()).append(tr(", or"));
     293                });
     294                line1.append(tr("is enabled.")).append("<br>");
     295                line2.append(tr(" all.")).append("</html>");
    236296                JOptionPane.showMessageDialog(
    237297                        this.getParent(),
    238                         tr("<html>Neither <strong>{0}</strong> nor <strong>{1}</strong> nor <strong>{2}</strong> is enabled.<br>"
    239                                         + "Please choose to either download OSM data, or GPX data, or Notes, or all.</html>",
    240                                 cbDownloadOsmData.getText(),
    241                                 cbDownloadGpxData.getText(),
    242                                 cbDownloadNotes.getText()
    243                         ),
     298                        line1.append(line2).toString(),
    244299                        tr("Error"),
    245300                        JOptionPane.ERROR_MESSAGE
     
    258313         *
    259314         * @return true if the user selected to download OSM data
    260          */
     315         * @deprecated since xxx -- use {@link OSMDownloadSource#getDownloadTypes} with
     316         *             {@code get(0).getCheckBox().isSelected()}
     317         */
     318        @Deprecated
    261319        public boolean isDownloadOsmData() {
    262             return cbDownloadOsmData.isSelected();
     320            return DOWNLOAD_SOURCES.get(0).getCheckBox().isSelected();
    263321        }
    264322
     
    267325         *
    268326         * @return true if the user selected to download GPX data
    269          */
     327         * @deprecated since xxx -- use {@link OSMDownloadSource#getDownloadTypes} with
     328         *             {@code get(1).getCheckBox().isSelected()}
     329         */
     330        @Deprecated
    270331        public boolean isDownloadGpxData() {
    271             return cbDownloadGpxData.isSelected();
     332            return DOWNLOAD_SOURCES.get(1).getCheckBox().isSelected();
    272333        }
    273334
     
    276337         *
    277338         * @return true if user selected to download notes
    278          */
     339         * @deprecated since xxx -- use {@link OSMDownloadSource#getDownloadTypes} with
     340         *             {@code get(2).getCheckBox().isSelected()}
     341         */
     342        @Deprecated
    279343        public boolean isDownloadNotes() {
    280             return cbDownloadNotes.isSelected();
     344            return DOWNLOAD_SOURCES.get(2).getCheckBox().isSelected();
    281345        }
    282346
     
    303367            }
    304368
    305             boolean isAreaTooLarge = false;
    306             if (!isDownloadNotes() && !isDownloadOsmData() && !isDownloadGpxData()) {
    307                 isAreaTooLarge = false;
    308             } else if (isDownloadNotes() && !isDownloadOsmData() && !isDownloadGpxData()) {
    309                 // see max_note_request_area in https://github.com/openstreetmap/openstreetmap-website/blob/master/config/settings.yml
    310                 isAreaTooLarge = bbox.getArea() > Config.getPref().getDouble("osm-server.max-request-area-notes", 25);
    311             } else {
    312                 // see max_request_area in https://github.com/openstreetmap/openstreetmap-website/blob/master/config/settings.yml
    313                 isAreaTooLarge = bbox.getArea() > Config.getPref().getDouble("osm-server.max-request-area", 0.25);
    314             }
    315 
    316             displaySizeCheckResult(isAreaTooLarge);
     369            displaySizeCheckResult(DOWNLOAD_SOURCES.stream()
     370                    .anyMatch(type -> type.isDownloadAreaTooLarge(bbox)));
    317371        }
    318372
     
    333387     */
    334388    static class OSMDownloadData {
    335         private final boolean downloadOSMData;
    336         private final boolean downloadNotes;
    337         private final boolean downloadGPX;
    338 
    339         OSMDownloadData(boolean downloadOSMData, boolean downloadNotes, boolean downloadGPX) {
    340             this.downloadOSMData = downloadOSMData;
    341             this.downloadNotes = downloadNotes;
    342             this.downloadGPX = downloadGPX;
    343         }
    344 
    345         boolean isDownloadOSMData() {
    346             return downloadOSMData;
    347         }
    348 
    349         boolean isDownloadNotes() {
    350             return downloadNotes;
    351         }
    352 
    353         boolean isDownloadGPX() {
    354             return downloadGPX;
     389
     390        private List<IDownloadSourceType> downloadPossibilities;
     391
     392        /**
     393         * @param downloadPossibilities A list of DataDownloadTypes (instantiated, with
     394         *                              options set)
     395         */
     396        OSMDownloadData(List<IDownloadSourceType> downloadPossibilities) {
     397            this.downloadPossibilities = downloadPossibilities;
     398        }
     399
     400        /**
     401         * @return A list of DataDownloadTypes (instantiated, with options set)
     402         */
     403        public List<IDownloadSourceType> getDownloadPossibilities() {
     404            return downloadPossibilities;
     405        }
     406    }
     407
     408    private static class OsmDataDownloadType implements IDownloadSourceType {
     409        static final BooleanProperty IS_ENABLED = new BooleanProperty("download.osm.data", true);
     410        JCheckBox cbDownloadOsmData;
     411
     412        @Override
     413        public JCheckBox getCheckBox(ChangeListener checkboxChangeListener) {
     414            if (cbDownloadOsmData == null) {
     415                cbDownloadOsmData = new JCheckBox(tr("OpenStreetMap data"), true);
     416                cbDownloadOsmData.setToolTipText(tr("Select to download OSM data in the selected download area."));
     417                cbDownloadOsmData.getModel().addChangeListener(checkboxChangeListener);
     418            }
     419            if (checkboxChangeListener != null) {
     420                cbDownloadOsmData.getModel().addChangeListener(checkboxChangeListener);
     421            }
     422            return cbDownloadOsmData;
     423        }
     424
     425        @Override
     426        public Class<? extends AbstractDownloadTask<DataSet>> getDownloadClass() {
     427            return DownloadOsmTask.class;
     428        }
     429
     430        @Override
     431        public BooleanProperty getBooleanProperty() {
     432            return IS_ENABLED;
     433        }
     434
     435        @Override
     436        public boolean isDownloadAreaTooLarge(Bounds bound) {
     437            // see max_request_area in
     438            // https://github.com/openstreetmap/openstreetmap-website/blob/master/config/example.application.yml
     439            return bound.getArea() > Config.getPref().getDouble("osm-server.max-request-area", 0.25);
     440        }
     441    }
     442
     443    private static class GpsDataDownloadType implements IDownloadSourceType {
     444        static final BooleanProperty IS_ENABLED = new BooleanProperty("download.osm.gps", false);
     445        private JCheckBox cbDownloadGpxData;
     446
     447        @Override
     448        public JCheckBox getCheckBox(ChangeListener checkboxChangeListener) {
     449            if (cbDownloadGpxData == null) {
     450                cbDownloadGpxData = new JCheckBox(tr("Raw GPS data"));
     451                cbDownloadGpxData.setToolTipText(tr("Select to download GPS traces in the selected download area."));
     452            }
     453            if (checkboxChangeListener != null) {
     454                cbDownloadGpxData.getModel().addChangeListener(checkboxChangeListener);
     455            }
     456
     457            return cbDownloadGpxData;
     458        }
     459
     460        @Override
     461        public Class<? extends AbstractDownloadTask<GpxData>> getDownloadClass() {
     462            return DownloadGpsTask.class;
     463        }
     464
     465        @Override
     466        public BooleanProperty getBooleanProperty() {
     467            return IS_ENABLED;
     468        }
     469
     470        @Override
     471        public boolean isDownloadAreaTooLarge(Bounds bound) {
     472            return false;
     473        }
     474    }
     475
     476    private static class NotesDataDownloadType implements IDownloadSourceType {
     477        static final BooleanProperty IS_ENABLED = new BooleanProperty("download.osm.notes", false);
     478        private JCheckBox cbDownloadNotes;
     479
     480        @Override
     481        public JCheckBox getCheckBox(ChangeListener checkboxChangeListener) {
     482            if (cbDownloadNotes == null) {
     483                cbDownloadNotes = new JCheckBox(tr("Notes"));
     484                cbDownloadNotes.setToolTipText(tr("Select to download notes in the selected download area."));
     485            }
     486            if (checkboxChangeListener != null) {
     487                cbDownloadNotes.getModel().addChangeListener(checkboxChangeListener);
     488            }
     489
     490            return cbDownloadNotes;
     491        }
     492
     493        @Override
     494        public Class<? extends AbstractDownloadTask<NoteData>> getDownloadClass() {
     495            return DownloadNotesTask.class;
     496        }
     497
     498        @Override
     499        public BooleanProperty getBooleanProperty() {
     500            return IS_ENABLED;
     501        }
     502
     503        @Override
     504        public boolean isDownloadAreaTooLarge(Bounds bound) {
     505            // see max_note_request_area in
     506            // https://github.com/openstreetmap/openstreetmap-website/blob/master/config/example.application.yml
     507            return bound.getArea() > Config.getPref().getDouble("osm-server.max-request-area-notes", 25);
    355508        }
    356509    }
Note: See TracChangeset for help on using the changeset viewer.