- Timestamp:
- 2020-02-03T06:35:22+01:00 (5 years ago)
- Location:
- trunk
- Files:
-
- 9 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/gui/dialogs/relation/ChildRelationBrowser.java
r14713 r15811 13 13 import java.awt.event.MouseEvent; 14 14 import java.io.IOException; 15 import java.net.HttpURLConnection;16 15 import java.util.Arrays; 17 16 import java.util.HashSet; 18 import java.util.Iterator;19 17 import java.util.List; 20 18 import java.util.Set; 21 import java.util.Stack;22 19 import java.util.stream.Collectors; 23 20 … … 38 35 import org.openstreetmap.josm.data.osm.DefaultNameFormatter; 39 36 import org.openstreetmap.josm.data.osm.OsmPrimitive; 40 import org.openstreetmap.josm.data.osm.OsmPrimitiveType;41 37 import org.openstreetmap.josm.data.osm.Relation; 42 38 import org.openstreetmap.josm.data.osm.RelationMember; … … 48 44 import org.openstreetmap.josm.gui.progress.ProgressMonitor; 49 45 import org.openstreetmap.josm.gui.progress.swing.PleaseWaitProgressMonitor; 46 import org.openstreetmap.josm.gui.util.GuiHelper; 50 47 import org.openstreetmap.josm.gui.widgets.PopupMenuLauncher; 48 import org.openstreetmap.josm.io.MultiFetchServerObjectReader; 51 49 import org.openstreetmap.josm.io.OsmApi; 52 import org.openstreetmap.josm.io.OsmApiException;53 import org.openstreetmap.josm.io.OsmServerObjectReader;54 50 import org.openstreetmap.josm.io.OsmTransferException; 55 51 import org.openstreetmap.josm.tools.CheckParameterUtil; … … 340 336 } 341 337 338 protected MultiFetchServerObjectReader createReader() { 339 return MultiFetchServerObjectReader.create().setRecurseDownAppended(false).setRecurseDownRelations(true); 340 } 341 342 /** 343 * Merges the primitives in <code>ds</code> to the dataset of the edit layer 344 * 345 * @param ds the data set 346 */ 347 protected void mergeDataSet(DataSet ds) { 348 if (ds != null) { 349 final DataSetMerger visitor = new DataSetMerger(getLayer().getDataSet(), ds); 350 visitor.merge(); 351 if (!visitor.getConflicts().isEmpty()) { 352 getLayer().getConflicts().add(visitor.getConflicts()); 353 conflictsCount += visitor.getConflicts().size(); 354 } 355 } 356 } 357 342 358 protected void refreshView(Relation relation) { 343 for (int i = 0; i < childTree.getRowCount(); i++) { 344 Relation reference = (Relation) childTree.getPathForRow(i).getLastPathComponent(); 345 if (reference == relation) { 346 model.refreshNode(childTree.getPathForRow(i)); 347 } 348 } 359 GuiHelper.runInEDT(() -> { 360 for (int i = 0; i < childTree.getRowCount(); i++) { 361 Relation reference = (Relation) childTree.getPathForRow(i).getLastPathComponent(); 362 if (reference == relation) { 363 model.refreshNode(childTree.getPathForRow(i)); 364 } 365 } 366 }); 349 367 } 350 368 … … 375 393 */ 376 394 class DownloadAllChildrenTask extends DownloadTask { 377 private final Stack<Relation> relationsToDownload; 378 private final Set<Long> downloadedRelationIds; 395 private final Relation relation; 379 396 380 397 DownloadAllChildrenTask(Dialog parent, Relation r) { 381 398 super(tr("Download relation members"), parent); 382 relationsToDownload = new Stack<>(); 383 downloadedRelationIds = new HashSet<>(); 384 relationsToDownload.push(r); 399 relation = r; 385 400 } 386 401 … … 406 421 } 407 422 408 /**409 * Remembers the child relations to download410 *411 * @param parent the parent relation412 */413 protected void rememberChildRelationsToDownload(Relation parent) {414 downloadedRelationIds.add(parent.getId());415 for (RelationMember member: parent.getMembers()) {416 if (member.isRelation()) {417 Relation child = member.getRelation();418 if (!downloadedRelationIds.contains(child.getId())) {419 relationsToDownload.push(child);420 }421 }422 }423 }424 425 /**426 * Merges the primitives in <code>ds</code> to the dataset of the edit layer427 *428 * @param ds the data set429 */430 protected void mergeDataSet(DataSet ds) {431 if (ds != null) {432 final DataSetMerger visitor = new DataSetMerger(getLayer().getDataSet(), ds);433 visitor.merge();434 if (!visitor.getConflicts().isEmpty()) {435 getLayer().getConflicts().add(visitor.getConflicts());436 conflictsCount += visitor.getConflicts().size();437 }438 }439 }440 441 423 @Override 442 424 protected void realRun() throws SAXException, IOException, OsmTransferException { 443 425 try { 444 while (!relationsToDownload.isEmpty() && !canceled) { 445 Relation r = relationsToDownload.pop(); 446 if (r.isNew()) { 447 continue; 448 } 449 rememberChildRelationsToDownload(r); 450 progressMonitor.setCustomText(tr("Downloading relation {0}", r.getDisplayName(DefaultNameFormatter.getInstance()))); 451 OsmServerObjectReader reader = new OsmServerObjectReader(r.getId(), OsmPrimitiveType.RELATION, 452 true); 453 DataSet dataSet = null; 454 try { 455 dataSet = reader.parseOsm(progressMonitor 456 .createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false)); 457 } catch (OsmApiException e) { 458 if (e.getResponseCode() == HttpURLConnection.HTTP_GONE) { 459 warnBecauseOfDeletedRelation(r); 460 continue; 461 } 462 throw e; 463 } 464 mergeDataSet(dataSet); 465 refreshView(r); 426 MultiFetchServerObjectReader reader = createReader(); 427 reader.append(relation.getMemberPrimitives()); 428 DataSet dataSet = reader.parseOsm(progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false)); 429 mergeDataSet(dataSet); 430 Utils.filteredCollection(reader.getMissingPrimitives(), Relation.class).forEach(this::warnBecauseOfDeletedRelation); 431 for (Relation rel : dataSet.getRelations()) { 432 refreshView((Relation) getLayer().getDataSet().getPrimitiveById(rel)); 466 433 } 467 434 SwingUtilities.invokeLater(MainApplication.getMap()::repaint); … … 487 454 } 488 455 489 protected void mergeDataSet(DataSet dataSet) {490 if (dataSet != null) {491 final DataSetMerger visitor = new DataSetMerger(getLayer().getDataSet(), dataSet);492 visitor.merge();493 if (!visitor.getConflicts().isEmpty()) {494 getLayer().getConflicts().add(visitor.getConflicts());495 conflictsCount += visitor.getConflicts().size();496 }497 }498 }499 500 456 @Override 501 457 protected void realRun() throws SAXException, IOException, OsmTransferException { 502 458 try { 503 Iterator<Relation> it = relations.iterator(); 504 while (it.hasNext() && !canceled) { 505 Relation r = it.next(); 506 if (r.isNew()) { 507 continue; 508 } 509 progressMonitor.setCustomText(tr("Downloading relation {0}", r.getDisplayName(DefaultNameFormatter.getInstance()))); 510 OsmServerObjectReader reader = new OsmServerObjectReader(r.getId(), OsmPrimitiveType.RELATION, 511 true); 512 DataSet dataSet = reader.parseOsm(progressMonitor 513 .createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false)); 514 mergeDataSet(dataSet); 515 refreshView(r); 516 } 459 MultiFetchServerObjectReader reader = createReader(); 460 reader.append(relations); 461 DataSet dataSet = reader.parseOsm(progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false)); 462 mergeDataSet(dataSet); 463 464 for (Relation rel : dataSet.getRelations()) { 465 refreshView((Relation) getLayer().getDataSet().getPrimitiveById(rel)); 466 } 467 517 468 } catch (OsmTransferException e) { 518 469 if (canceled) { -
trunk/src/org/openstreetmap/josm/gui/dialogs/relation/DownloadRelationMemberTask.java
r13486 r15811 130 130 objectReader = MultiFetchServerObjectReader.create(); 131 131 } 132 objectReader.setRecurseDownAppended(false).setRecurseDownRelations(true); 132 133 objectReader.append(children); 133 134 progressMonitor.indeterminateSubTask( -
trunk/src/org/openstreetmap/josm/gui/dialogs/relation/DownloadRelationTask.java
r13486 r15811 12 12 import org.openstreetmap.josm.data.osm.DataSet; 13 13 import org.openstreetmap.josm.data.osm.DataSetMerger; 14 import org.openstreetmap.josm.data.osm.DefaultNameFormatter;15 14 import org.openstreetmap.josm.data.osm.Relation; 16 15 import org.openstreetmap.josm.gui.ExceptionDialogUtil; … … 18 17 import org.openstreetmap.josm.gui.PleaseWaitRunnable; 19 18 import org.openstreetmap.josm.gui.layer.OsmDataLayer; 20 import org.openstreetmap.josm.io.OsmServerObjectReader; 19 import org.openstreetmap.josm.gui.progress.ProgressMonitor; 20 import org.openstreetmap.josm.io.MultiFetchServerObjectReader; 21 import org.openstreetmap.josm.io.OsmServerReader; 21 22 import org.openstreetmap.josm.io.OsmTransferException; 22 23 import org.openstreetmap.josm.tools.CheckParameterUtil; … … 34 35 private final Collection<Relation> relations; 35 36 private final OsmDataLayer layer; 36 private OsmServer ObjectReader objectReader;37 private OsmServerReader objectReader; 37 38 38 39 /** … … 78 79 try { 79 80 final DataSet allDownloads = new DataSet(); 80 int i = 0;81 81 getProgressMonitor().setTicksCount(relations.size()); 82 for (Relation relation: relations) { 83 i++; 84 getProgressMonitor().setCustomText(tr("({0}/{1}): Downloading relation ''{2}''...", i, relations.size(), 85 relation.getDisplayName(DefaultNameFormatter.getInstance()))); 86 synchronized (this) { 87 if (canceled) return; 88 objectReader = new OsmServerObjectReader(relation.getPrimitiveId(), true /* full download */); 89 } 90 DataSet dataSet = objectReader.parseOsm( 91 getProgressMonitor().createSubTaskMonitor(0, false) 92 ); 93 if (dataSet == null) 82 MultiFetchServerObjectReader multiObjectReader; 83 synchronized (this) { 84 if (canceled) 94 85 return; 95 synchronized (this) { 96 if (canceled) return; 97 objectReader = null; 98 } 99 DataSetMerger merger = new DataSetMerger(allDownloads, dataSet); 100 merger.merge(); 101 getProgressMonitor().worked(1); 86 multiObjectReader = MultiFetchServerObjectReader.create(); 102 87 } 103 88 multiObjectReader.setRecurseDownRelations(true).setRecurseDownAppended(false); 89 multiObjectReader.append(relations); 90 DataSet dataSet = multiObjectReader.parseOsm(progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false)); 91 if (dataSet == null) 92 return; 93 synchronized (this) { 94 if (canceled) 95 return; 96 } 97 new DataSetMerger(allDownloads, dataSet).merge(); 104 98 SwingUtilities.invokeAndWait(() -> { 105 99 layer.mergeFrom(allDownloads); -
trunk/src/org/openstreetmap/josm/gui/io/AbstractPrimitiveTask.java
r13486 r15811 12 12 import org.openstreetmap.josm.data.osm.OsmPrimitiveType; 13 13 import org.openstreetmap.josm.data.osm.PrimitiveId; 14 import org.openstreetmap.josm.data.osm.Relation;15 14 import org.openstreetmap.josm.data.osm.Way; 16 15 import org.openstreetmap.josm.gui.ExceptionDialogUtil; … … 42 41 43 42 private boolean zoom; 44 private boolean downloadRelations; 45 private boolean fullRelation; 43 protected boolean fullRelation; 46 44 47 45 protected AbstractPrimitiveTask(String title, OsmDataLayer layer) { … … 71 69 72 70 /** 73 * Sets whether . 74 * @param downloadRelations {@code true} if 71 * Sets whether all members of the relation should be downloaded completely. 75 72 * @param fullRelation {@code true} if a full download is required, 76 73 * i.e., a download including the immediate children of a relation. 77 74 * @return {@code this} 75 * since 15811 (changed parameter list) 78 76 */ 79 public final AbstractPrimitiveTask setDownloadRelations(boolean downloadRelations, boolean fullRelation) { 80 this.downloadRelations = downloadRelations; 77 public final AbstractPrimitiveTask setDownloadRelations(boolean fullRelation) { 81 78 this.fullRelation = fullRelation; 82 79 return this; … … 101 98 if (canceled) 102 99 return; 103 multiObjectReader = MultiFetchServerObjectReader.create() ;100 multiObjectReader = MultiFetchServerObjectReader.create().setRecurseDownRelations(fullRelation); 104 101 } 105 102 initMultiFetchReader(multiObjectReader); … … 111 108 new DataSetMerger(ds, theirDataSet).merge(); 112 109 113 if (downloadRelations) {114 loadIncompleteRelationMembers();115 }116 117 110 loadIncompleteNodes(); 118 111 } catch (OsmTransferException e) { … … 120 113 return; 121 114 lastException = e; 122 }123 }124 125 protected void loadIncompleteRelationMembers() throws OsmTransferException {126 // if incomplete relation members exist, download them too127 for (Relation r : ds.getRelations()) {128 if (canceled)129 return;130 // Relations may be incomplete in case of nested relations if child relations are accessed before their parent131 // (it may happen because "relations" has no deterministic sort order, see #10388)132 if (r.isIncomplete() || r.hasIncompleteMembers()) {133 synchronized (this) {134 if (canceled)135 return;136 objectReader = new OsmServerObjectReader(r.getId(), OsmPrimitiveType.RELATION, fullRelation);137 }138 DataSet theirDataSet = objectReader.parseOsm(progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false));139 synchronized (this) {140 objectReader = null;141 }142 new DataSetMerger(ds, theirDataSet).merge();143 }144 115 } 145 116 } -
trunk/src/org/openstreetmap/josm/gui/io/DownloadPrimitivesTask.java
r10129 r15811 6 6 import java.util.List; 7 7 8 import org.openstreetmap.josm.data.osm.Node;9 import org.openstreetmap.josm.data.osm.OsmPrimitive;10 8 import org.openstreetmap.josm.data.osm.PrimitiveId; 11 import org.openstreetmap.josm.data.osm.Relation;12 import org.openstreetmap.josm.data.osm.Way;13 9 import org.openstreetmap.josm.gui.layer.OsmDataLayer; 14 10 import org.openstreetmap.josm.gui.progress.ProgressMonitor; … … 53 49 this.ids = ids; 54 50 setZoom(true); 55 setDownloadRelations( true,fullRelation);51 setDownloadRelations(fullRelation); 56 52 } 57 53 … … 59 55 protected void initMultiFetchReader(MultiFetchServerObjectReader reader) { 60 56 getProgressMonitor().indeterminateSubTask(tr("Initializing nodes to download ...")); 61 for (PrimitiveId id : ids) { 62 OsmPrimitive osm = layer.data.getPrimitiveById(id); 63 if (osm == null) { 64 switch (id.getType()) { 65 case NODE: 66 osm = new Node(id.getUniqueId()); 67 break; 68 case WAY: 69 osm = new Way(id.getUniqueId()); 70 break; 71 case RELATION: 72 osm = new Relation(id.getUniqueId()); 73 break; 74 default: throw new AssertionError(); 75 } 76 } 77 reader.append(osm); 78 } 57 reader.setRecurseDownRelations(fullRelation); 58 reader.appendIds(ids); 79 59 } 80 60 } -
trunk/src/org/openstreetmap/josm/gui/io/UpdatePrimitivesTask.java
r10129 r15811 7 7 import java.util.Collections; 8 8 9 import org.openstreetmap.josm.data.osm.Node;10 9 import org.openstreetmap.josm.data.osm.OsmPrimitive; 11 import org.openstreetmap.josm.data.osm.Relation;12 import org.openstreetmap.josm.data.osm.Way;13 10 import org.openstreetmap.josm.gui.layer.OsmDataLayer; 14 11 import org.openstreetmap.josm.io.MultiFetchServerObjectReader; 12 import org.openstreetmap.josm.spi.preferences.Config; 15 13 16 14 /** … … 33 31 super(tr("Update objects"), layer); 34 32 this.toUpdate = toUpdate != null ? toUpdate : Collections.<OsmPrimitive>emptyList(); 35 } 36 37 protected void initMultiFetchReaderWithNodes(MultiFetchServerObjectReader reader) { 38 getProgressMonitor().indeterminateSubTask(tr("Initializing nodes to update ...")); 39 for (OsmPrimitive primitive : toUpdate) { 40 if (primitive instanceof Node && !primitive.isNew()) { 41 reader.append(primitive); 42 } 43 } 44 } 45 46 protected void initMultiFetchReaderWithWays(MultiFetchServerObjectReader reader) { 47 getProgressMonitor().indeterminateSubTask(tr("Initializing ways to update ...")); 48 for (OsmPrimitive primitive : toUpdate) { 49 if (primitive instanceof Way && !primitive.isNew()) { 50 // this also adds way nodes 51 reader.append(primitive); 52 } 53 } 54 } 55 56 protected void initMultiFetchReaderWithRelations(MultiFetchServerObjectReader reader) { 57 getProgressMonitor().indeterminateSubTask(tr("Initializing relations to update ...")); 58 for (OsmPrimitive primitive : toUpdate) { 59 if (primitive instanceof Relation && !primitive.isNew()) { 60 // this also adds relation members 61 reader.append(primitive); 62 } 63 } 33 setDownloadRelations(Config.getPref().getBoolean("update.selected.complete-relation", true)); 64 34 } 65 35 66 36 @Override 67 37 protected void initMultiFetchReader(MultiFetchServerObjectReader reader) { 68 initMultiFetchReaderWithNodes(reader); 69 initMultiFetchReaderWithWays(reader); 70 initMultiFetchReaderWithRelations(reader); 38 // don't update new primitives 39 toUpdate.stream().filter(p -> !p.isNew()).forEach(reader::append); 71 40 } 72 41 } -
trunk/src/org/openstreetmap/josm/io/MultiFetchOverpassObjectReader.java
r15717 r15811 2 2 package org.openstreetmap.josm.io; 3 3 4 import java.util.Map.Entry; 4 5 import java.util.Set; 5 6 import java.util.stream.Collectors; 6 7 7 8 import org.openstreetmap.josm.data.osm.OsmPrimitiveType; 8 import org.openstreetmap.josm.tools. Utils;9 import org.openstreetmap.josm.tools.Logging; 9 10 10 11 /** … … 13 14 * @since 9241 14 15 */ 15 class MultiFetchOverpassObjectReader extends MultiFetchServerObjectReader {16 public class MultiFetchOverpassObjectReader extends MultiFetchServerObjectReader { 16 17 17 @Override 18 protected String buildRequestString(final OsmPrimitiveType type, Set<Long> idPackage) { 19 final String query = idPackage.stream() 20 .map(x -> type.getAPIName() + '(' + x + ");>;") 21 .collect(Collectors.joining("", "(", ");out meta;")); 22 return "interpreter?data=" + Utils.encodeUrl(query); 18 private static String getPackageString(final OsmPrimitiveType type, Set<Long> idPackage) { 19 return idPackage.stream().map(String::valueOf) 20 .collect(Collectors.joining(",", type.getAPIName() + (idPackage.size() == 1 ? "(" : "(id:"), ");")); 21 } 22 23 /** 24 * Create a single query for all elements 25 * @return the request string 26 */ 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: 37 sb.append(list); 38 if (recurseDownRelations) 39 sb.append(">>;"); 40 break; 41 case CLOSEDWAY: 42 case WAY: 43 sb.append('(').append(list).append(">;);"); 44 break; 45 case NODE: 46 sb.append(list); 47 } 48 } 49 } 50 String query = sb.toString(); 51 if (countTypes > 1) { 52 query = "(" + query + ");"; 53 } 54 query += "out meta;"; 55 Logging.debug("{0} {1}", "Generated Overpass query:", query); 56 return query; 23 57 } 24 58 … … 27 61 return OverpassDownloadReader.OVERPASS_SERVER.get(); 28 62 } 29 30 @Override31 protected boolean recursesDown() {32 // see https://wiki.openstreetmap.org/wiki/Overpass_API/Overpass_QL#Recurse_down_.28.3E.29 for documentation33 // accomplished using >; in the query string above34 return true;35 }36 63 } -
trunk/src/org/openstreetmap/josm/io/MultiFetchServerObjectReader.java
r15717 r15811 13 13 import java.util.HashSet; 14 14 import java.util.Iterator; 15 import java.util.LinkedHashMap; 15 16 import java.util.LinkedHashSet; 16 17 import java.util.List; 18 import java.util.Map; 19 import java.util.Map.Entry; 17 20 import java.util.Set; 18 21 import java.util.concurrent.Callable; … … 25 28 import java.util.stream.Collectors; 26 29 30 import org.openstreetmap.josm.data.Bounds; 27 31 import org.openstreetmap.josm.data.osm.DataSet; 28 32 import org.openstreetmap.josm.data.osm.DataSetMerger; … … 64 68 * this leads to a max. request URL of ~ 1900 Bytes ((10 digits + 1 Separator) * 170), 65 69 * which should be safe according to the 66 * <a href="http ://www.boutell.com/newfaq/misc/urllength.html">WWW FAQ</a>.70 * <a href="https://web.archive.org/web/20190902193246/https://boutell.com/newfaq/misc/urllength.html">WWW FAQ</a>. 67 71 */ 68 72 private static final int MAX_IDS_PER_REQUEST = 170; … … 71 75 private final Set<Long> ways; 72 76 private final Set<Long> relations; 73 private Set<PrimitiveId> missingPrimitives;77 private final Set<PrimitiveId> missingPrimitives; 74 78 private final DataSet outputDataSet; 79 protected final Map<OsmPrimitiveType, Set<Long>> primitivesMap; 80 81 protected boolean recurseDownRelations; 82 private boolean recurseDownAppended = true; 75 83 76 84 /** … … 83 91 this.outputDataSet = new DataSet(); 84 92 this.missingPrimitives = new LinkedHashSet<>(); 93 primitivesMap = new LinkedHashMap<>(); 94 primitivesMap.put(OsmPrimitiveType.RELATION, relations); 95 primitivesMap.put(OsmPrimitiveType.WAY, ways); 96 primitivesMap.put(OsmPrimitiveType.NODE, nodes); 85 97 } 86 98 … … 160 172 */ 161 173 public MultiFetchServerObjectReader appendNode(Node node) { 162 if (node == null ) return this;174 if (node == null || node.isNew()) return this; 163 175 remember(node.getPrimitiveId()); 164 176 return this; … … 172 184 */ 173 185 public MultiFetchServerObjectReader appendWay(Way way) { 174 if (way == null) return this; 175 if (way.isNew()) return this; 176 for (Node node: !recursesDown() ? way.getNodes() : Collections.<Node>emptyList()) { 177 if (!node.isNew()) { 178 remember(node.getPrimitiveId()); 186 if (way == null || way.isNew()) return this; 187 if (recurseDownAppended) { 188 for (Node node : way.getNodes()) { 189 if (!node.isNew()) { 190 remember(node.getPrimitiveId()); 191 } 179 192 } 180 193 } … … 190 203 */ 191 204 protected MultiFetchServerObjectReader appendRelation(Relation relation) { 192 if (relation == null) return this; 193 if (relation.isNew()) return this; 205 if (relation == null || relation.isNew()) return this; 194 206 remember(relation.getPrimitiveId()); 195 for (RelationMember member : !recursesDown() ? relation.getMembers() : Collections.<RelationMember>emptyList()) { 196 // avoid infinite recursion in case of cyclic dependencies in relations 197 if (OsmPrimitiveType.from(member.getMember()) == OsmPrimitiveType.RELATION 198 && relations.contains(member.getMember().getId())) { 199 continue; 200 } 201 if (!member.getMember().isIncomplete()) { 202 append(member.getMember()); 207 if (recurseDownAppended) { 208 for (RelationMember member : relation.getMembers()) { 209 // avoid infinite recursion in case of cyclic dependencies in relations 210 if (OsmPrimitiveType.from(member.getMember()) == OsmPrimitiveType.RELATION 211 && relations.contains(member.getMember().getId())) { 212 continue; 213 } 214 if (!member.getMember().isIncomplete()) { 215 append(member.getMember()); 216 } 203 217 } 204 218 } … … 234 248 for (OsmPrimitive primitive : primitives) { 235 249 append(primitive); 250 } 251 return this; 252 } 253 254 /** 255 * appends a list of {@link PrimitiveId} to the list of ids which will be fetched from the server. 256 * 257 * @param ids the list of primitive Ids (ignored, if null) 258 * @return this 259 * @since 15811 260 * 261 */ 262 public MultiFetchServerObjectReader appendIds(Collection<PrimitiveId> ids) { 263 if (ids == null) 264 return this; 265 for (PrimitiveId id : ids) { 266 if (id.isNew()) continue; 267 switch (id.getType()) { 268 case NODE: 269 nodes.add(id.getUniqueId()); 270 break; 271 case WAY: 272 case CLOSEDWAY: 273 ways.add(id.getUniqueId()); 274 break; 275 case MULTIPOLYGON: 276 case RELATION: 277 relations.add(id.getUniqueId()); 278 break; 279 default: 280 throw new AssertionError(); 281 } 236 282 } 237 283 return this; … … 276 322 protected void rememberNodesOfIncompleteWaysToLoad(DataSet from) { 277 323 for (Way w: from.getWays()) { 278 if (w.hasIncompleteNodes()) { 279 for (Node n: w.getNodes()) { 280 if (n.isIncomplete()) { 281 nodes.add(n.getId()); 282 } 324 for (Node n: w.getNodes()) { 325 if (n.isIncomplete()) { 326 nodes.add(n.getId()); 283 327 } 284 328 } … … 373 417 * 374 418 * @return the parsed data 419 * @param progressMonitor progress monitor 375 420 * @throws OsmTransferException if an error occurs while communicating with the API server 376 421 * @see #getMissingPrimitives() … … 379 424 @Override 380 425 public DataSet parseOsm(ProgressMonitor progressMonitor) throws OsmTransferException { 426 missingPrimitives.clear(); 381 427 int n = nodes.size() + ways.size() + relations.size(); 382 428 progressMonitor.beginTask(trn("Downloading {0} object from ''{1}''", 383 "Downloading {0} objects from ''{1}''", n, n, OsmApi.getOsmApi().getBaseUrl()));429 "Downloading {0} objects from ''{1}''", n, n, getBaseUrl())); 384 430 try { 385 missingPrimitives = new HashSet<>(); 386 if (isCanceled()) return null; 387 fetchPrimitives(ways, OsmPrimitiveType.WAY, progressMonitor); 388 if (isCanceled()) return null; 389 fetchPrimitives(nodes, OsmPrimitiveType.NODE, progressMonitor); 390 if (isCanceled()) return null; 391 fetchPrimitives(relations, OsmPrimitiveType.RELATION, progressMonitor); 392 if (outputDataSet != null) { 393 outputDataSet.deleteInvisible(); 394 } 431 if (this instanceof MultiFetchOverpassObjectReader) { 432 // calculate a single request for all the objects 433 String request = ((MultiFetchOverpassObjectReader) this).buildComplexRequestString(); 434 if (isCanceled()) 435 return null; 436 OverpassDownloadReader reader = new OverpassDownloadReader(new Bounds(0, 0, 0, 0), getBaseUrl(), request); 437 DataSet ds = reader.parseOsm(progressMonitor.createSubTaskMonitor(n, false)); 438 new DataSetMerger(outputDataSet, ds).merge(); 439 checkMissing(outputDataSet, progressMonitor); 440 } else { 441 downloadRelations(progressMonitor); 442 if (isCanceled()) 443 return null; 444 fetchPrimitives(ways, OsmPrimitiveType.WAY, progressMonitor); 445 if (isCanceled()) 446 return null; 447 fetchPrimitives(nodes, OsmPrimitiveType.NODE, progressMonitor); 448 } 449 outputDataSet.deleteInvisible(); 395 450 return outputDataSet; 396 451 } finally { … … 400 455 401 456 /** 457 * Workaround for difference in Oerpass API. 458 * As of now (version 7.55) Overpass api doesn't return invisible objects. 459 * Check if we have objects which do not appear in the dataset and fetch them from OSM instead. 460 * @param ds the dataset 461 * @param progressMonitor progress monitor 462 * @throws OsmTransferException if an error occurs while communicating with the API server 463 */ 464 private void checkMissing(DataSet ds, ProgressMonitor progressMonitor) throws OsmTransferException { 465 Set<OsmPrimitive> missing = new LinkedHashSet<>(); 466 for (Entry<OsmPrimitiveType, Set<Long>> e : primitivesMap.entrySet()) { 467 for (long id : e.getValue()) { 468 if (ds.getPrimitiveById(id, e.getKey()) == null) 469 missing.add(e.getKey().newInstance(id, true)); 470 } 471 } 472 if (isCanceled() || missing.isEmpty()) 473 return; 474 475 MultiFetchServerObjectReader missingReader = MultiFetchServerObjectReader.create(false); 476 missingReader.setRecurseDownAppended(false); 477 missingReader.setRecurseDownRelations(false); 478 missingReader.append(missing); 479 DataSet mds = missingReader.parseOsm(progressMonitor.createSubTaskMonitor(missing.size(), false)); 480 new DataSetMerger(ds, mds).merge(); 481 missingPrimitives.addAll(missingReader.getMissingPrimitives()); 482 } 483 484 /** 485 * Finds best way to download a set of relations. 486 * @param progressMonitor progress monitor 487 * @throws OsmTransferException if an error occurs while communicating with the API server 488 * @see #getMissingPrimitives() 489 */ 490 private void downloadRelations(ProgressMonitor progressMonitor) throws OsmTransferException { 491 Set<Long> toDownload = new LinkedHashSet<>(relations); 492 fetchPrimitives(toDownload, OsmPrimitiveType.RELATION, progressMonitor); 493 if (!recurseDownRelations) { 494 return; 495 } 496 // OSM multi-fetch api may return invisible objects 497 for (Relation r : outputDataSet.getRelations()) { 498 if (!r.isVisible()) 499 toDownload.remove(r.getUniqueId()); 500 } 501 while (!toDownload.isEmpty()) { 502 if (isCanceled()) 503 return; 504 final Set<Long> addedRelations = new LinkedHashSet<>(); 505 506 for (long id : toDownload) { 507 OsmServerObjectReader reader = new OsmServerObjectReader(id, OsmPrimitiveType.RELATION, true /* full*/); 508 DataSet ds = reader.parseOsm(progressMonitor.createSubTaskMonitor(1, false)); 509 if (ds != null) { 510 ds.getRelations().stream().map(OsmPrimitive::getUniqueId).filter(uid -> uid != id) 511 .forEach(addedRelations::add); 512 } 513 merge(ds); 514 } 515 if (addedRelations.isEmpty()) 516 break; 517 toDownload = addedRelations; 518 } 519 } 520 521 /** 402 522 * replies the set of ids of all primitives for which a fetch request to the 403 523 * server was submitted but which are not available from the server (the server … … 411 531 412 532 /** 413 * Whether this reader fetches nodes when loading ways, or members when loading relations. 414 * 415 * @return {@code true} if the reader recurses down 416 */ 417 protected boolean recursesDown() { 418 return false; 533 * Should downloaded relations be complete? 534 * @param recurseDownRelations true: yes, recurse down to retrieve the complete relation 535 * @return this 536 * @since 15811 537 */ 538 public MultiFetchServerObjectReader setRecurseDownRelations(boolean recurseDownRelations) { 539 this.recurseDownRelations = recurseDownRelations; 540 return this; 541 } 542 543 /** 544 * Determine how appended objects are treated. By default, all children of an appended object are also appended. 545 * @param recurseAppended false: do not append known children of appended objects, i.e. all nodes of way and all members of a relation 546 * @return this 547 * @since 15811 548 */ 549 public MultiFetchServerObjectReader setRecurseDownAppended(boolean recurseAppended) { 550 this.recurseDownAppended = recurseAppended; 551 return this; 419 552 } 420 553 … … 539 672 } catch (IOException ex) { 540 673 Logging.warn(ex); 674 throw new OsmTransferException(ex); 541 675 } 542 676 return result; -
trunk/test/unit/org/openstreetmap/josm/io/MultiFetchOverpassObjectReaderTest.java
r15717 r15811 5 5 6 6 import java.util.Arrays; 7 import java.util.TreeSet; 7 8 import org.junit.Rule; 9 import org.junit.Test; 10 import org.openstreetmap.josm.data.osm.Node; 11 import org.openstreetmap.josm.data.osm.Relation; 12 import org.openstreetmap.josm.data.osm.Way; 13 import org.openstreetmap.josm.testutils.JOSMTestRules; 8 14 9 15 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 10 import org.junit.Rule;11 import org.junit.Test;12 import org.openstreetmap.josm.data.osm.OsmPrimitiveType;13 import org.openstreetmap.josm.testutils.JOSMTestRules;14 import org.openstreetmap.josm.tools.Utils;15 16 16 17 /** … … 30 31 */ 31 32 @Test 32 public void testBuildRequestString() { 33 String requestString = new MultiFetchOverpassObjectReader() 34 .buildRequestString(OsmPrimitiveType.WAY, new TreeSet<>(Arrays.asList(130L, 123L, 126L))); 35 assertEquals("interpreter?data=" + Utils.encodeUrl("(way(123);>;way(126);>;way(130);>;);out meta;"), requestString); 33 public void testBuildRequestWaysString() { 34 MultiFetchOverpassObjectReader reader = new MultiFetchOverpassObjectReader(); 35 reader.append(Arrays.asList(new Way(123), new Way(126), new Way(130))); 36 String requestString = reader.buildComplexRequestString(); 37 assertEquals("(way(id:123,126,130);>;);out meta;", requestString); 38 } 39 40 /** 41 * Test {@link MultiFetchOverpassObjectReader#buildRequestString} 42 */ 43 @Test 44 public void testBuildRequestRelationsString() { 45 MultiFetchOverpassObjectReader reader = new MultiFetchOverpassObjectReader(); 46 reader.append(Arrays.asList(new Relation(123), new Relation(126), new Relation(130))); 47 reader.setRecurseDownRelations(true); 48 String requestString = reader.buildComplexRequestString(); 49 assertEquals("relation(id:123,126,130);>>;out meta;", requestString); 50 reader.setRecurseDownRelations(false); 51 requestString = reader.buildComplexRequestString(); 52 assertEquals("relation(id:123,126,130);out meta;", requestString); 53 } 54 55 /** 56 * Test {@link MultiFetchOverpassObjectReader#buildRequestString} 57 */ 58 @Test 59 public void testBuildComplexString() { 60 MultiFetchOverpassObjectReader reader = new MultiFetchOverpassObjectReader(); 61 reader.setRecurseDownRelations(true); 62 reader.append(Arrays.asList(new Relation(123), new Relation(126), new Relation(130), new Way(88), new Way(99), 63 new Node(1))); 64 String requestString = reader.buildComplexRequestString(); 65 assertEquals("(relation(id:123,126,130);>>;(way(id:88,99);>;);node(1););out meta;", requestString); 66 reader.setRecurseDownRelations(false); 67 requestString = reader.buildComplexRequestString(); 68 assertEquals("(relation(id:123,126,130);(way(id:88,99);>;);node(1););out meta;", requestString); 36 69 } 37 70
Note:
See TracChangeset
for help on using the changeset viewer.