source: josm/trunk/src/org/openstreetmap/josm/gui/dialogs/relation/sort/RelationNodeMap.java@ 6792

Last change on this file since 6792 was 6792, checked in by Don-vip, 10 years ago

Sonar - fix various issues

  • Property svn:eol-style set to native
File size: 10.9 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.dialogs.relation.sort;
3
4import static org.openstreetmap.josm.gui.dialogs.relation.sort.WayConnectionType.Direction.NONE;
5
6import java.util.ArrayList;
7import java.util.List;
8import java.util.Map;
9import java.util.Set;
10import java.util.TreeMap;
11import java.util.TreeSet;
12
13import org.openstreetmap.josm.data.osm.Node;
14import org.openstreetmap.josm.data.osm.RelationMember;
15import org.openstreetmap.josm.data.osm.Way;
16
17/**
18 * Auxiliary class for relation sorting.
19 *
20 * Constructs two mappings: One that maps each way to its nodes and the inverse mapping that
21 * maps each node to all ways that have this node.
22 * After construction both maps are consistent, but later on objects that are no longer needed
23 * are removed from the value sets.
24 * However the corresponding keys are not deleted even if they map to an empty set.
25 * Note that normal ways have 2 nodes (beginning and end) but roundabouts can have less or more
26 * (that are shared by other members).
27 *
28 * @author Christiaan Welvaart <cjw@time4t.net>
29 *
30 */
31public class RelationNodeMap {
32
33 private static class NodesWays{
34 public final Map<Node, Set<Integer>> nodes = new TreeMap<Node, Set<Integer>>();
35 public final Map<Integer, Set<Node>> ways = new TreeMap<Integer, Set<Node>>();
36 public final boolean oneWay;
37 public NodesWays(boolean oneWay){
38 this.oneWay = oneWay;
39 }
40 }
41
42 /*
43 * the maps. (Need TreeMap for efficiency.)
44 */
45 private final NodesWays map = new NodesWays(false);
46 /*
47 * Maps for oneways (forward/backward roles)
48 */
49
50 private final NodesWays onewayMap = new NodesWays(true);
51 private final NodesWays onewayReverseMap = new NodesWays(true);
52 /*
53 * Used to keep track of what members are done.
54 */
55 private final Set<Integer> remaining = new TreeSet<Integer>();
56 private final Map<Integer, Set<Node>> remainingOneway = new TreeMap<Integer, Set<Node>>();
57
58 /**
59 * All members that are incomplete or not a way
60 */
61 private final List<Integer> notSortable = new ArrayList<Integer>();
62
63 public static Node firstOnewayNode(RelationMember m){
64 if(!m.isWay()) return null;
65 if(m.getRole().equals("backward")) return m.getWay().lastNode();
66 return m.getWay().firstNode();
67 }
68
69 public static Node lastOnewayNode(RelationMember m){
70 if(!m.isWay()) return null;
71 if(m.getRole().equals("backward")) return m.getWay().firstNode();
72 return m.getWay().lastNode();
73 }
74
75 RelationNodeMap(List<RelationMember> members) {
76 for (int i = 0; i < members.size(); ++i) {
77 RelationMember m = members.get(i);
78 if (m.getMember().isIncomplete() || !m.isWay()) {
79 notSortable.add(i);
80 continue;
81 }
82
83 Way w = m.getWay();
84 if ((RelationSortUtils.roundaboutType(w) != NONE)) {
85 for (Node nd : w.getNodes()) {
86 addPair(nd, i);
87 }
88 } else if(RelationSortUtils.isOneway(m)) {
89 addNodeWayMap(firstOnewayNode(m), i);
90 addWayNodeMap(lastOnewayNode(m), i);
91 addNodeWayMapReverse(lastOnewayNode(m), i);
92 addWayNodeMapReverse(firstOnewayNode(m), i);
93 addRemainingForward(firstOnewayNode(m), i);
94 addRemainingForward(lastOnewayNode(m), i);
95 } else {
96 addPair(w.firstNode(), i);
97 addPair(w.lastNode(), i);
98 }
99 }
100
101 remaining.addAll(map.ways.keySet());
102 }
103
104 private void addPair(Node n, int i) {
105 Set<Integer> ts = map.nodes.get(n);
106 if (ts == null) {
107 ts = new TreeSet<Integer>();
108 map.nodes.put(n, ts);
109 }
110 ts.add(i);
111
112 Set<Node> ts2 = map.ways.get(i);
113 if (ts2 == null) {
114 ts2 = new TreeSet<Node>();
115 map.ways.put(i, ts2);
116 }
117 ts2.add(n);
118 }
119
120 private void addNodeWayMap(Node n, int i) {
121 Set<Integer> ts = onewayMap.nodes.get(n);
122 if (ts == null) {
123 ts = new TreeSet<Integer>();
124 onewayMap.nodes.put(n, ts);
125 }
126 ts.add(i);
127 }
128
129 private void addWayNodeMap(Node n, int i) {
130 Set<Node> ts2 = onewayMap.ways.get(i);
131 if (ts2 == null) {
132 ts2 = new TreeSet<Node>();
133 onewayMap.ways.put(i, ts2);
134 }
135 ts2.add(n);
136 }
137
138 private void addNodeWayMapReverse(Node n, int i) {
139 Set<Integer> ts = onewayReverseMap.nodes.get(n);
140 if (ts == null) {
141 ts = new TreeSet<Integer>();
142 onewayReverseMap.nodes.put(n, ts);
143 }
144 ts.add(i);
145 }
146
147 private void addWayNodeMapReverse(Node n, int i) {
148 Set<Node> ts2 = onewayReverseMap.ways.get(i);
149 if (ts2 == null) {
150 ts2 = new TreeSet<Node>();
151 onewayReverseMap.ways.put(i, ts2);
152 }
153 ts2.add(n);
154 }
155
156 private void addRemainingForward(Node n, int i) {
157 Set<Node> ts2 = remainingOneway.get(i);
158 if (ts2 == null) {
159 ts2 = new TreeSet<Node>();
160 remainingOneway.put(i, ts2);
161 }
162 ts2.add(n);
163 }
164
165 Integer firstOneway = null;
166 Node lastOnewayNode = null;
167 Node firstCircular = null;
168
169 /**
170 * Return a relation member that is linked to the
171 * member 'i', but has not been popped yet.
172 * Return null if there is no such member left.
173 */
174 public Integer popAdjacent(Integer way) {
175 if (lastOnewayNode != null) return popBackwardOnewayPart(way);
176 if (firstOneway != null) return popForwardOnewayPart(way);
177
178 if (map.ways.containsKey(way)){
179 for (Node n : map.ways.get(way)) {
180 Integer i = deleteAndGetAdjacentNode(map, n);
181 if(i != null) return i;
182
183 Integer j = deleteAndGetAdjacentNode(onewayMap, n);
184 if(j != null) {
185 firstOneway = j;
186 return j;
187 }
188 }
189 }
190
191 firstOneway = way;
192 return popForwardOnewayPart(way);
193 }
194
195 private Integer popForwardOnewayPart(Integer way) {
196 if(onewayMap.ways.containsKey(way)) {
197 for (Node n : onewayMap.ways.get(way)) {
198 Integer i = findAdjacentWay(onewayMap, n);
199 if(i == null) {
200 continue;
201 }
202
203 lastOnewayNode = processBackwardIfEndOfLoopReached(i);
204 if(lastOnewayNode != null)
205 return popBackwardOnewayPart(firstOneway);
206
207 deleteWayNode(onewayMap, i, n);
208 return i;
209 }
210 }
211
212 firstOneway = null;
213 return null;
214 }
215
216 private Node processBackwardIfEndOfLoopReached(Integer way) { //find if we didn't reach end of the loop (and process backward part)
217 if (onewayReverseMap.ways.containsKey(way)) {
218 for (Node n : onewayReverseMap.ways.get(way)) {
219 if((map.nodes.containsKey(n))
220 || (onewayMap.nodes.containsKey(n) && onewayMap.nodes.get(n).size() > 1))
221 return n;
222 if(firstCircular != null && firstCircular == n)
223 return firstCircular;
224 }
225 }
226 return null;
227 }
228
229 private Integer popBackwardOnewayPart(int way){
230 if (lastOnewayNode != null) {
231 TreeSet<Node> nodes = new TreeSet<Node>();
232 if (onewayReverseMap.ways.containsKey(way)) {
233 nodes.addAll(onewayReverseMap.ways.get(way));
234 }
235 if (map.ways.containsKey(way)) {
236 nodes.addAll(map.ways.get(way));
237 }
238 for (Node n : nodes) {
239 if(n == lastOnewayNode) { //if oneway part ends
240 firstOneway = null;
241 lastOnewayNode = null;
242 Integer j = deleteAndGetAdjacentNode(map, n);
243 if(j != null) return j;
244
245 Integer k = deleteAndGetAdjacentNode(onewayMap, n);
246 if(k != null) {
247 firstOneway = k;
248 return k;
249 }
250 }
251
252 Integer j = deleteAndGetAdjacentNode(onewayReverseMap, n);
253 if(j != null) return j;
254 }
255 }
256
257 firstOneway = null;
258 lastOnewayNode = null;
259
260 return null;
261 }
262
263 /**
264 * find next node in nw NodeWays structure, if the node is found delete and return it
265 * @param nw
266 * @param n
267 * @return node next to n
268 */
269 private Integer deleteAndGetAdjacentNode(NodesWays nw, Node n){
270 Integer j = findAdjacentWay(nw, n);
271 if(j == null) return null;
272 deleteWayNode(nw, j, n);
273 return j;
274 }
275
276 private Integer findAdjacentWay(NodesWays nw, Node n) {
277 Set<Integer> adj = nw.nodes.get(n);
278 if (adj == null || adj.isEmpty()) return null;
279 return adj.iterator().next();
280 }
281
282 private void deleteWayNode(NodesWays nw, Integer way, Node n){
283 if(nw.oneWay) {
284 doneOneway(way);
285 } else {
286 done(way);
287 }
288 nw.ways.get(way).remove(n);
289 }
290
291 /**
292 * Returns some remaining member or null if
293 * every sortable member has been processed.
294 */
295 public Integer pop() {
296 if (!remaining.isEmpty()){
297 Integer i = remaining.iterator().next();
298 done(i);
299 return i;
300 }
301
302 if (remainingOneway.isEmpty()) return null;
303 for(Integer i :remainingOneway.keySet()){ //find oneway, which is connected to more than one way (is between two oneway loops)
304 for(Node n : onewayReverseMap.ways.get(i)){
305 if(onewayReverseMap.nodes.containsKey(n) && onewayReverseMap.nodes.get(n).size() > 1) {
306 doneOneway(i);
307 firstCircular = n;
308 return i;
309 }
310 }
311 }
312
313 Integer i = remainingOneway.keySet().iterator().next();
314 doneOneway(i);
315 return i;
316 }
317
318 /**
319 * This relation member has been processed.
320 * Remove references in the map.nodes.
321 */
322 private void doneOneway(Integer i) {
323 Set<Node> nodesForward = remainingOneway.get(i);
324 for (Node n : nodesForward) {
325 if(onewayMap.nodes.containsKey(n)) {
326 onewayMap.nodes.get(n).remove(i);
327 }
328 if(onewayReverseMap.nodes.containsKey(n)) {
329 onewayReverseMap.nodes.get(n).remove(i);
330 }
331 }
332 remainingOneway.remove(i);
333 }
334
335 private void done(Integer i) {
336 remaining.remove(i);
337 Set<Node> nodes = map.ways.get(i);
338 for (Node n : nodes) {
339 boolean result = map.nodes.get(n).remove(i);
340 if (!result) throw new AssertionError();
341 }
342 }
343
344 public List<Integer> getNotSortableMembers() {
345 return notSortable;
346 }
347}
Note: See TracBrowser for help on using the repository browser.