- Timestamp:
- 2014-04-14T21:22:20+02:00 (11 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/actions/JoinAreasAction.java
r6913 r6976 160 160 return nodes; 161 161 } 162 163 /** 164 * Inverse inside and outside 165 */ 166 public void reverse() { 167 for(WayInPolygon way: ways) 168 way.insideToTheRight = !way.insideToTheRight; 169 Collections.reverse(ways); 170 } 162 171 } 163 172 … … 194 203 } 195 204 196 public boolean hasWays() {197 return !availableWays.isEmpty();198 } 199 200 public WayInPolygon startNewWay(WayInPolygon way) {205 public void removeWay(WayInPolygon way) { 206 availableWays.remove(way); 207 } 208 209 public void setStartWay(WayInPolygon way) { 201 210 lastWay = way; 202 lastWayReverse = !lastWay.insideToTheRight; 203 204 return lastWay; 211 lastWayReverse = !way.insideToTheRight; 205 212 } 206 213 … … 216 223 } 217 224 218 219 public WayInPolygon advanceNextLeftmostWay() { 220 return advanceNextWay(false); 221 } 222 223 public WayInPolygon advanceNextRightmostWay() { 224 return advanceNextWay(true); 225 } 226 227 private WayInPolygon advanceNextWay(boolean rightmost) { 228 225 /** 226 * Get the next way creating a clockwise path, ensure it is the most right way. #7959 227 * @return The next way. 228 */ 229 public WayInPolygon walk() { 229 230 Node headNode = !lastWayReverse ? lastWay.way.lastNode() : lastWay.way.firstNode(); 230 231 Node prevNode = !lastWayReverse ? lastWay.way.getNode(lastWay.way.getNodesCount() - 2) : lastWay.way.getNode(1); 231 232 233 double headAngle = Math.atan2(headNode.getEastNorth().east() - prevNode.getEastNorth().east(), 234 headNode.getEastNorth().north() - prevNode.getEastNorth().north()); 235 double bestAngle = 0; 236 232 237 //find best next way 233 238 WayInPolygon bestWay = null; 234 Node bestWayNextNode = null;235 239 boolean bestWayReverse = false; 236 240 237 241 for (WayInPolygon way : availableWays) { 238 if (way.way.firstNode().equals(headNode)) { 239 //start adjacent to headNode 240 Node nextNode = way.way.getNode(1); 241 242 if (nextNode.equals(prevNode)) 243 { 244 //this is the path we came from - ignore it. 245 } 246 else if (bestWay == null || (Geometry.isToTheRightSideOfLine(prevNode, headNode, bestWayNextNode, nextNode) == rightmost)) { 247 //the new way is better 248 bestWay = way; 249 bestWayReverse = false; 250 bestWayNextNode = nextNode; 251 } 252 } 253 254 if (way.way.lastNode().equals(headNode)) { 255 //end adjacent to headNode 256 Node nextNode = way.way.getNode(way.way.getNodesCount() - 2); 257 258 if (nextNode.equals(prevNode)) { 259 //this is the path we came from - ignore it. 260 } 261 else if (bestWay == null || (Geometry.isToTheRightSideOfLine(prevNode, headNode, bestWayNextNode, nextNode) == rightmost)) { 262 //the new way is better 263 bestWay = way; 264 bestWayReverse = true; 265 bestWayNextNode = nextNode; 266 } 242 Node nextNode; 243 244 // Check for a connected way 245 if (way.way.firstNode().equals(headNode) && way.insideToTheRight) { 246 nextNode = way.way.getNode(1); 247 } else if (way.way.lastNode().equals(headNode) && !way.insideToTheRight) { 248 nextNode = way.way.getNode(way.way.getNodesCount() - 2); 249 } else { 250 continue; 251 } 252 253 if(nextNode == prevNode) { 254 // go back 255 lastWay = way; 256 lastWayReverse = !way.insideToTheRight; 257 return lastWay; 258 } 259 260 double angle = Math.atan2(nextNode.getEastNorth().east() - headNode.getEastNorth().east(), 261 nextNode.getEastNorth().north() - headNode.getEastNorth().north()) - headAngle; 262 if(angle > Math.PI) 263 angle -= 2*Math.PI; 264 if(angle <= -Math.PI) 265 angle += 2*Math.PI; 266 267 // Now we have a valid candidate way, is it better than the previous one ? 268 if (bestWay == null || angle > bestAngle) { 269 //the new way is better 270 bestWay = way; 271 bestWayReverse = !way.insideToTheRight; 272 bestAngle = angle; 267 273 } 268 274 } … … 272 278 273 279 return lastWay; 274 }275 276 public boolean isLastWayInsideToTheRight() {277 return lastWayReverse != lastWay.insideToTheRight;278 }279 280 public Node getLastWayStartNode() {281 return lastWayReverse ? lastWay.way.lastNode() : lastWay.way.firstNode();282 }283 284 public Node getLastWayEndNode() {285 return lastWayReverse ? lastWay.way.firstNode() : lastWay.way.lastNode();286 280 } 287 281 } … … 318 312 LinkedList<Way> ways = new LinkedList<Way>(Main.main.getCurrentDataSet().getSelectedWays()); 319 313 addedRelations.clear(); 320 314 321 315 if (ways.isEmpty()) { 322 316 new Notification( … … 381 375 } 382 376 commitCommands(tr("Move tags from ways to relations")); 383 377 384 378 List<Way> allWays = new ArrayList<Way>(); 385 379 for (Multipolygon pol : result.polygons) { … … 969 963 //first find all discardable ways, by getting outer shells. 970 964 //this will produce incorrect boundaries in some cases, but second pass will fix it. 971 972 965 List<WayInPolygon> discardedWays = new ArrayList<WayInPolygon>(); 973 Set<WayInPolygon> processedWays = new HashSet<WayInPolygon>(); 974 WayTraverser traverser = new WayTraverser(multigonWays); 975 976 for (WayInPolygon startWay : multigonWays) { 977 if (processedWays.contains(startWay)) { 978 continue; 979 } 980 981 traverser.startNewWay(startWay); 982 983 List<WayInPolygon> boundary = new ArrayList<WayInPolygon>(); 984 WayInPolygon lastWay = startWay; 985 986 while (true) { 987 boundary.add(lastWay); 988 989 WayInPolygon bestWay = traverser.advanceNextLeftmostWay(); 990 boolean wayInsideToTheRight = bestWay == null ? false : traverser.isLastWayInsideToTheRight(); 991 992 if (bestWay == null || processedWays.contains(bestWay) || !wayInsideToTheRight) { 993 //bad segment chain - proceed to discard it 994 lastWay = null; 966 967 // In multigonWays collection, some way are just a point (i.e. way like nodeA-nodeA) 968 // This seems to appear when is apply over invalid way like #9911 test-case 969 // Remove all of these way to make the next work. 970 ArrayList<WayInPolygon> cleanMultigonWays = new ArrayList<WayInPolygon>(); 971 for(WayInPolygon way: multigonWays) 972 if(way.way.getNodesCount() == 2 && way.way.firstNode() == way.way.lastNode()) 973 discardedWays.add(way); 974 else 975 cleanMultigonWays.add(way); 976 977 WayTraverser traverser = new WayTraverser(cleanMultigonWays); 978 List<AssembledPolygon> result = new ArrayList<AssembledPolygon>(); 979 980 WayInPolygon startWay; 981 while((startWay = traverser.startNewWay()) != null) { 982 ArrayList<WayInPolygon> path = new ArrayList<WayInPolygon>(); 983 path.add(startWay); 984 while(true) { 985 WayInPolygon nextWay = traverser.walk(); 986 if(nextWay == null) 987 throw new RuntimeException("Join areas internal error."); 988 if(path.get(0) == nextWay) { 989 AssembledPolygon ring = new AssembledPolygon(path); 990 if(ring.getNodes().size() <= 2) { 991 // Invalid ring (2 nodes) -> remove 992 traverser.removeWays(path); 993 for(WayInPolygon way: path) 994 discardedResult.add(way.way); 995 } else { 996 // Close ring -> add 997 result.add(ring); 998 traverser.removeWays(path); 999 } 995 1000 break; 996 } else if (boundary.contains(bestWay)) { 997 //traversed way found - close the way 998 lastWay = bestWay; 999 break; 1001 } 1002 if(path.contains(nextWay)) { 1003 // Inner loop -> remove 1004 int index = path.indexOf(nextWay); 1005 while(path.size() > index) { 1006 WayInPolygon currentWay = path.get(index); 1007 discardedResult.add(currentWay.way); 1008 traverser.removeWay(currentWay); 1009 path.remove(index); 1010 } 1011 traverser.setStartWay(path.get(index-1)); 1000 1012 } else { 1001 //proceed to next segment 1002 lastWay = bestWay; 1003 } 1004 } 1005 1006 if (lastWay != null) { 1007 //way good 1008 processedWays.addAll(boundary); 1009 1010 //remove junk segments at the start 1011 while (boundary.get(0) != lastWay) { 1012 discardedWays.add(boundary.get(0)); 1013 boundary.remove(0); 1014 } 1015 } else { 1016 //way bad 1017 discardedWays.addAll(boundary); 1018 processedWays.addAll(boundary); 1019 } 1020 } 1021 1022 //now we have removed junk segments, collect the real result ways 1023 1024 traverser.removeWays(discardedWays); 1025 1026 List<AssembledPolygon> result = new ArrayList<AssembledPolygon>(); 1027 1028 while (traverser.hasWays()) { 1029 1030 WayInPolygon startWay = traverser.startNewWay(); 1031 List<WayInPolygon> boundary = new ArrayList<WayInPolygon>(); 1032 WayInPolygon curWay = startWay; 1033 1034 do { 1035 boundary.add(curWay); 1036 curWay = traverser.advanceNextRightmostWay(); 1037 1038 //should not happen 1039 if (curWay == null || !traverser.isLastWayInsideToTheRight()) 1040 throw new RuntimeException("Join areas internal error."); 1041 1042 } while (curWay != startWay); 1043 1044 //build result 1045 traverser.removeWays(boundary); 1046 result.add(new AssembledPolygon(boundary)); 1047 } 1048 1049 for (WayInPolygon way : discardedWays) { 1050 discardedResult.add(way.way); 1051 } 1052 1053 //split inner polygons that have several touching parts. 1054 result = fixTouchingPolygons(result); 1055 1056 return result; 1013 path.add(nextWay); 1014 } 1015 } 1016 } 1017 1018 return fixTouchingPolygons(result); 1057 1019 } 1058 1020 … … 1061 1023 * @param polygons the polygons to process. 1062 1024 */ 1063 public static List<AssembledPolygon> fixTouchingPolygons(List<AssembledPolygon> polygons) 1064 { 1025 public static List<AssembledPolygon> fixTouchingPolygons(List<AssembledPolygon> polygons) { 1065 1026 List<AssembledPolygon> newPolygons = new ArrayList<AssembledPolygon>(); 1066 1027 1067 for (AssembledPolygon innerPart : polygons) { 1068 WayTraverser traverser = new WayTraverser(innerPart.ways); 1069 1070 while (traverser.hasWays()) { 1071 1072 WayInPolygon startWay = traverser.startNewWay(); 1073 List<WayInPolygon> boundary = new ArrayList<WayInPolygon>(); 1074 WayInPolygon curWay = startWay; 1075 1076 Node startNode = traverser.getLastWayStartNode(); 1077 boundary.add(curWay); 1078 1079 while (startNode != traverser.getLastWayEndNode()) { 1080 curWay = traverser.advanceNextLeftmostWay(); 1081 boundary.add(curWay); 1082 1083 //should not happen 1084 if (curWay == null || !traverser.isLastWayInsideToTheRight()) 1028 for (AssembledPolygon ring : polygons) { 1029 ring.reverse(); 1030 WayTraverser traverser = new WayTraverser(ring.ways); 1031 WayInPolygon startWay; 1032 1033 while((startWay = traverser.startNewWay()) != null) { 1034 List<WayInPolygon> simpleRingWays = new ArrayList<WayInPolygon>(); 1035 simpleRingWays.add(startWay); 1036 WayInPolygon nextWay; 1037 while((nextWay = traverser.walk()) != startWay) { 1038 if(nextWay == null) 1085 1039 throw new RuntimeException("Join areas internal error."); 1086 } 1087 1088 //build result 1089 traverser.removeWays(boundary); 1090 newPolygons.add(new AssembledPolygon(boundary)); 1040 simpleRingWays.add(nextWay); 1041 } 1042 traverser.removeWays(simpleRingWays); 1043 AssembledPolygon simpleRing = new AssembledPolygon(simpleRingWays); 1044 simpleRing.reverse(); 1045 newPolygons.add(simpleRing); 1091 1046 } 1092 1047 }
Note:
See TracChangeset
for help on using the changeset viewer.