mirror of
https://github.com/FoggedLens/iD.git
synced 2026-02-13 09:12:52 +00:00
this had regressed in 5f1e405 – if an entity had two or more parent relations, the loop would abort after the first parent relation because `memo[entity.id]` would be set to true in the first parent iteration. what we want is to only abort if the same `relation` is seen again (in a r123 [-> …] -> r123 cirular relationship)
167 lines
4.6 KiB
JavaScript
167 lines
4.6 KiB
JavaScript
import RBush from 'rbush';
|
|
|
|
import { coreDifference } from './difference';
|
|
|
|
|
|
export function coreTree(head) {
|
|
// tree for entities
|
|
var _rtree = new RBush();
|
|
var _bboxes = {};
|
|
|
|
// maintain a separate tree for granular way segments
|
|
var _segmentsRTree = new RBush();
|
|
var _segmentsBBoxes = {};
|
|
var _segmentsByWayId = {};
|
|
|
|
var tree = {};
|
|
|
|
|
|
function entityBBox(entity) {
|
|
var bbox = entity.extent(head).bbox();
|
|
bbox.id = entity.id;
|
|
_bboxes[entity.id] = bbox;
|
|
return bbox;
|
|
}
|
|
|
|
|
|
function segmentBBox(segment) {
|
|
var extent = segment.extent(head);
|
|
// extent can be null if the node entities aren't in the graph for some reason
|
|
if (!extent) return null;
|
|
|
|
var bbox = extent.bbox();
|
|
bbox.segment = segment;
|
|
_segmentsBBoxes[segment.id] = bbox;
|
|
return bbox;
|
|
}
|
|
|
|
|
|
function removeEntity(entity) {
|
|
_rtree.remove(_bboxes[entity.id]);
|
|
delete _bboxes[entity.id];
|
|
|
|
if (_segmentsByWayId[entity.id]) {
|
|
_segmentsByWayId[entity.id].forEach(function(segment) {
|
|
_segmentsRTree.remove(_segmentsBBoxes[segment.id]);
|
|
delete _segmentsBBoxes[segment.id];
|
|
});
|
|
delete _segmentsByWayId[entity.id];
|
|
}
|
|
}
|
|
|
|
|
|
function loadEntities(entities) {
|
|
_rtree.load(entities.map(entityBBox));
|
|
|
|
var segments = [];
|
|
entities.forEach(function(entity) {
|
|
if (entity.segments) {
|
|
var entitySegments = entity.segments(head);
|
|
// cache these to make them easy to remove later
|
|
_segmentsByWayId[entity.id] = entitySegments;
|
|
segments = segments.concat(entitySegments);
|
|
}
|
|
});
|
|
if (segments.length) _segmentsRTree.load(segments.map(segmentBBox).filter(Boolean));
|
|
}
|
|
|
|
|
|
function updateParents(entity, insertions, memo) {
|
|
head.parentWays(entity).forEach(function(way) {
|
|
if (_bboxes[way.id]) {
|
|
removeEntity(way);
|
|
insertions[way.id] = way;
|
|
}
|
|
updateParents(way, insertions, memo);
|
|
});
|
|
|
|
head.parentRelations(entity).forEach(function(relation) {
|
|
if (memo[relation.id]) return;
|
|
memo[relation.id] = true;
|
|
if (_bboxes[relation.id]) {
|
|
removeEntity(relation);
|
|
insertions[relation.id] = relation;
|
|
}
|
|
updateParents(relation, insertions, memo);
|
|
});
|
|
}
|
|
|
|
|
|
tree.rebase = function(entities, force) {
|
|
var insertions = {};
|
|
|
|
for (var i = 0; i < entities.length; i++) {
|
|
var entity = entities[i];
|
|
if (!entity.visible) continue;
|
|
|
|
if (head.entities.hasOwnProperty(entity.id) || _bboxes[entity.id]) {
|
|
if (!force) {
|
|
continue;
|
|
} else if (_bboxes[entity.id]) {
|
|
removeEntity(entity);
|
|
}
|
|
}
|
|
|
|
insertions[entity.id] = entity;
|
|
updateParents(entity, insertions, {});
|
|
}
|
|
|
|
loadEntities(Object.values(insertions));
|
|
|
|
return tree;
|
|
};
|
|
|
|
|
|
function updateToGraph(graph) {
|
|
if (graph === head) return;
|
|
|
|
var diff = coreDifference(head, graph);
|
|
|
|
head = graph;
|
|
|
|
var changed = diff.didChange;
|
|
if (!changed.addition && !changed.deletion && !changed.geometry) return;
|
|
|
|
var insertions = {};
|
|
|
|
if (changed.deletion) {
|
|
diff.deleted().forEach(function(entity) {
|
|
removeEntity(entity);
|
|
});
|
|
}
|
|
|
|
if (changed.geometry) {
|
|
diff.modified().forEach(function(entity) {
|
|
removeEntity(entity);
|
|
insertions[entity.id] = entity;
|
|
updateParents(entity, insertions, {});
|
|
});
|
|
}
|
|
|
|
if (changed.addition) {
|
|
diff.created().forEach(function(entity) {
|
|
insertions[entity.id] = entity;
|
|
});
|
|
}
|
|
|
|
loadEntities(Object.values(insertions));
|
|
}
|
|
|
|
// returns an array of entities with bounding boxes overlapping `extent` for the given `graph`
|
|
tree.intersects = function(extent, graph) {
|
|
updateToGraph(graph);
|
|
return _rtree.search(extent.bbox())
|
|
.map(function(bbox) { return graph.entity(bbox.id); });
|
|
};
|
|
|
|
// returns an array of segment objects with bounding boxes overlapping `extent` for the given `graph`
|
|
tree.waySegments = function(extent, graph) {
|
|
updateToGraph(graph);
|
|
return _segmentsRTree.search(extent.bbox())
|
|
.map(function(bbox) { return bbox.segment; });
|
|
};
|
|
|
|
|
|
return tree;
|
|
}
|