Ticket #12303: 12303.5.patch
File 12303.5.patch, 19.5 KB (added by , 4 years ago) |
---|
-
src/org/openstreetmap/josm/actions/downloadtasks/DownloadReferrersTask.java
14 14 import javax.swing.JOptionPane; 15 15 import javax.swing.SwingUtilities; 16 16 17 import org.openstreetmap.josm.data.Bounds; 17 18 import org.openstreetmap.josm.data.osm.DataSet; 18 19 import org.openstreetmap.josm.data.osm.DataSetMerger; 19 20 import org.openstreetmap.josm.data.osm.Node; … … 26 27 import org.openstreetmap.josm.gui.PleaseWaitRunnable; 27 28 import org.openstreetmap.josm.gui.layer.OsmDataLayer; 28 29 import org.openstreetmap.josm.gui.progress.ProgressMonitor; 30 import org.openstreetmap.josm.io.MultiFetchOverpassObjectReader; 29 31 import org.openstreetmap.josm.io.MultiFetchServerObjectReader; 30 32 import org.openstreetmap.josm.io.OsmServerBackreferenceReader; 31 33 import org.openstreetmap.josm.io.OsmServerReader; 32 34 import org.openstreetmap.josm.io.OsmTransferException; 35 import org.openstreetmap.josm.io.OverpassDownloadReader; 33 36 import org.openstreetmap.josm.tools.CheckParameterUtil; 34 37 import org.openstreetmap.josm.tools.ExceptionUtil; 35 38 import org.xml.sax.SAXException; … … 154 157 @Override 155 158 protected void realRun() throws SAXException, IOException, OsmTransferException { 156 159 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++; 169 183 } 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(); 175 185 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(); 186 198 } 187 new DataSetMerger(parents, wayNodes).merge();188 199 } 189 200 } 190 201 } catch (OsmTransferException e) { … … 193 204 lastException = e; 194 205 } 195 206 } 207 196 208 } -
src/org/openstreetmap/josm/gui/io/DownloadFromOverpassTask.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.gui.io; 3 4 import static org.openstreetmap.josm.tools.I18n.tr; 5 6 import java.io.IOException; 7 8 import org.openstreetmap.josm.data.Bounds; 9 import org.openstreetmap.josm.data.osm.DataSet; 10 import org.openstreetmap.josm.data.osm.DataSetMerger; 11 import org.openstreetmap.josm.gui.ExceptionDialogUtil; 12 import org.openstreetmap.josm.gui.PleaseWaitRunnable; 13 import org.openstreetmap.josm.gui.progress.ProgressMonitor; 14 import org.openstreetmap.josm.io.OsmTransferException; 15 import org.openstreetmap.josm.io.OverpassDownloadReader; 16 import org.xml.sax.SAXException; 17 18 /** 19 * Download OSM data from Overpass API 20 * 21 */ 22 public 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
31 31 import org.openstreetmap.josm.gui.util.GuiHelper; 32 32 import org.openstreetmap.josm.gui.widgets.HtmlPanel; 33 33 import org.openstreetmap.josm.gui.widgets.JosmTextArea; 34 import org.openstreetmap.josm.io.MultiFetchOverpassObjectReader; 34 35 import org.openstreetmap.josm.io.OsmTransferException; 36 import org.openstreetmap.josm.io.OverpassDownloadReader; 35 37 import org.openstreetmap.josm.tools.GBC; 36 38 import org.xml.sax.SAXException; 37 39 … … 50 52 51 53 /** Temporary layer where downloaded primitives are put */ 52 54 private final OsmDataLayer tmpLayer; 53 /** Reference to the task that download requested primitives */54 private DownloadPrimitivesTask mainTask;55 55 /** Flag indicated that user ask for cancel this task */ 56 56 private boolean canceled; 57 57 /** Reference to the task currently running */ 58 58 private PleaseWaitRunnable currentTask; 59 private Set<PrimitiveId> missingPrimitives; 59 60 60 61 /** 61 62 * Constructor … … 101 102 102 103 @Override 103 104 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() { 104 124 getProgressMonitor().setTicksCount(ids.size()+1); 105 125 // 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)); 107 128 synchronized (this) { 108 129 currentTask = mainTask; 109 130 if (canceled) { … … 111 132 return; 112 133 } 113 134 } 135 114 136 currentTask.run(); 137 138 missingPrimitives = mainTask.getMissingPrimitives(); 139 115 140 // Then, download referrers for each primitive 116 141 if (downloadReferrers && tmpLayer.data != null) { 117 142 // see #18895: don't try to download parents for invisible objects … … 144 169 layer.mergeFrom(tmpLayer); 145 170 146 171 // Warm about missing primitives 147 final Set<PrimitiveId> errs = m ainTask.getMissingPrimitives();172 final Set<PrimitiveId> errs = missingPrimitives; 148 173 if (errs != null && !errs.isEmpty()) 149 174 GuiHelper.runInEDTAndWait(() -> reportProblemDialog(errs, 150 175 trn("Object could not be downloaded", "Some objects could not be downloaded", errs.size()), … … 190 215 return null; 191 216 } 192 217 List<PrimitiveId> downloaded = new ArrayList<>(ids); 193 downloaded.removeAll(m ainTask.getMissingPrimitives());218 downloaded.removeAll(missingPrimitives); 194 219 return downloaded; 195 220 } 196 221 -
src/org/openstreetmap/josm/io/MultiFetchOverpassObjectReader.java
1 1 // License: GPL. For details, see LICENSE file. 2 2 package org.openstreetmap.josm.io; 3 3 4 import java.util.Collection; 5 import java.util.Map; 4 6 import java.util.Map.Entry; 5 7 import java.util.Set; 8 import java.util.TreeMap; 9 import java.util.TreeSet; 6 10 import java.util.stream.Collectors; 7 11 8 12 import org.openstreetmap.josm.data.osm.OsmPrimitiveType; 13 import org.openstreetmap.josm.data.osm.PrimitiveId; 9 14 import org.openstreetmap.josm.tools.Logging; 10 15 11 16 /** … … 17 22 18 23 private static String getPackageString(final OsmPrimitiveType type, Set<Long> idPackage) { 19 24 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:"), ")")); 21 26 } 22 27 23 28 /** … … 30 35 for (Entry<OsmPrimitiveType, Set<Long>> e : primitivesMap.entrySet()) { 31 36 if (!e.getValue().isEmpty()) { 32 37 countTypes++; 33 String list = getPackageString(e.getKey(), e.getValue()) ;38 String list = getPackageString(e.getKey(), e.getValue()) + ";"; 34 39 switch (e.getKey()) { 35 40 case MULTIPOLYGON: 36 41 case RELATION: … … 61 66 return query; 62 67 } 63 68 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 64 151 @Override 65 152 protected String getBaseUrl() { 66 153 return OverpassDownloadReader.OVERPASS_SERVER.get(); -
src/org/openstreetmap/josm/io/MultiFetchServerObjectReader.java
12 12 import java.util.Collections; 13 13 import java.util.HashSet; 14 14 import java.util.Iterator; 15 import java.util.LinkedHashMap;16 15 import java.util.LinkedHashSet; 17 16 import java.util.List; 18 17 import java.util.Map; 19 18 import java.util.Map.Entry; 20 19 import java.util.Set; 20 import java.util.TreeMap; 21 21 import java.util.concurrent.Callable; 22 22 import java.util.concurrent.CompletionService; 23 23 import java.util.concurrent.ExecutionException; … … 89 89 relations = new LinkedHashSet<>(); 90 90 this.outputDataSet = new DataSet(); 91 91 this.missingPrimitives = new LinkedHashSet<>(); 92 primitivesMap = new LinkedHashMap<>();92 primitivesMap = new TreeMap<>(); 93 93 primitivesMap.put(OsmPrimitiveType.RELATION, relations); 94 94 primitivesMap.put(OsmPrimitiveType.WAY, ways); 95 95 primitivesMap.put(OsmPrimitiveType.NODE, nodes); … … 382 382 try { 383 383 if (this instanceof MultiFetchOverpassObjectReader) { 384 384 // calculate a single request for all the objects 385 String request = ((MultiFetchOverpassObjectReader) this).buildComplexRequestString();385 String request = MultiFetchOverpassObjectReader.genOverpassQuery(primitivesMap, true, false, recurseDownRelations); 386 386 if (isCanceled()) 387 387 return null; 388 388 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)); 390 390 new DataSetMerger(outputDataSet, ds).merge(); 391 391 checkMissing(outputDataSet, progressMonitor); 392 392 } else {