/** * Determine which ways to split. * * @param selectedWays List of user selected ways. * @param selectedNodes List of user selected nodes. * @return List of ways to split */ private List<Way> getApplicableWays(List<Way> selectedWays, List<Node> selectedNodes) { if (selectedNodes.isEmpty()) return null; // Special case - one of the selected ways touches (not cross) way that we // want to split if (selectedNodes.size() == 1) { Node n = selectedNodes.get(0); List<Way> referedWays = OsmPrimitive.getFilteredList(n.getReferrers(), Way.class); Way inTheMiddle = null; for (Way w : referedWays) { // Need to look at all nodes see #11184 for a case where node n is // firstNode, lastNode and also in the middle if (selectedWays.contains(w) && w.isInnerNode(n)) { if (inTheMiddle == null) { inTheMiddle = w; } else { inTheMiddle = null; break; } } } if (inTheMiddle != null) return Collections.singletonList(inTheMiddle); } // List of ways shared by all nodes List<Way> result = new ArrayList<>( OsmPrimitive.getFilteredList(selectedNodes.get(0).getReferrers(), Way.class)); for (int i = 1; i < selectedNodes.size(); i++) { List<OsmPrimitive> ref = selectedNodes.get(i).getReferrers(); for (Iterator<Way> it = result.iterator(); it.hasNext(); ) { if (!ref.contains(it.next())) { it.remove(); } } } // Remove broken ways for (Iterator<Way> it = result.iterator(); it.hasNext(); ) { if (it.next().getNodesCount() <= 2) { it.remove(); } } if (selectedWays.isEmpty()) return result; else { // Return only selected ways for (Iterator<Way> it = result.iterator(); it.hasNext(); ) { if (!selectedWays.contains(it.next())) { it.remove(); } } return result; } }
/** * Collect all nodes with more than one referrer. * * @param ways Ways from witch nodes are selected * @return List of nodes with more than one referrer */ private static List<Node> collectNodesWithExternReferers(List<Way> ways) { List<Node> withReferrers = new ArrayList<>(); for (Way w : ways) { for (Node n : w.getNodes()) { if (n.getReferrers().size() > 1) { withReferrers.add(n); } } } return withReferrers; }
/** * Determines if ways can be joined into a polygon. * * @param ways The ways collection to check * @return true if all ways can be joined into a polygon */ protected static boolean checkWaysArePolygon(Collection<Way> ways) { // For each way, nodes strictly between first and last should't be reference by an other way for (Way way : ways) { for (Node node : way.getNodes()) { if (way.isFirstLastNode(node)) continue; for (Way wayOther : ways) { if (way == wayOther) continue; if (node.getReferrers().contains(wayOther)) return false; } } } // Test if ways can be joined Way currentWay = null; Node startNode = null, endNode = null; int used = 0; while (true) { Way nextWay = null; for (Way w : ways) { if (w.isClosed()) return ways.size() == 1; if (w == currentWay) continue; if (currentWay == null) { nextWay = w; startNode = w.firstNode(); endNode = w.lastNode(); break; } if (w.firstNode() == endNode) { nextWay = w; endNode = w.lastNode(); break; } if (w.lastNode() == endNode) { nextWay = w; endNode = w.firstNode(); break; } } if (nextWay == null) return false; used += 1; currentWay = nextWay; if (endNode == startNode) return used == ways.size(); } }
@Override public void visit(Node n) { List<Relation> associatedStreets = getAndCheckAssociatedStreets(n); // Find house number without proper location (neither addr:street, associatedStreet, addr:place // or addr:interpolation) if (n.hasKey(ADDR_HOUSE_NUMBER) && !n.hasKey(ADDR_STREET) && !n.hasKey(ADDR_PLACE)) { for (Relation r : associatedStreets) { if (r.hasTag("type", ASSOCIATED_STREET)) { return; } } for (Way w : OsmPrimitive.getFilteredList(n.getReferrers(), Way.class)) { if (w.hasKey(ADDR_INTERPOLATION) && w.hasKey(ADDR_STREET)) { return; } } // No street found errors.add( new AddressError(HOUSE_NUMBER_WITHOUT_STREET, n, tr("House number without street"))); } }
/** * Replies the collection of nodes referred to by primitives in <code>primitivesToDelete</code> * which can be deleted too. A node can be deleted if * * <ul> * <li>it is untagged (see {@link Node#isTagged()} * <li>it is not referred to by other non-deleted primitives outside of <code>primitivesToDelete * </code> * </ul> * * @param primitivesToDelete the primitives to delete * @return the collection of nodes referred to by primitives in <code>primitivesToDelete</code> * which can be deleted too */ protected static Collection<Node> computeNodesToDelete( Collection<OsmPrimitive> primitivesToDelete) { Collection<Node> nodesToDelete = new HashSet<>(); for (Way way : OsmPrimitive.getFilteredList(primitivesToDelete, Way.class)) { for (Node n : way.getNodes()) { if (n.isTagged()) { continue; } Collection<OsmPrimitive> referringPrimitives = n.getReferrers(); referringPrimitives.removeAll(primitivesToDelete); int count = 0; for (OsmPrimitive p : referringPrimitives) { if (!p.isDeleted()) { count++; } } if (count == 0) { nodesToDelete.add(n); } } } return nodesToDelete; }
@Override public void actionPerformed(ActionEvent e) { if (!isEnabled()) return; Collection<OsmPrimitive> sel = getCurrentDataSet().getAllSelected(); layer = Main.map.mapView.getEditLayer(); toPurge = new HashSet<OsmPrimitive>(sel); toPurgeAdditionally = new ArrayList<OsmPrimitive>(); toPurgeChecked = new HashSet<OsmPrimitive>(); // Add referrer, unless the object to purge is not new // and the parent is a relation HashSet<OsmPrimitive> toPurgeRecursive = new HashSet<OsmPrimitive>(); while (!toPurge.isEmpty()) { for (OsmPrimitive osm : toPurge) { for (OsmPrimitive parent : osm.getReferrers()) { if (toPurge.contains(parent) || toPurgeChecked.contains(parent) || toPurgeRecursive.contains(parent)) { continue; } if (parent instanceof Way || (parent instanceof Relation && osm.isNew())) { toPurgeAdditionally.add(parent); toPurgeRecursive.add(parent); } } toPurgeChecked.add(osm); } toPurge = toPurgeRecursive; toPurgeRecursive = new HashSet<OsmPrimitive>(); } makeIncomplete = new HashSet<OsmPrimitive>(); // Find the objects that will be incomplete after purging. // At this point, all parents of new to-be-purged primitives are // also to-be-purged and // all parents of not-new to-be-purged primitives are either // to-be-purged or of type relation. TOP: for (OsmPrimitive child : toPurgeChecked) { if (child.isNew()) { continue; } for (OsmPrimitive parent : child.getReferrers()) { if (parent instanceof Relation && !toPurgeChecked.contains(parent)) { makeIncomplete.add(child); continue TOP; } } } // Add untagged way nodes. Do not add nodes that have other // referrers not yet to-be-purged. if (Main.pref.getBoolean("purge.add_untagged_waynodes", true)) { Set<OsmPrimitive> wayNodes = new HashSet<OsmPrimitive>(); for (OsmPrimitive osm : toPurgeChecked) { if (osm instanceof Way) { Way w = (Way) osm; NODE: for (Node n : w.getNodes()) { if (n.isTagged() || toPurgeChecked.contains(n)) { continue; } for (OsmPrimitive ref : n.getReferrers()) { if (ref != w && !toPurgeChecked.contains(ref)) { continue NODE; } } wayNodes.add(n); } } } toPurgeChecked.addAll(wayNodes); toPurgeAdditionally.addAll(wayNodes); } if (Main.pref.getBoolean("purge.add_relations_with_only_incomplete_members", true)) { Set<Relation> relSet = new HashSet<Relation>(); for (OsmPrimitive osm : toPurgeChecked) { for (OsmPrimitive parent : osm.getReferrers()) { if (parent instanceof Relation && !(toPurgeChecked.contains(parent)) && hasOnlyIncompleteMembers((Relation) parent, toPurgeChecked, relSet)) { relSet.add((Relation) parent); } } } /** Add higher level relations (list gets extended while looping over it) */ List<Relation> relLst = new ArrayList<Relation>(relSet); for (int i = 0; i < relLst.size(); ++i) { for (OsmPrimitive parent : relLst.get(i).getReferrers()) { if (!(toPurgeChecked.contains(parent)) && hasOnlyIncompleteMembers((Relation) parent, toPurgeChecked, relLst)) { relLst.add((Relation) parent); } } } relSet = new HashSet<Relation>(relLst); toPurgeChecked.addAll(relSet); toPurgeAdditionally.addAll(relSet); } boolean modified = false; for (OsmPrimitive osm : toPurgeChecked) { if (osm.isModified()) { modified = true; break; } } ExtendedDialog confirmDlg = new ExtendedDialog( Main.parent, tr("Confirm Purging"), new String[] {tr("Purge"), tr("Cancel")}); confirmDlg.setContent(buildPanel(modified), false); confirmDlg.setButtonIcons(new String[] {"ok", "cancel"}); int answer = confirmDlg.showDialog().getValue(); if (answer != 1) return; Main.pref.put("purge.clear_undo_redo", cbClearUndoRedo.isSelected()); Main.main.undoRedo.add( new PurgeCommand(Main.map.mapView.getEditLayer(), toPurgeChecked, makeIncomplete)); if (cbClearUndoRedo.isSelected()) { Main.main.undoRedo.clean(); getCurrentDataSet().clearSelectionHistory(); } }
/** * Collect all nodes with more than one referrer. * * @param ways Ways from witch nodes are selected * @return List of nodes with more than one referrer */ private List<Node> collectNodesWithExternReferers(List<Way> ways) { ArrayList<Node> withReferrers = new ArrayList<>(); for (Way w : ways) for (Node n : w.getNodes()) if (n.getReferrers().size() > 1) withReferrers.add(n); return withReferrers; }