Ticket #12303: 12303.4.patch
| File 12303.4.patch, 19.4 KB (added by , 6 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; … … 150 153 @Override 151 154 protected void realRun() throws SAXException, IOException, OsmTransferException { 152 155 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++; 165 179 } 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(); 171 181 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(); 182 194 } 183 new DataSetMerger(parents, wayNodes).merge();184 195 } 185 196 } 186 197 } catch (OsmTransferException e) { … … 189 200 lastException = e; 190 201 } 191 202 } 203 192 204 } -
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) { 117 142 currentTask = new DownloadReferrersTask(tmpLayer, ids); … … 139 164 layer.mergeFrom(tmpLayer); 140 165 141 166 // Warm about missing primitives 142 final Set<PrimitiveId> errs = m ainTask.getMissingPrimitives();167 final Set<PrimitiveId> errs = missingPrimitives; 143 168 if (errs != null && !errs.isEmpty()) 144 169 GuiHelper.runInEDTAndWait(() -> reportProblemDialog(errs, 145 170 trn("Object could not be downloaded", "Some objects could not be downloaded", errs.size()), … … 185 210 return null; 186 211 } 187 212 List<PrimitiveId> downloaded = new ArrayList<>(ids); 188 downloaded.removeAll(m ainTask.getMissingPrimitives());213 downloaded.removeAll(missingPrimitives); 189 214 return downloaded; 190 215 } 191 216 -
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: … … 56 61 return query; 57 62 } 58 63 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 59 145 @Override 60 146 protected String getBaseUrl() { 61 147 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 {
