Ticket #12303: 12303.4.patch

File 12303.4.patch, 19.4 KB (added by GerdP, 4 months ago)
  • 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;
     
    150153    @Override
    151154    protected void realRun() throws SAXException, IOException, OsmTransferException {
    152155        try {
    153             progressMonitor.setTicksCount(children.size());
    154             int i = 1;
    155             for (PrimitiveId p : children) {
    156                 if (canceled)
    157                     return;
    158                 String msg;
    159                 String id = Long.toString(p.getUniqueId());
    160                 switch(p.getType()) {
    161                 case NODE: msg = tr("({0}/{1}) Loading parents of node {2}", i, children.size(), id); break;
    162                 case WAY: msg = tr("({0}/{1}) Loading parents of way {2}", i, children.size(), id); break;
    163                 case RELATION: msg = tr("({0}/{1}) Loading parents of relation {2}", i, children.size(), id); break;
    164                 default: throw new AssertionError();
     156            if (Boolean.TRUE.equals(OverpassDownloadReader.FOR_MULTI_FETCH.get())) {
     157                String request = MultiFetchOverpassObjectReader.genOverpassQuery(children, false, true, false);
     158                reader = new OverpassDownloadReader(new Bounds(0, 0, 0, 0),
     159                        OverpassDownloadReader.OVERPASS_SERVER.get(), request);
     160                DataSet ds = reader.parseOsm(progressMonitor.createSubTaskMonitor(1, false));
     161                new DataSetMerger(parents, ds).merge();
     162            } else {
     163                progressMonitor.setTicksCount(children.size());
     164                int i = 1;
     165                for (PrimitiveId p : children) {
     166                    if (canceled)
     167                        return;
     168                    String msg;
     169                    String id = Long.toString(p.getUniqueId());
     170                    switch(p.getType()) {
     171                    case NODE: msg = tr("({0}/{1}) Loading parents of node {2}", i, children.size(), id); break;
     172                    case WAY: msg = tr("({0}/{1}) Loading parents of way {2}", i, children.size(), id); break;
     173                    case RELATION: msg = tr("({0}/{1}) Loading parents of relation {2}", i, children.size(), id); break;
     174                    default: throw new AssertionError();
     175                    }
     176                    progressMonitor.subTask(msg);
     177                    downloadParents(p.getUniqueId(), p.getType(), progressMonitor);
     178                    i++;
    165179                }
    166                 progressMonitor.subTask(msg);
    167                 downloadParents(p.getUniqueId(), p.getType(), progressMonitor);
    168                 i++;
    169             }
    170             Collection<Way> ways = parents.getWays();
     180                Collection<Way> ways = parents.getWays();
    171181
    172             if (!ways.isEmpty()) {
    173                 // Collect incomplete nodes of parent ways
    174                 Set<Node> nodes = ways.stream().flatMap(w -> w.getNodes().stream().filter(OsmPrimitive::isIncomplete))
    175                         .collect(Collectors.toSet());
    176                 if (!nodes.isEmpty()) {
    177                     reader = MultiFetchServerObjectReader.create();
    178                     ((MultiFetchServerObjectReader) reader).append(nodes);
    179                     DataSet wayNodes = reader.parseOsm(progressMonitor.createSubTaskMonitor(1, false));
    180                     synchronized (this) { // avoid race condition in cancel()
    181                         reader = null;
     182                if (!ways.isEmpty()) {
     183                    // Collect incomplete nodes of parent ways
     184                    Set<Node> nodes = ways.stream().flatMap(w -> w.getNodes().stream().filter(OsmPrimitive::isIncomplete))
     185                            .collect(Collectors.toSet());
     186                    if (!nodes.isEmpty()) {
     187                        reader = MultiFetchServerObjectReader.create();
     188                        ((MultiFetchServerObjectReader) reader).append(nodes);
     189                        DataSet wayNodes = reader.parseOsm(progressMonitor.createSubTaskMonitor(1, false));
     190                        synchronized (this) { // avoid race condition in cancel()
     191                            reader = null;
     192                        }
     193                        new DataSetMerger(parents, wayNodes).merge();
    182194                    }
    183                     new DataSetMerger(parents, wayNodes).merge();
    184195                }
    185196            }
    186197        } catch (OsmTransferException e) {
     
    189200            lastException = e;
    190201        }
    191202    }
     203
    192204}
  • 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) {
    117142            currentTask = new DownloadReferrersTask(tmpLayer, ids);
     
    139164            layer.mergeFrom(tmpLayer);
    140165
    141166        // Warm about missing primitives
    142         final Set<PrimitiveId> errs = mainTask.getMissingPrimitives();
     167        final Set<PrimitiveId> errs = missingPrimitives;
    143168        if (errs != null && !errs.isEmpty())
    144169            GuiHelper.runInEDTAndWait(() -> reportProblemDialog(errs,
    145170                    trn("Object could not be downloaded", "Some objects could not be downloaded", errs.size()),
     
    185210                return null;
    186211        }
    187212        List<PrimitiveId> downloaded = new ArrayList<>(ids);
    188         downloaded.removeAll(mainTask.getMissingPrimitives());
     213        downloaded.removeAll(missingPrimitives);
    189214        return downloaded;
    190215    }
    191216
  • 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:
     
    5661        return query;
    5762    }
    5863
     64    /**
     65     * Generate single overpass query to retrieve multiple primitives. Can be used to download parents,
     66     * children, the objects, or any combination of them.
     67     * @param ids the collection of ids
     68     * @param includeObjects if false, don't retrieve the primitives (e.g. only the referrers)
     69     * @param recurseUp if true, referrers (parents) of the objects are downloaded and all nodes of parent ways
     70     * @param recurseDownRelations true: yes, recurse down to retrieve complete relations
     71     * @return the overpass query
     72     */
     73    public static String genOverpassQuery(Collection<PrimitiveId> ids, boolean includeObjects, boolean recurseUp,
     74            boolean recurseDownRelations) {
     75        Map<OsmPrimitiveType, Set<Long>> primitivesMap = new TreeMap<>();
     76        for (PrimitiveId p : ids) {
     77            primitivesMap.computeIfAbsent(p.getType(), k -> new TreeSet<>()).add(p.getUniqueId());
     78        }
     79        return genOverpassQuery(primitivesMap, includeObjects, recurseUp, recurseDownRelations);
     80    }
     81
     82    /**
     83     * Generate single overpass query to retrieve multiple primitives. Can be used to download parents,
     84     * children, the objects, or any combination of them.
     85     * @param primitivesMap map containing the primitives
     86     * @param includeObjects if false, don't retrieve the primitives (e.g. only the referrers)
     87     * @param recurseUp if true, referrers (parents) of the objects are downloaded and all nodes of parent ways
     88     * @param recurseDownRelations true: yes, recurse down to retrieve complete relations
     89     * @return the overpass query
     90     */
     91    public static String genOverpassQuery(Map<OsmPrimitiveType, Set<Long>> primitivesMap, boolean includeObjects,
     92            boolean recurseUp, boolean recurseDownRelations) {
     93        StringBuilder sb = new StringBuilder(128);
     94        StringBuilder setsToInclude = new StringBuilder("(");
     95        for (Entry<OsmPrimitiveType, Set<Long>> e : primitivesMap.entrySet()) {
     96            if (!e.getValue().isEmpty()) {
     97                sb.append(getPackageString(e.getKey(), e.getValue()));
     98                switch (e.getKey()) {
     99                case NODE:
     100                    sb.append("->.n;");
     101                    if (includeObjects) {
     102                        setsToInclude.append(".n;");
     103                    }
     104                    if (recurseUp) {
     105                        sb.append(".n;way(bn)->.wn;.n;rel(bn)->.rn;");
     106                        setsToInclude.append(".wn;node(w);.rn;");
     107                    }
     108                    break;
     109                case CLOSEDWAY:
     110                case WAY:
     111                    sb.append("->.w;");
     112                    if (includeObjects) {
     113                        setsToInclude.append(".w;>;");
     114                    }
     115                    if (recurseUp) {
     116                        sb.append(".w;rel(bw)->.pw;");
     117                        setsToInclude.append(".pw;");
     118                    }
     119                    break;
     120                case MULTIPOLYGON:
     121                case RELATION:
     122                    sb.append("->.r;");
     123                    if (includeObjects) {
     124                        setsToInclude.append(".r;");
     125                    }
     126                    if (recurseUp) {
     127                        sb.append(".r;rel(br)->.pr;");
     128                        setsToInclude.append(".pr;");
     129                    }
     130                    if (recurseDownRelations) {
     131                        sb.append(".r;>>->.rm;");
     132                        setsToInclude.append(".rm;");
     133                    }
     134                    break;
     135                }
     136            }
     137        }
     138        setsToInclude.append(");");
     139        sb.append(setsToInclude).append("out meta;");
     140        String query = sb.toString();
     141        Logging.debug("{0} {1}", "Possible Overpass query:", query);
     142        return query;
     143    }
     144
    59145    @Override
    60146    protected String getBaseUrl() {
    61147        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 {