Changeset 18976 in josm for trunk/src/org/openstreetmap
- Timestamp:
- 2024-02-13T09:29:09+01:00 (9 months ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/data/validation/tests/DuplicateRelation.java
r17476 r18976 2 2 package org.openstreetmap.josm.data.validation.tests; 3 3 4 import static org.openstreetmap.josm.tools.I18n.marktr; 4 5 import static org.openstreetmap.josm.tools.I18n.tr; 5 6 6 7 import java.util.ArrayList; 7 8 import java.util.Collection; 8 import java.util.HashSet;9 9 import java.util.LinkedList; 10 10 import java.util.List; … … 20 20 import org.openstreetmap.josm.data.coor.LatLon; 21 21 import org.openstreetmap.josm.data.osm.AbstractPrimitive; 22 import org.openstreetmap.josm.data.osm.DataSet; 22 23 import org.openstreetmap.josm.data.osm.Node; 23 24 import org.openstreetmap.josm.data.osm.OsmPrimitive; … … 37 38 public class DuplicateRelation extends Test { 38 39 40 private static final String DUPLICATED_RELATIONS = marktr("Duplicated relations"); 41 39 42 /** 40 43 * Class to store one relation members and information about it … … 111 114 private static class RelationMembers { 112 115 /** Set of member objects of the relation */ 113 private final Set<RelMember> members;116 private final List<RelMember> members; 114 117 115 118 /** Store relation information … … 117 120 */ 118 121 RelationMembers(List<RelationMember> members) { 119 this.members = new HashSet<>(members.size());122 this.members = new ArrayList<>(members.size()); 120 123 for (RelationMember member : members) { 121 124 this.members.add(new RelMember(member)); … … 176 179 protected static final int SAME_RELATION = 1902; 177 180 178 /** MultiMap of all relations */ 179 private MultiMap<RelationPair, OsmPrimitive> relations; 180 181 /** MultiMap of all relations, regardless of keys */ 182 private MultiMap<List<RelationMember>, OsmPrimitive> relationsNoKeys; 183 184 /** List of keys without useful information */ 185 private final Set<String> ignoreKeys = new HashSet<>(AbstractPrimitive.getUninterestingKeys()); 181 /** Code number of relation with same members error */ 182 protected static final int IDENTICAL_MEMBERLIST = 1903; 183 184 185 /** List of all initially visited testable relations*/ 186 List<Relation> visited; 186 187 187 188 /** … … 189 190 */ 190 191 public DuplicateRelation() { 191 super(tr( "Duplicated relations"),192 super(tr(DUPLICATED_RELATIONS), 192 193 tr("This test checks that there are no relations with same tags and same members with same roles.")); 193 194 } … … 196 197 public void startTest(ProgressMonitor monitor) { 197 198 super.startTest(monitor); 198 relations = new MultiMap<>(1000);199 relationsNoKeys = new MultiMap<>(1000); 199 visited = new ArrayList<>(); 200 200 201 } 201 202 202 203 @Override 203 204 public void endTest() { 204 for (Set<OsmPrimitive> duplicated : relations.values()) { 205 if (!visited.isEmpty()) 206 performChecks(); 207 visited = null; 208 super.endTest(); 209 } 210 211 private void performChecks() { 212 MultiMap<RelationPair, Relation> relations = new MultiMap<>(1000); 213 // MultiMap of all relations, regardless of keys 214 MultiMap<List<RelationMember>, Relation> sameMembers = new MultiMap<>(1000); 215 216 for (Relation r : visited) { 217 final List<RelationMember> rMembers = getSortedMembers(r); 218 sameMembers.put(rMembers, r); 219 addToRelations(relations, r, true); 220 } 221 222 if (partialSelection) { 223 // add data for relations which were not in the initial selection when 224 // they can be duplicates 225 DataSet ds = visited.iterator().next().getDataSet(); 226 for (Relation r : ds.getRelations()) { 227 if (r.isDeleted() || r.getMembers().isEmpty() || visited.contains(r)) 228 continue; 229 final List<RelationMember> rMembers = getSortedMembers(r); 230 if (sameMembers.containsKey(rMembers)) 231 sameMembers.put(rMembers, r); 232 233 addToRelations(relations, r, false); 234 } 235 } 236 237 for (Set<Relation> duplicated : sameMembers.values()) { 238 if (duplicated.size() > 1) { 239 checkOrderAndTags(duplicated); 240 } 241 } 242 243 performGeometryTest(relations); 244 } 245 246 private void performGeometryTest(MultiMap<RelationPair, Relation> relations) { 247 // perform special test to find relations with different members but (possibly) same geometry 248 // this test is rather speculative and works only with complete members 249 for (Set<Relation> duplicated : relations.values()) { 205 250 if (duplicated.size() > 1) { 206 251 TestError testError = TestError.builder(this, Severity.ERROR, DUPLICATE_RELATION) 207 .message(tr( "Duplicated relations"))252 .message(tr(DUPLICATED_RELATIONS)) 208 253 .primitives(duplicated) 209 254 .build(); 210 errors.add(testError); 211 } 212 } 213 relations = null; 214 for (Set<OsmPrimitive> duplicated : relationsNoKeys.values()) { 215 if (duplicated.size() > 1) { 216 TestError testError = TestError.builder(this, Severity.OTHER, SAME_RELATION) 217 .message(tr("Relations with same members")) 218 .primitives(duplicated) 219 .build(); 220 errors.add(testError); 221 } 222 } 223 relationsNoKeys = null; 224 super.endTest(); 255 if (errors.stream().noneMatch(e -> e.isSimilar(testError))) { 256 errors.add(testError); 257 } 258 } 259 } 260 } 261 262 private static void addToRelations(MultiMap<RelationPair, Relation> relations, Relation r, boolean forceAdd) { 263 if (r.isUsable() && !r.hasIncompleteMembers()) { 264 RelationPair rKey = new RelationPair(r.getMembers(), cleanedKeys(r)); 265 if (forceAdd || (!relations.isEmpty() && !relations.containsKey(rKey))) 266 relations.put(rKey, r); 267 } 225 268 } 226 269 227 270 @Override 228 271 public void visit(Relation r) { 229 if (!r.isUsable() || r.hasIncompleteMembers() || "tmc".equals(r.get("type")) || "TMC".equals(r.get("type")) 230 || "destination_sign".equals(r.get("type")) || r.getMembers().isEmpty()) 231 return; 232 List<RelationMember> rMembers = r.getMembers(); 233 Map<String, String> rkeys = r.getKeys(); 234 for (String key : ignoreKeys) { 235 rkeys.remove(key); 236 } 237 RelationPair rKey = new RelationPair(rMembers, rkeys); 238 relations.put(rKey, r); 239 relationsNoKeys.put(rMembers, r); 272 if (!r.isDeleted() && r.getMembersCount() > 0) 273 visited.add(r); 274 } 275 276 /** 277 * Check a list of relations which are guaranteed to have the same members, possibly in different order 278 * and possibly different tags. 279 * @param sameMembers the list of relations 280 */ 281 private void checkOrderAndTags(Set<Relation> sameMembers) { 282 MultiMap<List<RelationMember>, Relation> sameOrder = new MultiMap<>(); 283 MultiMap<Map<String, String>, Relation> sameKeys = new MultiMap<>(); 284 for (Relation r : sameMembers) { 285 sameOrder.put(r.getMembers(), r); 286 sameKeys.put(cleanedKeys(r), r); 287 } 288 for (Set<Relation> duplicated : sameKeys.values()) { 289 if (duplicated.size() > 1) { 290 reportDuplicateKeys(duplicated); 291 } 292 } 293 for (Set<Relation> duplicated : sameOrder.values()) { 294 if (duplicated.size() > 1) { 295 reportDuplicateMembers(duplicated); 296 } 297 } 298 List<Relation> primitives = sameMembers.stream().filter(r -> !ignoredType(r)).collect(Collectors.toList()); 299 // report this collection if not already reported 300 if (primitives.size() > 1 && errors.stream().noneMatch(e -> e.getPrimitives().containsAll(primitives))) { 301 // same members, possibly different order 302 TestError testError = TestError.builder(this, Severity.OTHER, SAME_RELATION) 303 .message(tr("Relations with same members")) 304 .primitives(primitives) 305 .build(); 306 errors.add(testError); 307 } 308 } 309 310 /** 311 * Check collection of relations with the same keys and members, possibly in different order 312 * @param duplicated collection of relations, caller must make sure that they have the same keys and members 313 */ 314 private void reportDuplicateKeys(Collection<Relation> duplicated) { 315 Relation first = duplicated.iterator().next(); 316 if (memberOrderMatters(first)) { 317 List<Relation> toCheck = new ArrayList<>(duplicated); 318 while (toCheck.size() > 1) { 319 Relation ref = toCheck.iterator().next(); 320 List<Relation> same = toCheck.stream() 321 .filter(r -> r.getMembers().equals(ref.getMembers())).collect(Collectors.toList()); 322 if (same.size() > 1) { 323 // same members and keys, members in same order 324 TestError testError = TestError.builder(this, Severity.ERROR, DUPLICATE_RELATION) 325 .message(tr(DUPLICATED_RELATIONS)) 326 .primitives(same) 327 .build(); 328 errors.add(testError); 329 } 330 toCheck.removeAll(same); 331 } 332 } else { 333 // same members and keys, possibly different order 334 TestError testError = TestError.builder(this, Severity.ERROR, DUPLICATE_RELATION) 335 .message(tr(DUPLICATED_RELATIONS)) 336 .primitives(duplicated) 337 .build(); 338 errors.add(testError); 339 } 340 } 341 342 /** 343 * Report relations with the same member(s) in the same order, keys may be different 344 * @param duplicated collection of relations with identical member list 345 */ 346 private void reportDuplicateMembers(Set<Relation> duplicated) { 347 List<Relation> primitives = duplicated.stream().filter(r -> !ignoredType(r)).collect(Collectors.toList()); 348 if (primitives.size() > 1 && errors.stream().noneMatch(e -> e.getPrimitives().containsAll(primitives))) { 349 TestError testError = TestError.builder(this, Severity.OTHER, IDENTICAL_MEMBERLIST) 350 .message(tr("Identical members")) 351 .primitives(primitives) 352 .build(); 353 errors.add(testError); 354 } 355 356 } 357 358 private static boolean memberOrderMatters(Relation r) { 359 return r.hasTag("type", "route", "waterway"); // add more types? 360 } 361 362 private static boolean ignoredType(Relation r) { 363 return r.hasTag("type", "tmc", "TMC", "destination_sign"); // see r11783 364 } 365 366 /** return tags of a primitive after removing all discardable tags 367 * 368 * @param p the primitive 369 * @return map with cleaned tags 370 */ 371 private static Map<String, String> cleanedKeys(OsmPrimitive p) { 372 Map<String, String> cleaned = p.getKeys(); 373 for (String key : AbstractPrimitive.getDiscardableKeys()) { 374 cleaned.remove(key); 375 } 376 return cleaned; 377 } 378 379 /** 380 * Order members of given relation by type, unique id, and role. 381 * @param r the relation 382 * @return sorted list of members 383 */ 384 private static List<RelationMember> getSortedMembers(Relation r) { 385 return r.getMembers().stream().sorted((m1, m2) -> { 386 int d = m1.getMember().compareTo(m2.getMember()); 387 if (d != 0) 388 return d; 389 return m1.getRole().compareTo(m2.getRole()); 390 }).collect(Collectors.toList()); 240 391 } 241 392 … … 301 452 public boolean isFixable(TestError testError) { 302 453 if (!(testError.getTester() instanceof DuplicateRelation) 303 || testError.getCode() == SAME_RELATION) return false;454 || testError.getCode() != DUPLICATE_RELATION) return false; 304 455 305 456 // We fix it only if there is no more than one relation that is relation member.
Note:
See TracChangeset
for help on using the changeset viewer.