Use simple objects for data, simplify graph

This commit is contained in:
Tom MacWright
2012-11-05 10:48:58 -05:00
parent efdccfd00b
commit c8b5e238b3
8 changed files with 116 additions and 81 deletions

View File

@@ -50,6 +50,7 @@
<script type='text/javascript' src='js/iD/format/GeoJSON.js'></script>
<script type='text/javascript' src='js/iD/format/XML.js'></script>
<script type='text/javascript' src='js/iD/graph/pdata.js'></script>
<script type='text/javascript' src='js/iD/graph/Node.js'></script>
<script type='text/javascript' src='js/iD/graph/Relation.js'></script>
<script type='text/javascript' src='js/iD/graph/Way.js'></script>

View File

@@ -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;
}
// <tag k="highway" v="unclassified"/>
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;
}
// <member type="node" ref="364933006" role=""/>
function getMembers(obj) {
var members = [],
elems = obj.getElementsByTagName('member');
@@ -54,38 +50,45 @@ iD.Connection = function(graph) {
return members;
}
// <node id="1831881213"
// version="1"
// changeset="12370172"
// lat="54.0900666"
// lon="12.2539381"
// user="lafkor"
// uid="75625"
// visible="true"
// timestamp="2012-07-20T09:43:19Z">
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;

View File

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

View File

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

View File

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

View File

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

46
js/iD/graph/pdata.js Normal file
View File

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

View File

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