Move graph out of connection, move more logic to Util, update tests.

This commit is contained in:
Tom MacWright
2012-10-31 18:17:54 -04:00
parent 303128df1c
commit 7154b68b71
7 changed files with 152 additions and 205 deletions

View File

@@ -64,6 +64,7 @@
</div>
<script>
var connection = new iD.Connection("http://www.overpass-api.de/api/xapi?");
connection.graph(new iD.Graph());
var m = d3.select('#map');
// Initialise map
@@ -83,26 +84,13 @@
// ----------------------------------------------------
// Mode button handlers
d3.select('#add-place').on('click', function() {
map.controller.setState(new iD.controller.shape.NoSelection('node'));
});
d3.select('#add-road').on('click', function() {
// map.controller.setState(new iD.controller.shape.DrawWay('way'));
console.log(iD.DrawWay.enter());
console.log(iD.DrawWay.enter(map));
});
d3.select('#add-area').on('click', function() {
map.controller.setState(new iD.controller.shape.NoSelection());
});
function grid(resp) {
map.setCentre({
lon: resp.results[0][0].lon,
lat: resp.results[0][0].lat
});
}
d3.select('#geocode-form').on('submit', function() {
d3.event.preventDefault();
var val = d3.select('#geocode-location').node().value;
@@ -110,29 +98,6 @@
scr.src = 'http://api.tiles.mapbox.com/v3/mapbox/geocode/' +
encodeURIComponent(val) + '.jsonp?callback=grid';
});
/*
$('#undo').click(function() {
map.controller.undoStack.undo();
map.updateUIs(true, true);
});
$('#redo').click(function() {
controller.undoStack.redo();
map.updateUIs(true, true);
});
// ----------------------------------------------------
// Map control handlers
$('#zoomIn').click(function() {
map.zoomIn();
});
$('#zoomOut').click(function() {
map.zoomOut();
});
*/
</script>
</body>
</html>

View File

@@ -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;
};

View File

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

View File

@@ -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;

View File

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

View File

@@ -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());

View File

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