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

Last change on this file since 343 was 343, checked in by gebner, 17 years ago

Merge 0.5.

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