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

Last change on this file since 203 was 203, checked in by imi, 17 years ago
  • Fixed a bug in the Merger/Conflict to auto-resolve marked changes without real changes
  • Fixed bug in split way, that reversed the order of segments in one way.
  • Fixed bug where dialogs did not update anymore after loading a dataset twice
File size: 9.2 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 (mergeAfterId(mergedSegments, ds.segments, other))
85 return;
86
87 Segment my = null;
88 for (Segment ls : ds.segments) {
89 if (match(other, ls)) {
90 my = ls;
91 break;
92 }
93 }
94 if (my == null)
95 ds.segments.add(other);
96 else if (my.incomplete && !other.incomplete) {
97 mergedSegments.put(other, my);
98 my.cloneFrom(other);
99 } else if (!other.incomplete) {
100 mergedSegments.put(other, my);
101 mergeCommon(my, other);
102 if (my.modified && !other.modified)
103 return;
104 if (!match(my.from, other.from)) {
105 my.from = other.from;
106 my.modified = other.modified;
107 }
108 if (!match(my.to, other.to)) {
109 my.to = other.to;
110 my.modified = other.modified;
111 }
112 }
113 }
114
115 private <T extends OsmPrimitive> void cloneFromExceptIncomplete(T myOsm, T otherOsm) {
116 if (!(myOsm instanceof Way))
117 myOsm.cloneFrom(otherOsm);
118 else {
119 Way my = (Way)myOsm;
120 Way other = (Way)otherOsm;
121 HashMap<Long, Segment> copy = new HashMap<Long, Segment>();
122 for (Segment s : my.segments)
123 copy.put(s.id, s);
124 my.cloneFrom(other);
125 my.segments.clear();
126 for (Segment s : other.segments) {
127 Segment myS = copy.get(s.id);
128 if (s.incomplete && myS != null && !myS.incomplete) {
129 mergedSegments.put(s, myS);
130 my.segments.add(myS);
131 } else
132 my.segments.add(s);
133 }
134 }
135 }
136
137 /**
138 * Merge the way if id matches or if all segments matches and the
139 * id is zero of either way.
140 */
141 public void visit(Way other) {
142 if (mergeAfterId(null, ds.ways, other))
143 return;
144
145 Way my = null;
146 for (Way w : ds.ways) {
147 if (match(other, w)) {
148 my = w;
149 break;
150 }
151 }
152 if (my == null) {
153 // Add the way and replace any incomplete segments that we already have
154 ds.ways.add(other);
155 for (Segment s : other.segments) {
156 if (s.incomplete) {
157 for (Segment ourSegment : ds.segments) {
158 if (ourSegment.id == s.id) {
159 mergedSegments.put(s, ourSegment);
160 break;
161 }
162 }
163 }
164 }
165 } else {
166 mergeCommon(my, other);
167 if (my.modified && !other.modified)
168 return;
169 boolean same = true;
170 Iterator<Segment> it = other.segments.iterator();
171 for (Segment ls : my.segments) {
172 if (!match(ls, it.next()))
173 same = false;
174 }
175 if (!same) {
176 HashMap<Long, Segment> copy = new HashMap<Long, Segment>();
177 for (Segment s : my.segments)
178 copy.put(s.id, s);
179 my.segments.clear();
180 for (Segment s : other.segments) {
181 Segment myS = copy.get(s.id);
182 if (s.incomplete && myS != null && !myS.incomplete) {
183 mergedSegments.put(s, myS);
184 my.segments.add(myS);
185 } else
186 my.segments.add(s);
187 }
188 my.modified = other.modified;
189 }
190 }
191 }
192
193 /**
194 * Postprocess the dataset and fix all merged references to point to the actual
195 * data.
196 */
197 public void fixReferences() {
198 for (Segment s : ds.segments)
199 fixSegment(s);
200 for (OsmPrimitive osm : conflicts.values())
201 if (osm instanceof Segment)
202 fixSegment((Segment)osm);
203 for (Way w : ds.ways)
204 fixWay(w);
205 for (OsmPrimitive osm : conflicts.values())
206 if (osm instanceof Way)
207 fixWay((Way)osm);
208 }
209
210 private void fixWay(Way w) {
211 boolean replacedSomething = false;
212 LinkedList<Segment> newSegments = new LinkedList<Segment>();
213 for (Segment ls : w.segments) {
214 Segment otherLs = mergedSegments.get(ls);
215 newSegments.add(otherLs == null ? ls : otherLs);
216 if (otherLs != null)
217 replacedSomething = true;
218 }
219 if (replacedSomething) {
220 w.segments.clear();
221 w.segments.addAll(newSegments);
222 }
223 for (Segment ls : w.segments)
224 fixSegment(ls);
225 }
226
227 private void fixSegment(Segment ls) {
228 if (mergedNodes.containsKey(ls.from))
229 ls.from = mergedNodes.get(ls.from);
230 if (mergedNodes.containsKey(ls.to))
231 ls.to = mergedNodes.get(ls.to);
232 }
233
234 /**
235 * @return Whether the nodes matches (in sense of "be mergable").
236 */
237 private boolean match(Node n1, Node n2) {
238 if (n1.id == 0 || n2.id == 0)
239 return n1.coor.equalsEpsilon(n2.coor);
240 return n1.id == n2.id;
241 }
242
243 /**
244 * @return Whether the segments matches (in sense of "be mergable").
245 */
246 private boolean match(Segment ls1, Segment ls2) {
247 if (ls1.id == ls2.id && ls1.id != 0)
248 return true;
249 if (ls1.incomplete || ls2.incomplete)
250 return false;
251 return match(ls1.from, ls2.from) && match(ls1.to, ls2.to);
252 }
253
254 /**
255 * @return Whether the ways matches (in sense of "be mergable").
256 */
257 private boolean match(Way w1, Way w2) {
258 if (w1.id == 0 || w2.id == 0) {
259 if (w1.segments.size() != w2.segments.size())
260 return false;
261 Iterator<Segment> it = w1.segments.iterator();
262 for (Segment ls : w2.segments)
263 if (!match(ls, it.next()))
264 return false;
265 return true;
266 }
267 return w1.id == w2.id;
268 }
269
270 /**
271 * Merge the common parts of an osm primitive.
272 * @param my The object, the information gets merged into
273 * @param other The object, the information gets merged from
274 */
275 private void mergeCommon(OsmPrimitive my, OsmPrimitive other) {
276 if (other.deleted)
277 my.delete(true);
278 if (my.id == 0 || !my.modified || other.modified) {
279 if (my.id == 0 && other.id != 0) {
280 my.id = other.id;
281 my.modified = other.modified; // match a new node
282 } else if (my.id != 0 && other.id != 0 && other.modified)
283 my.modified = true;
284 }
285 if (other.keys == null)
286 return;
287 if (my.keySet().containsAll(other.keys.entrySet()))
288 return;
289 if (my.keys == null)
290 my.keys = other.keys;
291 else
292 my.keys.putAll(other.keys);
293 my.modified = true;
294 }
295
296 /**
297 * @return <code>true</code>, if no merge is needed or merge is performed already.
298 */
299 private <P extends OsmPrimitive> boolean mergeAfterId(Map<P,P> merged, Collection<P> primitives, P other) {
300 for (P my : primitives) {
301 Date d1 = my.timestamp == null ? new Date(0) : my.timestamp;
302 Date d2 = other.timestamp == null ? new Date(0) : other.timestamp;
303 if (my.realEqual(other, false))
304 return true; // no merge needed.
305 if (my.realEqual(other, true)) {
306 // they differ in modified/timestamp combination only. Auto-resolve it.
307 if (merged != null)
308 merged.put(other, my);
309 if (d1.before(d2)) {
310 my.modified = other.modified;
311 my.timestamp = other.timestamp;
312 }
313 return true; // merge done.
314 }
315 if (my.id == other.id && my.id != 0) {
316 if (my instanceof Segment && ((Segment)my).incomplete)
317 return false; // merge always over an incomplete
318 if (my.modified && other.modified) {
319 conflicts.put(my, other);
320 if (merged != null)
321 merged.put(other, my);
322 } else if (!my.modified && !other.modified) {
323 if (d1.before(d2)) {
324 cloneFromExceptIncomplete(my, other);
325 if (merged != null)
326 merged.put(other, my);
327 }
328 } else if (other.modified) {
329 if (d1.after(d2)) {
330 conflicts.put(my, other);
331 if (merged != null)
332 merged.put(other, my);
333 } else {
334 cloneFromExceptIncomplete(my, other);
335 if (merged != null)
336 merged.put(other, my);
337 }
338 } else if (my.modified) {
339 if (d2.after(d1)) {
340 conflicts.put(my, other);
341 if (merged != null)
342 merged.put(other, my);
343 }
344 }
345 return true;
346 }
347 }
348 return false;
349 }
350}
Note: See TracBrowser for help on using the repository browser.