Ticket #24717: 24717-v1.patch

File 24717-v1.patch, 7.1 KB (added by mattmccutchen, 2 weeks ago)
  • src/org/openstreetmap/josm/actions/UpdateDataAction.java

    From Mon Sep 17 00:00:00 2001
    From: Matt McCutchen <matt@mattmccutchen.net>
    Date: Thu, 15 Oct 2026 16:21:55 -0400
    
    In PostDownloadProcessor, decide whether to use the full-update algorithm for detecting potentially deleted primitives based on whether the "Update data" action was used rather than comparing the bounding boxes. Fixes a bug where area optimization would confuse the bounding box comparison (#24717).
    ---
     .../josm/actions/UpdateDataAction.java             | 29 +-----------
     .../actions/downloadtasks/DownloadTaskList.java    | 52 +++++++++++++++++++---
     2 files changed, 49 insertions(+), 32 deletions(-)
    
    diff --git a/src/org/openstreetmap/josm/actions/UpdateDataAction.java b/src/org/openstreetmap/josm/actions/UpdateDataAction.java
    index 35f375054c..eabb643b5a 100644
    a b import static org.openstreetmap.josm.tools.I18n.tr;  
    66
    77import java.awt.event.ActionEvent;
    88import java.awt.event.KeyEvent;
    9 import java.awt.geom.Area;
    10 import java.util.List;
    119import java.util.concurrent.Future;
    1210import java.util.stream.Collectors;
    1311
    … … public class UpdateDataAction extends JosmAction {  
    6159        if (!isEnabled() || editLayer == null || !editLayer.isDownloadable())
    6260            return;
    6361
    64         List<Area> areas = editLayer.data.getDataSources().stream()
    65                 .map(ds -> new Area(ds.bounds.asRect()))
    66                 .collect(Collectors.toList());
    67 
    68         // The next two blocks removes every intersection from every DataSource Area
    69         // This prevents downloading the same data numerous times at intersections
    70         // and also skips smaller bounding boxes that are contained within larger ones entirely.
    71         for (int i = 0; i < areas.size(); i++) {
    72             for (int j = i+1; j < areas.size(); j++) {
    73                 areas.get(i).subtract(areas.get(j));
    74             }
    75         }
    76 
    77         for (int i = areas.size()-1; i > 0; i--) {
    78             for (int j = i-1; j > 0; j--) {
    79                 areas.get(i).subtract(areas.get(j));
    80             }
    81         }
    82 
    83         List<Area> areasToDownload = areas.stream()
    84                 .filter(a -> !a.isEmpty())
    85                 .collect(Collectors.toList());
    86 
    87         if (areasToDownload.isEmpty()) {
     62        if (editLayer.data.getDataSources().isEmpty()) {
    8863            // no bounds defined in the dataset? We update all but the incomplete primitives in the data set
    8964            UpdateSelectionAction.updatePrimitives(editLayer.data.allPrimitives().stream()
    9065                    .filter(p -> !p.isNew() && !p.isIncomplete()).collect(Collectors.toList()));
    … … public class UpdateDataAction extends JosmAction {  
    9267            // bounds defined? => use the bbox downloader
    9368            final PleaseWaitProgressMonitor monitor = new PleaseWaitProgressMonitor(tr("Download data"));
    9469            final Future<?> future = new DownloadTaskList(Config.getPref().getBoolean("update.data.zoom-after-download"))
    95                     .download(false /* no new layer */, areasToDownload, true, false, monitor);
     70                    .fullUpdate(monitor);
    9671            waitFuture(future, monitor);
    9772        }
    9873    }
  • src/org/openstreetmap/josm/actions/downloadtasks/DownloadTaskList.java

    diff --git a/src/org/openstreetmap/josm/actions/downloadtasks/DownloadTaskList.java b/src/org/openstreetmap/josm/actions/downloadtasks/DownloadTaskList.java
    index f8369409b1..73b56326fa 100644
    a b import org.openstreetmap.josm.tools.Utils;  
    4747public class DownloadTaskList {
    4848    private final List<DownloadTask> tasks = new LinkedList<>();
    4949    private final List<Future<?>> taskFutures = new LinkedList<>();
     50    private boolean isFullUpdate;
    5051    private final boolean zoomAfterDownload;
    5152    private ProgressMonitor progressMonitor;
    5253
    … … public class DownloadTaskList {  
    131132        }
    132133    }
    133134
     135    /**
     136     * Fully updates the current edit layer by re-downloading all the layer's
     137     * declared bounding boxes from the OSM server.
     138     *
     139     * For full updates, this method should be preferred to the {@code download}
     140     * methods because it uses a more effective algorithm to detect primitives that
     141     * may have been deleted from the server; see #24717.
     142     *
     143     * @param progressMonitor The progress monitor
     144     * @return The Future representing the asynchronous download task
     145     */
     146    public Future<?> fullUpdate(ProgressMonitor progressMonitor) {
     147        OsmDataLayer editLayer = MainApplication.getLayerManager().getEditLayer();
     148        if (editLayer == null || !editLayer.isDownloadable())
     149            throw new IllegalStateException("The current edit layer is not downloadable.");
     150        if (editLayer.data.getDataSources().isEmpty())
     151            throw new IllegalStateException("The current edit layer has no declared bounds.");
     152
     153        List<Area> areas = editLayer.data.getDataSources().stream()
     154                .map(ds -> new Area(ds.bounds.asRect()))
     155                .collect(Collectors.toList());
     156
     157        // The next two blocks removes every intersection from every DataSource Area
     158        // This prevents downloading the same data numerous times at intersections
     159        // and also skips smaller bounding boxes that are contained within larger ones entirely.
     160        for (int i = 0; i < areas.size(); i++) {
     161            for (int j = i+1; j < areas.size(); j++) {
     162                areas.get(i).subtract(areas.get(j));
     163            }
     164        }
     165
     166        for (int i = areas.size()-1; i > 0; i--) {
     167            for (int j = i-1; j > 0; j--) {
     168                areas.get(i).subtract(areas.get(j));
     169            }
     170        }
     171
     172        List<Area> areasToDownload = areas.stream()
     173                .filter(a -> !a.isEmpty())
     174                .collect(Collectors.toList());
     175
     176        isFullUpdate = true;
     177        return download(false /* no new layer */, areasToDownload, true, false, progressMonitor);
     178    }
     179
    134180    /**
    135181     * Replies the set of ids of all complete, non-new primitives (i.e. those with !primitive.incomplete)
    136182     * @param ds data set
    … … public class DownloadTaskList {  
    275321                        .filter(DownloadOsmTask.class::isInstance).map(DownloadOsmTask.class::cast)
    276322                        .filter(t -> t.getDownloadedData() != null)
    277323                        .collect(Collectors.toList());
    278                 final Set<Bounds> tasksBounds = osmTasks.stream()
    279                         .flatMap(t -> t.getDownloadedData().getDataSourceBounds().stream())
    280                         .collect(Collectors.toSet());
    281                 final Set<Bounds> layerBounds = new LinkedHashSet<>(editDataSet.getDataSourceBounds());
    282324                final Set<OsmPrimitive> myPrimitives = new LinkedHashSet<>();
    283                 if (layerBounds.equals(tasksBounds)) {
     325                if (isFullUpdate) {
    284326                    // the full edit layer is updated (we have downloaded again all its current bounds)
    285327                    myPrimitives.addAll(getCompletePrimitives(editDataSet));
    286328                    for (DownloadOsmTask task : osmTasks) {