Ticket #12303: 12303.5.patch

File 12303.5.patch, 19.5 KB (added by GerdP, 4 years ago)

updated patch, doesn't do a full recursive download of children

  • src/org/openstreetmap/josm/actions/downloadtasks/DownloadReferrersTask.java

     
    1414import javax.swing.JOptionPane;
    1515import javax.swing.SwingUtilities;
    1616
     17import org.openstreetmap.josm.data.Bounds;
    1718import org.openstreetmap.josm.data.osm.DataSet;
    1819import org.openstreetmap.josm.data.osm.DataSetMerger;
    1920import org.openstreetmap.josm.data.osm.Node;
     
    2627import org.openstreetmap.josm.gui.PleaseWaitRunnable;
    2728import org.openstreetmap.josm.gui.layer.OsmDataLayer;
    2829import org.openstreetmap.josm.gui.progress.ProgressMonitor;
     30import org.openstreetmap.josm.io.MultiFetchOverpassObjectReader;
    2931import org.openstreetmap.josm.io.MultiFetchServerObjectReader;
    3032import org.openstreetmap.josm.io.OsmServerBackreferenceReader;
    3133import org.openstreetmap.josm.io.OsmServerReader;
    3234import org.openstreetmap.josm.io.OsmTransferException;
     35import org.openstreetmap.josm.io.OverpassDownloadReader;
    3336import org.openstreetmap.josm.tools.CheckParameterUtil;
    3437import org.openstreetmap.josm.tools.ExceptionUtil;
    3538import org.xml.sax.SAXException;
     
    154157    @Override
    155158    protected void realRun() throws SAXException, IOException, OsmTransferException {
    156159        try {
    157             progressMonitor.setTicksCount(children.size());
    158             int i = 1;
    159             for (PrimitiveId p : children) {
    160                 if (canceled)
    161                     return;
    162                 String msg;
    163                 String id = Long.toString(p.getUniqueId());
    164                 switch(p.getType()) {
    165                 case NODE: msg = tr("({0}/{1}) Loading parents of node {2}", i, children.size(), id); break;
    166                 case WAY: msg = tr("({0}/{1}) Loading parents of way {2}", i, children.size(), id); break;
    167                 case RELATION: msg = tr("({0}/{1}) Loading parents of relation {2}", i, children.size(), id); break;
    168                 default: throw new AssertionError();
     160            if (Boolean.TRUE.equals(OverpassDownloadReader.FOR_MULTI_FETCH.get())) {
     161                String request = MultiFetchOverpassObjectReader.genOverpassQuery(children, false, true, false);
     162                reader = new OverpassDownloadReader(new Bounds(0, 0, 0, 0),
     163                        OverpassDownloadReader.OVERPASS_SERVER.get(), request);
     164                DataSet ds = reader.parseOsm(progressMonitor.createSubTaskMonitor(1, false));
     165                new DataSetMerger(parents, ds).merge();
     166            } else {
     167                progressMonitor.setTicksCount(children.size());
     168                int i = 1;
     169                for (PrimitiveId p : children) {
     170                    if (canceled)
     171                        return;
     172                    String msg;
     173                    String id = Long.toString(p.getUniqueId());
     174                    switch(p.getType()) {
     175                    case NODE: msg = tr("({0}/{1}) Loading parents of node {2}", i, children.size(), id); break;
     176                    case WAY: msg = tr("({0}/{1}) Loading parents of way {2}", i, children.size(), id); break;
     177                    case RELATION: msg = tr("({0}/{1}) Loading parents of relation {2}", i, children.size(), id); break;
     178                    default: throw new AssertionError();
     179                    }
     180                    progressMonitor.subTask(msg);
     181                    downloadParents(p.getUniqueId(), p.getType(), progressMonitor);
     182                    i++;
    169183                }
    170                 progressMonitor.subTask(msg);
    171                 downloadParents(p.getUniqueId(), p.getType(), progressMonitor);
    172                 i++;
    173             }
    174             Collection<Way> ways = parents.getWays();
     184                Collection<Way> ways = parents.getWays();
    175185
    176             if (!ways.isEmpty()) {
    177                 // Collect incomplete nodes of parent ways
    178                 Set<Node> nodes = ways.stream().flatMap(w -> w.getNodes().stream().filter(OsmPrimitive::isIncomplete))
    179                         .collect(Collectors.toSet());
    180                 if (!nodes.isEmpty()) {
    181                     reader = MultiFetchServerObjectReader.create();
    182                     ((MultiFetchServerObjectReader) reader).append(nodes);
    183                     DataSet wayNodes = reader.parseOsm(progressMonitor.createSubTaskMonitor(1, false));
    184                     synchronized (this) { // avoid race condition in cancel()
    185                         reader = null;
     186                if (!ways.isEmpty()) {
     187                    // Collect incomplete nodes of parent ways
     188                    Set<Node> nodes = ways.stream().flatMap(w -> w.getNodes().stream().filter(OsmPrimitive::isIncomplete))
     189                            .collect(Collectors.toSet());
     190                    if (!nodes.isEmpty()) {
     191                        reader = MultiFetchServerObjectReader.create();
     192                        ((MultiFetchServerObjectReader) reader).append(nodes);
     193                        DataSet wayNodes = reader.parseOsm(progressMonitor.createSubTaskMonitor(1, false));
     194                        synchronized (this) { // avoid race condition in cancel()
     195                            reader = null;
     196                        }
     197                        new DataSetMerger(parents, wayNodes).merge();
    186198                    }
    187                     new DataSetMerger(parents, wayNodes).merge();
    188199                }
    189200            }
    190201        } catch (OsmTransferException e) {
     
    193204            lastException = e;
    194205        }
    195206    }
     207
    196208}
  • src/org/openstreetmap/josm/gui/io/DownloadFromOverpassTask.java

     
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.gui.io;
     3
     4import static org.openstreetmap.josm.tools.I18n.tr;
     5
     6import java.io.IOException;
     7
     8import org.openstreetmap.josm.data.Bounds;
     9import org.openstreetmap.josm.data.osm.DataSet;
     10import org.openstreetmap.josm.data.osm.DataSetMerger;
     11import org.openstreetmap.josm.gui.ExceptionDialogUtil;
     12import org.openstreetmap.josm.gui.PleaseWaitRunnable;
     13import org.openstreetmap.josm.gui.progress.ProgressMonitor;
     14import org.openstreetmap.josm.io.OsmTransferException;
     15import org.openstreetmap.josm.io.OverpassDownloadReader;
     16import org.xml.sax.SAXException;
     17
     18/**
     19 * Download OSM data from Overpass API
     20 *
     21 */
     22public class DownloadFromOverpassTask extends PleaseWaitRunnable {
     23    private boolean canceled;
     24    private final String request;
     25    private final DataSet ds;
     26    private Exception lastException;
     27
     28    /**
     29     * Constructor
     30     * @param request the overpass query
     31     * @param ds the {@code DataSet} instance that should contain the downloaded data
     32     * @param monitor ProgressMonitor to use or null to create a new one.
     33     */
     34    public DownloadFromOverpassTask(String request, DataSet ds, ProgressMonitor monitor) {
     35        super(tr("Download objects via Overpass API"), monitor, false);
     36        this.request = request;
     37        this.ds = ds;
     38    }
     39
     40    @Override
     41    protected void cancel() {
     42        canceled = true;
     43    }
     44
     45    @Override
     46    protected void realRun() throws SAXException, IOException, OsmTransferException {
     47        try {
     48            OverpassDownloadReader reader = new OverpassDownloadReader(new Bounds(0, 0, 0, 0),
     49                    OverpassDownloadReader.OVERPASS_SERVER.get(), request);
     50            DataSet tmpDs = reader.parseOsm(progressMonitor.createSubTaskMonitor(1, false));
     51            if (!canceled) {
     52                new DataSetMerger(ds, tmpDs).merge();
     53            }
     54        } catch (OsmTransferException e) {
     55            if (canceled)
     56                return;
     57            lastException = e;
     58        }
     59
     60    }
     61
     62    @Override
     63    protected void finish() {
     64        if (canceled)
     65            return;
     66        if (lastException != null) {
     67            ExceptionDialogUtil.explainException(lastException);
     68        }
     69    }
     70}
  • src/org/openstreetmap/josm/gui/io/DownloadPrimitivesWithReferrersTask.java

     
    3131import org.openstreetmap.josm.gui.util.GuiHelper;
    3232import org.openstreetmap.josm.gui.widgets.HtmlPanel;
    3333import org.openstreetmap.josm.gui.widgets.JosmTextArea;
     34import org.openstreetmap.josm.io.MultiFetchOverpassObjectReader;
    3435import org.openstreetmap.josm.io.OsmTransferException;
     36import org.openstreetmap.josm.io.OverpassDownloadReader;
    3537import org.openstreetmap.josm.tools.GBC;
    3638import org.xml.sax.SAXException;
    3739
     
    5052
    5153    /** Temporary layer where downloaded primitives are put */
    5254    private final OsmDataLayer tmpLayer;
    53     /** Reference to the task that download requested primitives */
    54     private DownloadPrimitivesTask mainTask;
    5555    /** Flag indicated that user ask for cancel this task */
    5656    private boolean canceled;
    5757    /** Reference to the task currently running */
    5858    private PleaseWaitRunnable currentTask;
     59    private Set<PrimitiveId> missingPrimitives;
    5960
    6061    /**
    6162     * Constructor
     
    101102
    102103    @Override
    103104    protected void realRun() throws SAXException, IOException, OsmTransferException {
     105        if (Boolean.TRUE.equals(OverpassDownloadReader.FOR_MULTI_FETCH.get())) {
     106            useOverpassApi();
     107        } else {
     108            useOSMApi();
     109        }
     110    }
     111
     112    private void useOverpassApi() {
     113        String request = MultiFetchOverpassObjectReader.genOverpassQuery(ids, true, downloadReferrers, full);
     114        currentTask = new DownloadFromOverpassTask(request, tmpLayer.data, getProgressMonitor().createSubTaskMonitor(1, false));
     115        currentTask.run();
     116        missingPrimitives = new HashSet<>();
     117        for (PrimitiveId id : ids) {
     118            if (tmpLayer.data.getPrimitiveById(id) == null)
     119                missingPrimitives.add(id);
     120        }
     121    }
     122
     123    private void useOSMApi() {
    104124        getProgressMonitor().setTicksCount(ids.size()+1);
    105125        // First, download primitives
    106         mainTask = new DownloadPrimitivesTask(tmpLayer, ids, full, getProgressMonitor().createSubTaskMonitor(1, false));
     126        DownloadPrimitivesTask mainTask = new DownloadPrimitivesTask(tmpLayer, ids, full,
     127                getProgressMonitor().createSubTaskMonitor(1, false));
    107128        synchronized (this) {
    108129            currentTask = mainTask;
    109130            if (canceled) {
     
    111132                return;
    112133            }
    113134        }
     135
    114136        currentTask.run();
     137
     138        missingPrimitives = mainTask.getMissingPrimitives();
     139
    115140        // Then, download referrers for each primitive
    116141        if (downloadReferrers && tmpLayer.data != null) {
    117142            // see #18895: don't try to download parents for invisible objects
     
    144169            layer.mergeFrom(tmpLayer);
    145170
    146171        // Warm about missing primitives
    147         final Set<PrimitiveId> errs = mainTask.getMissingPrimitives();
     172        final Set<PrimitiveId> errs = missingPrimitives;
    148173        if (errs != null && !errs.isEmpty())
    149174            GuiHelper.runInEDTAndWait(() -> reportProblemDialog(errs,
    150175                    trn("Object could not be downloaded", "Some objects could not be downloaded", errs.size()),
     
    190215                return null;
    191216        }
    192217        List<PrimitiveId> downloaded = new ArrayList<>(ids);
    193         downloaded.removeAll(mainTask.getMissingPrimitives());
     218        downloaded.removeAll(missingPrimitives);
    194219        return downloaded;
    195220    }
    196221
  • src/org/openstreetmap/josm/io/MultiFetchOverpassObjectReader.java

     
    11// License: GPL. For details, see LICENSE file.
    22package org.openstreetmap.josm.io;
    33
     4import java.util.Collection;
     5import java.util.Map;
    46import java.util.Map.Entry;
    57import java.util.Set;
     8import java.util.TreeMap;
     9import java.util.TreeSet;
    610import java.util.stream.Collectors;
    711
    812import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
     13import org.openstreetmap.josm.data.osm.PrimitiveId;
    914import org.openstreetmap.josm.tools.Logging;
    1015
    1116/**
     
    1722
    1823    private static String getPackageString(final OsmPrimitiveType type, Set<Long> idPackage) {
    1924        return idPackage.stream().map(String::valueOf)
    20                 .collect(Collectors.joining(",", type.getAPIName() + (idPackage.size() == 1 ? "(" : "(id:"), ");"));
     25                .collect(Collectors.joining(",", type.getAPIName() + (idPackage.size() == 1 ? "(" : "(id:"), ")"));
    2126    }
    2227
    2328    /**
     
    3035        for (Entry<OsmPrimitiveType, Set<Long>> e : primitivesMap.entrySet()) {
    3136            if (!e.getValue().isEmpty()) {
    3237                countTypes++;
    33                 String list = getPackageString(e.getKey(), e.getValue());
     38                String list = getPackageString(e.getKey(), e.getValue()) + ";";
    3439                switch (e.getKey()) {
    3540                case MULTIPOLYGON:
    3641                case RELATION:
     
    6166        return query;
    6267    }
    6368
     69    /**
     70     * Generate single overpass query to retrieve multiple primitives. Can be used to download parents,
     71     * children, the objects, or any combination of them.
     72     * @param ids the collection of ids
     73     * @param includeObjects if false, don't retrieve the primitives (e.g. only the referrers)
     74     * @param recurseUp if true, referrers (parents) of the objects are downloaded and all nodes of parent ways
     75     * @param recurseDownRelations true: yes, recurse down to retrieve complete relations
     76     * @return the overpass query
     77     */
     78    public static String genOverpassQuery(Collection<PrimitiveId> ids, boolean includeObjects, boolean recurseUp,
     79            boolean recurseDownRelations) {
     80        Map<OsmPrimitiveType, Set<Long>> primitivesMap = new TreeMap<>();
     81        for (PrimitiveId p : ids) {
     82            primitivesMap.computeIfAbsent(p.getType(), k -> new TreeSet<>()).add(p.getUniqueId());
     83        }
     84        return genOverpassQuery(primitivesMap, includeObjects, recurseUp, recurseDownRelations);
     85    }
     86
     87    /**
     88     * Generate single overpass query to retrieve multiple primitives. Can be used to download parents,
     89     * children, the objects, or any combination of them.
     90     * @param primitivesMap map containing the primitives
     91     * @param includeObjects if false, don't retrieve the primitives (e.g. only the referrers)
     92     * @param recurseUp if true, referrers (parents) of the objects are downloaded and all nodes of parent ways
     93     * @param recurseDownRelations true: yes, recurse down to retrieve complete relations
     94     * @return the overpass query
     95     */
     96    public static String genOverpassQuery(Map<OsmPrimitiveType, Set<Long>> primitivesMap, boolean includeObjects,
     97            boolean recurseUp, boolean recurseDownRelations) {
     98        StringBuilder sb = new StringBuilder(128);
     99        StringBuilder setsToInclude = new StringBuilder("(");
     100        for (Entry<OsmPrimitiveType, Set<Long>> e : primitivesMap.entrySet()) {
     101            if (!e.getValue().isEmpty()) {
     102                sb.append(getPackageString(e.getKey(), e.getValue()));
     103                switch (e.getKey()) {
     104                case NODE:
     105                    sb.append("->.n;");
     106                    if (includeObjects) {
     107                        setsToInclude.append(".n;");
     108                    }
     109                    if (recurseUp) {
     110                        sb.append(".n;way(bn)->.wn;.n;rel(bn)->.rn;");
     111                        setsToInclude.append(".wn;node(w);.rn;");
     112                    }
     113                    break;
     114                case CLOSEDWAY:
     115                case WAY:
     116                    sb.append("->.w;");
     117                    if (includeObjects) {
     118                        setsToInclude.append(".w;>;");
     119                    }
     120                    if (recurseUp) {
     121                        sb.append(".w;rel(bw)->.pw;");
     122                        setsToInclude.append(".pw;");
     123                    }
     124                    break;
     125                case MULTIPOLYGON:
     126                case RELATION:
     127                    sb.append("->.r;");
     128                    if (includeObjects) {
     129                        setsToInclude.append(".r;");
     130                    }
     131                    if (recurseUp) {
     132                        sb.append(".r;rel(br)->.pr;");
     133                        setsToInclude.append(".pr;");
     134                    }
     135                    if (recurseDownRelations) {
     136                        // get complete ways and nodes of the relation and next level of sub relations
     137                        sb.append(".r;rel(r)->.rm;");
     138                        setsToInclude.append(".r;>;.rm;");
     139                    }
     140                    break;
     141                }
     142            }
     143        }
     144        setsToInclude.append(");");
     145        sb.append(setsToInclude).append("out meta;");
     146        String query = sb.toString();
     147        Logging.debug("{0} {1}", "Possible Overpass query:", query);
     148        return query;
     149    }
     150
    64151    @Override
    65152    protected String getBaseUrl() {
    66153        return OverpassDownloadReader.OVERPASS_SERVER.get();
  • src/org/openstreetmap/josm/io/MultiFetchServerObjectReader.java

     
    1212import java.util.Collections;
    1313import java.util.HashSet;
    1414import java.util.Iterator;
    15 import java.util.LinkedHashMap;
    1615import java.util.LinkedHashSet;
    1716import java.util.List;
    1817import java.util.Map;
    1918import java.util.Map.Entry;
    2019import java.util.Set;
     20import java.util.TreeMap;
    2121import java.util.concurrent.Callable;
    2222import java.util.concurrent.CompletionService;
    2323import java.util.concurrent.ExecutionException;
     
    8989        relations = new LinkedHashSet<>();
    9090        this.outputDataSet = new DataSet();
    9191        this.missingPrimitives = new LinkedHashSet<>();
    92         primitivesMap = new LinkedHashMap<>();
     92        primitivesMap = new TreeMap<>();
    9393        primitivesMap.put(OsmPrimitiveType.RELATION, relations);
    9494        primitivesMap.put(OsmPrimitiveType.WAY, ways);
    9595        primitivesMap.put(OsmPrimitiveType.NODE, nodes);
     
    382382        try {
    383383            if (this instanceof MultiFetchOverpassObjectReader) {
    384384                // calculate a single request for all the objects
    385                 String request = ((MultiFetchOverpassObjectReader) this).buildComplexRequestString();
     385                String request = MultiFetchOverpassObjectReader.genOverpassQuery(primitivesMap, true, false, recurseDownRelations);
    386386                if (isCanceled())
    387387                    return null;
    388388                OverpassDownloadReader reader = new OverpassDownloadReader(new Bounds(0, 0, 0, 0), getBaseUrl(), request);
    389                 DataSet ds = reader.parseOsm(progressMonitor.createSubTaskMonitor(n, false));
     389                DataSet ds = reader.parseOsm(progressMonitor.createSubTaskMonitor(1, false));
    390390                new DataSetMerger(outputDataSet, ds).merge();
    391391                checkMissing(outputDataSet, progressMonitor);
    392392            } else {