Ticket #17768: 17768-beta.patch
File 17768-beta.patch, 11.2 KB (added by , 6 years ago) |
---|
-
src/org/openstreetmap/josm/actions/CreateMultipolygonAction.java
11 11 import java.util.Collections; 12 12 import java.util.HashMap; 13 13 import java.util.HashSet; 14 import java.util.LinkedHashSet; 14 15 import java.util.List; 15 16 import java.util.Map; 16 17 import java.util.Map.Entry; … … 28 29 import org.openstreetmap.josm.command.SequenceCommand; 29 30 import org.openstreetmap.josm.data.UndoRedoHandler; 30 31 import org.openstreetmap.josm.data.osm.DataSet; 31 import org.openstreetmap.josm.data.osm.MultipolygonBuilder;32 import org.openstreetmap.josm.data.osm.MultipolygonBuilder.JoinedPolygon;33 32 import org.openstreetmap.josm.data.osm.OsmPrimitive; 34 33 import org.openstreetmap.josm.data.osm.OsmUtils; 35 34 import org.openstreetmap.josm.data.osm.Relation; 36 35 import org.openstreetmap.josm.data.osm.RelationMember; 37 36 import org.openstreetmap.josm.data.osm.Way; 37 import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon; 38 import org.openstreetmap.josm.data.validation.TestError; 39 import org.openstreetmap.josm.data.validation.tests.MultipolygonTest; 38 40 import org.openstreetmap.josm.gui.MainApplication; 39 41 import org.openstreetmap.josm.gui.Notification; 40 42 import org.openstreetmap.josm.gui.dialogs.relation.DownloadRelationMemberTask; … … 210 212 Set<Way> ways = new HashSet<>(selectedWays); 211 213 ways.addAll(selectedMultipolygonRelation.getMemberPrimitives(Way.class)); 212 214 213 final MultipolygonBuilder polygon = analyzeWays(ways, true);214 if (polygon == null) {215 return null; //could not make multipolygon.216 } else{217 return Pair.create(selectedMultipolygonRelation, createRelation(polygon, selectedMultipolygonRelation));215 // even if no way was added the inner/outer roles might be different 216 MultipolygonTest mpTest = new MultipolygonTest(); 217 Pair<Multipolygon, Relation> tested = mpTest.makeFromWays(ways); 218 if (mpTest.getErrors().isEmpty()) { 219 return mergeRelationsMembers(selectedMultipolygonRelation, tested.b); 218 220 } 221 showErrors(mpTest.getErrors()); 222 return null; //could not make multipolygon. 219 223 } 220 224 221 225 /** 226 * Merge members of multipolygon relation. Maintains the order of the old relation. May change roles, 227 * removes duplicate and non-way members and adds new members found in {@code calculated}. 228 * @param old old multipolygon relation 229 * @param calculated calculated multipolygon relation 230 * @return pair of old and new multipolygon relation 231 */ 232 private static Pair<Relation, Relation> mergeRelationsMembers(Relation old, Relation calculated) { 233 Set<RelationMember> merged = new LinkedHashSet<>(); 234 boolean foundDiff = false; 235 boolean foundNonWayMember = false; 236 // maintain order of members in updated relation 237 for (RelationMember oldMem :old.getMembers()) { 238 if (oldMem.isNode() || oldMem.isRelation()) { 239 foundNonWayMember = true; 240 continue; 241 } 242 for (RelationMember newMem : calculated.getMembers()) { 243 if (newMem.getMember().equals(oldMem.getMember())) { 244 if (!newMem.getRole().equals(oldMem.getRole())) { 245 foundDiff = true; 246 } 247 foundDiff |= !merged.add(newMem); // detect duplicate members in old relation 248 break; 249 } 250 } 251 } 252 if (foundNonWayMember) { 253 foundDiff = true; 254 GuiHelper.runInEDT(() -> new Notification(tr("Non-Way removed from multipolygon")).setIcon(JOptionPane.WARNING_MESSAGE).show()); 255 } 256 foundDiff |= merged.addAll(calculated.getMembers()); 257 if (!foundDiff) { 258 GuiHelper.runInEDT(() -> new Notification(tr("Nothing changed")).setDuration(Notification.TIME_SHORT).setIcon(JOptionPane.INFORMATION_MESSAGE).show()); 259 return null; 260 } 261 Relation toModify = new Relation(old); 262 toModify.setMembers(new ArrayList<>(merged)); 263 return Pair.create(old, toModify); 264 } 265 266 /** 222 267 * Returns a {@link Pair} null and the newly created/modified multipolygon {@link Relation}. 223 268 * @param selectedWays selected ways 224 269 * @param showNotif if {@code true}, shows a notification if an error occurs … … 225 270 * @return pair of null and new multipolygon relation 226 271 */ 227 272 public static Pair<Relation, Relation> createMultipolygonRelation(Collection<Way> selectedWays, boolean showNotif) { 273 MultipolygonTest mpTest = new MultipolygonTest(); 274 Pair<Multipolygon, Relation> tested = mpTest.makeFromWays(selectedWays); 275 tested.b.setMembers(RelationSorter.sortMembersByConnectivity(tested.b.getMembers())); 276 if (mpTest.getErrors().isEmpty()) 277 return Pair.create(null, tested.b); 278 if (showNotif) { 279 showErrors(mpTest.getErrors()); 280 } 281 return null; //could not make multipolygon. 282 } 228 283 229 final MultipolygonBuilder polygon = analyzeWays(selectedWays, showNotif); 230 if (polygon == null) { 231 return null; //could not make multipolygon. 232 } else { 233 return Pair.create(null, createRelation(polygon, null)); 284 private static void showErrors(List<TestError> errors) { 285 StringBuilder sb = new StringBuilder(); 286 for (TestError e : errors) { 287 sb.append(e.getMessage()).append('\n'); 234 288 } 289 GuiHelper.runInEDT(() -> new Notification(sb.toString()).setIcon(JOptionPane.INFORMATION_MESSAGE).show()); 235 290 } 236 291 237 292 /** … … 287 342 } 288 343 } 289 344 290 /**291 * This method analyzes ways and creates multipolygon.292 * @param selectedWays list of selected ways293 * @param showNotif if {@code true}, shows a notification if an error occurs294 * @return <code>null</code>, if there was a problem with the ways.295 */296 private static MultipolygonBuilder analyzeWays(Collection<Way> selectedWays, boolean showNotif) {297 298 MultipolygonBuilder pol = new MultipolygonBuilder();299 final String error = pol.makeFromWays(selectedWays);300 301 if (error != null) {302 if (showNotif) {303 GuiHelper.runInEDT(() ->304 new Notification(error)305 .setIcon(JOptionPane.INFORMATION_MESSAGE)306 .show());307 }308 return null;309 } else {310 return pol;311 }312 }313 314 /**315 * Builds a relation from polygon ways.316 * @param pol data storage class containing polygon information317 * @param clone relation to clone, can be null318 * @return multipolygon relation319 */320 private static Relation createRelation(MultipolygonBuilder pol, Relation clone) {321 // Create new relation322 Relation rel = clone != null ? new Relation(clone) : new Relation();323 rel.put("type", "multipolygon");324 // Add ways to it325 for (JoinedPolygon jway:pol.outerWays) {326 addMembers(jway, rel, "outer");327 }328 329 for (JoinedPolygon jway:pol.innerWays) {330 addMembers(jway, rel, "inner");331 }332 333 if (clone == null) {334 rel.setMembers(RelationSorter.sortMembersByConnectivity(rel.getMembers()));335 }336 337 return rel;338 }339 340 private static void addMembers(JoinedPolygon polygon, Relation rel, String role) {341 final int count = rel.getMembersCount();342 final Set<Way> ways = new HashSet<>(polygon.ways);343 for (int i = 0; i < count; i++) {344 final RelationMember m = rel.getMember(i);345 if (ways.contains(m.getMember()) && !role.equals(m.getRole())) {346 rel.setMember(i, new RelationMember(role, m.getMember()));347 }348 }349 ways.removeAll(rel.getMemberPrimitivesList());350 for (final Way way : ways) {351 rel.addMember(new RelationMember(role, way));352 }353 }354 355 345 private static final List<String> DEFAULT_LINEAR_TAGS = Arrays.asList("barrier", "fence_type", "source"); 356 346 357 347 /** -
src/org/openstreetmap/josm/data/validation/tests/MultipolygonTest.java
38 38 import org.openstreetmap.josm.tools.Geometry; 39 39 import org.openstreetmap.josm.tools.Geometry.PolygonIntersection; 40 40 import org.openstreetmap.josm.tools.Logging; 41 import org.openstreetmap.josm.tools.Pair; 41 42 42 43 /** 43 44 * Checks if multipolygons are valid … … 78 79 private static final int FOUND_INSIDE = 1; 79 80 private static final int FOUND_OUTSIDE = 2; 80 81 82 /** set when used to build a multipolygon relation */ 83 private Relation createdRelation; 84 81 85 /** 82 86 * Constructs a new {@code MultipolygonTest}. 83 87 */ … … 463 467 if (list == null || list.isEmpty()) { 464 468 return; 465 469 } 466 470 if (r == createdRelation) { 471 List<RelationMember> modMembers = new ArrayList<>(); 472 for (PolygonLevel pol : list) { 473 final String calculatedRole = (pol.level % 2 == 0) ? "outer" : "inner"; 474 for (long wayId : pol.outerWay.getWayIds()) { 475 RelationMember member = wayMap.get(wayId); 476 modMembers.add(new RelationMember(calculatedRole, member.getMember())); 477 } 478 } 479 r.setMembers(modMembers); 480 return; 481 } 467 482 for (PolygonLevel pol : list) { 468 String calculatedRole = (pol.level % 2 == 0) ? "outer" : "inner";483 final String calculatedRole = (pol.level % 2 == 0) ? "outer" : "inner"; 469 484 for (long wayId : pol.outerWay.getWayIds()) { 470 485 RelationMember member = wayMap.get(wayId); 471 486 if (!calculatedRole.equals(member.getRole())) { … … 893 908 return null; 894 909 } 895 910 } 911 912 /** 913 * Create a multipolygon relation from the given ways and test it. 914 * @param ways the collection of ways 915 * @return a pair of a {@link Multipolygon} instance and the relation. 916 */ 917 public Pair<Multipolygon, Relation> makeFromWays(Collection<Way> ways) { 918 Relation r = new Relation(); 919 createdRelation = r; 920 r.put("type", "multipolygon"); 921 for (Way w : ways) { 922 r.addMember(new RelationMember("", w)); 923 } 924 errors.clear(); 925 Multipolygon polygon = null; 926 boolean hasRepeatedMembers = checkRepeatedWayMembers(r); 927 // Rest of checks is only for complete multipolygon 928 if (!hasRepeatedMembers && !r.hasIncompleteMembers()) { 929 polygon = new Multipolygon(r); 930 checkGeometryAndRoles(r, polygon); 931 } 932 errors.removeIf(e->e.getSeverity() == Severity.OTHER); 933 return new Pair<>(polygon, r); 934 } 935 896 936 }