1 | package reverter;
|
---|
2 |
|
---|
3 | import static org.openstreetmap.josm.tools.I18n.tr;
|
---|
4 |
|
---|
5 | import java.net.HttpURLConnection;
|
---|
6 | import java.util.Arrays;
|
---|
7 | import java.util.Collection;
|
---|
8 | import java.util.Collections;
|
---|
9 | import java.util.HashSet;
|
---|
10 | import java.util.Iterator;
|
---|
11 | import java.util.List;
|
---|
12 |
|
---|
13 | import org.openstreetmap.josm.Main;
|
---|
14 | import org.openstreetmap.josm.command.Command;
|
---|
15 | import org.openstreetmap.josm.command.DeleteCommand;
|
---|
16 | import org.openstreetmap.josm.command.conflict.ConflictAddCommand;
|
---|
17 | import org.openstreetmap.josm.data.conflict.Conflict;
|
---|
18 | import org.openstreetmap.josm.data.coor.LatLon;
|
---|
19 | import org.openstreetmap.josm.data.osm.Changeset;
|
---|
20 | import org.openstreetmap.josm.data.osm.DataSet;
|
---|
21 | import org.openstreetmap.josm.data.osm.Node;
|
---|
22 | import org.openstreetmap.josm.data.osm.OsmPrimitive;
|
---|
23 | import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
|
---|
24 | import org.openstreetmap.josm.data.osm.PrimitiveId;
|
---|
25 | import org.openstreetmap.josm.data.osm.Relation;
|
---|
26 | import org.openstreetmap.josm.data.osm.RelationMemberData;
|
---|
27 | import org.openstreetmap.josm.data.osm.SimplePrimitiveId;
|
---|
28 | import org.openstreetmap.josm.data.osm.Way;
|
---|
29 | import org.openstreetmap.josm.data.osm.history.HistoryNode;
|
---|
30 | import org.openstreetmap.josm.data.osm.history.HistoryOsmPrimitive;
|
---|
31 | import org.openstreetmap.josm.data.osm.history.HistoryRelation;
|
---|
32 | import org.openstreetmap.josm.data.osm.history.HistoryWay;
|
---|
33 | import org.openstreetmap.josm.gui.layer.OsmDataLayer;
|
---|
34 | import org.openstreetmap.josm.gui.progress.ProgressMonitor;
|
---|
35 | import org.openstreetmap.josm.gui.util.GuiHelper;
|
---|
36 | import org.openstreetmap.josm.io.MultiFetchServerObjectReader;
|
---|
37 | import org.openstreetmap.josm.io.OsmApiException;
|
---|
38 | import org.openstreetmap.josm.io.OsmTransferException;
|
---|
39 |
|
---|
40 | import reverter.corehacks.ChangesetDataSet;
|
---|
41 | import reverter.corehacks.ChangesetDataSet.ChangesetDataSetEntry;
|
---|
42 | import reverter.corehacks.ChangesetDataSet.ChangesetModificationType;
|
---|
43 | import reverter.corehacks.OsmServerChangesetReader;
|
---|
44 |
|
---|
45 | /**
|
---|
46 | * Fetches and stores data for reverting of specific changeset.
|
---|
47 | * @author Upliner
|
---|
48 | *
|
---|
49 | */
|
---|
50 | public class ChangesetReverter {
|
---|
51 |
|
---|
52 | public static enum RevertType {
|
---|
53 | FULL,
|
---|
54 | SELECTION,
|
---|
55 | SELECTION_WITH_UNDELETE
|
---|
56 | }
|
---|
57 |
|
---|
58 | public static final Collection<Long> MODERATOR_REDACTION_ACCOUNTS = Collections.unmodifiableCollection(Arrays.asList(
|
---|
59 | 722137L, // OSMF Redaction Account
|
---|
60 | 760215L // pnorman redaction revert
|
---|
61 | ));
|
---|
62 |
|
---|
63 | public final int changesetId;
|
---|
64 | public final Changeset changeset;
|
---|
65 | public final RevertType revertType;
|
---|
66 |
|
---|
67 | private final OsmDataLayer layer; // data layer associated with reverter
|
---|
68 | private final DataSet ds; // DataSet associated with reverter
|
---|
69 | private final ChangesetDataSet cds; // Current changeset data
|
---|
70 | private DataSet nds; // Dataset that contains new objects downloaded by reverter
|
---|
71 |
|
---|
72 | private final HashSet<PrimitiveId> missing = new HashSet<>();
|
---|
73 |
|
---|
74 | private final HashSet<HistoryOsmPrimitive> created = new HashSet<>();
|
---|
75 | private final HashSet<HistoryOsmPrimitive> updated = new HashSet<>();
|
---|
76 | private final HashSet<HistoryOsmPrimitive> deleted = new HashSet<>();
|
---|
77 |
|
---|
78 | //// Handling missing objects
|
---|
79 | ////////////////////////////////////////
|
---|
80 | private void addIfMissing(PrimitiveId id) {
|
---|
81 | OsmPrimitive p = ds.getPrimitiveById(id);
|
---|
82 | if (p == null || p.isIncomplete()) {
|
---|
83 | missing.add(id);
|
---|
84 | }
|
---|
85 | }
|
---|
86 | private void addMissingHistoryIds(Iterable<HistoryOsmPrimitive> primitives) {
|
---|
87 | for (HistoryOsmPrimitive p : primitives) {
|
---|
88 | addIfMissing(p.getPrimitiveId());
|
---|
89 | if (p.getType() == OsmPrimitiveType.WAY) {
|
---|
90 | for (long nd : ((HistoryWay)p).getNodes()) {
|
---|
91 | addIfMissing(new SimplePrimitiveId(nd,OsmPrimitiveType.NODE));
|
---|
92 | }
|
---|
93 | }
|
---|
94 | }
|
---|
95 | }
|
---|
96 |
|
---|
97 | private void addMissingIds(Iterable<OsmPrimitive> primitives) {
|
---|
98 | for (OsmPrimitive p : primitives) {
|
---|
99 | addIfMissing(p);
|
---|
100 | if (p.getType() == OsmPrimitiveType.WAY) {
|
---|
101 | for (Node nd : ((Way)p).getNodes()) {
|
---|
102 | addIfMissing(nd);
|
---|
103 | }
|
---|
104 | }
|
---|
105 | }
|
---|
106 | }
|
---|
107 |
|
---|
108 | /**
|
---|
109 | * Checks if {@see ChangesetDataSetEntry} conforms to current RevertType
|
---|
110 | * @param entry entry to be checked
|
---|
111 | * @return <code>true</code> if {@see ChangesetDataSetEntry} conforms to current RevertType
|
---|
112 | */
|
---|
113 | private boolean checkOsmChangeEntry(ChangesetDataSetEntry entry) {
|
---|
114 | if (revertType == RevertType.FULL) return true;
|
---|
115 | if (revertType == RevertType.SELECTION_WITH_UNDELETE &&
|
---|
116 | entry.getModificationType() == ChangesetModificationType.DELETED) {
|
---|
117 | return true;
|
---|
118 | }
|
---|
119 | OsmPrimitive p = ds.getPrimitiveById(entry.getPrimitive().getPrimitiveId());
|
---|
120 | if (p == null) return false;
|
---|
121 | return p.isSelected();
|
---|
122 | }
|
---|
123 |
|
---|
124 | /**
|
---|
125 | * creates a reverter for specific changeset and fetches initial data
|
---|
126 | * @param changesetId
|
---|
127 | * @param monitor
|
---|
128 | * @throws OsmTransferException
|
---|
129 | * @throws RevertRedactedChangesetException
|
---|
130 | */
|
---|
131 | public ChangesetReverter(int changesetId, RevertType revertType, boolean newLayer, ProgressMonitor monitor)
|
---|
132 | throws OsmTransferException, RevertRedactedChangesetException {
|
---|
133 | this.changesetId = changesetId;
|
---|
134 | if (newLayer) {
|
---|
135 | this.ds = new DataSet();
|
---|
136 | this.layer = new OsmDataLayer(this.ds, tr("Reverted changeset") + tr(" [id: {0}]", String.valueOf(changesetId)), null);
|
---|
137 | } else {
|
---|
138 | this.layer = Main.main.getEditLayer();
|
---|
139 | this.ds = layer.data;
|
---|
140 | }
|
---|
141 | this.revertType = revertType;
|
---|
142 |
|
---|
143 | OsmServerChangesetReader csr = new OsmServerChangesetReader();
|
---|
144 | monitor.beginTask("", 2);
|
---|
145 | changeset = csr.readChangeset(changesetId, monitor.createSubTaskMonitor(1, false));
|
---|
146 | if (MODERATOR_REDACTION_ACCOUNTS.contains(changeset.getUser().getId())) {
|
---|
147 | throw new RevertRedactedChangesetException(tr("It is not allowed to revert changeset from {0}", changeset.getUser().getName()));
|
---|
148 | }
|
---|
149 | try {
|
---|
150 | cds = csr.downloadChangeset(changesetId, monitor.createSubTaskMonitor(1, false));
|
---|
151 | } finally {
|
---|
152 | monitor.finishTask();
|
---|
153 | if (newLayer) {
|
---|
154 | GuiHelper.runInEDT(new Runnable() {
|
---|
155 | @Override
|
---|
156 | public void run() {
|
---|
157 | Main.main.addLayer(layer);
|
---|
158 | }
|
---|
159 | });
|
---|
160 | }
|
---|
161 | }
|
---|
162 |
|
---|
163 | // Build our own lists of created/updated/modified objects for better performance
|
---|
164 | for (Iterator<ChangesetDataSetEntry> it = cds.iterator();it.hasNext();) {
|
---|
165 | ChangesetDataSetEntry entry = it.next();
|
---|
166 | if (!checkOsmChangeEntry(entry)) continue;
|
---|
167 | if (entry.getModificationType() == ChangesetModificationType.CREATED) {
|
---|
168 | created.add(entry.getPrimitive());
|
---|
169 | } else if (entry.getModificationType() == ChangesetModificationType.UPDATED) {
|
---|
170 | updated.add(entry.getPrimitive());
|
---|
171 | } else if (entry.getModificationType() == ChangesetModificationType.DELETED) {
|
---|
172 | deleted.add(entry.getPrimitive());
|
---|
173 | } else throw new AssertionError();
|
---|
174 | }
|
---|
175 | }
|
---|
176 | public void checkMissingCreated() {
|
---|
177 | addMissingHistoryIds(created);
|
---|
178 | }
|
---|
179 | public void checkMissingUpdated() {
|
---|
180 | addMissingHistoryIds(updated);
|
---|
181 | }
|
---|
182 | public void checkMissingDeleted() {
|
---|
183 | addMissingHistoryIds(deleted);
|
---|
184 | }
|
---|
185 |
|
---|
186 | private void readObjectVersion(OsmServerMultiObjectReader rdr, PrimitiveId id, int version, ProgressMonitor progressMonitor) throws OsmTransferException {
|
---|
187 | boolean readOK = false;
|
---|
188 | while (!readOK && version >= 1) {
|
---|
189 | try {
|
---|
190 | rdr.readObject(id, version, progressMonitor.createSubTaskMonitor(1, true));
|
---|
191 | readOK = true;
|
---|
192 | } catch (OsmApiException e) {
|
---|
193 | if (e.getResponseCode() != HttpURLConnection.HTTP_FORBIDDEN) {
|
---|
194 | throw e;
|
---|
195 | }
|
---|
196 | String message = "Version "+version+" of "+id+" is unauthorized";
|
---|
197 | if (version > 1) {
|
---|
198 | message += ", requesting previous one";
|
---|
199 | }
|
---|
200 | Main.info(message);
|
---|
201 | version--;
|
---|
202 | }
|
---|
203 | }
|
---|
204 | if (!readOK) {
|
---|
205 | Main.warn("Cannot retrieve any previous version of "+id);
|
---|
206 | }
|
---|
207 | }
|
---|
208 |
|
---|
209 | /**
|
---|
210 | * fetch objects that were updated or deleted by changeset
|
---|
211 | * @param progressMonitor
|
---|
212 | * @throws OsmTransferException
|
---|
213 | */
|
---|
214 | @SuppressWarnings("unchecked")
|
---|
215 | public void downloadObjectsHistory(ProgressMonitor progressMonitor) throws OsmTransferException {
|
---|
216 | final OsmServerMultiObjectReader rdr = new OsmServerMultiObjectReader();
|
---|
217 |
|
---|
218 | progressMonitor.beginTask(tr("Downloading objects history"),updated.size()+deleted.size()+1);
|
---|
219 | try {
|
---|
220 | for (HashSet<HistoryOsmPrimitive> collection : Arrays.asList(new HashSet[]{updated, deleted})) {
|
---|
221 | for (HistoryOsmPrimitive entry : collection) {
|
---|
222 | PrimitiveId id = entry.getPrimitiveId();
|
---|
223 | readObjectVersion(rdr, id, cds.getEarliestVersion(id)-1, progressMonitor);
|
---|
224 | if (progressMonitor.isCanceled()) return;
|
---|
225 | }
|
---|
226 | }
|
---|
227 | nds = rdr.parseOsm(progressMonitor.createSubTaskMonitor(1, true));
|
---|
228 | for (OsmPrimitive p : nds.allPrimitives()) {
|
---|
229 | if (!p.isIncomplete()) {
|
---|
230 | addMissingIds(Collections.singleton(p));
|
---|
231 | } else {
|
---|
232 | if (ds.getPrimitiveById(p.getPrimitiveId()) == null) {
|
---|
233 | switch (p.getType()) {
|
---|
234 | case NODE: ds.addPrimitive(new Node(p.getUniqueId())); break;
|
---|
235 | case CLOSEDWAY:
|
---|
236 | case WAY: ds.addPrimitive(new Way(p.getUniqueId())); break;
|
---|
237 | case MULTIPOLYGON:
|
---|
238 | case RELATION: ds.addPrimitive(new Relation(p.getUniqueId())); break;
|
---|
239 | default: throw new AssertionError();
|
---|
240 | }
|
---|
241 | }
|
---|
242 | }
|
---|
243 | }
|
---|
244 | } finally {
|
---|
245 | progressMonitor.finishTask();
|
---|
246 | }
|
---|
247 | }
|
---|
248 |
|
---|
249 | public void downloadMissingPrimitives(ProgressMonitor monitor) throws OsmTransferException {
|
---|
250 | if (!hasMissingObjects()) return;
|
---|
251 | MultiFetchServerObjectReader rdr = new MultiFetchServerObjectReader();
|
---|
252 | for (PrimitiveId id : missing) {
|
---|
253 | switch (id.getType()) {
|
---|
254 | case NODE:
|
---|
255 | rdr.append(new Node(id.getUniqueId()));
|
---|
256 | break;
|
---|
257 | case CLOSEDWAY:
|
---|
258 | case WAY:
|
---|
259 | rdr.append(new Way(id.getUniqueId()));
|
---|
260 | break;
|
---|
261 | case MULTIPOLYGON:
|
---|
262 | case RELATION:
|
---|
263 | rdr.append(new Relation(id.getUniqueId()));
|
---|
264 | break;
|
---|
265 | default: throw new AssertionError();
|
---|
266 | }
|
---|
267 | }
|
---|
268 | DataSet source = rdr.parseOsm(monitor);
|
---|
269 | for (OsmPrimitive p : source.allPrimitives()) {
|
---|
270 | if (!p.isVisible() && !p.isDeleted()) {
|
---|
271 | p.setDeleted(true);
|
---|
272 | p.setModified(false);
|
---|
273 | }
|
---|
274 | }
|
---|
275 | layer.mergeFrom(source);
|
---|
276 | missing.clear();
|
---|
277 | }
|
---|
278 |
|
---|
279 | private static Conflict<? extends OsmPrimitive> CreateConflict(OsmPrimitive p, boolean isMyDeleted) {
|
---|
280 | switch (p.getType()) {
|
---|
281 | case NODE:
|
---|
282 | return new Conflict<>((Node)p,new Node((Node)p), isMyDeleted);
|
---|
283 | case CLOSEDWAY:
|
---|
284 | case WAY:
|
---|
285 | return new Conflict<>((Way)p,new Way((Way)p), isMyDeleted);
|
---|
286 | case MULTIPOLYGON:
|
---|
287 | case RELATION:
|
---|
288 | return new Conflict<>((Relation)p,new Relation((Relation)p), isMyDeleted);
|
---|
289 | default: throw new AssertionError();
|
---|
290 | }
|
---|
291 | }
|
---|
292 |
|
---|
293 | private boolean hasEqualSemanticAttributes(OsmPrimitive current,HistoryOsmPrimitive history) {
|
---|
294 | if (!current.getKeys().equals(history.getTags())) return false;
|
---|
295 | switch (current.getType()) {
|
---|
296 | case NODE:
|
---|
297 | LatLon currentCoor = ((Node)current).getCoor();
|
---|
298 | LatLon historyCoor = ((HistoryNode)history).getCoords();
|
---|
299 | if (currentCoor == historyCoor || (currentCoor != null && historyCoor != null && currentCoor.equals(historyCoor)))
|
---|
300 | return true;
|
---|
301 | // Handle case where a deleted note has been restored to avoid false conflicts (fix #josm8660)
|
---|
302 | if (currentCoor != null && historyCoor == null) {
|
---|
303 | LatLon previousCoor = ((Node)nds.getPrimitiveById(history.getPrimitiveId())).getCoor();
|
---|
304 | return previousCoor != null && previousCoor.equals(currentCoor);
|
---|
305 | }
|
---|
306 | return false;
|
---|
307 | case CLOSEDWAY:
|
---|
308 | case WAY:
|
---|
309 | List<Node> currentNodes = ((Way)current).getNodes();
|
---|
310 | List<Long> historyNodes = ((HistoryWay)history).getNodes();
|
---|
311 | if (currentNodes.size() != historyNodes.size()) return false;
|
---|
312 | for (int i = 0; i < currentNodes.size(); i++) {
|
---|
313 | if (currentNodes.get(i).getId() != historyNodes.get(i)) return false;
|
---|
314 | }
|
---|
315 | return true;
|
---|
316 | case MULTIPOLYGON:
|
---|
317 | case RELATION:
|
---|
318 | List<org.openstreetmap.josm.data.osm.RelationMember> currentMembers =
|
---|
319 | ((Relation)current).getMembers();
|
---|
320 | List<RelationMemberData> historyMembers = ((HistoryRelation)history).getMembers();
|
---|
321 | if (currentMembers.size() != historyMembers.size()) return false;
|
---|
322 | for (int i = 0; i < currentMembers.size(); i++) {
|
---|
323 | org.openstreetmap.josm.data.osm.RelationMember currentMember =
|
---|
324 | currentMembers.get(i);
|
---|
325 | RelationMemberData historyMember = historyMembers.get(i);
|
---|
326 | if (!currentMember.getRole().equals(historyMember.getRole())) return false;
|
---|
327 | if (!currentMember.getMember().getPrimitiveId().equals(new SimplePrimitiveId(
|
---|
328 | historyMember.getMemberId(),historyMember.getMemberType()))) return false;
|
---|
329 | }
|
---|
330 | return true;
|
---|
331 | default: throw new AssertionError();
|
---|
332 | }
|
---|
333 | }
|
---|
334 |
|
---|
335 | /**
|
---|
336 | * Builds a list of commands that will revert the changeset
|
---|
337 | *
|
---|
338 | */
|
---|
339 | public List<Command> getCommands() {
|
---|
340 | if (this.nds == null) return null;
|
---|
341 |
|
---|
342 | //////////////////////////////////////////////////////////////////////////
|
---|
343 | // Create commands to restore/update all affected objects
|
---|
344 | DataSetCommandMerger merger = new DataSetCommandMerger(nds,ds);
|
---|
345 | List<Command> cmds = merger.getCommandList();
|
---|
346 |
|
---|
347 | //////////////////////////////////////////////////////////////////////////
|
---|
348 | // Create a set of objects to be deleted
|
---|
349 |
|
---|
350 | HashSet<OsmPrimitive> toDelete = new HashSet<>();
|
---|
351 | // Mark objects that has visible=false to be deleted
|
---|
352 | for (OsmPrimitive p : nds.allPrimitives()) {
|
---|
353 | if (!p.isVisible()) {
|
---|
354 | OsmPrimitive dp = ds.getPrimitiveById(p);
|
---|
355 | if (dp != null) toDelete.add(dp);
|
---|
356 | }
|
---|
357 | }
|
---|
358 | // Mark all created objects to be deleted
|
---|
359 | for (HistoryOsmPrimitive id : created) {
|
---|
360 | OsmPrimitive p = ds.getPrimitiveById(id.getPrimitiveId());
|
---|
361 | if (p != null) toDelete.add(p);
|
---|
362 | }
|
---|
363 |
|
---|
364 | //////////////////////////////////////////////////////////////////////////
|
---|
365 | // Check reversion against current dataset and create necessary conflicts
|
---|
366 |
|
---|
367 | HashSet<OsmPrimitive> conflicted = new HashSet<>();
|
---|
368 |
|
---|
369 | for (Conflict<? extends OsmPrimitive> conflict : merger.getConflicts()) {
|
---|
370 | cmds.add(new ConflictAddCommand(layer,conflict));
|
---|
371 | }
|
---|
372 |
|
---|
373 | // Check objects versions
|
---|
374 | for (Iterator<ChangesetDataSetEntry> it = cds.iterator();it.hasNext();) {
|
---|
375 | ChangesetDataSetEntry entry = it.next();
|
---|
376 | if (!checkOsmChangeEntry(entry)) continue;
|
---|
377 | HistoryOsmPrimitive hp = entry.getPrimitive();
|
---|
378 | OsmPrimitive dp = ds.getPrimitiveById(hp.getPrimitiveId());
|
---|
379 | if (dp == null || dp.isIncomplete())
|
---|
380 | throw new IllegalStateException(tr("Missing merge target for {0} with id {1}",
|
---|
381 | hp.getType(), hp.getId()));
|
---|
382 |
|
---|
383 | if (hp.getVersion() != dp.getVersion()
|
---|
384 | && (hp.isVisible() || dp.isVisible()) &&
|
---|
385 | /* Don't create conflict if changeset object and dataset object
|
---|
386 | * has same semantic attributes (but different versions) */
|
---|
387 | !hasEqualSemanticAttributes(dp,hp)
|
---|
388 | /* Don't create conflict if the object has to be deleted but has already been deleted */
|
---|
389 | && !(toDelete.contains(dp) && dp.isDeleted())) {
|
---|
390 | cmds.add(new ConflictAddCommand(layer,CreateConflict(dp,
|
---|
391 | entry.getModificationType() == ChangesetModificationType.CREATED)));
|
---|
392 | conflicted.add(dp);
|
---|
393 | }
|
---|
394 | }
|
---|
395 |
|
---|
396 | /* Check referrers for deleted objects: if object is referred by another object that
|
---|
397 | * isn't going to be deleted or modified, create a conflict.
|
---|
398 | */
|
---|
399 | for (Iterator<OsmPrimitive> it = toDelete.iterator(); it.hasNext();) {
|
---|
400 | OsmPrimitive p = it.next();
|
---|
401 | if (p.isDeleted()) {
|
---|
402 | it.remove();
|
---|
403 | continue;
|
---|
404 | }
|
---|
405 | for (OsmPrimitive referrer : p.getReferrers()) {
|
---|
406 | if (toDelete.contains(referrer)) continue; // object is going to be deleted
|
---|
407 | if (nds.getPrimitiveById(referrer) != null)
|
---|
408 | continue; /* object is going to be modified so it cannot refer to
|
---|
409 | * objects created in changeset to be reverted
|
---|
410 | */
|
---|
411 | if (!conflicted.contains(p)) {
|
---|
412 | cmds.add(new ConflictAddCommand(layer,CreateConflict(p, true)));
|
---|
413 | conflicted.add(p);
|
---|
414 | }
|
---|
415 | it.remove();
|
---|
416 | break;
|
---|
417 | }
|
---|
418 | }
|
---|
419 |
|
---|
420 | // Create a Command to delete all marked objects
|
---|
421 | List<? extends OsmPrimitive> list;
|
---|
422 | list = OsmPrimitive.getFilteredList(toDelete, Relation.class);
|
---|
423 | if (!list.isEmpty()) cmds.add(new DeleteCommand(list));
|
---|
424 | list = OsmPrimitive.getFilteredList(toDelete, Way.class);
|
---|
425 | if (!list.isEmpty()) cmds.add(new DeleteCommand(list));
|
---|
426 | list = OsmPrimitive.getFilteredList(toDelete, Node.class);
|
---|
427 | if (!list.isEmpty()) cmds.add(new DeleteCommand(list));
|
---|
428 | return cmds;
|
---|
429 | }
|
---|
430 |
|
---|
431 | public boolean hasMissingObjects() {
|
---|
432 | return !missing.isEmpty();
|
---|
433 | }
|
---|
434 |
|
---|
435 | public void fixNodesWithoutCoordinates(ProgressMonitor progressMonitor) throws OsmTransferException {
|
---|
436 | for (Node n : nds.getNodes()) {
|
---|
437 | if (!n.isDeleted() && n.getCoor() == null) {
|
---|
438 | PrimitiveId id = n.getPrimitiveId();
|
---|
439 | OsmPrimitive p = ds.getPrimitiveById(id);
|
---|
440 | if (p instanceof Node && p.getVersion() > 1) {
|
---|
441 | LatLon coor = ((Node)p).getCoor();
|
---|
442 | if (coor == null) {
|
---|
443 | final OsmServerMultiObjectReader rdr = new OsmServerMultiObjectReader();
|
---|
444 | readObjectVersion(rdr, id, p.getVersion()-1, progressMonitor);
|
---|
445 | Collection<OsmPrimitive> result = rdr.parseOsm(progressMonitor.createSubTaskMonitor(1, true)).allPrimitives();
|
---|
446 | if (!result.isEmpty()) {
|
---|
447 | coor = ((Node)result.iterator().next()).getCoor();
|
---|
448 | }
|
---|
449 | }
|
---|
450 | if (coor != null) {
|
---|
451 | n.setCoor(coor);
|
---|
452 | }
|
---|
453 | }
|
---|
454 | }
|
---|
455 | }
|
---|
456 | }
|
---|
457 | }
|
---|