Changeset 15160 in josm
- Timestamp:
- 2019-06-06T08:23:30+02:00 (6 years ago)
- Location:
- trunk/src/org/openstreetmap/josm
- Files:
-
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/actions/CreateMultipolygonAction.java
r15143 r15160 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; … … 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; … … 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; … … 36 37 import org.openstreetmap.josm.data.osm.RelationMember; 37 38 import org.openstreetmap.josm.data.osm.Way; 39 import org.openstreetmap.josm.data.validation.TestError; 40 import org.openstreetmap.josm.data.validation.tests.MultipolygonTest; 38 41 import org.openstreetmap.josm.gui.MainApplication; 39 42 import org.openstreetmap.josm.gui.Notification; … … 205 208 * @param selectedWays selected ways 206 209 * @param selectedMultipolygonRelation selected multipolygon relation 207 * @return pair of old and new multipolygon relation 210 * @return pair of old and new multipolygon relation if a difference was found, else the pair contains the old relation twice 208 211 */ 209 212 public static Pair<Relation, Relation> updateMultipolygonRelation(Collection<Way> selectedWays, Relation selectedMultipolygonRelation) { … … 213 216 ways.addAll(selectedMultipolygonRelation.getMemberPrimitives(Way.class)); 214 217 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)); 220 } 218 // even if no way was added the inner/outer roles might be different 219 MultipolygonTest mpTest = new MultipolygonTest(); 220 Relation calculated = mpTest.makeFromWays(ways); 221 if (mpTest.getErrors().isEmpty()) { 222 return mergeRelationsMembers(selectedMultipolygonRelation, calculated); 223 } 224 showErrors(mpTest.getErrors()); 225 return null; //could not make multipolygon. 226 } 227 228 /** 229 * Merge members of multipolygon relation. Maintains the order of the old relation. May change roles, 230 * removes duplicate and non-way members and adds new members found in {@code calculated}. 231 * @param old old multipolygon relation 232 * @param calculated calculated multipolygon relation 233 * @return pair of old and new multipolygon relation if a difference was found, else the pair contains the old relation twice 234 */ 235 private static Pair<Relation, Relation> mergeRelationsMembers(Relation old, Relation calculated) { 236 Set<RelationMember> merged = new LinkedHashSet<>(); 237 boolean foundDiff = false; 238 int nonWayMember = 0; 239 // maintain order of members in updated relation 240 for (RelationMember oldMem :old.getMembers()) { 241 if (oldMem.isNode() || oldMem.isRelation()) { 242 nonWayMember++; 243 continue; 244 } 245 for (RelationMember newMem : calculated.getMembers()) { 246 if (newMem.getMember().equals(oldMem.getMember())) { 247 if (!newMem.getRole().equals(oldMem.getRole())) { 248 foundDiff = true; 249 } 250 foundDiff |= !merged.add(newMem); // detect duplicate members in old relation 251 break; 252 } 253 } 254 } 255 if (nonWayMember > 0) { 256 foundDiff = true; 257 String msg = trn("Non-Way member removed from multipolygon", "Non-Way members removed from multipolygon", nonWayMember); 258 GuiHelper.runInEDT(() -> new Notification(msg).setIcon(JOptionPane.WARNING_MESSAGE).show()); 259 } 260 foundDiff |= merged.addAll(calculated.getMembers()); 261 if (!foundDiff) { 262 return Pair.create(old, old); // unchanged 263 } 264 Relation toModify = new Relation(old); 265 toModify.setMembers(new ArrayList<>(merged)); 266 return Pair.create(old, toModify); 221 267 } 222 268 … … 228 274 */ 229 275 public static Pair<Relation, Relation> createMultipolygonRelation(Collection<Way> selectedWays, boolean showNotif) { 230 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)); 276 MultipolygonTest mpTest = new MultipolygonTest(); 277 Relation calculated = mpTest.makeFromWays(selectedWays); 278 calculated.setMembers(RelationSorter.sortMembersByConnectivity(calculated.getMembers())); 279 if (mpTest.getErrors().isEmpty()) 280 return Pair.create(null, calculated); 281 if (showNotif) { 282 showErrors(mpTest.getErrors()); 283 } 284 return null; //could not make multipolygon. 285 } 286 287 private static void showErrors(List<TestError> errors) { 288 if (!errors.isEmpty()) { 289 StringBuilder sb = new StringBuilder(); 290 Set<String> errorMessages = new LinkedHashSet<>(); 291 errors.forEach(e-> errorMessages.add(e.getMessage())); 292 Iterator<String> iter = errorMessages.iterator(); 293 while (iter.hasNext()) { 294 sb.append(iter.next()); 295 if (iter.hasNext()) 296 sb.append('\n'); 297 } 298 GuiHelper.runInEDT(() -> new Notification(sb.toString()).setIcon(JOptionPane.INFORMATION_MESSAGE).show()); 236 299 } 237 300 } … … 252 315 return null; 253 316 } 317 boolean unchanged = rr.a == rr.b; 254 318 final Relation existingRelation = rr.a; 255 319 final Relation relation = rr.b; … … 261 325 commandName = getName(false); 262 326 } else { 263 list.add(new ChangeCommand(existingRelation, relation)); 327 if (!unchanged) { 328 list.add(new ChangeCommand(existingRelation, relation)); 329 } 330 if (list.isEmpty()) { 331 if (unchanged) { 332 MultipolygonTest mpTest = new MultipolygonTest(); 333 mpTest.visit(existingRelation); 334 if (!mpTest.getErrors().isEmpty()) { 335 showErrors(mpTest.getErrors()); 336 return null; 337 } 338 } 339 340 GuiHelper.runInEDT(() -> new Notification(tr("Nothing changed")).setDuration(Notification.TIME_SHORT).setIcon(JOptionPane.INFORMATION_MESSAGE).show()); 341 return null; 342 } 264 343 commandName = getName(true); 265 344 } … … 290 369 } 291 370 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 371 private static final List<String> DEFAULT_LINEAR_TAGS = Arrays.asList("barrier", "fence_type", "source"); 358 372 -
trunk/src/org/openstreetmap/josm/data/osm/MultipolygonBuilder.java
r14436 r15160 6 6 import java.awt.Rectangle; 7 7 import java.awt.geom.Area; 8 import java.io.IOException;9 import java.io.ObjectInputStream;10 import java.io.ObjectOutputStream;11 8 import java.util.ArrayList; 12 9 import java.util.Collection; 13 10 import java.util.Collections; 14 import java.util.HashMap;15 11 import java.util.HashSet; 16 12 import java.util.List; 17 13 import java.util.Map; 18 14 import java.util.Set; 19 import java.util.concurrent.ForkJoinPool;20 import java.util.concurrent.ForkJoinTask;21 import java.util.concurrent.RecursiveTask;22 import java.util.function.Supplier;23 15 import java.util.stream.Collectors; 24 16 17 import org.openstreetmap.josm.data.validation.tests.MultipolygonTest; 25 18 import org.openstreetmap.josm.tools.CheckParameterUtil; 26 19 import org.openstreetmap.josm.tools.Geometry; 27 import org.openstreetmap.josm.tools.Geometry.PolygonIntersection;28 import org.openstreetmap.josm.tools.Logging;29 20 import org.openstreetmap.josm.tools.MultiMap; 30 21 import org.openstreetmap.josm.tools.Pair; 31 import org.openstreetmap.josm.tools.Utils;32 22 33 23 /** … … 39 29 public class MultipolygonBuilder { 40 30 41 private static final ForkJoinPool THREAD_POOL = newForkJoinPool();42 43 private static ForkJoinPool newForkJoinPool() {44 try {45 return Utils.newForkJoinPool(46 "multipolygon_creation.numberOfThreads", "multipolygon-builder-%d", Thread.NORM_PRIORITY);47 } catch (SecurityException e) {48 Logging.log(Logging.LEVEL_ERROR, "Unable to create new ForkJoinPool", e);49 return null;50 }51 }52 53 /**54 * Helper class to avoid unneeded costly intersection calculations.55 * If the intersection between polygons a and b was calculated we also know56 * the result of intersection between b and a. The lookup in the hash tables is57 * much faster than the intersection calculation.58 */59 private static class IntersectionMatrix {60 private final Map<Pair<JoinedPolygon, JoinedPolygon>, PolygonIntersection> results;61 62 IntersectionMatrix(Collection<JoinedPolygon> polygons) {63 results = new HashMap<>(Utils.hashMapInitialCapacity(polygons.size() * polygons.size()));64 }65 66 /**67 * Compute the reverse result of the intersection test done by {@code Geometry.polygonIntersection(Area a1, Area a2)}68 *69 * @param intersection the intersection result for polygons a1 and a2 (in that order)70 * @return the intersection result for a2 and a171 */72 private static PolygonIntersection getReverseIntersectionResult(PolygonIntersection intersection) {73 switch (intersection) {74 case FIRST_INSIDE_SECOND:75 return PolygonIntersection.SECOND_INSIDE_FIRST;76 case SECOND_INSIDE_FIRST:77 return PolygonIntersection.FIRST_INSIDE_SECOND;78 default:79 return intersection;80 }81 }82 83 /**84 * Returns the precomputed intersection between two polygons if known. Otherwise perform {@code computation}.85 *86 * @param a1 first polygon87 * @param a2 second polygon88 * @param computation the computation to perform when intersection is unknown89 * @return the intersection between two polygons90 * @see Map#computeIfAbsent91 */92 PolygonIntersection computeIfAbsent(JoinedPolygon a1, JoinedPolygon a2, Supplier<PolygonIntersection> computation) {93 PolygonIntersection intersection = results.get(Pair.create(a1, a2));94 if (intersection == null) {95 intersection = computation.get();96 synchronized (results) {97 results.put(Pair.create(a1, a2), intersection);98 results.put(Pair.create(a2, a1), getReverseIntersectionResult(intersection));99 }100 }101 return intersection;102 }103 104 }105 106 31 /** 107 32 * Represents one polygon that consists of multiple ways. 108 33 */ 109 34 public static class JoinedPolygon { 35 /** list of ways building this polygon */ 110 36 public final List<Way> ways; 37 /** list of flags that indicate if the nodes of the way in the same position where reversed */ 111 38 public final List<Boolean> reversed; 39 /** the nodes of the polygon, first node is not duplicated as last node. */ 112 40 public final List<Node> nodes; 41 /** the area in east/north space */ 113 42 public final Area area; 43 /** the integer bounds, 44 * @deprecated better use area.getBounds2D() 45 * */ 46 @Deprecated 114 47 public final Rectangle bounds; 115 48 … … 140 73 */ 141 74 public List<Node> getNodes() { 142 List<Node> nodes = new ArrayList<>();75 List<Node> ringNodes = new ArrayList<>(); 143 76 144 77 for (int waypos = 0; waypos < this.ways.size(); waypos++) { 145 78 Way way = this.ways.get(waypos); 146 boolean reversed = this.reversed.get(waypos).booleanValue(); 147 148 if (!reversed) { 79 80 if (!this.reversed.get(waypos)) { 149 81 for (int pos = 0; pos < way.getNodesCount() - 1; pos++) { 150 nodes.add(way.getNode(pos));82 ringNodes.add(way.getNode(pos)); 151 83 } 152 84 } else { 153 85 for (int pos = way.getNodesCount() - 1; pos > 0; pos--) { 154 nodes.add(way.getNode(pos));86 ringNodes.add(way.getNode(pos)); 155 87 } 156 88 } 157 89 } 158 90 159 return nodes; 160 } 161 } 162 163 /** 164 * Helper storage class for finding findOuterWays 165 */ 166 static class PolygonLevel { 167 public final int level; // nesting level, even for outer, odd for inner polygons. 168 public final JoinedPolygon outerWay; 169 170 public List<JoinedPolygon> innerWays; 171 172 PolygonLevel(JoinedPolygon pol, int level) { 173 this.outerWay = pol; 174 this.level = level; 175 this.innerWays = new ArrayList<>(); 91 return ringNodes; 176 92 } 177 93 } … … 202 118 /** 203 119 * Splits ways into inner and outer JoinedWays. Sets {@link #innerWays} and {@link #outerWays} to the result. 204 * TODO: Currently cannot process touching polygons. See code in JoinAreasAction.120 * Calculation is done in {@link MultipolygonTest#makeFromWays(Collection)} to ensure that the result is a valid multipolygon. 205 121 * @param ways ways to analyze 206 122 * @return error description if the ways cannot be split, {@code null} if all fine. 207 123 */ 208 124 public String makeFromWays(Collection<Way> ways) { 209 try { 210 List<JoinedPolygon> joinedWays = joinWays(ways); 211 //analyze witch way is inside witch outside. 212 return makeFromPolygons(joinedWays); 213 } catch (JoinedPolygonCreationException ex) { 214 Logging.debug(ex); 215 return ex.getMessage(); 216 } 125 MultipolygonTest mpTest = new MultipolygonTest(); 126 Relation calculated = mpTest.makeFromWays(ways); 127 if (!mpTest.getErrors().isEmpty()) { 128 return mpTest.getErrors().iterator().next().getMessage(); 129 } 130 Pair<List<JoinedPolygon>, List<JoinedPolygon>> outerInner = joinWays(calculated); 131 this.outerWays.clear(); 132 this.innerWays.clear(); 133 this.outerWays.addAll(outerInner.a); 134 this.innerWays.addAll(outerInner.b); 135 return null; 217 136 } 218 137 … … 328 247 return joinedWays; 329 248 } 330 331 /**332 * This method analyzes which ways are inner and which outer. Sets {@link #innerWays} and {@link #outerWays} to the result.333 * @param polygons polygons to analyze334 * @return error description if the ways cannot be split, {@code null} if all fine.335 */336 private String makeFromPolygons(List<JoinedPolygon> polygons) {337 List<PolygonLevel> list = findOuterWaysMultiThread(polygons);338 339 if (list == null) {340 return tr("There is an intersection between ways.");341 }342 343 this.outerWays.clear();344 this.innerWays.clear();345 346 //take every other level347 for (PolygonLevel pol : list) {348 if (pol.level % 2 == 0) {349 this.outerWays.add(pol.outerWay);350 } else {351 this.innerWays.add(pol.outerWay);352 }353 }354 355 return null;356 }357 358 private static Pair<Boolean, List<JoinedPolygon>> findInnerWaysCandidates(IntersectionMatrix cache,359 JoinedPolygon outerWay, Collection<JoinedPolygon> boundaryWays) {360 boolean outerGood = true;361 List<JoinedPolygon> innerCandidates = new ArrayList<>();362 363 for (JoinedPolygon innerWay : boundaryWays) {364 if (innerWay == outerWay) {365 continue;366 }367 368 // Preliminary computation on bounds. If bounds do not intersect, no need to do a costly area intersection369 if (outerWay.bounds.intersects(innerWay.bounds)) {370 // Bounds intersection, let's see in detail371 final PolygonIntersection intersection = cache.computeIfAbsent(outerWay, innerWay,372 () -> Geometry.polygonIntersection(outerWay.area, innerWay.area));373 374 if (intersection == PolygonIntersection.FIRST_INSIDE_SECOND) {375 outerGood = false; // outer is inside another polygon376 break;377 } else if (intersection == PolygonIntersection.SECOND_INSIDE_FIRST) {378 innerCandidates.add(innerWay);379 } else if (intersection == PolygonIntersection.CROSSING) {380 // ways intersect381 return null;382 }383 }384 }385 386 return new Pair<>(outerGood, innerCandidates);387 }388 389 /**390 * Collects outer way and corresponding inner ways from all boundaries.391 * @param boundaryWays boundary ways392 * @return the outermostWay, or {@code null} if intersection found.393 */394 private static List<PolygonLevel> findOuterWaysMultiThread(List<JoinedPolygon> boundaryWays) {395 final IntersectionMatrix cache = new IntersectionMatrix(boundaryWays);396 if (THREAD_POOL != null) {397 return THREAD_POOL.invoke(new Worker(cache, boundaryWays, 0, boundaryWays.size(), new ArrayList<PolygonLevel>(),398 Math.max(32, boundaryWays.size() / THREAD_POOL.getParallelism() / 3)));399 } else {400 return new Worker(cache, boundaryWays, 0, boundaryWays.size(), new ArrayList<PolygonLevel>(), 0).computeDirectly();401 }402 }403 404 private static class Worker extends RecursiveTask<List<PolygonLevel>> {405 406 // Needed for Findbugs / Coverity because parent class is serializable407 private static final long serialVersionUID = 1L;408 409 private final transient List<JoinedPolygon> input;410 private final int from;411 private final int to;412 private final transient List<PolygonLevel> output;413 private final int directExecutionTaskSize;414 private final IntersectionMatrix cache;415 416 Worker(IntersectionMatrix cache, List<JoinedPolygon> input, int from, int to, List<PolygonLevel> output, int directExecutionTaskSize) {417 this.cache = cache;418 this.input = input;419 this.from = from;420 this.to = to;421 this.output = output;422 this.directExecutionTaskSize = directExecutionTaskSize;423 }424 425 /**426 * Collects outer way and corresponding inner ways from all boundaries.427 * @param level nesting level428 * @param cache cache that tracks previously calculated results429 * @param boundaryWays boundary ways430 * @return the outermostWay, or {@code null} if intersection found.431 */432 private static List<PolygonLevel> findOuterWaysRecursive(int level, IntersectionMatrix cache, List<JoinedPolygon> boundaryWays) {433 434 final List<PolygonLevel> result = new ArrayList<>();435 436 for (JoinedPolygon outerWay : boundaryWays) {437 if (processOuterWay(level, cache, boundaryWays, result, outerWay) == null) {438 return null;439 }440 }441 442 return result;443 }444 445 private static List<PolygonLevel> processOuterWay(int level, IntersectionMatrix cache, List<JoinedPolygon> boundaryWays,446 final List<PolygonLevel> result, JoinedPolygon outerWay) {447 Pair<Boolean, List<JoinedPolygon>> p = findInnerWaysCandidates(cache, outerWay, boundaryWays);448 if (p == null) {449 // ways intersect450 return null;451 }452 453 if (p.a) {454 //add new outer polygon455 PolygonLevel pol = new PolygonLevel(outerWay, level);456 457 //process inner ways458 if (!p.b.isEmpty()) {459 List<PolygonLevel> innerList = findOuterWaysRecursive(level + 1, cache, p.b);460 if (innerList == null) {461 return null; //intersection found462 }463 464 result.addAll(innerList);465 466 for (PolygonLevel pl : innerList) {467 if (pl.level == level + 1) {468 pol.innerWays.add(pl.outerWay);469 }470 }471 }472 473 result.add(pol);474 }475 return result;476 }477 478 @Override479 protected List<PolygonLevel> compute() {480 if (to - from <= directExecutionTaskSize) {481 return computeDirectly();482 } else {483 final Collection<ForkJoinTask<List<PolygonLevel>>> tasks = new ArrayList<>();484 for (int fromIndex = from; fromIndex < to; fromIndex += directExecutionTaskSize) {485 tasks.add(new Worker(cache, input, fromIndex, Math.min(fromIndex + directExecutionTaskSize, to),486 new ArrayList<PolygonLevel>(), directExecutionTaskSize));487 }488 for (ForkJoinTask<List<PolygonLevel>> task : ForkJoinTask.invokeAll(tasks)) {489 List<PolygonLevel> res = task.join();490 if (res == null) {491 return null;492 }493 output.addAll(res);494 }495 return output;496 }497 }498 499 List<PolygonLevel> computeDirectly() {500 for (int i = from; i < to; i++) {501 if (processOuterWay(0, cache, input, output, input.get(i)) == null) {502 return null;503 }504 }505 return output;506 }507 508 private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {509 // Needed for Findbugs / Coverity because parent class is serializable510 ois.defaultReadObject();511 }512 513 private void writeObject(ObjectOutputStream oos) throws IOException {514 // Needed for Findbugs / Coverity because parent class is serializable515 oos.defaultWriteObject();516 }517 }518 249 } -
trunk/src/org/openstreetmap/josm/data/validation/tests/MultipolygonTest.java
r15132 r15160 79 79 private static final int FOUND_OUTSIDE = 2; 80 80 81 /** set when used to build a multipolygon relation */ 82 private Relation createdRelation; 83 81 84 /** 82 85 * Constructs a new {@code MultipolygonTest}. … … 464 467 return; 465 468 } 466 469 if (r == createdRelation) { 470 List<RelationMember> modMembers = new ArrayList<>(); 471 for (PolygonLevel pol : list) { 472 final String calculatedRole = (pol.level % 2 == 0) ? "outer" : "inner"; 473 for (long wayId : pol.outerWay.getWayIds()) { 474 RelationMember member = wayMap.get(wayId); 475 modMembers.add(new RelationMember(calculatedRole, member.getMember())); 476 } 477 } 478 r.setMembers(modMembers); 479 return; 480 } 467 481 for (PolygonLevel pol : list) { 468 String calculatedRole = (pol.level % 2 == 0) ? "outer" : "inner";482 final String calculatedRole = (pol.level % 2 == 0) ? "outer" : "inner"; 469 483 for (long wayId : pol.outerWay.getWayIds()) { 470 484 RelationMember member = wayMap.get(wayId); … … 894 908 } 895 909 } 910 911 /** 912 * Create a multipolygon relation from the given ways and test it. 913 * @param ways the collection of ways 914 * @return a pair of a {@link Multipolygon} instance and the relation. 915 * @since 15160 916 */ 917 public 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 r; 935 } 936 896 937 } -
trunk/src/org/openstreetmap/josm/tools/Geometry.java
r15107 r15160 1016 1016 for (JoinedPolygon out : outerInner.a) { 1017 1017 if (a1 == null 1018 ? nodeInsidePolygon(nodes.get(0), out. getNodes())1018 ? nodeInsidePolygon(nodes.get(0), out.nodes) 1019 1019 : PolygonIntersection.FIRST_INSIDE_SECOND == polygonIntersection(a1, out.area)) { 1020 1020 boolean insideInner = false; 1021 1021 // If inside an outer, check it is not inside an inner 1022 1022 for (JoinedPolygon in : outerInner.b) { 1023 if (a1 == null ? nodeInsidePolygon(nodes.get(0), in. getNodes())1023 if (a1 == null ? nodeInsidePolygon(nodes.get(0), in.nodes) 1024 1024 : in.area.getBounds2D().contains(a1.getBounds2D()) 1025 1025 && polygonIntersection(a1, in.area) == PolygonIntersection.FIRST_INSIDE_SECOND
Note:
See TracChangeset
for help on using the changeset viewer.