From 3f4a6e1b587b3fee55ad0dcc8c48b8edea8ad886 Mon Sep 17 00:00:00 2001
From: =?utf-8?q?Ilpo=20J=C3=A4rvinen?= <ilpo.jarvinen@helsinki.fi>
Date: Sun, 18 Dec 2011 02:58:07 +0200
Subject: [PATCH 2/2] Make followline to handle wraps and stop at ambiguities
When the toFollow way is closed and wraps, the code only
worked for one direction. I first came across very complicated
fix using the existing code, but then decided to make the
code logic followable to mortals too. While recoding, also
another issue was easy to fix, namely, the followline previously
picked just any suitable extension that if found. Instead,
make it to stop when first ambiguity is encountered forcing
the user to point to which direction the way should be extended.
---
.../josm/actions/FollowLineAction.java | 62 +++++++++-----------
src/org/openstreetmap/josm/data/osm/Way.java | 25 ++++++++
2 files changed, 53 insertions(+), 34 deletions(-)
diff --git a/src/org/openstreetmap/josm/actions/FollowLineAction.java b/src/org/openstreetmap/josm/actions/FollowLineAction.java
index 58fe454..6fbffec 100644
a
|
b
|
import java.awt.event.KeyEvent;
|
8 | 8 | import java.util.Collection; |
9 | 9 | import java.util.Iterator; |
10 | 10 | import java.util.List; |
| 11 | import java.util.Set; |
11 | 12 | |
12 | 13 | import org.openstreetmap.josm.Main; |
13 | 14 | import org.openstreetmap.josm.actions.mapmode.DrawAction; |
… |
… |
public class FollowLineAction extends JosmAction {
|
82 | 83 | List<OsmPrimitive> referrers = last.getReferrers(); |
83 | 84 | if (referrers.size() < 2) return; // There's nothing to follow |
84 | 85 | |
| 86 | Node newPoint = null; |
85 | 87 | Iterator<OsmPrimitive> i = referrers.iterator(); |
86 | 88 | while (i.hasNext()) { |
87 | 89 | OsmPrimitive referrer = i.next(); |
… |
… |
public class FollowLineAction extends JosmAction {
|
92 | 94 | if (toFollow.equals(follower)) { |
93 | 95 | continue; |
94 | 96 | } |
95 | | List<Node> points = toFollow.getNodes(); |
96 | | int indexOfLast = points.indexOf(last); |
97 | | int indexOfPrev = points.indexOf(prev); |
98 | | if ((indexOfLast == -1) || (indexOfPrev == -1)) { // Both points must belong to both lines |
| 97 | Set<Node> points = toFollow.getNeighbours(last); |
| 98 | if (!points.remove(prev) || (points.size() == 0)) |
99 | 99 | continue; |
100 | | } |
101 | | if (Math.abs(indexOfPrev - indexOfLast) != 1) { // ...and be consecutive |
102 | | continue; |
103 | | } |
104 | | Node newPoint = null; |
105 | | if (indexOfPrev == (indexOfLast - 1)) { |
106 | | if ((indexOfLast + 1) < points.size()) { |
107 | | newPoint = points.get(indexOfLast + 1); |
108 | | } |
109 | | } else { |
110 | | if ((indexOfLast - 1) >= 0) { |
111 | | newPoint = points.get(indexOfLast - 1); |
112 | | } |
113 | | } |
114 | | if (newPoint != null) { |
115 | | Way newFollower = new Way(follower); |
116 | | if (reversed) { |
117 | | newFollower.addNode(0, newPoint); |
118 | | } else { |
119 | | newFollower.addNode(newPoint); |
120 | | } |
121 | | Main.main.undoRedo.add(new ChangeCommand(follower, newFollower)); |
122 | | osmLayer.data.clearSelection(); |
123 | | osmLayer.data.addSelected(newFollower); |
124 | | osmLayer.data.addSelected(newPoint); |
125 | | // "viewport following" mode for tracing long features |
126 | | // from aerial imagery or GPS tracks. |
127 | | if (Main.map.mapView.viewportFollowing) { |
128 | | Main.map.mapView.smoothScrollTo(newPoint.getEastNorth()); |
129 | | }; |
| 100 | if (points.size() > 1) // Ambiguous junction? |
130 | 101 | return; |
| 102 | |
| 103 | Node newPointCandidate = points.iterator().next(); |
| 104 | |
| 105 | if ((newPoint != null) && (newPoint != newPointCandidate)) |
| 106 | return; // Ambiguous junction, force to select next |
| 107 | |
| 108 | newPoint = newPointCandidate; |
| 109 | } |
| 110 | if (newPoint != null) { |
| 111 | Way newFollower = new Way(follower); |
| 112 | if (reversed) { |
| 113 | newFollower.addNode(0, newPoint); |
| 114 | } else { |
| 115 | newFollower.addNode(newPoint); |
131 | 116 | } |
| 117 | Main.main.undoRedo.add(new ChangeCommand(follower, newFollower)); |
| 118 | osmLayer.data.clearSelection(); |
| 119 | osmLayer.data.addSelected(newFollower); |
| 120 | osmLayer.data.addSelected(newPoint); |
| 121 | // "viewport following" mode for tracing long features |
| 122 | // from aerial imagery or GPS tracks. |
| 123 | if (Main.map.mapView.viewportFollowing) { |
| 124 | Main.map.mapView.smoothScrollTo(newPoint.getEastNorth()); |
| 125 | }; |
132 | 126 | } |
133 | 127 | } |
134 | 128 | } |
diff --git a/src/org/openstreetmap/josm/data/osm/Way.java b/src/org/openstreetmap/josm/data/osm/Way.java
index 8336bc4..137fbc4 100644
a
|
b
|
import java.util.ArrayList;
|
7 | 7 | import java.util.Arrays; |
8 | 8 | import java.util.List; |
9 | 9 | import java.util.Set; |
| 10 | import java.util.HashSet; |
10 | 11 | |
11 | 12 | import org.openstreetmap.josm.Main; |
12 | 13 | import org.openstreetmap.josm.data.coor.LatLon; |
… |
… |
public final class Way extends OsmPrimitive implements IWay {
|
138 | 139 | return false; |
139 | 140 | } |
140 | 141 | |
| 142 | /** |
| 143 | * Return nodes adjacent to <code>node</code> |
| 144 | * |
| 145 | * @param node the node. May be null. |
| 146 | * @return Set of nodes adjacent to <code>node</code> |
| 147 | * @since 4666 |
| 148 | */ |
| 149 | public Set<Node> getNeighbours(Node node) { |
| 150 | HashSet<Node> neigh = new HashSet<Node>(); |
| 151 | |
| 152 | if (node == null) return neigh; |
| 153 | |
| 154 | Node[] nodes = this.nodes; |
| 155 | for (int i=0; i<nodes.length; i++) { |
| 156 | if (nodes[i].equals(node)) { |
| 157 | if (i > 0) |
| 158 | neigh.add(nodes[i-1]); |
| 159 | if (i < nodes.length-1) |
| 160 | neigh.add(nodes[i+1]); |
| 161 | } |
| 162 | } |
| 163 | return neigh; |
| 164 | } |
| 165 | |
141 | 166 | public List<Pair<Node,Node>> getNodePairs(boolean sort) { |
142 | 167 | List<Pair<Node,Node>> chunkSet = new ArrayList<Pair<Node,Node>>(); |
143 | 168 | if (isIncomplete()) return chunkSet; |