Changeset 15574 in josm for trunk/src/org
- Timestamp:
- 2019-12-09T09:47:20+01:00 (5 years ago)
- Location:
- trunk/src/org/openstreetmap/josm
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/actions/CombineWayAction.java
r15555 r15574 32 32 import org.openstreetmap.josm.data.osm.TagCollection; 33 33 import org.openstreetmap.josm.data.osm.Way; 34 import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon; 35 import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon.JoinedWay; 34 36 import org.openstreetmap.josm.data.preferences.BooleanProperty; 37 import org.openstreetmap.josm.data.validation.Test; 38 import org.openstreetmap.josm.data.validation.tests.OverlappingWays; 39 import org.openstreetmap.josm.data.validation.tests.SelfIntersectingWay; 35 40 import org.openstreetmap.josm.gui.ExtendedDialog; 36 41 import org.openstreetmap.josm.gui.MainApplication; … … 123 128 124 129 // try to build a new way which includes all the combined ways 125 NodeGraph graph = NodeGraph.createNearlyUndirectedGraphFromNodeWays(ways); 126 List<Node> path = graph.buildSpanningPathNoRemove(); 127 if (path == null) { 130 List<Node> path = tryJoin(ways); 131 if (path.isEmpty()) { 128 132 warnCombiningImpossible(); 129 133 return null; … … 137 141 List<Way> reversedWays = new LinkedList<>(); 138 142 List<Way> unreversedWays = new LinkedList<>(); 139 for (Way w: ways) { 140 // Treat zero or one-node ways as unreversed as Combine action action is a good way to fix them (see #8971) 141 if (w.getNodesCount() < 2 || (path.indexOf(w.getNode(0)) + 1) == path.lastIndexOf(w.getNode(1))) { 142 unreversedWays.add(w); 143 } else { 144 reversedWays.add(w); 145 } 146 } 143 detectReversedWays(ways, path, reversedWays, unreversedWays); 147 144 // reverse path if all ways have been reversed 148 145 if (unreversedWays.isEmpty()) { … … 164 161 } 165 162 // if there are still reversed ways with direction-dependent tags, reverse their tags 166 if (!reversedWays.isEmpty() && PROP_REVERSE_WAY.get()) {163 if (!reversedWays.isEmpty() && Boolean.TRUE.equals(PROP_REVERSE_WAY.get())) { 167 164 List<Way> unreversedTagWays = new ArrayList<>(ways); 168 165 unreversedTagWays.removeAll(reversedWays); … … 211 208 212 209 return new Pair<>(targetWay, sequenceCommand); 210 } 211 212 protected static void detectReversedWays(Collection<Way> ways, List<Node> path, List<Way> reversedWays, 213 List<Way> unreversedWays) { 214 for (Way w: ways) { 215 // Treat zero or one-node ways as unreversed as Combine action action is a good way to fix them (see #8971) 216 if (w.getNodesCount() < 2) { 217 unreversedWays.add(w); 218 } else { 219 boolean foundStartSegment = false; 220 int last = path.lastIndexOf(w.getNode(0)); 221 222 for (int i = path.indexOf(w.getNode(0)); i <= last; i++) { 223 if (path.get(i) == w.getNode(0) && i + 1 < path.size() && w.getNode(1) == path.get(i + 1)) { 224 foundStartSegment = true; 225 break; 226 } 227 } 228 if (foundStartSegment) { 229 unreversedWays.add(w); 230 } else { 231 reversedWays.add(w); 232 } 233 } 234 } 235 } 236 237 protected static List<Node> tryJoin(Collection<Way> ways) { 238 List<Node> path = joinWithMultipolygonCode(ways); 239 if (path.isEmpty()) { 240 NodeGraph graph = NodeGraph.createNearlyUndirectedGraphFromNodeWays(ways); 241 path = graph.buildSpanningPathNoRemove(); 242 } 243 return path; 244 } 245 246 /** 247 * Use {@link Multipolygon#joinWays(Collection)} to join ways. 248 * @param ways the ways 249 * @return List of nodes of the combined ways or null if ways could not be combined to a single way. 250 * Result may contain overlapping segments. 251 */ 252 private static List<Node> joinWithMultipolygonCode(Collection<Way> ways) { 253 // sort so that old unclosed ways appear first 254 LinkedList<Way> toJoin = new LinkedList<>(ways); 255 toJoin.sort((o1, o2) -> { 256 int d = Boolean.compare(o1.isNew(), o2.isNew()); 257 if (d == 0) 258 d = Boolean.compare(o1.isClosed(), o2.isClosed()); 259 return d; 260 }); 261 Collection<JoinedWay> list = Multipolygon.joinWays(toJoin); 262 if (list.size() == 1) { 263 // ways form a single line string 264 return new ArrayList<>(list.iterator().next().getNodes()); 265 } 266 return Collections.emptyList(); 213 267 } 214 268 … … 238 292 if (combineResult == null) 239 293 return; 294 240 295 final Way selectedWay = combineResult.a; 241 296 UndoRedoHandler.getInstance().add(combineResult.b); 297 Test test = new OverlappingWays(); 298 test.startTest(null); 299 test.visit(combineResult.a); 300 test.endTest(); 301 if (test.getErrors().isEmpty()) { 302 test = new SelfIntersectingWay(); 303 test.startTest(null); 304 test.visit(combineResult.a); 305 test.endTest(); 306 } 307 if (!test.getErrors().isEmpty()) { 308 new Notification(test.getErrors().get(0).getMessage()) 309 .setIcon(JOptionPane.WARNING_MESSAGE) 310 .setDuration(Notification.TIME_SHORT) 311 .show(); 312 } 242 313 if (selectedWay != null) { 243 314 GuiHelper.runInEDT(() -> ds.setSelected(selectedWay)); … … 262 333 setEnabled(numWays >= 2); 263 334 } 335 264 336 } -
trunk/src/org/openstreetmap/josm/data/osm/NodeGraph.java
r15559 r15574 12 12 import java.util.LinkedHashMap; 13 13 import java.util.LinkedHashSet; 14 import java.util.LinkedList;15 14 import java.util.List; 16 15 import java.util.Map; … … 18 17 import java.util.Optional; 19 18 import java.util.Set; 20 import java.util.Stack;21 19 import java.util.TreeMap; 22 20 … … 249 247 } 250 248 251 protected List<Node> buildPathFromNodePairs( Stack<NodePair> path) {252 List<Node> ret = new LinkedList<>();253 for (NodePair pair : path) {249 protected List<Node> buildPathFromNodePairs(Deque<NodePair> path) { 250 List<Node> ret = new ArrayList<>(path.size() + 1); 251 for (NodePair pair : path) { 254 252 ret.add(pair.getA()); 255 253 } 256 ret.add(path.peek ().getB());254 ret.add(path.peekLast().getB()); 257 255 return ret; 258 256 } … … 264 262 * 265 263 * @param startNode the start node 266 * @return the spanning path; null,if no path is found264 * @return the spanning path; empty list if no path is found 267 265 */ 268 266 protected List<Node> buildSpanningPath(Node startNode) { 269 267 if (startNode != null) { 270 // do not simply replace `Stack` by `ArrayDeque` because of different iteration behaviour, see #11957 271 Stack<NodePair> path = new Stack<>(); 268 Deque<NodePair> path = new ArrayDeque<>(); 272 269 Set<NodePair> dupCheck = new HashSet<>(); 273 Stack<NodePair> nextPairs = new Stack<>();270 Deque<NodePair> nextPairs = new ArrayDeque<>(); 274 271 nextPairs.addAll(getOutboundPairs(startNode)); 275 272 while (!nextPairs.isEmpty()) { 276 NodePair cur = nextPairs. pop();273 NodePair cur = nextPairs.removeLast(); 277 274 if (!dupCheck.contains(cur) && !dupCheck.contains(cur.swap())) { 278 while (!path.isEmpty() && !path.peek ().isPredecessorOf(cur)) {279 dupCheck.remove(path. pop());275 while (!path.isEmpty() && !path.peekLast().isPredecessorOf(cur)) { 276 dupCheck.remove(path.removeLast()); 280 277 } 281 path. push(cur);278 path.addLast(cur); 282 279 dupCheck.add(cur); 283 if (isSpanningWay(path)) return buildPathFromNodePairs(path); 284 nextPairs.addAll(getOutboundPairs(path.peek())); 280 if (isSpanningWay(path)) 281 return buildPathFromNodePairs(path); 282 nextPairs.addAll(getOutboundPairs(path.peekLast())); 285 283 } 286 284 } … … 324 322 * any duplicated edge was removed. 325 323 * 326 * @return the path; null, if no path was foundor duplicated edges were found327 * @since 155 55324 * @return List of nodes that build the path; an empty list if no path or duplicated edges were found 325 * @since 15573 (return value not null) 328 326 */ 329 327 public List<Node> buildSpanningPathNoRemove() { 330 if (edges.size() != addedEdges) 331 return null; 332 return buildSpanningPath(); 328 List<Node> path = null; 329 if (edges.size() == addedEdges) 330 path = buildSpanningPath(); 331 return path == null ? Collections.emptyList() : path; 333 332 } 334 333
Note:
See TracChangeset
for help on using the changeset viewer.