Ignore:
Timestamp:
2020-06-13T06:35:37+02:00 (4 years ago)
Author:
GerdP
Message:

fix #12303: When downloading objects with Overpass API, use recurse up to fetch referrers

  • new static method genOverpassQuery() to generate a single overpass query for all wanted objects
  • use POST instead of PUT to send the query
  • add handling for missing primitives (Overpass doesn't return invisible objects and doesn't a rc 404)
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/io/MultiFetchOverpassObjectReader.java

    r16008 r16611  
    22package org.openstreetmap.josm.io;
    33
    4 import java.util.Map.Entry;
     4import java.util.Arrays;
     5import java.util.Collection;
     6import java.util.LinkedHashMap;
     7import java.util.List;
     8import java.util.Map;
    59import java.util.Set;
     10import java.util.TreeSet;
    611import java.util.stream.Collectors;
    712
    813import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
     14import org.openstreetmap.josm.data.osm.PrimitiveId;
    915import org.openstreetmap.josm.tools.Logging;
    1016
     
    1521 */
    1622public class MultiFetchOverpassObjectReader extends MultiFetchServerObjectReader {
     23    private static final List<OsmPrimitiveType> wantedOrder = Arrays.asList(OsmPrimitiveType.RELATION,
     24            OsmPrimitiveType.WAY, OsmPrimitiveType.NODE);
    1725
    1826    private static String getPackageString(final OsmPrimitiveType type, Set<Long> idPackage) {
    1927        return idPackage.stream().map(String::valueOf)
    20                 .collect(Collectors.joining(",", type.getAPIName() + (idPackage.size() == 1 ? "(" : "(id:"), ");"));
     28                .collect(Collectors.joining(",", type.getAPIName() + (idPackage.size() == 1 ? "(" : "(id:"), ")"));
    2129    }
    2230
    2331    /**
    24      * Create a single query for all elements
    25      * @return the request string
     32     * Generate single overpass query to retrieve multiple primitives. Can be used to download parents,
     33     * children, the objects, or any combination of them.
     34     * @param ids the collection of ids
     35     * @param includeObjects if false, don't retrieve the primitives (e.g. only the referrers)
     36     * @param recurseUp if true, referrers (parents) of the objects are downloaded and all nodes of parent ways
     37     * @param recurseDownRelations true: yes, recurse down to retrieve complete relations
     38     * @return the overpass query
     39     * @since xxx
    2640     */
    27     protected String buildComplexRequestString() {
    28         StringBuilder sb = new StringBuilder();
    29         int countTypes = 0;
    30         for (Entry<OsmPrimitiveType, Set<Long>> e : primitivesMap.entrySet()) {
    31             if (!e.getValue().isEmpty()) {
    32                 countTypes++;
    33                 String list = getPackageString(e.getKey(), e.getValue());
    34                 switch (e.getKey()) {
    35                 case MULTIPOLYGON:
    36                 case RELATION:
     41    public static String genOverpassQuery(Collection<? extends PrimitiveId> ids, boolean includeObjects, boolean recurseUp,
     42            boolean recurseDownRelations) {
     43        Map<OsmPrimitiveType, Set<Long>> primitivesMap = new LinkedHashMap<>();
     44        Arrays.asList(OsmPrimitiveType.RELATION, OsmPrimitiveType.WAY, OsmPrimitiveType.NODE)
     45                .forEach(type -> primitivesMap.put(type, new TreeSet<>()));
     46        for (PrimitiveId p : ids) {
     47            primitivesMap.get(p.getType()).add(p.getUniqueId());
     48        }
     49        return genOverpassQuery(primitivesMap, includeObjects, recurseUp, recurseDownRelations);
     50    }
     51
     52    /**
     53     * Generate single overpass query to retrieve multiple primitives. Can be used to download parents,
     54     * children, the objects, or any combination of them.
     55     * @param primitivesMap map containing the primitives
     56     * @param includeObjects if false, don't retrieve the primitives (e.g. only the referrers)
     57     * @param recurseUp if true, referrers (parents) of the objects are downloaded and all nodes of parent ways
     58     * @param recurseDownRelations true: yes, recurse down to retrieve complete relations
     59     * @return the overpass query
     60     */
     61    protected static String genOverpassQuery(Map<OsmPrimitiveType, Set<Long>> primitivesMap, boolean includeObjects,
     62            boolean recurseUp, boolean recurseDownRelations) {
     63        if (!(includeObjects || recurseUp || recurseDownRelations))
     64            throw new IllegalArgumentException("At least one options must be true");
     65        StringBuilder sb = new StringBuilder(128);
     66        StringBuilder setsToInclude = new StringBuilder();
     67        StringBuilder up = new StringBuilder();
     68        String down = null;
     69        for (OsmPrimitiveType type : wantedOrder) {
     70            Set<Long> set = primitivesMap.get(type);
     71            if (!set.isEmpty()) {
     72                sb.append(getPackageString(type, set));
     73                if (type == OsmPrimitiveType.NODE) {
     74                    sb.append("->.n;");
     75                    if (includeObjects) {
     76                        setsToInclude.append(".n;");
     77                    }
     78                    if (recurseUp) {
     79                        up.append(".n;way(bn)->.wn;.n;rel(bn)->.rn;");
     80                        setsToInclude.append(".wn;node(w);.rn;");
     81                    }
     82                } else if (type == OsmPrimitiveType.WAY) {
     83                    sb.append("->.w;");
     84                    if (includeObjects) {
     85                        setsToInclude.append(".w;>;");
     86                    }
     87                    if (recurseUp) {
     88                        up.append(".w;rel(bw)->.pw;");
     89                        setsToInclude.append(".pw;");
     90                    }
     91                } else {
     92                    sb.append("->.r;");
     93                    if (includeObjects) {
     94                        setsToInclude.append(".r;");
     95                    }
     96                    if (recurseUp) {
     97                        up.append(".r;rel(br)->.pr;");
     98                        setsToInclude.append(".pr;");
     99                    }
    37100                    if (recurseDownRelations) {
    38                         sb.append('(').append(list);
    39                         sb.setLength(sb.length()-1); // remove semicolon
    40                         //recurse down only one level, see #18835
    41                         sb.append("->.r;.r;rel(r);.r;way(r);>;.r;node(r););");
    42                     } else {
    43                         sb.append(list);
     101                        // get complete ways and nodes of the relation and next level of sub relations
     102                        down = ".r;rel(r)->.rm;";
     103                        setsToInclude.append(".r;>;.rm;");
    44104                    }
    45                     break;
    46                 case CLOSEDWAY:
    47                 case WAY:
    48                     sb.append('(').append(list).append(">;);");
    49                     break;
    50                 case NODE:
    51                     sb.append(list);
    52105                }
    53106            }
    54107        }
     108        if (up.length() > 0) {
     109            sb.append(up);
     110        }
     111        if (down != null) {
     112            sb.append(down);
     113        }
     114        sb.append('(').append(setsToInclude).append(");");
     115
     116        sb.append("out meta;");
    55117        String query = sb.toString();
    56         if (countTypes > 1) {
    57             query = "(" + query + ");";
    58         }
    59         query += "out meta;";
    60118        Logging.debug("{0} {1}", "Generated Overpass query:", query);
    61119        return query;
Note: See TracChangeset for help on using the changeset viewer.