Refactor style, abstract id references

This is the first commit that really goes whole hog in terms of
creating an object graph. It's quite close, though it could be faster
in a lot of ways.
This commit is contained in:
Tom MacWright
2012-10-31 19:05:43 -04:00
parent 7154b68b71
commit a1b1df504b
12 changed files with 156 additions and 191 deletions
+3 -3
View File
@@ -15,6 +15,7 @@
<script type="text/javascript" src="js/iD/id.js"></script>
<script type="text/javascript" src="js/iD/actions/UndoStack.js"></script>
<script type="text/javascript" src="js/iD/Util.js"></script>
<script type="text/javascript" src="js/iD/renderer/style.js"></script>
<script type="text/javascript" src="js/iD/Taginfo.js"></script>
<script type="text/javascript" src="js/iD/controller/controller.js"></script>
@@ -36,8 +37,6 @@
<script type="text/javascript" src="js/iD/Entity.js"></script>
<script type="text/javascript" src="js/iD/Way.js"></script>
<script type="text/javascript" src="js/iD/Connection.js"></script>
<script type="text/javascript" src="js/iD/Controller.js"></script>
<script type="text/javascript" src="js/iD/controller/DrawWay.js"></script>
<script type="text/javascript" src="js/iD/Graph.js"></script>
<div id="map"></div>
@@ -64,7 +63,8 @@
</div>
<script>
var connection = new iD.Connection("http://www.overpass-api.de/api/xapi?");
connection.graph(new iD.Graph());
var graph = new iD.Graph();
connection.graph(graph);
var m = d3.select('#map');
// Initialise map
+60 -4
View File
@@ -31,20 +31,76 @@ iD.Connection = function() {
d3.xml(url, parse(callback));
}
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);
}
return nodes;
}
function getTags(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;
}
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 = {
id: id,
type: type,
role: role
};
members.push(o);
}
return members;
}
function objectData(obj) {
return {
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)
};
}
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];
graph.process(obj);
}
var ways = dom.childNodes[0].getElementsByTagName('way'),
relations = dom.childNodes[0].getElementsByTagName('relation'),
nodes = dom.childNodes[0].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]));
callback(null);
};
}
connection.graph = function(x) {
if (!arguments.length) return graph;
graph = x;
return connection;
};
-17
View File
@@ -1,17 +0,0 @@
iD.Controller = function() {
var controller = {},
state = null;
controller.undoStack = new iD.UndoStack();
controller.setState = function(newState) {
// summary: Enter a new ControllerState, firing exitState on the old one, and enterState on the new one.
if (newState === state) { return; }
if (state) state.exitState();
newState.controller = controller;
state = newState;
newState.enterState();
};
return controller;
};
+2 -6
View File
@@ -9,21 +9,18 @@ iD.Entity = function () {
};
iD.Entity.prototype = {
// Parent-handling
// a relation or way which contains this entity
addParent: function (x) {
// summary: Record a parent (a relation or way which contains this entity).
this.parents[x._id] = x;
},
removeParent: function (x) {
// summary: Remove a parent (e.g. when node removed from a way).
delete this.parents[x._id];
},
hasParent: function (x) {
// summary: Does this entity have the specified parent (e.g. is it in a certain relation)?
// summary: Does this entity have the specified parent (e.g. is it in a certain relation)?
return !!this.parents[x._id];
},
parentObjects: function () {
// summary: List of all parents of this entity.
var objects = [];
for (var i in this.parents) {
objects.push(this.parents[i]);
@@ -31,7 +28,6 @@ iD.Entity.prototype = {
return objects;
},
hasParentWays: function () {
// summary: Does this entity have any parents which are ways?
var parentObjects = this.parentObjects();
for (var i = 0; i < parentObjects.length; i++) {
if (parentObjects[i].type === 'way') return true;
+19 -79
View File
@@ -4,87 +4,27 @@ iD.Graph = function() {
};
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;
insert: function(o) {
var obj;
if (o.type === 'node') {
obj = new iD.Node(
o.id,
o.lat,
o.lon,
o.tags);
} else if (o.type === 'way') {
obj = new iD.Way(
o.id,
o.nodes,
o.tags);
} else if (o.type === 'relation') {
obj = new iD.Relation(
o.id,
o.members,
o.tags);
}
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) {
if (obj && (!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);
}
}
};
-4
View File
@@ -1,18 +1,14 @@
// [Node](http://wiki.openstreetmap.org/wiki/Node)
iD.Node = function(id, lat, lon, tags, loaded) {
// summary: An OSM node.
this.type = 'node';
this.id = id;
this._id = iD.Util.id();
this.entity = new iD.Entity();
this.lat = lat;
this.lon = lon;
// TODO: keep or trash this custom
this[0] = lon;
this[1] = lat;
this.tags = tags;
this.loaded = (loaded === undefined) ? true : loaded;
this.modified = this.id < 0;
};
iD.Node.prototype = {
-3
View File
@@ -9,9 +9,6 @@ iD.Relation = function(id, members, tags, loaded) {
this.tags = tags;
this.modified = this.id < 0;
this.loaded = (loaded === undefined) ? true : loaded;
for (var i = 0; i < members.length; i++) {
members[i].entity.entity.addParent(this);
}
};
iD.Relation.prototype = {
-25
View File
@@ -30,28 +30,3 @@ iD.Util.friendlyName = function(entity) {
return n.length === 0 ? 'unknown' : n.join('; ');
};
iD.Util.TAG_CLASSES = {
'highway': true,
'railway': true,
'motorway': true,
'amenity': true,
'landuse': true,
'building': true,
'bridge': true
};
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(' ');
};
};
+5 -11
View File
@@ -7,29 +7,22 @@
// 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, nodes, tags, loaded) {
// summary: An OSM way.
nodes = nodes || [];
tags = tags || {};
this.type = 'way';
this.id = id;
this._id = iD.Util.id();
this.deleted = false;
this.entity = new iD.Entity();
this.tags = tags || {};
this.tags = tags;
this.nodes = nodes;
this.loaded = (loaded === undefined) ? true : loaded;
this.modified = this.id < 0;
this.nodes = [];
this.extent = {};
if (nodes) {
for (var i = 0; i < nodes.length; i++) {
this.addNode(nodes[i]);
}
}
};
iD.Way.prototype = {
addNode: function(node) {
node.entity.addParent(this);
this.nodes.push(node);
this._bounds = null;
return this;
@@ -63,6 +56,7 @@ iD.Way.prototype = {
// ---------------------
// Bounding-box handling
intersects: function(extent) {
return true;
// No-node ways are inside of nothing.
if (!this.nodes.length) return false;
var bounds = this.bounds();
+9 -39
View File
@@ -9,7 +9,6 @@ iD.Map = function(obj) {
selection = [],
width = obj.width || 800,
height = obj.height || 400,
controller = iD.Controller(),
projection = d3.geo.mercator()
.scale(512).translate([512, 512]),
connection = obj.connection,
@@ -114,37 +113,9 @@ iD.Map = function(obj) {
}
function nodeline(d) {
return linegen(d.nodes);
}
var highway_stack = [
'motorway',
'motorway_link',
'trunk',
'trunk_link',
'primary',
'primary_link',
'secondary',
'tertiary',
'unclassified',
'residential',
'service',
'footway'
];
function waystack(a, b) {
if (!a || !b) return 0;
if (a.tags.layer !== undefined && b.tags.layer !== undefined) {
return a.tags.layer - b.tags.layer;
}
if (a.tags.bridge) return 1;
if (b.tags.bridge) return -1;
var as = 0, bs = 0;
if (a.tags.highway && b.tags.highway) {
as -= highway_stack.indexOf(a.tags.highway);
bs -= highway_stack.indexOf(b.tags.highway);
}
return as - bs;
return linegen(d.nodes.map(function(n) {
return connection.graph().index[n];
}));
}
// This is an unfortunate hack that should be improved.
@@ -158,18 +129,18 @@ iD.Map = function(obj) {
};
}
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'));
var class_stroke = augmentSelect(iD.Style.styleClasses('stroke')),
class_fill = augmentSelect(iD.Style.styleClasses('stroke')),
class_area = augmentSelect(iD.Style.styleClasses('area')),
class_marker = augmentSelect(iD.Style.styleClasses('marker')),
class_casing = augmentSelect(iD.Style.styleClasses('casing'));
function drawVector() {
var all = connection.intersects(extent());
var ways = all.filter(function(a) {
return a.type === 'way' && !a.isClosed();
}).sort(waystack),
}).sort(iD.Style.waystack),
areas = all.filter(function(a) {
return a.type === 'way' && a.isClosed();
}),
@@ -295,7 +266,6 @@ iD.Map = function(obj) {
map.zoomOut = zoomOut;
map.connection = connection;
map.controller = controller;
map.projection = projection;
redraw();
+57
View File
@@ -0,0 +1,57 @@
iD.Style = {};
iD.Style.highway_stack = [
'motorway',
'motorway_link',
'trunk',
'trunk_link',
'primary',
'primary_link',
'secondary',
'tertiary',
'unclassified',
'residential',
'service',
'footway'
];
iD.Style.waystack = function(a, b) {
if (!a || !b) return 0;
if (a.tags.layer !== undefined && b.tags.layer !== undefined) {
return a.tags.layer - b.tags.layer;
}
if (a.tags.bridge) return 1;
if (b.tags.bridge) return -1;
var as = 0, bs = 0;
if (a.tags.highway && b.tags.highway) {
as -= iD.Style.highway_stack.indexOf(a.tags.highway);
bs -= iD.Style.highway_stack.indexOf(b.tags.highway);
}
return as - bs;
};
iD.Style.TAG_CLASSES = {
'highway': true,
'railway': true,
'motorway': true,
'amenity': true,
'landuse': true,
'building': true,
'bridge': true
};
iD.Style.styleClasses = function(pre) {
return function(d) {
var tags = d.tags;
var c = [pre];
function clean(x) {
return iD.Style.TAG_CLASSES[x];
}
for (var k in tags) {
if (!clean(k)) continue;
c.push(k + '-' + tags[k]);
c.push(k);
}
return c.join(' ');
};
};
+1
View File
@@ -25,6 +25,7 @@
<script type="text/javascript" src="../js/iD/Controller.js"></script>
<script type="text/javascript" src="../js/iD/renderer/markers.js"></script>
<script type="text/javascript" src="../js/iD/renderer/Map.js"></script>
<script type="text/javascript" src="../js/iD/renderer/Style.js"></script>
<script type="text/javascript" src="../js/iD/renderer/tiles.js"></script>
<script type="text/javascript" src="../js/iD/Graph.js"></script>