diff --git a/index.html b/index.html
index 3e69ec52d..23ac6da76 100755
--- a/index.html
+++ b/index.html
@@ -53,7 +53,6 @@ require(["dojo/dom-geometry","dojo/dom-class","dojo/on","dojo/dom","dojo/Evented
width: dom.byId('map').offsetWidth,
height: dom.byId('map').offsetHeight
});
- conn.registerMap(map);
map.ruleset = ruleset;
// Initialise controller
diff --git a/js/iD/Connection.js b/js/iD/Connection.js
index bbeac1d63..e055ef982 100755
--- a/js/iD/Connection.js
+++ b/js/iD/Connection.js
@@ -7,68 +7,65 @@
if (typeof iD === 'undefined') iD = {};
iD.Connection = function(apiURL) {
- // summary: The data store, including methods to fetch data from (and, eventually, save data to)
- // an OSM API server.
- this.nextNode = -1; // next negative ids
- this.nextWay = -1; // |
- this.nextRelation = -1; // |
- this.nodes={};
- this.ways={};
- this.relations= {};
- this.pois = {};
- this.maps=[];
- this.modified=false;
- this.apiBaseURL=apiURL;
- this.callback = null;
-};
+ // summary: The data store, including methods to fetch data from (and, eventually, save data to)
+ // an OSM API server.
+ var nextNode = -1, // next negative ids
+ nextWay = -1, // |
+ nextRelation = -1, // |
+ nodes = {},
+ ways = {},
+ relations = {},
+ pois = {},
+ modified = false,
+ apiBaseURL = apiURL;
-iD.Connection.prototype = {
- _assign:function(obj) {
- // summary: Save an entity to the data store.
- switch (obj.entityType) {
- case "node": this.nodes[obj.id]=obj; break;
- case "way": this.ways[obj.id]=obj; break;
- case "relation": this.relations[obj.id]=obj; break;
- }
- },
+ var connection = {};
- _getOrCreate:function(id,type) {
- // summary: Return an entity if it exists: if not, create an empty one with the given id, and return that.
- switch (type) {
- case "node":
- if (!this.nodes[id]) this._assign(new iD.Node(this, id, NaN, NaN, {}, false));
- return this.nodes[id];
- case "way":
- if (!this.ways[id]) this._assign(new iD.Way(this, id, [], {}, false));
- return this.ways[id];
- case "relation":
- if (!this.relations[id]) this._assign(new iD.Relation(this, id, [], {}, false));
- return this.relations[id];
- }
- },
+ function assign(obj) {
+ // summary: Save an entity to the data store.
+ switch (obj.entityType) {
+ case "node": nodes[obj.id]=obj; break;
+ case "way": ways[obj.id]=obj; break;
+ case "relation": relations[obj.id]=obj; break;
+ }
+ }
- doCreateNode:function(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(this, this.nextNode--, lat, lon, tags, true);
- perform(new iD.actions.CreateEntityAction(node, _.bind(this._assign, this) ));
- return node; // iD.Node
- },
+ 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 (!nodes[id]) assign(new iD.Node(connection, id, NaN, NaN, {}, false));
+ return nodes[id];
+ } else if (type === 'way') {
+ if (!ways[id]) assign(new iD.Way(connection, id, [], {}, false));
+ return ways[id];
+ } else if (type === 'relation') {
+ if (!relations[id]) assign(new iD.Relation(connection, id, [], {}, false));
+ return relations[id];
+ }
+ }
- doCreateWay:function(tags, nodes, perform) {
- // summary: Create a new way and save it in the data store, using an undo stack.
- var way = new iD.Way(this, this.nextWay--, nodes.concat(), tags, true);
- perform(new iD.actions.CreateEntityAction(way, _.bind(this._assign, this) ));
- return way;
- },
+ 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(connection, nextNode--, lat, lon, tags, true);
+ perform(new iD.actions.CreateEntityAction(node, assign));
+ return node; // iD.Node
+ }
- doCreateRelation:function(tags, members, perform) {
- // summary: Create a new relation and save it in the data store, using an undo stack.
- var relation = new iD.Relation(this, this.nextRelation--, members.concat(), tags, true);
- perform(new iD.actions.CreateEntityAction(relation, _.bind(this._assign, this) ));
- return relation;
- },
+ 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(connection, nextWay--, nodes.concat(), tags, true);
+ perform(new iD.actions.CreateEntityAction(way, assign));
+ return way;
+ }
- getObjectsByBbox:function(left,right,top,bottom) {
+ 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(connection, nextRelation--, members.concat(), tags, true);
+ perform(new iD.actions.CreateEntityAction(relation, assign));
+ return relation;
+ }
+
+ function getObjectsByBbox(left,right,top,bottom) {
// summary: Find all drawable entities that are within a given bounding box.
// returns: Object An object with four properties: .poisInside, .poisOutside, .waysInside, .waysOutside.
// Each one is an array of entities.
@@ -78,162 +75,139 @@ iD.Connection.prototype = {
waysInside: [],
waysOutside: []
};
- for (var id in this.ways) {
- var way = this.ways[id];
+ for (var id in ways) {
+ var way = ways[id];
if (way.within(left,right,top,bottom)) { o.waysInside.push(way); }
else { o.waysOutside.push(way); }
}
- _.each(this.pois, function(node) {
+ _.each(pois, function(node) {
if (node.within(left,right,top,bottom)) { o.poisInside.push(node); }
else { o.poisOutside.push(node); }
});
return o;
- },
+ }
- // ---------------
- // Redraw handling
+ // ------------
+ // POI handling
+ function updatePOIs(nodelist) {
+ // summary: Update the list of POIs (nodes not in ways) from a supplied array of nodes.
+ _.each(nodelist, function(node) {
+ if (node.entity.hasParentWays()) {
+ delete pois[node._id];
+ } else {
+ pois[node._id] = node;
+ }
+ });
+ }
- registerMap:function(map) {
- // summary: Record that a Map object wants updates from this Connection.
- this.maps.push(map);
- },
+ function getPOIs() {
+ // summary: Return a list of all the POIs in connection Connection.
+ return _.values(pois);
+ }
- refreshMaps:function() {
- // summary: Redraw all the Map objects that take data from this Connection.
- _.each(this.maps, function(map) {
- map.updateUIs(false,true);
- });
- },
+ function registerPOI(node) {
+ // summary: Register a node as a POI (not in a way).
+ pois[node._id] = node;
+ }
- refreshEntity: function(entity) {
- // summary: Redraw a particular entity on all the Map objects that take data from this Connection.
- _.each(this.maps, function(map) {
- map.refreshUI(entity);
- });
- },
+ function unregisterPOI(node) {
+ // summary: Mark a node as no longer being a POI (it's now in a way).
+ delete pois[node._id];
+ }
- // ------------
- // POI handling
- updatePOIs:function(nodelist) {
- // summary: Update the list of POIs (nodes not in ways) from a supplied array of nodes.
- for (var i in nodelist) {
- if (nodelist[i].entity.hasParentWays()) {
- delete this.pois[nodelist[i]._id];
- } else {
- this.pois[nodelist[i]._id] = nodelist[i];
- }
- }
- },
+ // ----------
+ // OSM parser
- getPOIs:function() {
- // summary: Return a list of all the POIs in this Connection.
- return _.values(this.pois);
- },
-
- registerPOI:function(node) {
- // summary: Register a node as a POI (not in a way).
- this.pois[node._id] = node;
- },
-
- unregisterPOI:function(node) {
- // summary: Mark a node as no longer being a POI (it's now in a way).
- delete this.pois[node._id];
- },
-
- // ----------
- // OSM parser
-
- loadFromAPI: function(box) {
+ function loadFromAPI(box, callback) {
// summary: Request data within the bbox from an external OSM server. Currently hardcoded
// to use Overpass API (which has the relevant CORS headers).
- this.loadFromURL("http://www.overpass-api.de/api/xapi?map?bbox=" + [box.west, box.south, box.east, box.north]);
- },
+ loadFromURL("http://www.overpass-api.de/api/xapi?map?bbox=" +
+ [box.west, box.south, box.east, box.north], callback);
+ }
- loadFromURL: function(url) {
- // summary: Load all data from a given URL.
- $.ajax({
+ function loadFromURL(url, callback) {
+ // summary: Load all data from a given URL.
+ $.ajax({
url: url,
- context: this,
headers: { "X-Requested-With": null },
- success: this._processOSM
+ success: parse(callback)
});
- },
+ }
- _processOSM:function(dom) {
- var nodelist = [];
- for (var i in dom.childNodes[0].childNodes) {
- var obj = dom.childNodes[0].childNodes[i];
- switch(obj.nodeName) {
-
- case "node":
- var node = new iD.Node(this,
+ function parse(callback) {
+ return function(dom) {
+ var nodelist = _.compact(_.map(dom.childNodes[0].childNodes, function(obj) {
+ if (obj.nodeName === 'node') {
+ var node = new iD.Node(connection,
+getAttribute(obj, 'id'),
+getAttribute(obj, 'lat'),
+getAttribute(obj, 'lon'),
getTags(obj));
- this._assign(node);
- nodelist.push(node);
- break;
-
- case "way":
- var way = new iD.Way(this,
- getAttribute(obj,'id'),
- getNodes(obj, this),
+ assign(node);
+ return node;
+ } else if (obj.nodeName === 'way') {
+ var way = new iD.Way(connection,
+ getAttribute(obj, 'id'),
+ getNodes(obj, connection),
getTags(obj));
- this._assign(way);
- break;
-
- case "relation":
- var relation = new iD.Relation(this,
- getAttribute(obj,'id'),
- getMembers(obj, this),
+ assign(way);
+ } else if (obj.nodeName === 'relation') {
+ var relation = new iD.Relation(connection,
+ getAttribute(obj, 'id'),
+ getMembers(obj, connection),
getTags(obj));
- this._assign(relation);
- break;
- }
- }
- this.updatePOIs(nodelist);
- this.refreshMaps();
- if (this.callback) { this.callback(); }
+ assign(relation);
+ }
+ }));
+ updatePOIs(nodelist);
+ if (callback) { callback(nodelist); }
- // Private functions to parse DOM created from XML file
- function filterNodeName(n) {
- return function(item) {
- return item.nodeName === n;
- };
- }
+ // Private functions to parse DOM created from XML file
+ function filterNodeName(n) {
+ return function(item) { return item.nodeName === n; };
+ }
- function getAttribute(obj, name) {
- return _.find(obj.attributes, filterNodeName(name)).nodeValue;
- }
+ function getAttribute(obj, name) {
+ return _.find(obj.attributes, filterNodeName(name)).nodeValue;
+ }
- function getTags(obj) {
- return _(obj.childNodes).chain()
+ function getTags(obj) {
+ return _(obj.childNodes).chain()
.filter(filterNodeName('tag'))
.map(function(item) {
return [getAttribute(item,'k'), getAttribute(item,'v')];
}).object().value();
- }
+ }
- function getNodes(obj,conn) {
- return _(obj.childNodes).chain()
+ function getNodes(obj,conn) {
+ return _(obj.childNodes).chain()
.filter(filterNodeName('nd'))
.map(function(item) {
- return conn.nodes[getAttribute(item,'ref')];
+ return nodes[getAttribute(item,'ref')];
}).value();
- }
+ }
- function getMembers(obj,conn) {
- return _(obj.childNodes).chain()
- .filter(filterNodeName('member'))
- .map(function(item) {
- var id = getAttribute(item,'ref'),
+ function getMembers(obj,conn) {
+ return _(obj.childNodes).chain()
+ .filter(filterNodeName('member'))
+ .map(function(item) {
+ var id = getAttribute(item,'ref'),
type = getAttribute(item,'type'),
role = getAttribute(item,'role');
- var obj = conn._getOrCreate(id,type);
- return new iD.RelationMember(obj,role);
- }).value();
- }
- }
+ var obj = getOrCreate(id,type);
+ return new iD.RelationMember(obj,role);
+ }).value();
+ }
+ };
+ }
+
+ connection.nodes = nodes;
+ connection.ways = ways;
+ connection.relations = relations;
+ connection.loadFromAPI = loadFromAPI;
+ connection.loadFromURL = loadFromURL;
+ connection.getObjectsByBbox = getObjectsByBbox;
+
+ return connection;
};
diff --git a/js/iD/renderer/Map.js b/js/iD/renderer/Map.js
index 2ad184980..0245b175c 100755
--- a/js/iD/renderer/Map.js
+++ b/js/iD/renderer/Map.js
@@ -224,10 +224,10 @@ declare("iD.renderer.Map", null, {
download:function() {
// summary: Ask the connection to download data for the current viewport.
- this.conn.loadFromAPI(this.extent);
+ this.conn.loadFromAPI(this.extent, _.bind(this.updateUIs, this));
},
- updateUIs:function(redraw,remove) {
+ updateUIs: function(redraw, remove) {
// summary: Draw/refresh all EntityUIs within the bbox, and remove any others.
// redraw: Boolean Should we redraw any UIs that are already present?
// remove: Boolean Should we delete any UIs that are no longer in the bbox?
@@ -243,7 +243,7 @@ declare("iD.renderer.Map", null, {
else if (redraw) { m.wayuis[way.id].recalculate(); m.wayuis[way.id].redraw(); }
});
- if (remove) {
+ if (remove !== false) {
array.forEach(o.waysOutside, function(way) {
if (m.wayuis[way.id]) { // && !m.wayuis[way.id].purgable
if (redraw) { m.wayuis[way.id].recalculate(); m.wayuis[way.id].redraw(); }
@@ -257,7 +257,7 @@ declare("iD.renderer.Map", null, {
else if (redraw) { m.nodeuis[poi.id].redraw(); }
});
- if (remove) {
+ if (remove !== false) {
array.forEach(o.poisOutside, function(poi) {
if (m.nodeuis[poi.id]) { // && !m.nodeuis[poi.id].purgable
if (redraw) { m.nodeuis[poi.id].redraw(); }