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'); };