Ticket #17768: 17768-v0.patch
File 17768-v0.patch, 12.4 KB (added by , 6 years ago) |
---|
-
src/org/openstreetmap/josm/actions/CreateMultipolygonAction.java
2 2 package org.openstreetmap.josm.actions; 3 3 4 4 import static org.openstreetmap.josm.tools.I18n.tr; 5 import static org.openstreetmap.josm.tools.I18n.trn; 5 6 6 7 import java.awt.event.ActionEvent; 7 8 import java.awt.event.KeyEvent; … … 11 12 import java.util.Collections; 12 13 import java.util.HashMap; 13 14 import java.util.HashSet; 15 import java.util.Iterator; 16 import java.util.LinkedHashSet; 14 17 import java.util.List; 15 18 import java.util.Map; 16 19 import java.util.Map.Entry; … … 28 31 import org.openstreetmap.josm.command.SequenceCommand; 29 32 import org.openstreetmap.josm.data.UndoRedoHandler; 30 33 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 34 import org.openstreetmap.josm.data.osm.OsmPrimitive; 34 35 import org.openstreetmap.josm.data.osm.OsmUtils; 35 36 import org.openstreetmap.josm.data.osm.Relation; 36 37 import org.openstreetmap.josm.data.osm.RelationMember; 37 38 import org.openstreetmap.josm.data.osm.Way; 39 import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon; 40 import org.openstreetmap.josm.data.validation.TestError; 41 import org.openstreetmap.josm.data.validation.tests.MultipolygonTest; 38 42 import org.openstreetmap.josm.gui.MainApplication; 39 43 import org.openstreetmap.josm.gui.Notification; 40 44 import org.openstreetmap.josm.gui.dialogs.relation.DownloadRelationMemberTask; … … 212 216 Set<Way> ways = new HashSet<>(selectedWays); 213 217 ways.addAll(selectedMultipolygonRelation.getMemberPrimitives(Way.class)); 214 218 215 final MultipolygonBuilder polygon = analyzeWays(ways, true);216 if (polygon == null) {217 return null; //could not make multipolygon.218 } else{219 return Pair.create(selectedMultipolygonRelation, createRelation(polygon, selectedMultipolygonRelation));219 // even if no way was added the inner/outer roles might be different 220 MultipolygonTest mpTest = new MultipolygonTest(); 221 Pair<Multipolygon, Relation> tested = mpTest.makeFromWays(ways); 222 if (mpTest.getErrors().isEmpty()) { 223 return mergeRelationsMembers(selectedMultipolygonRelation, tested.b); 220 224 } 225 showErrors(mpTest.getErrors()); 226 return null; //could not make multipolygon. 221 227 } 222 228 223 229 /** 230 * Merge members of multipolygon relation. Maintains the order of the old relation. May change roles, 231 * removes duplicate and non-way members and adds new members found in {@code calculated}. 232 * @param old old multipolygon relation 233 * @param calculated calculated multipolygon relation 234 * @return pair of old and new multipolygon relation 235 */ 236 private static Pair<Relation, Relation> mergeRelationsMembers(Relation old, Relation calculated) { 237 Set<RelationMember> merged = new LinkedHashSet<>(); 238 boolean foundDiff = false; 239 int nonWayMember = 0; 240 // maintain order of members in updated relation 241 for (RelationMember oldMem :old.getMembers()) { 242 if (oldMem.isNode() || oldMem.isRelation()) { 243 nonWayMember++; 244 continue; 245 } 246 for (RelationMember newMem : calculated.getMembers()) { 247 if (newMem.getMember().equals(oldMem.getMember())) { 248 if (!newMem.getRole().equals(oldMem.getRole())) { 249 foundDiff = true; 250 } 251 foundDiff |= !merged.add(newMem); // detect duplicate members in old relation 252 break; 253 } 254 } 255 } 256 if (nonWayMember > 0) { 257 foundDiff = true; 258 String msg = trn("Non-Way member removed from multipolygon", "Non-Way members removed from multipolygon", nonWayMember); 259 GuiHelper.runInEDT(() -> new Notification(msg).setIcon(JOptionPane.WARNING_MESSAGE).show()); 260 } 261 foundDiff |= merged.addAll(calculated.getMembers()); 262 if (!foundDiff) { 263 return Pair.create(old, old); // unchanged 264 } 265 Relation toModify = new Relation(old); 266 toModify.setMembers(new ArrayList<>(merged)); 267 return Pair.create(old, toModify); 268 } 269 270 /** 224 271 * Returns a {@link Pair} null and the newly created/modified multipolygon {@link Relation}. 225 272 * @param selectedWays selected ways 226 273 * @param showNotif if {@code true}, shows a notification if an error occurs … … 227 274 * @return pair of null and new multipolygon relation 228 275 */ 229 276 public static Pair<Relation, Relation> createMultipolygonRelation(Collection<Way> selectedWays, boolean showNotif) { 277 MultipolygonTest mpTest = new MultipolygonTest(); 278 Pair<Multipolygon, Relation> tested = mpTest.makeFromWays(selectedWays); 279 tested.b.setMembers(RelationSorter.sortMembersByConnectivity(tested.b.getMembers())); 280 if (mpTest.getErrors().isEmpty()) 281 return Pair.create(null, tested.b); 282 if (showNotif) { 283 showErrors(mpTest.getErrors()); 284 } 285 return null; //could not make multipolygon. 286 } 230 287 231 final MultipolygonBuilder polygon = analyzeWays(selectedWays, showNotif); 232 if (polygon == null) { 233 return null; //could not make multipolygon. 234 } else { 235 return Pair.create(null, createRelation(polygon, null)); 288 private static void showErrors(List<TestError> errors) { 289 if (!errors.isEmpty()) { 290 StringBuilder sb = new StringBuilder(); 291 Set<String> errorMessages = new LinkedHashSet<>(); 292 errors.forEach(e-> errorMessages.add(e.getMessage())); 293 Iterator<String> iter = errorMessages.iterator(); 294 while (iter.hasNext()) { 295 sb.append(iter.next()); 296 if (iter.hasNext()) 297 sb.append('\n'); 298 } 299 GuiHelper.runInEDT(() -> new Notification(sb.toString()).setIcon(JOptionPane.INFORMATION_MESSAGE).show()); 236 300 } 237 301 } 238 302 … … 260 324 list.add(new AddCommand(selectedWays.iterator().next().getDataSet(), relation)); 261 325 commandName = getName(false); 262 326 } else { 263 list.add(new ChangeCommand(existingRelation, relation)); 327 if (existingRelation != relation) { 328 list.add(new ChangeCommand(existingRelation, relation)); 329 } 330 if (list.isEmpty()) { 331 GuiHelper.runInEDT(() -> new Notification(tr("Nothing changed")).setDuration(Notification.TIME_SHORT).setIcon(JOptionPane.INFORMATION_MESSAGE).show()); 332 return null; 333 } 264 334 commandName = getName(true); 265 335 } 266 336 return Pair.create(new SequenceCommand(commandName, list), relation); … … 289 359 } 290 360 } 291 361 292 /**293 * This method analyzes ways and creates multipolygon.294 * @param selectedWays list of selected ways295 * @param showNotif if {@code true}, shows a notification if an error occurs296 * @return <code>null</code>, if there was a problem with the ways.297 */298 private static MultipolygonBuilder analyzeWays(Collection<Way> selectedWays, boolean showNotif) {299 300 MultipolygonBuilder pol = new MultipolygonBuilder();301 final String error = pol.makeFromWays(selectedWays);302 303 if (error != null) {304 if (showNotif) {305 GuiHelper.runInEDT(() ->306 new Notification(error)307 .setIcon(JOptionPane.INFORMATION_MESSAGE)308 .show());309 }310 return null;311 } else {312 return pol;313 }314 }315 316 /**317 * Builds a relation from polygon ways.318 * @param pol data storage class containing polygon information319 * @param clone relation to clone, can be null320 * @return multipolygon relation321 */322 private static Relation createRelation(MultipolygonBuilder pol, Relation clone) {323 // Create new relation324 Relation rel = clone != null ? new Relation(clone) : new Relation();325 rel.put("type", "multipolygon");326 // Add ways to it327 for (JoinedPolygon jway:pol.outerWays) {328 addMembers(jway, rel, "outer");329 }330 331 for (JoinedPolygon jway:pol.innerWays) {332 addMembers(jway, rel, "inner");333 }334 335 if (clone == null) {336 rel.setMembers(RelationSorter.sortMembersByConnectivity(rel.getMembers()));337 }338 339 return rel;340 }341 342 private static void addMembers(JoinedPolygon polygon, Relation rel, String role) {343 final int count = rel.getMembersCount();344 final Set<Way> ways = new HashSet<>(polygon.ways);345 for (int i = 0; i < count; i++) {346 final RelationMember m = rel.getMember(i);347 if (ways.contains(m.getMember()) && !role.equals(m.getRole())) {348 rel.setMember(i, new RelationMember(role, m.getMember()));349 }350 }351 ways.removeAll(rel.getMemberPrimitivesList());352 for (final Way way : ways) {353 rel.addMember(new RelationMember(role, way));354 }355 }356 357 362 private static final List<String> DEFAULT_LINEAR_TAGS = Arrays.asList("barrier", "fence_type", "source"); 358 363 359 364 /** -
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 if (!hasRepeatedMembers) { 928 polygon = new Multipolygon(r); 929 // don't check style consistency here 930 checkGeometryAndRoles(r, polygon); 931 } 932 createdRelation = null; 933 errors.removeIf(e->e.getSeverity() == Severity.OTHER); 934 return new Pair<>(polygon, r); 935 } 936 896 937 }