source: josm/src/org/openstreetmap/josm/data/osm/visitor/MergeVisitor.java@ 159

Last change on this file since 159 was 159, checked in by imi, 18 years ago
  • moved translations into plugins
  • added main menu control to main
  • added ImageProvider access to plugins
File size: 9.0 KB
Line 
1package org.openstreetmap.josm.data.osm.visitor;
2
3import java.util.Collection;
4import java.util.Date;
5import java.util.HashMap;
6import java.util.Iterator;
7import java.util.LinkedList;
8import java.util.Map;
9
10import org.openstreetmap.josm.data.osm.DataSet;
11import org.openstreetmap.josm.data.osm.Node;
12import org.openstreetmap.josm.data.osm.OsmPrimitive;
13import org.openstreetmap.josm.data.osm.Segment;
14import org.openstreetmap.josm.data.osm.Way;
15
16/**
17 * A visitor that get a data set at construction time and merge every visited object
18 * into it.
19 *
20 * @author imi
21 */
22public class MergeVisitor implements Visitor {
23
24 /**
25 * Map from primitives in the database to visited primitives. (Attention: The other way
26 * round than mergedNodes and mergedSegments)
27 */
28 public Map<OsmPrimitive, OsmPrimitive> conflicts = new HashMap<OsmPrimitive, OsmPrimitive>();
29
30 private final DataSet ds;
31
32 /**
33 * A list of all nodes that got replaced with other nodes.
34 * Key is the node in the other's dataset and the value is the one that is now
35 * in ds.nodes instead.
36 */
37 private final Map<Node, Node> mergedNodes = new HashMap<Node, Node>();
38 /**
39 * A list of all segments that got replaced with others.
40 * Key is the segment in the other's dataset and the value is the one that is now
41 * in ds.segments.
42 */
43 private final Map<Segment, Segment> mergedSegments = new HashMap<Segment, Segment>();
44
45 public MergeVisitor(DataSet ds) {
46 this.ds = ds;
47 }
48
49 /**
50 * Merge the node if the id matches with any of the internal set or if
51 * either id is zero, merge if lat/lon matches.
52 */
53 public void visit(Node other) {
54 if (mergeAfterId(mergedNodes, ds.nodes, other))
55 return;
56
57 Node my = null;
58 for (Node n : ds.nodes) {
59 if (match(n, other)) {
60 my = n;
61 break;
62 }
63 }
64 if (my == null)
65 ds.nodes.add(other);
66 else {
67 mergedNodes.put(other, my);
68 mergeCommon(my, other);
69 if (my.modified && !other.modified)
70 return;
71 if (!my.coor.equalsEpsilon(other.coor)) {
72 my.coor = other.coor;
73 my.eastNorth = other.eastNorth;
74 my.modified = other.modified;
75 }
76 }
77 }
78
79 /**
80 * Merge the segment if id matches or if both nodes are the same (and the
81 * id is zero of either segment). Nodes are the "same" when they @see match
82 */
83 public void visit(Segment other) {
84 if (other.incomplete)
85 return; // won't merge in an incomplete line segment!
86
87 if (mergeAfterId(mergedSegments, ds.segments, other))
88 return;
89
90 Segment my = null;
91 for (Segment ls : ds.segments) {
92 if (match(other, ls)) {
93 my = ls;
94 break;
95 }
96 }
97 if (my == null)
98 ds.segments.add(other);
99 else if (my.incomplete && !other.incomplete) {
100 mergedSegments.put(other, my);
101 my.cloneFrom(other);
102 } else if (!other.incomplete) {
103 mergedSegments.put(other, my);
104 mergeCommon(my, other);
105 if (my.modified && !other.modified)
106 return;
107 if (!match(my.from, other.from)) {
108 my.from = other.from;
109 my.modified = other.modified;
110 }
111 if (!match(my.to, other.to)) {
112 my.to = other.to;
113 my.modified = other.modified;
114 }
115 }
116 }
117
118 private <T extends OsmPrimitive> void cloneFromExceptIncomplete(T myOsm, T otherOsm) {
119 if (!(myOsm instanceof Way))
120 myOsm.cloneFrom(otherOsm);
121 else {
122 Way my = (Way)myOsm;
123 Way other = (Way)otherOsm;
124 HashMap<Long, Segment> copy = new HashMap<Long, Segment>();
125 for (Segment s : my.segments)
126 copy.put(s.id, s);
127 my.cloneFrom(other);
128 my.segments.clear();
129 for (Segment s : other.segments) {
130 Segment myS = copy.get(s.id);
131 if (s.incomplete && myS != null && !myS.incomplete) {
132 mergedSegments.put(s, myS);
133 my.segments.add(myS);
134 } else
135 my.segments.add(s);
136 }
137 }
138 }
139
140 /**
141 * Merge the way if id matches or if all segments matches and the
142 * id is zero of either way.
143 */
144 public void visit(Way other) {
145 if (mergeAfterId(null, ds.ways, other))
146 return;
147
148 Way my = null;
149 for (Way w : ds.ways) {
150 if (match(other, w)) {
151 my = w;
152 break;
153 }
154 }
155 if (my == null) {
156 // Add the way and replace any incomplete segments that we already have
157 ds.ways.add(other);
158 for (Segment s : other.segments) {
159 if (s.incomplete) {
160 for (Segment ourSegment : ds.segments) {
161 if (ourSegment.id == s.id) {
162 mergedSegments.put(s, ourSegment);
163 break;
164 }
165 }
166 }
167 }
168 } else {
169 mergeCommon(my, other);
170 if (my.modified && !other.modified)
171 return;
172 boolean same = true;
173 Iterator<Segment> it = other.segments.iterator();
174 for (Segment ls : my.segments) {
175 if (!match(ls, it.next()))
176 same = false;
177 }
178 if (!same) {
179 HashMap<Long, Segment> copy = new HashMap<Long, Segment>();
180 for (Segment s : my.segments)
181 copy.put(s.id, s);
182 my.segments.clear();
183 for (Segment s : other.segments) {
184 Segment myS = copy.get(s.id);
185 if (s.incomplete && myS != null && !myS.incomplete) {
186 mergedSegments.put(s, myS);
187 my.segments.add(myS);
188 } else
189 my.segments.add(s);
190 }
191 my.modified = other.modified;
192 }
193 }
194 }
195
196 /**
197 * Postprocess the dataset and fix all merged references to point to the actual
198 * data.
199 */
200 public void fixReferences() {
201 for (Segment s : ds.segments)
202 fixSegment(s);
203 for (OsmPrimitive osm : conflicts.values())
204 if (osm instanceof Segment)
205 fixSegment((Segment)osm);
206 for (Way w : ds.ways)
207 fixWay(w);
208 for (OsmPrimitive osm : conflicts.values())
209 if (osm instanceof Way)
210 fixWay((Way)osm);
211 }
212
213 private void fixWay(Way w) {
214 boolean replacedSomething = false;
215 LinkedList<Segment> newSegments = new LinkedList<Segment>();
216 for (Segment ls : w.segments) {
217 Segment otherLs = mergedSegments.get(ls);
218 newSegments.add(otherLs == null ? ls : otherLs);
219 if (otherLs != null)
220 replacedSomething = true;
221 }
222 if (replacedSomething) {
223 w.segments.clear();
224 w.segments.addAll(newSegments);
225 }
226 for (Segment ls : w.segments)
227 fixSegment(ls);
228 }
229
230 private void fixSegment(Segment ls) {
231 if (mergedNodes.containsKey(ls.from))
232 ls.from = mergedNodes.get(ls.from);
233 if (mergedNodes.containsKey(ls.to))
234 ls.to = mergedNodes.get(ls.to);
235 }
236
237 /**
238 * @return Whether the nodes matches (in sense of "be mergable").
239 */
240 private boolean match(Node n1, Node n2) {
241 if (n1.id == 0 || n2.id == 0)
242 return n1.coor.equalsEpsilon(n2.coor);
243 return n1.id == n2.id;
244 }
245
246 /**
247 * @return Whether the segments matches (in sense of "be mergable").
248 */
249 private boolean match(Segment ls1, Segment ls2) {
250 if (ls1.id == ls2.id && ls1.id != 0)
251 return true;
252 if (ls1.incomplete || ls2.incomplete)
253 return false;
254 return match(ls1.from, ls2.from) && match(ls1.to, ls2.to);
255 }
256
257 /**
258 * @return Whether the ways matches (in sense of "be mergable").
259 */
260 private boolean match(Way w1, Way w2) {
261 if (w1.id == 0 || w2.id == 0) {
262 if (w1.segments.size() != w2.segments.size())
263 return false;
264 Iterator<Segment> it = w1.segments.iterator();
265 for (Segment ls : w2.segments)
266 if (!match(ls, it.next()))
267 return false;
268 return true;
269 }
270 return w1.id == w2.id;
271 }
272
273 /**
274 * Merge the common parts of an osm primitive.
275 * @param my The object, the information gets merged into
276 * @param other The object, the information gets merged from
277 */
278 private void mergeCommon(OsmPrimitive my, OsmPrimitive other) {
279 if (other.deleted)
280 my.delete(true);
281 if (my.id == 0 || !my.modified || other.modified) {
282 if (my.id == 0 && other.id != 0) {
283 my.id = other.id;
284 my.modified = other.modified; // match a new node
285 } else if (my.id != 0 && other.id != 0 && other.modified)
286 my.modified = true;
287 }
288 if (other.keys == null)
289 return;
290 if (my.keySet().containsAll(other.keys.entrySet()))
291 return;
292 if (my.keys == null)
293 my.keys = other.keys;
294 else
295 my.keys.putAll(other.keys);
296 my.modified = true;
297 }
298
299 /**
300 * @return <code>true</code>, if no merge is needed.
301 */
302 private <P extends OsmPrimitive> boolean mergeAfterId(Map<P,P> merged, Collection<P> primitives, P other) {
303 for (P my : primitives) {
304 if (my.realEqual(other))
305 return true; // no merge needed.
306 if (my.id == other.id && my.id != 0) {
307 if (my instanceof Segment && ((Segment)my).incomplete)
308 return false; // merge always over an incomplete
309 Date d1 = my.timestamp == null ? new Date(0) : my.timestamp;
310 Date d2 = other.timestamp == null ? new Date(0) : other.timestamp;
311 if (my.modified && other.modified) {
312 conflicts.put(my, other);
313 if (merged != null)
314 merged.put(other, my);
315 } else if (!my.modified && !other.modified) {
316 if (d1.before(d2)) {
317 cloneFromExceptIncomplete(my, other);
318 if (merged != null)
319 merged.put(other, my);
320 }
321 } else if (other.modified) {
322 if (d1.after(d2)) {
323 conflicts.put(my, other);
324 if (merged != null)
325 merged.put(other, my);
326 } else {
327 cloneFromExceptIncomplete(my, other);
328 if (merged != null)
329 merged.put(other, my);
330 }
331 } else if (my.modified) {
332 if (d2.after(d1)) {
333 conflicts.put(my, other);
334 if (merged != null)
335 merged.put(other, my);
336 }
337 }
338 return true;
339 }
340 }
341 return false;
342 }
343}
Note: See TracBrowser for help on using the repository browser.