@Override public boolean isFixable(TestError testError) { if (!(testError.getTester() instanceof DuplicateWay)) return false; // Do not automatically fix same ways with different tags if (testError.getCode() != DUPLICATE_WAY) return false; // We fix it only if there is no more than one way that is relation member. Collection<? extends OsmPrimitive> sel = testError.getPrimitives(); HashSet<Way> ways = new HashSet<Way>(); for (OsmPrimitive osm : sel) { if (osm instanceof Way) { ways.add((Way) osm); } } if (ways.size() < 2) return false; int waysWithRelations = 0; for (Way w : ways) { List<Relation> rel = OsmPrimitive.getFilteredList(w.getReferrers(), Relation.class); if (!rel.isEmpty()) { ++waysWithRelations; } } return (waysWithRelations <= 1); }
/** Fix the error by removing all but one instance of duplicate ways */ @Override public Command fixError(TestError testError) { Collection<? extends OsmPrimitive> sel = testError.getPrimitives(); HashSet<Way> ways = new HashSet<Way>(); for (OsmPrimitive osm : sel) { if (osm instanceof Way) { ways.add((Way) osm); } } if (ways.size() < 2) return null; long idToKeep = 0; Way wayToKeep = ways.iterator().next(); // Only one way will be kept - the one with lowest positive ID, if such exist // or one "at random" if no such exists. Rest of the ways will be deleted for (Way w : ways) { if (!w.isNew()) { if (idToKeep == 0 || w.getId() < idToKeep) { idToKeep = w.getId(); wayToKeep = w; } } } // Find the way that is member of one or more relations. (If any) Way wayWithRelations = null; List<Relation> relations = null; for (Way w : ways) { List<Relation> rel = OsmPrimitive.getFilteredList(w.getReferrers(), Relation.class); if (!rel.isEmpty()) { if (wayWithRelations != null) throw new AssertionError( "Cannot fix duplicate Ways: More than one way is relation member."); wayWithRelations = w; relations = rel; } } Collection<Command> commands = new LinkedList<Command>(); // Fix relations. if (wayWithRelations != null && wayToKeep != wayWithRelations) { for (Relation rel : relations) { Relation newRel = new Relation(rel); for (int i = 0; i < newRel.getMembers().size(); ++i) { RelationMember m = newRel.getMember(i); if (wayWithRelations.equals(m.getMember())) { newRel.setMember(i, new RelationMember(m.getRole(), wayToKeep)); } } commands.add(new ChangeCommand(rel, newRel)); } } // Delete all ways in the list // Note: nodes are not deleted, these can be detected and deleted at next pass ways.remove(wayToKeep); commands.add(new DeleteCommand(ways)); return new SequenceCommand(tr("Delete duplicate ways"), commands); }
static SplitWayResult doSplitWay( OsmDataLayer layer, Way way, Way wayToKeep, List<Way> newWays, List<OsmPrimitive> newSelection) { Collection<Command> commandList = new ArrayList<>(newWays.size()); Collection<String> nowarnroles = Main.pref.getCollection( "way.split.roles.nowarn", Arrays.asList( "outer", "inner", "forward", "backward", "north", "south", "east", "west")); // Change the original way final Way changedWay = new Way(way); changedWay.setNodes(wayToKeep.getNodes()); commandList.add(new ChangeCommand(way, changedWay)); if (!newSelection.contains(way)) { newSelection.add(way); } newWays.remove(wayToKeep); for (Way wayToAdd : newWays) { commandList.add(new AddCommand(layer, wayToAdd)); newSelection.add(wayToAdd); } boolean warnmerole = false; boolean warnme = false; // now copy all relations to new way also for (Relation r : OsmPrimitive.getFilteredList(way.getReferrers(), Relation.class)) { if (!r.isUsable()) { continue; } Relation c = null; String type = r.get("type"); if (type == null) { type = ""; } int i_c = 0, i_r = 0; List<RelationMember> relationMembers = r.getMembers(); for (RelationMember rm : relationMembers) { if (rm.isWay() && rm.getMember() == way) { boolean insert = true; if ("restriction".equals(type)) { /* this code assumes the restriction is correct. No real error checking done */ String role = rm.getRole(); if ("from".equals(role) || "to".equals(role)) { OsmPrimitive via = null; for (RelationMember rmv : r.getMembers()) { if ("via".equals(rmv.getRole())) { via = rmv.getMember(); } } List<Node> nodes = new ArrayList<>(); if (via != null) { if (via instanceof Node) { nodes.add((Node) via); } else if (via instanceof Way) { nodes.add(((Way) via).lastNode()); nodes.add(((Way) via).firstNode()); } } Way res = null; for (Node n : nodes) { if (changedWay.isFirstLastNode(n)) { res = way; } } if (res == null) { for (Way wayToAdd : newWays) { for (Node n : nodes) { if (wayToAdd.isFirstLastNode(n)) { res = wayToAdd; } } } if (res != null) { if (c == null) { c = new Relation(r); } c.addMember(new RelationMember(role, res)); c.removeMembersFor(way); insert = false; } } else { insert = false; } } else if (!"via".equals(role)) { warnme = true; } } else if (!("route".equals(type)) && !("multipolygon".equals(type))) { warnme = true; } if (c == null) { c = new Relation(r); } if (insert) { if (rm.hasRole() && !nowarnroles.contains(rm.getRole())) { warnmerole = true; } Boolean backwards = null; int k = 1; while (i_r - k >= 0 || i_r + k < relationMembers.size()) { if ((i_r - k >= 0) && relationMembers.get(i_r - k).isWay()) { Way w = relationMembers.get(i_r - k).getWay(); if ((w.lastNode() == way.firstNode()) || w.firstNode() == way.firstNode()) { backwards = Boolean.FALSE; } else if ((w.firstNode() == way.lastNode()) || w.lastNode() == way.lastNode()) { backwards = Boolean.TRUE; } break; } if ((i_r + k < relationMembers.size()) && relationMembers.get(i_r + k).isWay()) { Way w = relationMembers.get(i_r + k).getWay(); if ((w.lastNode() == way.firstNode()) || w.firstNode() == way.firstNode()) { backwards = Boolean.TRUE; } else if ((w.firstNode() == way.lastNode()) || w.lastNode() == way.lastNode()) { backwards = Boolean.FALSE; } break; } k++; } int j = i_c; for (Way wayToAdd : newWays) { RelationMember em = new RelationMember(rm.getRole(), wayToAdd); j++; if ((backwards != null) && backwards) { c.addMember(i_c, em); } else { c.addMember(j, em); } } i_c = j; } } i_c++; i_r++; } if (c != null) { commandList.add(new ChangeCommand(layer, r, c)); } } if (warnmerole) { new Notification( tr( "A role based relation membership was copied to all new ways.<br>You should verify this and correct it when necessary.")) .setIcon(JOptionPane.WARNING_MESSAGE) .show(); } else if (warnme) { new Notification( tr( "A relation membership was copied to all new ways.<br>You should verify this and correct it when necessary.")) .setIcon(JOptionPane.WARNING_MESSAGE) .show(); } return new SplitWayResult( new SequenceCommand( /* for correct i18n of plural forms - see #9110 */ trn( "Split way {0} into {1} part", "Split way {0} into {1} parts", newWays.size(), way.getDisplayName(DefaultNameFormatter.getInstance()), newWays.size()), commandList), newSelection, way, newWays); }