Files
iD/js/id/graph/graph.js
2013-01-25 15:07:58 -05:00

227 lines
6.4 KiB
JavaScript

iD.Graph = function(entities, mutable) {
if (!(this instanceof iD.Graph)) return new iD.Graph(entities, mutable);
if (_.isArray(entities)) {
this.entities = Object.create(this.original);
for (var i = 0; i < entities.length; i++) {
this.entities[entities[i].id] = entities[i];
}
} else {
this.entities = _.extend(Object.create(this.original), entities);
}
this.transients = {};
this._parentWays = {};
this._parentRels = {};
this._fetches = {};
if (!mutable) {
this.freeze();
}
};
iD.Graph.prototype = {
original: {},
entity: function(id) {
return this.entities[id];
},
transient: function(entity, key, fn) {
var id = entity.id,
transients = this.transients[id] ||
(this.transients[id] = {});
if (transients[key] !== undefined) {
return transients[key];
}
return transients[key] = fn.call(entity);
},
parentWays: function(entity) {
var ent, id, parents;
if (!this._parentWays.calculated) {
for (var i in this.entities) {
ent = this.entities[i];
if (ent && ent.type === 'way') {
for (var j = 0; j < ent.nodes.length; j++) {
id = ent.nodes[j];
parents = this._parentWays[id] = this._parentWays[id] || [];
if (parents.indexOf(ent) < 0) {
parents.push(ent);
}
}
}
}
this._parentWays.calculated = true;
}
return this._parentWays[entity.id] || [];
},
isPoi: function(entity) {
return this.parentWays(entity).length === 0;
},
parentRelations: function(entity) {
var ent, id, parents;
if (!this._parentRels.calculated) {
for (var i in this.entities) {
ent = this.entities[i];
if (ent && ent.type === 'relation') {
for (var j = 0; j < ent.members.length; j++) {
id = ent.members[j].id;
parents = this._parentRels[id] = this._parentRels[id] || [];
if (parents.indexOf(ent) < 0) {
parents.push(ent);
}
}
}
}
this._parentRels.calculated = true;
}
return this._parentRels[entity.id] || [];
},
merge: function(graph) {
for (var i in graph.entities) {
this.original[i] = graph.entities[i];
}
return this;
},
replace: function(entity) {
return this.update(function () {
this.entities[entity.id] = entity;
});
},
remove: function(entity) {
return this.update(function () {
if (entity.created()) {
delete this.entities[entity.id];
} else {
this.entities[entity.id] = undefined;
}
});
},
update: function() {
var graph = this.frozen ? iD.Graph(_.clone(this.entities), true) : this;
for (var i = 0; i < arguments.length; i++) {
arguments[i].call(graph, graph);
}
return this.frozen ? graph.freeze() : this;
},
freeze: function() {
this.frozen = true;
if (iD.debug) {
Object.freeze(this);
Object.freeze(this.entities);
}
return this;
},
// get all objects that intersect an extent.
intersects: function(extent) {
var items = [];
for (var i in this.entities) {
var entity = this.entities[i];
if (entity && entity.intersects(extent, this)) {
items.push(this.fetch(entity.id));
}
}
return items;
},
// Resolve the id references in a way, replacing them with actual objects.
fetch: function(id) {
if (this._fetches[id]) return this._fetches[id];
var entity = this.entities[id], nodes = [];
if (!entity || !entity.nodes || !entity.nodes.length) return entity;
for (var i = 0, l = entity.nodes.length; i < l; i++) {
nodes[i] = this.fetch(entity.nodes[i]);
}
return (this._fetches[id] = iD.Entity(entity, {nodes: nodes}));
},
difference: function (graph) {
var result = [],
keys = Object.keys(this.entities),
entity, oldentity, id;
for (var i = 0; i < keys.length; i++) {
id = keys[i];
entity = this.entities[id];
oldentity = graph.entities[id];
if (entity !== oldentity) {
if (entity && entity.type === 'way' &&
oldentity && oldentity.type === 'way') {
result = result
.concat(_.difference(entity.nodes, oldentity.nodes))
.concat(_.difference(oldentity.nodes, entity.nodes));
} else if (entity && entity.type === 'way') {
result = result.concat(entity.nodes);
} else if (oldentity && oldentity.type === 'way') {
result = result.concat(oldentity.nodes);
}
result.push(id);
}
}
keys = Object.keys(graph.entities);
for (var i = 0; i < keys.length; i++) {
id = keys[i];
entity = graph.entities[id];
if (entity && !this.entities.hasOwnProperty(id)) {
result.push(id);
if (entity.type === 'way') result = result.concat(entity.nodes);
}
}
return result.sort();
},
modified: function() {
var result = [];
_.each(this.entities, function(entity, id) {
if (entity && entity.modified()) result.push(id);
});
return result;
},
created: function() {
var result = [];
_.each(this.entities, function(entity, id) {
if (entity && entity.created()) result.push(id);
});
return result;
},
deleted: function() {
var result = [];
_.each(this.entities, function(entity, id) {
if (!entity) result.push(id);
});
return result;
},
reset: function() {
iD.Graph.prototype.original = {};
}
};