Ticket #10744: josm-7155-ease-multipolygon-handling-and-creation.patch
| File josm-7155-ease-multipolygon-handling-and-creation.patch, 12.6 KB (added by , 12 years ago) |
|---|
-
src/org/openstreetmap/josm/actions/SplitWayAction.java
60 60 * @param command The command to be performed to split the way (which is saved for later retrieval by the {@link #getCommand} method) 61 61 * @param newSelection The new list of selected primitives ids (which is saved for later retrieval by the {@link #getNewSelection} method) 62 62 * @param originalWay The original way being split (which is saved for later retrieval by the {@link #getOriginalWay} method) 63 * @param newWays The resulting new ways (which is saved for later retrieval by the {@link #get OriginalWay} method)63 * @param newWays The resulting new ways (which is saved for later retrieval by the {@link #getNewWays} method) 64 64 */ 65 65 public SplitWayResult(Command command, List<? extends PrimitiveId> newSelection, Way originalWay, List<Way> newWays) { 66 66 this.command = command; … … 100 100 public List<Way> getNewWays() { 101 101 return newWays; 102 102 } 103 104 /** 105 * Replies the resulting new ways and the original way 106 * @return All resulting ways 107 */ 108 public List<Way> getAllWays() { 109 List<Way> ret = new ArrayList<>(); 110 ret.add(originalWay); 111 ret.addAll(newWays); 112 return ret; 113 } 103 114 } 104 115 105 116 /** … … 249 260 * Splits the nodes of {@code wayToSplit} into a list of node sequences 250 261 * which are separated at the nodes in {@code splitPoints}. 251 262 * 252 * This method displays warning messages if {@code wayToSplit} and/or 253 * {@code splitPoints} aren't consistent. 263 * This method does not hog the GUI thread. 254 264 * 255 * Returns null, if building the split chunks fails.256 *257 265 * @param wayToSplit the way to split. Must not be null. 258 266 * @param splitPoints the nodes where the way is split. Must not be null. 259 267 * @return the list of chunks 260 268 */ 261 public static List<List<Node>> buildSplitChunks (Way wayToSplit, List<Node> splitPoints){269 public static List<List<Node>> buildSplitChunksNoGUI(Way wayToSplit, Collection<Node> splitPoints){ 262 270 CheckParameterUtil.ensureParameterNotNull(wayToSplit, "wayToSplit"); 263 271 CheckParameterUtil.ensureParameterNotNull(splitPoints, "splitPoints"); 264 272 … … 289 297 && wayChunks.get(0).get(0) == lastWayChunk.get(lastWayChunk.size() - 1) 290 298 && !nodeSet.contains(wayChunks.get(0).get(0))) { 291 299 if (wayChunks.size() == 2) { 292 new Notification( 293 tr("You must select two or more nodes to split a circular way.")) 294 .setIcon(JOptionPane.WARNING_MESSAGE) 295 .show(); 296 return null; 300 wayChunks.get(0).set(wayChunks.get(0).size() - 1, wayChunks.get(0).get(0)); 301 wayChunks.remove(1); 302 return wayChunks; 297 303 } 298 304 lastWayChunk.remove(lastWayChunk.size() - 1); 299 305 lastWayChunk.addAll(wayChunks.get(0)); … … 301 307 wayChunks.set(0, lastWayChunk); 302 308 } 303 309 310 return wayChunks; 311 } 312 313 /** 314 * Splits the nodes of {@code wayToSplit} into a list of node sequences 315 * which are separated at the nodes in {@code splitPoints}. 316 * 317 * This method displays warning messages if {@code wayToSplit} and/or 318 * {@code splitPoints} aren't consistent. 319 * 320 * Returns null, if building the split chunks fails. 321 * 322 * @param wayToSplit the way to split. Must not be null. 323 * @param splitPoints the nodes where the way is split. Must not be null. 324 * @return the list of chunks 325 */ 326 public static List<List<Node>> buildSplitChunks(Way wayToSplit, List<Node> splitPoints) { 327 List<List<Node>> wayChunks = buildSplitChunksNoGUI(wayToSplit, splitPoints); 328 304 329 if (wayChunks.size() < 2) { 305 330 if (wayChunks.get(0).get(0) == wayChunks.get(0).get(wayChunks.get(0).size() - 1)) { 306 331 new Notification( … … 313 338 .setIcon(JOptionPane.WARNING_MESSAGE) 314 339 .show(); 315 340 } 316 return null;317 341 } 318 return wayChunks; 342 343 return (wayChunks.size() < 2) ? null : wayChunks; 319 344 } 320 345 321 346 /** … … 333 358 * @return the result from the split operation 334 359 */ 335 360 public static SplitWayResult splitWay(OsmDataLayer layer, Way way, List<List<Node>> wayChunks, Collection<? extends OsmPrimitive> selection) { 361 if (selection == null) selection = new ArrayList<>(); 362 336 363 // build a list of commands, and also a new selection list 337 364 Collection<Command> commandList = new ArrayList<>(wayChunks.size()); 338 365 List<OsmPrimitive> newSelection = new ArrayList<>(selection.size() + wayChunks.size()); … … 527 554 public static SplitWayResult split(OsmDataLayer layer, Way way, List<Node> atNodes, Collection<? extends OsmPrimitive> selection) { 528 555 List<List<Node>> chunks = buildSplitChunks(way, atNodes); 529 556 if (chunks == null) return null; 530 return splitWay(layer, way, chunks, selection);557 return splitWay(layer, way, chunks, selection); 531 558 } 532 559 533 560 @Override -
src/org/openstreetmap/josm/data/osm/MultipolygonBuilder.java
8 8 import java.util.ArrayList; 9 9 import java.util.Collection; 10 10 import java.util.Collections; 11 import java.util.HashMap; 11 12 import java.util.HashSet; 12 13 import java.util.List; 14 import java.util.Map; 13 15 import java.util.Set; 14 16 import java.util.concurrent.Callable; 15 17 import java.util.concurrent.ExecutionException; … … 16 18 import java.util.concurrent.ExecutorService; 17 19 import java.util.concurrent.Future; 18 20 21 import org.openstreetmap.josm.Main; 22 import org.openstreetmap.josm.actions.SplitWayAction; 23 import org.openstreetmap.josm.actions.SplitWayAction.SplitWayResult; 24 import org.openstreetmap.josm.command.DeleteCommand; 19 25 import org.openstreetmap.josm.tools.Geometry; 20 26 import org.openstreetmap.josm.tools.Geometry.PolygonIntersection; 21 27 import org.openstreetmap.josm.tools.MultiMap; 22 28 import org.openstreetmap.josm.tools.Pair; 29 import org.openstreetmap.josm.tools.Predicate; 23 30 import org.openstreetmap.josm.tools.Utils; 24 31 25 32 /** … … 129 136 } 130 137 131 138 /** 132 * S plitsways into inner and outer JoinedWays. Sets {@link #innerWays} and {@link #outerWays} to the result.139 * Separate ways into inner and outer JoinedWays. Sets {@link #innerWays} and {@link #outerWays} to the result. 133 140 * TODO: Currently cannot process touching polygons. See code in JoinAreasAction. 134 141 * @param ways ways to analyze 135 * @return error description if the ways cannot be s plit, {@code null} if all fine.142 * @return error description if the ways cannot be separated, {@code null} if all fine. 136 143 */ 137 144 public String makeFromWays(Collection<Way> ways) { 138 145 try { 139 146 List<JoinedPolygon> joinedWays = joinWays(ways); 140 //analyze w itch way is inside witch outside.147 //analyze which way is inside which outside. 141 148 return makeFromPolygons(joinedWays); 142 149 } catch (JoinedPolygonCreationException ex) { 143 150 return ex.getMessage(); … … 159 166 } 160 167 161 168 /** 169 * Replies the current dataset 170 * 171 * @return the current dataset. null, if no current dataset exists 172 */ 173 protected static DataSet getCurrentDataSet() { 174 return Main.main != null ? Main.main.getCurrentDataSet() : null; 175 } 176 177 /** 162 178 * Joins the given {@code ways} to multipolygon rings. 163 179 * @param ways the ways to join. 164 180 * @return a list of multipolygon rings. … … 167 183 public static List<JoinedPolygon> joinWays(Collection<Way> ways) throws JoinedPolygonCreationException { 168 184 List<JoinedPolygon> joinedWays = new ArrayList<>(); 169 185 186 { /* code block to ease the pain in MP handling - looks for overlapped ways that 187 * already are in another _MP_ relation and tries its best to reuse segments. 188 * it does auto-splitting and -selection and exclusively deletes unneeded parts 189 * from the original selection. these parts are replaced by reusable way segments. 190 * consecutively used, overlapping ways should disappear for adjacent MP areas. 191 */ 192 final Map<Way, Set<Node>> splits = new HashMap<>(); 193 final Set<Node> ways_nodes = new HashSet<>(); 194 for (final Way w: ways) { 195 ways_nodes.addAll(w.getNodes()); 196 197 for (final Pair<Node,Node> p: w.getNodePairs(false)) { 198 Collection<Way> dups = Utils.filter( 199 OsmPrimitive.getFilteredList(p.b.getReferrers(), Way.class), 200 new Predicate<Way>() { 201 public boolean evaluate(final Way v) { 202 return v.equals(w) || v.containsNode(p.a) && Utils.exists( 203 OsmPrimitive.getFilteredList(v.getReferrers(), Relation.class), 204 new Predicate<Relation>() { 205 public boolean evaluate(final Relation r) { 206 return r.hasTag("type", "multipolygon"); 207 } 208 } 209 ); 210 } 211 } 212 ); 213 214 // TODO: handle (rare?) cases where dups.size() > 2 applies or stop and warn user 215 if (dups.size() > 1) { 216 for (Way d: dups) { 217 if (!splits.containsKey(d)) { 218 splits.put(d, new HashSet<Node>()); 219 } 220 for (Node n: Pair.toArrayList(p)) { 221 if (splits.get(d).contains(n)) { 222 splits.get(d).remove(n); 223 } else { 224 splits.get(d).add(n); 225 } 226 } 227 } 228 } 229 230 } 231 } 232 233 ways = new ArrayList<>(ways); 234 235 final List<Way> reuse = new ArrayList<>(); 236 for (final Way d: splits.keySet()) { 237 List<List<Node>> chunks = 238 SplitWayAction.buildSplitChunksNoGUI(d, splits.get(d)); 239 240 if (chunks.size() > 1) { 241 SplitWayResult r = SplitWayAction.splitWay(Main.main.getEditLayer(), d, chunks, null); 242 Main.main.undoRedo.addNoRedraw(r.getCommand()); 243 244 if (ways.contains(r.getOriginalWay())) { 245 ways.addAll(r.getNewWays()); 246 } else { 247 reuse.addAll(Utils.filter(r.getAllWays(), new Predicate<Way>() { 248 public boolean evaluate(final Way w) { 249 return ways_nodes.containsAll(w.getNodes()); 250 } 251 })); 252 } 253 } else if (!ways.contains(d)) { 254 reuse.add(d); 255 } 256 } 257 258 final Set<Node> reuse_nodes = new HashSet<>(); 259 for (final Way w: reuse) { 260 reuse_nodes.addAll(w.getNodes()); 261 } 262 263 Collection<Way> dups = new ArrayList<>(Utils.filter(ways, new Predicate<Way>() { 264 public boolean evaluate(final Way w) { 265 return reuse_nodes.containsAll(w.getNodes()); 266 } 267 })); 268 269 Main.debug(ways.toString() +"\n --> original way collection\n"); 270 if (!dups.isEmpty()) { 271 Main.debug(dups.toString() +"\n --> split dups about to be deleted\n"); 272 Main.debug(reuse.toString()+"\n --> reusable ways detected\n"); 273 274 ways.removeAll(dups); 275 ways.addAll(reuse); 276 Main.main.undoRedo.addNoRedraw(DeleteCommand.delete(Main.main.getEditLayer(), dups)); 277 278 Main.debug(ways.toString() +"\n --> modified way collection to be joined\n"); 279 } 280 } /* end code block to ease the pain in MP handling */ 281 170 282 //collect ways connecting to each node. 171 283 MultiMap<Node, Way> nodesWithConnectedWays = new MultiMap<>(); 172 284 Set<Way> usedWays = new HashSet<>();
