mirror of
https://github.com/FoggedLens/iD.git
synced 2026-02-12 16:52:50 +00:00
Start traversing the graph, other changes
Adds GeoJSON viewing to features, unifies the way that features refer to their children.
This commit is contained in:
34
NOTES.md
34
NOTES.md
@@ -11,17 +11,35 @@ To be clear, this data model is something like
|
||||
\- ways -> nodes
|
||||
\- nodes
|
||||
|
||||
## Actions
|
||||
|
||||
Actions are operations on OSM data like adding nodes, moving ways,
|
||||
and so on. They are initiated by controller states, like
|
||||
`iD.controller.ControllerState` initiates a `CreatePOIAction` and
|
||||
adds it to the undo stack.
|
||||
## Performance
|
||||
|
||||
## Entities
|
||||
Main performance concerns of iD:
|
||||
|
||||
`iD.Entity` is the door from pure objects like `iD.Node` into a hierarchy
|
||||
of objects - it provides handling of parents, children, and so on.
|
||||
### Panning & zooming performance of the map
|
||||
|
||||
SVG redraws are costly, especially when they require all features to
|
||||
be reprojected.
|
||||
|
||||
Approaches:
|
||||
|
||||
* Using CSS transforms for intermediate map states, and then redrawing when
|
||||
map movement stops
|
||||
* "In-between" projecting features to make reprojection cheaper
|
||||
|
||||
### Memory overhead of objects
|
||||
|
||||
Many things will be stored by iD. With the graph structure in place, we'll
|
||||
be storing much more.
|
||||
|
||||
## Connection, Graph, Map
|
||||
|
||||
The Map is a display and manipulation element. It should have minimal particulars
|
||||
of how exactly to store or retrieve data. It gets data from Connection and
|
||||
asks for it from Graph.
|
||||
|
||||
Graph stores all of the objects and all of the versions of those objects.
|
||||
Connection requests objects over HTTP, parses them, and provides them to Graph.
|
||||
|
||||
## loaded
|
||||
|
||||
|
||||
@@ -175,6 +175,7 @@ table td {
|
||||
|
||||
.inspector-wrap a.permalink {
|
||||
text-decoration:none;
|
||||
margin-right:2em;
|
||||
font: normal 11px/20px 'Helvetica'
|
||||
}
|
||||
|
||||
|
||||
12
index.html
12
index.html
@@ -63,16 +63,10 @@
|
||||
Imagery <a href="http://opengeodata.org/microsoft-imagery-details">© 2012</a> Bing, GeoEye, Getmapping, Intermap, Microsoft.</p>
|
||||
</div>
|
||||
<script>
|
||||
var connection = new iD.Connection(new iD.Graph());
|
||||
|
||||
var m = d3.select('#map');
|
||||
// Initialise map
|
||||
var map = iD.Map({
|
||||
selector: "#map",
|
||||
connection: connection,
|
||||
width: m.node().offsetWidth,
|
||||
height: m.node().offsetHeight
|
||||
});
|
||||
var m = d3.select('#map');
|
||||
var map = iD.Map('#map');
|
||||
|
||||
map.setZoom(18)
|
||||
.setCentre({ lat: 40.7965621, lon: -74.4773184 });
|
||||
iD.Hash().map(map);
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
iD.Connection = function(graph) {
|
||||
// summary: The data store, including methods to fetch data from (and, eventually, save data to)
|
||||
// an OSM API server.
|
||||
var nextNode = -1,
|
||||
nextWay = -1,
|
||||
nextRelation = -1,
|
||||
@@ -49,22 +47,15 @@ iD.Connection = function(graph) {
|
||||
}
|
||||
|
||||
function getMembers(obj) {
|
||||
var members = [];
|
||||
var elems = obj.getElementsByTagName('member');
|
||||
var members = [],
|
||||
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);
|
||||
members.push({
|
||||
id: +elems[i].attributes.ref.nodeValue,
|
||||
type: elems[i].attributes.type.nodeValue,
|
||||
role: elems[i].attributes.role.nodeValue
|
||||
});
|
||||
}
|
||||
|
||||
return members;
|
||||
@@ -87,9 +78,10 @@ iD.Connection = function(graph) {
|
||||
if (!dom.childNodes) {
|
||||
return callback(new Error('Bad request'));
|
||||
}
|
||||
var ways = dom.childNodes[0].getElementsByTagName('way'),
|
||||
relations = dom.childNodes[0].getElementsByTagName('relation'),
|
||||
nodes = dom.childNodes[0].getElementsByTagName('node');
|
||||
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]));
|
||||
|
||||
@@ -21,7 +21,7 @@ iD.GeoJSON = {
|
||||
properties: entity.tags,
|
||||
geometry: {
|
||||
'type': 'LineString',
|
||||
'coordinates': _.map(entity.nodes, function(node) {
|
||||
'coordinates': _.map(entity.children, function(node) {
|
||||
return [node.lon, node.lat];
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
iD.Graph = function() {
|
||||
this.index = {};
|
||||
this.nodes = [];
|
||||
};
|
||||
|
||||
iD.Graph.prototype = {
|
||||
@@ -26,5 +25,15 @@ iD.Graph.prototype = {
|
||||
if (obj && (!this.index[obj.id] || !this.index[obj.id].loaded)) {
|
||||
this.index[obj.id] = obj;
|
||||
}
|
||||
},
|
||||
|
||||
fetch: function(id) {
|
||||
var obj = _.clone(this.index[id]);
|
||||
var children = [];
|
||||
for (var i = 0; i < obj.children.length; i++) {
|
||||
children.push(this.index[obj.children[i]]);
|
||||
}
|
||||
obj.children = children;
|
||||
return obj;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -4,6 +4,7 @@ iD.Node = function(id, lat, lon, tags, loaded) {
|
||||
this.lat = lat;
|
||||
this.lon = lon;
|
||||
this.tags = tags || {};
|
||||
this.children = [];
|
||||
this.loaded = (loaded === undefined) ? true : loaded;
|
||||
};
|
||||
|
||||
@@ -20,4 +21,4 @@ iD.Node.prototype = {
|
||||
|
||||
};
|
||||
|
||||
iD.Util.extend(iD.Node, iD.Entity);
|
||||
// iD.Util.extend(iD.Node, iD.Entity);
|
||||
|
||||
@@ -12,9 +12,4 @@ iD.Relation.prototype = {
|
||||
intersects: function() { return true; }
|
||||
};
|
||||
|
||||
iD.Util.extend(iD.Relation, iD.Entity);
|
||||
|
||||
iD.RelationMember = function(entity, role) {
|
||||
this.entity = entity;
|
||||
this.role = role;
|
||||
};
|
||||
// iD.Util.extend(iD.Relation, iD.Entity);
|
||||
|
||||
@@ -38,3 +38,11 @@ iD.Util.extend = function(child, parent) {
|
||||
}
|
||||
return child;
|
||||
};
|
||||
|
||||
iD.Util.codeWindow = function(content) {
|
||||
top.win = window.open('','contentWindow',
|
||||
'width=350,height=350,menubar=0' +
|
||||
',toolbar=1,status=0,scrollbars=1,resizable=1');
|
||||
top.win.document.writeln('<pre>' + content + '</pre>');
|
||||
top.win.document.close();
|
||||
};
|
||||
|
||||
@@ -65,4 +65,4 @@ iD.Way.prototype = {
|
||||
}
|
||||
};
|
||||
|
||||
iD.Util.extend(iD.Way, iD.Entity);
|
||||
// iD.Util.extend(iD.Way, iD.Entity);
|
||||
|
||||
@@ -4,18 +4,30 @@
|
||||
// ----------------------------------------------------------------------
|
||||
// Connection base class
|
||||
|
||||
iD.Map = function(obj) {
|
||||
var map = {},
|
||||
iD.Map = function(parentSelector) {
|
||||
var graph = new iD.Graph(),
|
||||
connection = new iD.Connection(graph);
|
||||
map = {},
|
||||
parent = d3.selectAll(parentSelector),
|
||||
selection = [],
|
||||
width = obj.width || 800,
|
||||
height = obj.height || 400,
|
||||
width = parent.node().offsetWidth,
|
||||
height = parent.node().offsetHeight,
|
||||
projection = d3.geo.mercator()
|
||||
.scale(512).translate([512, 512]),
|
||||
connection = obj.connection;
|
||||
dispatch = d3.dispatch('move');
|
||||
|
||||
var event = d3.dispatch('move');
|
||||
var zoombehavior = d3.behavior.zoom()
|
||||
.translate(projection.translate())
|
||||
.scale(projection.scale())
|
||||
.scaleExtent([256, 134217728]),
|
||||
dragbehavior = d3.behavior.drag()
|
||||
.origin(function(d) {
|
||||
var p = projection(d);
|
||||
return { x: p[0], y: p[1] };
|
||||
})
|
||||
.on("drag", dragmove);
|
||||
|
||||
var inspector = iD.Inspector();
|
||||
var inspector = iD.Inspector(graph);
|
||||
|
||||
var linegen = d3.svg.line()
|
||||
.x(function(d) {
|
||||
@@ -27,33 +39,20 @@ iD.Map = function(obj) {
|
||||
return projection([node.lon, node.lat])[1];
|
||||
});
|
||||
|
||||
var zoombehavior = d3.behavior.zoom()
|
||||
.translate(projection.translate())
|
||||
.scale(projection.scale())
|
||||
.scaleExtent([256, 134217728]);
|
||||
|
||||
var dragbehavior = d3.behavior.drag()
|
||||
.origin(function(d) {
|
||||
var p = projection(d);
|
||||
return { x: p[0], y: p[1] };
|
||||
})
|
||||
.on("drag", dragmove);
|
||||
|
||||
// http://bl.ocks.org/1557377
|
||||
function dragmove(d) {
|
||||
d3.select(this).attr('transform', function() {
|
||||
return 'translate(' + d3.event.x + ',' + d3.event.y + ')';
|
||||
});
|
||||
var ll = projection.invert([d3.event.x, d3.event.y]);
|
||||
d[0] = ll[0];
|
||||
d[1] = ll[1];
|
||||
d.lon = ll[0];
|
||||
d.lat = ll[1];
|
||||
drawVector();
|
||||
}
|
||||
|
||||
zoombehavior.on('zoom', redraw);
|
||||
|
||||
var surface = d3.selectAll(obj.selector)
|
||||
.append('svg')
|
||||
var surface = parent.append('svg')
|
||||
.call(zoombehavior);
|
||||
|
||||
var defs = surface.append('defs');
|
||||
@@ -214,15 +213,6 @@ iD.Map = function(obj) {
|
||||
.attr('transform', function(d) {
|
||||
return 'translate(' + projection(d) + ')';
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function zoomIn() {
|
||||
return setZoom(getZoom() + 1);
|
||||
}
|
||||
|
||||
function zoomOut() {
|
||||
return setZoom(getZoom() - 1);
|
||||
}
|
||||
|
||||
function getZoom(zoom) {
|
||||
@@ -239,13 +229,16 @@ iD.Map = function(obj) {
|
||||
return map;
|
||||
}
|
||||
|
||||
function zoomIn() { return setZoom(getZoom() + 1); }
|
||||
function zoomOut() { return setZoom(getZoom() - 1); }
|
||||
|
||||
function redraw() {
|
||||
if (d3.event) {
|
||||
projection
|
||||
.translate(d3.event.translate)
|
||||
.scale(d3.event.scale);
|
||||
}
|
||||
event.move(map);
|
||||
dispatch.move(map);
|
||||
tileclient.redraw();
|
||||
drawVector();
|
||||
download();
|
||||
@@ -275,10 +268,12 @@ iD.Map = function(obj) {
|
||||
|
||||
map.download = download;
|
||||
map.extent = extent;
|
||||
|
||||
map.setCentre = setCentre;
|
||||
map.setCenter = setCentre;
|
||||
|
||||
map.getCentre = getCenter;
|
||||
map.getCenter = getCenter;
|
||||
|
||||
map.getZoom = getZoom;
|
||||
map.setZoom = setZoom;
|
||||
map.zoomIn = zoomIn;
|
||||
@@ -290,5 +285,5 @@ iD.Map = function(obj) {
|
||||
|
||||
setSize(width, height);
|
||||
redraw();
|
||||
return d3.rebind(map, event, 'on');
|
||||
return d3.rebind(map, dispatch, 'on');
|
||||
};
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
// iD/ui/DragAndDrop.js
|
||||
|
||||
/*
|
||||
Singleton-like class for POI drag and drop.
|
||||
Could potentially be a ControllerState.
|
||||
*/
|
||||
|
||||
|
||||
define(['dojo/_base/declare','dojo/_base/lang','dojo/_base/xhr','dojo/dom','dojo/dom-geometry','dojo/dnd/Target'], function(declare,lang,xhr,dom,domGeom){
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// DragAndDrop class
|
||||
|
||||
declare("iD.ui.DragAndDrop", null, {
|
||||
|
||||
mapdiv:null,
|
||||
map:null,
|
||||
divname:"",
|
||||
grid:null,
|
||||
dragmove:null,
|
||||
dragevent:null,
|
||||
|
||||
ICONPATH: 'icons/',
|
||||
ITEMSPERROW: 5,
|
||||
|
||||
constructor:function(divname, map, gridname) {
|
||||
// summary: Singleton-like class for POI drag and drop. Loads a config file (draganddrop.json)
|
||||
// on initialisation, then populates the drop-down palette with it.
|
||||
this.divname=divname;
|
||||
dom.byId(divname).ondragover = lang.hitch(this,this.update);
|
||||
dom.byId(divname).ondrop = function(e) { e.preventDefault(); }; // required by Firefox
|
||||
this.map=map;
|
||||
this.grid=dom.byId(gridname);
|
||||
|
||||
// Load drag and drop config file
|
||||
dojo.xhrGet({
|
||||
url: "draganddrop.json",
|
||||
handleAs: "json",
|
||||
load: lang.hitch(this, function(obj) { this.drawGrid(obj); } ),
|
||||
error: function(err) {
|
||||
// console.log("couldn't load");
|
||||
}
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
drawGrid:function(obj) {
|
||||
// summary: Draw the grid of icons based on the JSON-derived object loaded.
|
||||
var row;
|
||||
for (var i=0; i<obj.length; i++) {
|
||||
var item=obj[i];
|
||||
if (!row || row.cells.length==this.ITEMSPERROW) {
|
||||
row=document.createElement('tr');
|
||||
this.grid.appendChild(row);
|
||||
}
|
||||
var cell=document.createElement('td');
|
||||
var img=document.createElement('img');
|
||||
img.setAttribute('src',this.ICONPATH+item.icon)
|
||||
img.setAttribute('alt',item.tags);
|
||||
img.setAttribute('draggable',true);
|
||||
img.ondragend=lang.hitch(this,this.end);
|
||||
img.style.float='left';
|
||||
cell.appendChild(img);
|
||||
cell.appendChild(document.createTextNode(item.name));
|
||||
row.appendChild(cell);
|
||||
}
|
||||
},
|
||||
|
||||
update:function(event) {
|
||||
// summary: Handler for dragging over the map; tells the browser that it's droppable here.
|
||||
this.dragevent=event;
|
||||
event.preventDefault();
|
||||
},
|
||||
|
||||
end:function(event) {
|
||||
// summary: Create a new POI from the dropped icon.
|
||||
var lon=this.map.coord2lon(this.map.mouseX(this.dragevent));
|
||||
var lat=this.map.coord2lat(this.map.mouseY(this.dragevent));
|
||||
var tags=this._parseKeyValues(event.target.getAttribute('alt'));
|
||||
|
||||
var action=new iD.actions.CreatePOIAction(this.map.conn,tags,lat,lon);
|
||||
this.map.controller.undoStack.addAction(action);
|
||||
var node=action.getNode();
|
||||
this.map.createUI(node);
|
||||
|
||||
dijit.byId('addPOI').closeDropDown();
|
||||
this.map.controller.setState(new iD.controller.edit.SelectedPOINode(node));
|
||||
},
|
||||
|
||||
_parseKeyValues:function(string) {
|
||||
// summary: Turn a string of format k1=v1;k2=v2;... into a hash.
|
||||
var pairs=string.split(';');
|
||||
var tags={};
|
||||
for (var i in pairs) {
|
||||
var kv=pairs[i].split('=');
|
||||
tags[kv[0]]=kv[1];
|
||||
}
|
||||
return tags;
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// End of module
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
iD.Inspector = function(selection) {
|
||||
iD.Inspector = function(graph) {
|
||||
var event = d3.dispatch('change', 'update');
|
||||
|
||||
function inspector(selection) {
|
||||
@@ -20,6 +20,16 @@ iD.Inspector = function(selection) {
|
||||
d.type + '/' + d.id)
|
||||
.text('#' + d.id);
|
||||
|
||||
head.append('a')
|
||||
.attr('class', 'permalink')
|
||||
.attr('href', '#')
|
||||
.text(d.id + '.geojson')
|
||||
.on('click', function() {
|
||||
iD.Util.codeWindow(
|
||||
JSON.stringify(iD.GeoJSON.mapping(graph.fetch(d.id)), null, 2));
|
||||
d3.event.stopPropagation();
|
||||
});
|
||||
|
||||
var table = d3.select(this)
|
||||
.append('table')
|
||||
.attr('class', 'inspector');
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
describe('Relation Member', function() {
|
||||
describe('Relation', function() {
|
||||
var rm;
|
||||
|
||||
beforeEach(function() {
|
||||
rm = new iD.RelationMember();
|
||||
rm = new iD.Relation();
|
||||
});
|
||||
|
||||
it('is instantiated', function() {
|
||||
|
||||
Reference in New Issue
Block a user