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

Last change on this file since 86 was 86, checked in by imi, 18 years ago
  • added conflicts and resolve conflict dialog

This is one of those "changed everything" checkpoint.

File size: 7.5 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.Segment;
12import org.openstreetmap.josm.data.osm.Node;
13import org.openstreetmap.josm.data.osm.OsmPrimitive;
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 /**
116 * Merge the way if id matches or if all segments matches and the
117 * id is zero of either way.
118 */
119 public void visit(Way other) {
120 if (mergeAfterId(null, ds.ways, other))
121 return;
122
123 Way my = null;
124 for (Way t : ds.ways) {
125 if (match(other, t)) {
126 my = t;
127 break;
128 }
129 }
130 if (my == null)
131 ds.ways.add(other);
132 else {
133 mergeCommon(my, other);
134 if (my.modified && !other.modified)
135 return;
136 boolean same = true;
137 Iterator<Segment> it = other.segments.iterator();
138 for (Segment ls : my.segments) {
139 if (!match(ls, it.next()))
140 same = false;
141 }
142 if (!same) {
143 my.segments.clear();
144 my.segments.addAll(other.segments);
145 my.modified = other.modified;
146 }
147 }
148 }
149
150 /**
151 * Postprocess the dataset and fix all merged references to point to the actual
152 * data.
153 */
154 public void fixReferences() {
155 for (Segment s : ds.segments)
156 fixSegment(s);
157 for (OsmPrimitive osm : conflicts.values())
158 if (osm instanceof Segment)
159 fixSegment((Segment)osm);
160 for (Way w : ds.ways)
161 fixWay(w);
162 for (OsmPrimitive osm : conflicts.values())
163 if (osm instanceof Way)
164 fixWay((Way)osm);
165 }
166
167 private void fixWay(Way t) {
168 boolean replacedSomething = false;
169 LinkedList<Segment> newSegments = new LinkedList<Segment>();
170 for (Segment ls : t.segments) {
171 Segment otherLs = mergedSegments.get(ls);
172 newSegments.add(otherLs == null ? ls : otherLs);
173 if (otherLs != null)
174 replacedSomething = true;
175 }
176 if (replacedSomething) {
177 t.segments.clear();
178 t.segments.addAll(newSegments);
179 }
180 for (Segment ls : t.segments) {
181 fixSegment(ls);
182 }
183 }
184
185 private void fixSegment(Segment ls) {
186 if (mergedNodes.containsKey(ls.from))
187 ls.from = mergedNodes.get(ls.from);
188 if (mergedNodes.containsKey(ls.to))
189 ls.to = mergedNodes.get(ls.to);
190 }
191
192 /**
193 * @return Whether the nodes matches (in sense of "be mergable").
194 */
195 private boolean match(Node n1, Node n2) {
196 if (n1.id == 0 || n2.id == 0)
197 return n1.coor.equalsEpsilon(n2.coor);
198 return n1.id == n2.id;
199 }
200
201 /**
202 * @return Whether the segments matches (in sense of "be mergable").
203 */
204 private boolean match(Segment ls1, Segment ls2) {
205 if (ls1.id == ls2.id)
206 return true;
207 if (ls1.incomplete || ls2.incomplete)
208 return false;
209 return match(ls1.from, ls2.from) && match(ls1.to, ls2.to);
210 }
211
212 /**
213 * @return Whether the ways matches (in sense of "be mergable").
214 */
215 private boolean match(Way t1, Way t2) {
216 if (t1.id == 0 || t2.id == 0) {
217 if (t1.segments.size() != t2.segments.size())
218 return false;
219 Iterator<Segment> it = t1.segments.iterator();
220 for (Segment ls : t2.segments)
221 if (!match(ls, it.next()))
222 return false;
223 return true;
224 }
225 return t1.id == t2.id;
226 }
227
228 /**
229 * Merge the common parts of an osm primitive.
230 * @param my The object, the information gets merged into
231 * @param other The object, the information gets merged from
232 */
233 private void mergeCommon(OsmPrimitive my, OsmPrimitive other) {
234 if (other.deleted)
235 my.delete(true);
236 if (my.id == 0 || !my.modified || other.modified) {
237 if (my.id == 0 && other.id != 0) {
238 my.id = other.id;
239 my.modified = other.modified; // match a new node
240 } else if (my.id != 0 && other.id != 0 && other.modified)
241 my.modified = true;
242 }
243 if (other.keys == null)
244 return;
245 if (my.keySet().containsAll(other.keys.entrySet()))
246 return;
247 if (my.keys == null)
248 my.keys = other.keys;
249 else
250 my.keys.putAll(other.keys);
251 my.modified = true;
252 }
253
254 private <P extends OsmPrimitive> boolean mergeAfterId(Map<P,P> merged, Collection<P> primitives, P other) {
255 for (P my : primitives) {
256 if (my.realEqual(other))
257 return true; // no merge needed.
258 if (my.id == other.id) {
259 Date d1 = my.timestamp == null ? new Date(0) : my.timestamp;
260 Date d2 = other.timestamp == null ? new Date(0) : other.timestamp;
261 if (my.modified && other.modified) {
262 conflicts.put(my, other);
263 if (merged != null)
264 merged.put(other, my);
265 } else if (!my.modified && !other.modified) {
266 if (d1.before(d2)) {
267 my.cloneFrom(other);
268 if (merged != null)
269 merged.put(other, my);
270 }
271 } else if (other.modified) {
272 if (d1.after(d2)) {
273 conflicts.put(my, other);
274 if (merged != null)
275 merged.put(other, my);
276 } else {
277 my.cloneFrom(other);
278 if (merged != null)
279 merged.put(other, my);
280 }
281 } else if (my.modified) {
282 if (d2.after(d1)) {
283 conflicts.put(my, other);
284 if (merged != null)
285 merged.put(other, my);
286 }
287 }
288 return true;
289 }
290 }
291 return false;
292 }
293}
Note: See TracBrowser for help on using the repository browser.