diff --git a/index.html b/index.html
index 754bc5137..9c5d8ff83 100755
--- a/index.html
+++ b/index.html
@@ -50,6 +50,7 @@
+
diff --git a/js/iD/Connection.js b/js/iD/Connection.js
index bd2879dbd..81db5aff9 100755
--- a/js/iD/Connection.js
+++ b/js/iD/Connection.js
@@ -6,11 +6,6 @@ iD.Connection = function(graph) {
var connection = {};
- function all() {
- return graph.nodes;
- }
-
-
// Request data within the bbox from an external OSM server.
function loadFromAPI(box, callback) {
loadFromURL(apiURL + 'map?bbox=' +
@@ -24,12 +19,12 @@ iD.Connection = function(graph) {
function getNodes(obj) {
var nodes = [], nelems = obj.getElementsByTagName('nd');
for (var i = 0; i < nelems.length; i++) {
- var item = nelems[i];
- nodes.push(+item.attributes.ref.nodeValue);
+ nodes.push(+nelems[i].attributes.ref.nodeValue);
}
return nodes;
}
+ //
function getTags(obj) {
var tags = {}, tagelems = obj.getElementsByTagName('tag');
for (var i = 0; i < tagelems.length; i++) {
@@ -39,6 +34,7 @@ iD.Connection = function(graph) {
return tags;
}
+ //
function getMembers(obj) {
var members = [],
elems = obj.getElementsByTagName('member');
@@ -54,38 +50,45 @@ iD.Connection = function(graph) {
return members;
}
+ //
function objectData(obj) {
- return {
+ var o = {
type: obj.nodeName,
- id: +obj.attributes.id.nodeValue,
- tags: getTags(obj),
- lat: (obj.attributes.lat) ? +obj.attributes.lat.nodeValue : null,
- lon: (obj.attributes.lon) ? +obj.attributes.lon.nodeValue : null,
members: getMembers(obj),
- nodes: getNodes(obj)
+ nodes: getNodes(obj),
+ tags: getTags(obj)
};
+ var numbers = {id: true, lat: true, lon: true };
+ for (var i = 0; i < obj.attributes.length; i++) {
+ var n = obj.attributes[i].nodeName;
+ var v = obj.attributes[i].nodeValue;
+ o[n] = numbers[n] ? +v : v;
+ }
+ return o;
}
function parse(callback) {
return function(dom) {
- if (!dom.childNodes) {
- return callback(new Error('Bad request'));
- }
- var root = dom.childNodes[0],
- ways = root.getElementsByTagName('way'),
- relations = root.getElementsByTagName('relation'),
- nodes = root.getElementsByTagName('node');
- var i;
- for (i = 0; i < ways.length; i++) graph.insert(objectData(ways[i]));
- for (i = 0; i < relations.length; i++) graph.insert(objectData(relations[i]));
- for (i = 0; i < nodes.length; i++) graph.insert(objectData(nodes[i]));
+ if (!dom.childNodes) return callback(new Error('Bad request'));
+ var root = dom.childNodes[0];
+ connection.graph.insert(_.map(root.getElementsByTagName('way'), objectData));
+ connection.graph.insert(_.map(root.getElementsByTagName('node'), objectData));
+ connection.graph.insert(_.map(root.getElementsByTagName('relation'), objectData));
callback(null);
};
}
connection.graph = graph;
- connection.all = all;
connection.loadFromAPI = loadFromAPI;
+ connection.objectData = objectData;
connection.loadFromURL = loadFromURL;
connection.apiURL = apiURL;
diff --git a/js/iD/format/GeoJSON.js b/js/iD/format/GeoJSON.js
index f3e628ded..08f9257fd 100644
--- a/js/iD/format/GeoJSON.js
+++ b/js/iD/format/GeoJSON.js
@@ -21,7 +21,7 @@ iD.GeoJSON = {
properties: entity.tags,
geometry: {
'type': 'LineString',
- 'coordinates': _.map(entity.children, function(node) {
+ 'coordinates': entity.nodes.map(function(node) {
return [node.lon, node.lat];
})
}
diff --git a/js/iD/format/XML.js b/js/iD/format/XML.js
index ed2827c09..8fd3e179a 100644
--- a/js/iD/format/XML.js
+++ b/js/iD/format/XML.js
@@ -35,7 +35,7 @@ iD.XML = {
JXON.unbuild({
way: {
'@id': entity.id,
- 'nd': entity.children.map(function(e) {
+ 'nd': entity.nodes.map(function(e) {
return {
keyAttributes: {
ref: e.id
diff --git a/js/iD/graph/Graph.js b/js/iD/graph/Graph.js
index faef664bd..3a4036e8c 100644
--- a/js/iD/graph/Graph.js
+++ b/js/iD/graph/Graph.js
@@ -1,57 +1,43 @@
-iD.Graph = function() {
-};
+iD.Graph = function() { };
iD.Graph.prototype = {
- index: {},
+ head: {},
- insert: function(o) {
- var obj;
- // Do not reinsert OSM objects
- if (this.index[o.id]) return;
- if (o.type === 'node') {
- obj = {
- type: 'node',
- id: o.id,
- uid: uuid.v4(),
- lat: o.lat,
- lon: o.lon,
- tags: o.tags
- };
- } else if (o.type === 'way') {
- obj = new iD.Way(
- o.id,
- uuid.v4(),
- o.nodes,
- o.tags);
- } else if (o.type === 'relation') {
- obj = new iD.Relation(
- o.id,
- uuid.v4(),
- o.members,
- o.tags);
+ // stack of previous versions of this datastructure
+ prev: [],
+
+ insert: function(a) {
+ for (var i = 0; i < a.length; i++) {
+ if (this.head[a[i].id]) return;
+ this.head[a[i].id] = a[i];
}
- if (!obj) return;
- this.index[obj.id] = [obj];
+ },
+
+ modify: function(callback) {
+ // Previous version pushed onto stack
+ var o = pdata.object(this.head).get();
+ prev.push(o);
+
+ // Make head a copy with no common history
+ this.head = pdata.object(this.head).get();
},
intersects: function(version, extent) {
// summary: Find all drawable entities that are within a given bounding box.
// Each one is an array of entities.
var items = [];
- for (var i in this.index) {
- if (this.index[i][version]) {
- items.push(this.index[i][version]);
- }
+ for (var i in this.head) {
+ if (this.head[i]) items.push(this.head[i]);
}
return items;
},
- fetch: function(object) {
- var o = this.index[object][0];
+ fetch: function(id) {
+ var o = this.head[id];
var f = _.clone(o);
- if (!f.children || !f.children.length) return f;
- f.children = f.children.map(function(c) {
+ if (!f.nodes || !f.nodes.length) return f;
+ f.nodes = f.nodes.map(function(c) {
return this.fetch(c);
}.bind(this));
return f;
diff --git a/js/iD/graph/Way.js b/js/iD/graph/Way.js
index 22ef23fd3..eb66c8030 100644
--- a/js/iD/graph/Way.js
+++ b/js/iD/graph/Way.js
@@ -6,19 +6,15 @@
//
// If a a way is _closed_, it is assumed to be an area unless it has a
// `highway` or `barrier` tag and is not also tagged `area`.
-iD.Way = function(id, uid, children, tags, loaded) {
- this.id = id;
- this.uid = uid;
- this.tags = tags || {};
- this.children = children || [];
- this.loaded = (loaded === undefined) ? true : loaded;
- this.extent = {};
+
+iD.Way = {
+ isClosed: function(w) {
+ if (!w.nodes.length) return true;
+ return w.nodes[w.nodes.length - 1] === w.nodes[0];
+ }
};
-iD.Way.prototype = {
-
- type: 'way',
-
+/*
// JOSM: http://josm.openstreetmap.de/browser/josm/trunk/src/org/openstreetmap/josm/data/osm/Way.java#L466
isClosed: function() {
// summary: Is this a closed way (first and last nodes the same)?
@@ -64,3 +60,4 @@ iD.Way.prototype = {
bounds[0][1] > extent[1][1]);
}
};
+*/
diff --git a/js/iD/graph/pdata.js b/js/iD/graph/pdata.js
new file mode 100644
index 000000000..101d2b455
--- /dev/null
+++ b/js/iD/graph/pdata.js
@@ -0,0 +1,46 @@
+var pdata = {};
+
+pdata.object = function(input) {
+
+ var v = clone(input),
+ proxy = {};
+
+ function clone(x) {
+ var v = {};
+ for (var k in x) v[k] = x[k];
+ return v;
+ }
+
+ // Remove a key from the object. This is like `delete`,
+ // but does not delete the key in this closure's object
+ proxy.remove = function(key) {
+ var n = {}, k, i;
+ if (typeof key === 'object') {
+ var keys = {};
+ for (i = 0; i < key.length; i++) keys[key[i]] = true;
+ for (k in v) if (!keys[k]) n[k] = v[k];
+ } else {
+ for (k in v) if (k !== key) n[k] = v[k];
+ }
+ return pdata.object(n);
+ };
+
+ // Set a value or values in the object. Overwrites
+ // existing values.
+ proxy.set = function(vals) {
+ var n = clone(v);
+ for (var j in vals) {
+ n[j] = vals[j];
+ }
+ return pdata.object(n);
+ };
+
+ // Get the contained object.
+ proxy.get = function() {
+ return clone(v);
+ };
+
+ return proxy;
+};
+
+if (typeof module !== 'undefined') module.exports = pdata;
diff --git a/js/iD/renderer/Map.js b/js/iD/renderer/Map.js
index 79bf3916f..8f2e1f363 100755
--- a/js/iD/renderer/Map.js
+++ b/js/iD/renderer/Map.js
@@ -44,17 +44,17 @@ iD.Map = function(elem) {
// geo
linegen = d3.svg.line()
.x(function(d) {
- var node = connection.graph.index[d][version];
+ var node = graph.head[d];
return projection([node.lon, node.lat])[0];
})
.y(function(d) {
- var node = connection.graph.index[d][version];
+ var node = graph.head[d];
return projection([node.lon, node.lat])[1];
}),
// Abstract linegen so that it pulls from `.children`. This
// makes it possible to call simply `.attr('d', nodeline)`.
nodeline = function(d) {
- return linegen(d.children);
+ return linegen(d.nodes);
},
// Abstract a key function that looks for uids. This is given
// as a second argument to `.data()`.
@@ -99,10 +99,10 @@ iD.Map = function(elem) {
var all = graph.intersects(version, getExtent());
var ways = all.filter(function(a) {
- return a.type === 'way' && !a.isClosed();
+ return a.type === 'way' && !iD.Way.isClosed(a);
}).sort(iD.Style.waystack),
areas = all.filter(function(a) {
- return a.type === 'way' && a.isClosed();
+ return a.type === 'way' && iD.Way.isClosed(a);
}),
points = all.filter(function(a) {
return a.type === 'node';
@@ -222,6 +222,7 @@ iD.Map = function(elem) {
download();
drawVector();
} else {
+ // TODO: hide vector features
}
}
@@ -321,5 +322,6 @@ iD.Map = function(elem) {
parent.node().offsetWidth,
parent.node().offsetHeight);
redraw();
+
return d3.rebind(map, dispatch, 'on');
};