From 7154b68b71428bc062e2e634a7f15d447118e113 Mon Sep 17 00:00:00 2001 From: Tom MacWright Date: Wed, 31 Oct 2012 18:17:54 -0400 Subject: [PATCH] Move graph out of connection, move more logic to Util, update tests. --- index.html | 39 +------------ js/iD/Connection.js | 128 ++++-------------------------------------- js/iD/Graph.js | 88 +++++++++++++++++++++++++++++ js/iD/Relation.js | 6 ++ js/iD/Util.js | 39 +++++++------ js/iD/renderer/Map.js | 43 ++++++-------- test/spec/Entity.js | 14 ++--- 7 files changed, 152 insertions(+), 205 deletions(-) diff --git a/index.html b/index.html index 4fcfc6f90..a2c276ae8 100755 --- a/index.html +++ b/index.html @@ -64,6 +64,7 @@ diff --git a/js/iD/Connection.js b/js/iD/Connection.js index 1693dde37..aa1c55e29 100755 --- a/js/iD/Connection.js +++ b/js/iD/Connection.js @@ -4,67 +4,19 @@ iD.Connection = function() { var nextNode = -1, nextWay = -1, nextRelation = -1, - entities = {}, - relations = {}, - apiURL = 'http://www.openstreetmap.org/api/0.6/', - modified = false; + graph = {}, + apiURL = 'http://www.openstreetmap.org/api/0.6/'; var connection = {}; function all() { - return d3.values(entities); - } - - function assign(obj) { - // summary: Save an entity to the data store. - if (obj.type === 'relation') { - if (!relations[obj.id]) relations[obj.id] = obj; - } else if (!entities[obj.id] || !entities[obj.id].loaded) { - entities[obj.id] = obj; - } - } - - function getOrCreate(id, type) { - // summary: Return an entity if it exists: if not, create an empty one with the given id, and return that. - if (type === 'node') { - if (!entities[id]) assign(new iD.Node(id, NaN, NaN, {}, false)); - return entities[id]; - } else if (type === 'way') { - if (!entities[id]) { - assign(new iD.Way(id, [], {}, false)); - } - return entities[id]; - } else if (type === 'relation') { - if (!relations[id]) assign(new iD.Relation(id, [], {}, false)); - return relations[id]; - } - } - - function doCreateNode(tags, lat, lon, perform) { - // summary: Create a new node and save it in the data store, using an undo stack. - var node = new iD.Node(nextNode--, lat, lon, tags, true); - perform(new iD.actions.CreateEntityAction(node, assign)); - return node; // iD.Node - } - - function doCreateWay(tags, nodes, perform) { - // summary: Create a new way and save it in the data store, using an undo stack. - var way = new iD.Way(nextWay--, nodes.concat(), tags, true); - perform(new iD.actions.CreateEntityAction(way, assign)); - return way; - } - - function doCreateRelation(tags, members, perform) { - // summary: Create a new relation and save it in the data store, using an undo stack. - var relation = new iD.Relation(nextRelation--, members.concat(), tags, true); - perform(new iD.actions.CreateEntityAction(relation, assign)); - return relation; + return d3.values(graph.index); } function intersects(extent) { // summary: Find all drawable entities that are within a given bounding box. // Each one is an array of entities. - return d3.values(entities).filter(function(e, id) { + return d3.values(graph.index).filter(function(e, id) { return e.intersects(extent); }); } @@ -79,87 +31,29 @@ iD.Connection = function() { d3.xml(url, parse(callback)); } - function getTags(obj) { - var tags = {}; - var tagelems = obj.getElementsByTagName('tag'); - // Doesn't use underscore for performance reasons - for (var i = 0; i < tagelems.length; i++) { - var item = tagelems[i]; - tags[item.attributes.k.nodeValue] = item.attributes.v.nodeValue; - } - return tags; - } - - function getNodes(obj) { - var nodes = []; - var nelems = obj.getElementsByTagName('nd'); - // Doesn't use underscore for performance reasons - for (var i = 0; i < nelems.length; i++) { - var item = nelems[i]; - nodes.push(entities[item.attributes.ref.nodeValue]); - } - return nodes; - } - - function getMembers(obj) { - var members = []; - var elems = obj.getElementsByTagName('member'); - - for (var i = 0; i < elems.length; i++) { - var item = elems[i]; - var id = item.attributes.ref.nodeValue, - type = item.attributes.type.nodeValue, - role = item.attributes.role.nodeValue; - - var o = getOrCreate(id, type); - members.push(new iD.RelationMember(o, role)); - } - - return members; - } - function parse(callback) { return function(dom) { if (!dom.childNodes) { return callback(new Error('Bad request')); } for (var i = 0; i < dom.childNodes[0].childNodes.length; i++) { - var obj = dom.childNodes[0].childNodes[i], attrib; - if (obj.nodeName === 'node') { - var node = new iD.Node( - obj.attributes.id.nodeValue, - +obj.attributes.lat.nodeValue, - +obj.attributes.lon.nodeValue, - getTags(obj)); - assign(node); - } else if (obj.nodeName === 'way') { - var way = new iD.Way( - obj.attributes.id.nodeValue, - getNodes(obj), - getTags(obj)); - assign(way); - } else if (obj.nodeName === 'relation') { - var relation = new iD.Relation( - obj.attributes.id.nodeValue, - getMembers(obj, connection), - getTags(obj)); - assign(relation); - } + var obj = dom.childNodes[0].childNodes[i]; + graph.process(obj); } callback(null); }; } - connection.entities = entities; + connection.graph = function(x) { + graph = x; + return connection; + }; + connection.all = all; - connection.relations = relations; connection.loadFromAPI = loadFromAPI; connection.loadFromURL = loadFromURL; connection.apiURL = apiURL; connection.intersects = intersects; - connection.doCreateNode = doCreateNode; - connection.doCreateWay = doCreateWay; - connection.doCreateRelation = doCreateRelation; return connection; }; diff --git a/js/iD/Graph.js b/js/iD/Graph.js index d36b3fece..22ba8f3ec 100644 --- a/js/iD/Graph.js +++ b/js/iD/Graph.js @@ -1,2 +1,90 @@ iD.Graph = function() { + this.index = {}; + this.nodes = []; +}; + +iD.Graph.prototype = { + + getTags: function(obj) { + var tags = {}, tagelems = obj.getElementsByTagName('tag'); + for (var i = 0; i < tagelems.length; i++) { + var item = tagelems[i]; + tags[item.attributes.k.nodeValue] = item.attributes.v.nodeValue; + } + return tags; + }, + + getNodes: function(obj) { + var nodes = [], nelems = obj.getElementsByTagName('nd'); + for (var i = 0; i < nelems.length; i++) { + var item = nelems[i]; + nodes.push(this.index[item.attributes.ref.nodeValue]); + } + return nodes; + }, + + getMembers: function(obj) { + var members = []; + var elems = obj.getElementsByTagName('member'); + + for (var i = 0; i < elems.length; i++) { + var item = elems[i]; + var id = item.attributes.ref.nodeValue, + type = item.attributes.type.nodeValue, + role = item.attributes.role.nodeValue; + + var o = this.getOrCreate(id, type); + members.push(new iD.RelationMember(o, role)); + } + + return members; + }, + + assign: function(obj) { + // summary: Save an entity to the data store. + if (obj.type === 'relation') { + if (!this.index[obj.id]) this.index[obj.id] = obj; + } else if (!this.index[obj.id] || !this.index[obj.id].loaded) { + this.index[obj.id] = obj; + } + }, + + getOrCreate: function(id, type) { + // summary: Return an entity if it exists: if not, create an empty one with the given id, and return that. + if (type === 'node') { + if (!this.index[id]) this.assign(new iD.Node(id)); + return this.index[id]; + } else if (type === 'way') { + if (!this.index[id]) { + this.assign(new iD.Way(id)); + } + return this.index[id]; + } else if (type === 'relation') { + if (!this.index[id]) this.assign(new iD.Relation(id)); + return this.index[id]; + } + }, + + process: function(obj) { + if (obj.nodeName === 'node') { + var node = new iD.Node( + obj.attributes.id.nodeValue, + +obj.attributes.lat.nodeValue, + +obj.attributes.lon.nodeValue, + this.getTags(obj)); + this.assign(node); + } else if (obj.nodeName === 'way') { + var way = new iD.Way( + obj.attributes.id.nodeValue, + this.getNodes(obj), + this.getTags(obj)); + this.assign(way); + } else if (obj.nodeName === 'relation') { + var relation = new iD.Relation( + obj.attributes.id.nodeValue, + this.getMembers(obj, connection), + this.getTags(obj)); + this.assign(relation); + } + } }; diff --git a/js/iD/Relation.js b/js/iD/Relation.js index 6f9511f1a..22e631684 100644 --- a/js/iD/Relation.js +++ b/js/iD/Relation.js @@ -1,4 +1,6 @@ iD.Relation = function(id, members, tags, loaded) { + members = members || []; + tags = tags || {}; this.type = 'relation'; this.id = id; this._id = iD.Util.id(); @@ -12,6 +14,10 @@ iD.Relation = function(id, members, tags, loaded) { } }; +iD.Relation.prototype = { + intersects: function() { return true; } +}; + iD.RelationMember = function(entity, role) { this.entity = entity; this.role = role; diff --git a/js/iD/Util.js b/js/iD/Util.js index 8955dcefb..206ff4fc9 100644 --- a/js/iD/Util.js +++ b/js/iD/Util.js @@ -30,23 +30,28 @@ iD.Util.friendlyName = function(entity) { return n.length === 0 ? 'unknown' : n.join('; '); }; -// TODO: don't use a cache here? -iD.Util._presets = {}; -iD.Util.presets = function(type, callback) { - if (iD.Util._presets[type]) return callback(iD.Util._presets[type]); - $.ajax({ - url: 'presets/' + type + '.json', - dataType: "json", - error: function() { - if (typeof console !== 'undefined') console.error(arguments); - }, - success: function(resp) { - iD.Util._presets[type] = resp; - return callback(resp); - } - }); +iD.Util.TAG_CLASSES = { + 'highway': true, + 'railway': true, + 'motorway': true, + 'amenity': true, + 'landuse': true, + 'building': true, + 'bridge': true }; -iD.Util.tileKey = function(coord) { - return coord.z + ',' + coord.x + ',' + coord.y; +iD.Util.styleClasses = function(pre) { + return function(d) { + var tags = d.tags; + var c = [pre]; + function clean(x) { + return iD.Util.TAG_CLASSES[x]; + } + for (var k in tags) { + if (!clean(k)) continue; + c.push(k + '-' + tags[k]); + c.push(k); + } + return c.join(' '); + }; }; diff --git a/js/iD/renderer/Map.js b/js/iD/renderer/Map.js index e02ab7fbe..6f75f24d9 100755 --- a/js/iD/renderer/Map.js +++ b/js/iD/renderer/Map.js @@ -17,9 +17,6 @@ iD.Map = function(obj) { var inspector = iD.Inspector(); - var tagclasses = [ - 'highway', 'railway', 'motorway', 'amenity', 'landuse', 'building', 'bridge']; - var linegen = d3.svg.line() .x(function(d) { return projection(d)[0]; }) .y(function(d) { return projection(d)[1]; }); @@ -100,25 +97,6 @@ iD.Map = function(obj) { function key(d) { return d._id; } - function classes(pre) { - return function(d) { - var tags = d.tags; - var c = [pre]; - function clean(x) { - return tagclasses.indexOf(x) !== -1; - } - for (var k in tags) { - if (!clean(k)) continue; - c.push(k + '-' + tags[k]); - c.push(k); - } - if (selection.indexOf(d._id) !== -1) { - c.push('active'); - } - return c.join(' '); - }; - } - function deselectClick() { selection = []; drawVector(); @@ -169,11 +147,22 @@ iD.Map = function(obj) { return as - bs; } - var class_stroke = classes('stroke'), - class_fill = classes('stroke'), - class_area = classes('area'), - class_marker = classes('marker'), - class_casing = classes('casing'); + // This is an unfortunate hack that should be improved. + function augmentSelect(fn) { + return function(d) { + var c = fn(d); + if (selection.indexOf(d._id) !== -1) { + c += ' active'; + } + return c; + }; + } + + var class_stroke = augmentSelect(iD.Util.styleClasses('stroke')), + class_fill = augmentSelect(iD.Util.styleClasses('stroke')), + class_area = augmentSelect(iD.Util.styleClasses('area')), + class_marker = augmentSelect(iD.Util.styleClasses('marker')), + class_casing = augmentSelect(iD.Util.styleClasses('casing')); function drawVector() { var all = connection.intersects(extent()); diff --git a/test/spec/Entity.js b/test/spec/Entity.js index e774a809e..7632c760b 100644 --- a/test/spec/Entity.js +++ b/test/spec/Entity.js @@ -6,22 +6,22 @@ describe('Entity', function () { }); it('has no entity type', function () { - expect(entity.entityType).toEqual(''); + expect(entity.type).toEqual(''); }); describe('#parentWays', function () { it('returns an array of parents with entityType way', function () { - entity.addParent({_id: 1, entityType: 'way'}); - entity.addParent({_id: 2, entityType: 'node'}); - expect(entity.parentWays()).toEqual([{_id: 1, entityType: 'way'}]); + entity.addParent({_id: 1, type: 'way'}); + entity.addParent({_id: 2, type: 'node'}); + expect(entity.parentWays()).toEqual([{_id: 1, type: 'way'}]); }); }); describe('#parentRelations', function () { it('returns an array of parents with entityType relation', function () { - entity.addParent({_id: 1, entityType: 'way'}); - entity.addParent({_id: 2, entityType: 'relation'}); - expect(entity.parentRelations()).toEqual([{_id: 2, entityType: 'relation'}]); + entity.addParent({_id: 1, type: 'way'}); + entity.addParent({_id: 2, type: 'relation'}); + expect(entity.parentRelations()).toEqual([{_id: 2, type: 'relation'}]); }); }); });