diff --git a/index.html b/index.html
index fd25cafe4..0e61eb136 100644
--- a/index.html
+++ b/index.html
@@ -30,12 +30,21 @@
+
+
+
+
+
+
+
+
+
@@ -58,7 +67,7 @@
-
+
diff --git a/js/id/renderer/map.js b/js/id/renderer/map.js
index 74b021f90..adad35730 100644
--- a/js/id/renderer/map.js
+++ b/js/id/renderer/map.js
@@ -15,7 +15,7 @@ iD.Map = function() {
background = iD.Background()
.projection(projection),
transformProp = iD.util.prefixCSSProperty('Transform'),
- supersurface, surface, defs, tilegroup, r, g, alength;
+ supersurface, surface, defs, tilegroup, r, g;
function map() {
tilegroup = this.append('div')
@@ -48,9 +48,6 @@ iD.Map = function() {
return (mem[i] = r.append('g').attr('class', 'layer-g layer-' + i)) && mem;
}, {});
- var arrow = surface.append('text').text('►----');
- alength = arrow.node().getComputedTextLength();
- arrow.remove();
map.size(this.size());
map.surface = surface;
@@ -58,10 +55,6 @@ iD.Map = function() {
}
function pxCenter() { return [dimensions[0] / 2, dimensions[1] / 2]; }
- function getline(d) { return d._line; }
- function nodeline(d) {
- return 'M' + _.pluck(d.nodes, 'loc').map(projection).map(iD.util.geo.roundCoords).join('L');
- }
function drawVector(difference) {
if (surface.style(transformProp) != 'none') return;
@@ -94,101 +87,12 @@ iD.Map = function() {
if (all.length > 10000) return editOff();
else editOn();
- for (var i = 0; i < all.length; i++) {
- var entity = all[i];
- switch (entity.geometry()) {
- case 'line':
- entity._line = nodeline(entity);
- ways.push(entity);
- lines.push(entity);
- break;
-
- case 'area':
- entity._line = nodeline(entity);
- ways.push(entity);
- areas.push(entity);
- break;
-
- case 'point':
- points.push(entity);
- break;
-
- case 'vertex':
- vertices.push(entity);
- break;
- }
- }
-
- var parentStructure = graph.parentStructure(ways);
- var wayAccuracyHandles = [];
- for (i = 0; i < ways.length; i++) {
- accuracyHandles(ways[i], wayAccuracyHandles);
- }
-
- drawVertices(vertices, parentStructure, filter);
- drawAccuracyHandles(wayAccuracyHandles, filter);
- drawCasings(lines, filter);
- drawFills(areas, filter);
- drawStrokes(lines, filter);
- drawPoints(points, filter);
- }
-
- // updates handles by reference
- function accuracyHandles(way, handles) {
- for (var i = 0; i < way.nodes.length - 1; i++) {
- if (iD.util.geo.dist(way.nodes[i].loc, way.nodes[i + 1].loc) > 0.0001) {
- handles.push({
- loc: iD.util.geo.interp(way.nodes[i].loc, way.nodes[i + 1].loc, 0.5),
- way: way.id,
- index: i + 1,
- accuracy: true
- });
- }
- }
- }
-
- function pointTransform(entity) {
- return 'translate(' + iD.util.geo.roundCoords(projection(entity.loc)) + ')';
- }
-
- function drawVertices(vertices, parentStructure, filter) {
- function shared(d) { return parentStructure[d.id] > 1; }
-
- var circles = g.hit.selectAll('g.vertex')
- .filter(filter)
- .data(vertices, iD.Entity.key);
-
- circles.exit().remove();
-
- var cg = circles.enter()
- .insert('g', ':first-child')
- .attr('class', 'node vertex');
-
- cg.append('circle')
- .attr('class', 'stroke')
- .attr('r', 6);
-
- cg.append('circle')
- .attr('class', 'fill')
- .attr('r', 4);
-
- circles.attr('transform', pointTransform)
- .classed('shared', shared);
-
- // Selecting the following implicitly
- // sets the data (vertix entity) on the elements
- circles.select('circle.fill');
- circles.select('circle.stroke');
- }
-
- function drawAccuracyHandles(waynodes, filter) {
- var handles = g.hit.selectAll('circle.accuracy-handle')
- .filter(filter)
- .data(waynodes, function (d) { return [d.way, d.index].join(","); });
- handles.exit().remove();
- handles.enter().append('circle')
- .attr({ r: 3, 'class': 'accuracy-handle' });
- handles.attr('transform', pointTransform);
+ surface
+ .call(iD.svg.Points(), graph, all, filter, projection)
+ .call(iD.svg.Vertices(), graph, all, filter, projection)
+ .call(iD.svg.Lines(), graph, all, filter, projection)
+ .call(iD.svg.Areas(), graph, all, filter, projection)
+ .call(iD.svg.Midpoints(), graph, all, filter, projection);
}
function editOff() {
@@ -197,94 +101,6 @@ iD.Map = function() {
function editOn() { }
- function drawLines(data, filter, group, fixedClasses) {
- var lines = group.selectAll('path')
- .filter(filter)
- .data(data, iD.Entity.key);
-
- lines.exit().remove();
-
- lines.enter().append('path')
- .attr('class', fixedClasses);
-
- lines
- .order()
- .attr('d', getline)
- .call(iD.Style.styleClasses());
-
- return lines;
- }
-
- function drawFills(areas, filter) {
- drawLines(areas, filter, g.fill, 'way area');
- }
-
- function drawCasings(ways, filter) {
- drawLines(ways, filter, g.casing, 'way line casing');
- }
-
- function drawPoints(points, filter) {
- var groups = g.hit.selectAll('g.point')
- .filter(filter)
- .data(points, iD.Entity.key);
-
- groups.exit().remove();
-
- var group = groups.enter().append('g')
- .attr('class', 'node point');
-
- group.append('circle')
- .attr('class', 'stroke')
- .attr({ r: 10 });
-
- group.append('circle')
- .attr('class', 'fill')
- .attr({ r: 10 });
-
- group.append('image')
- .attr({ width: 16, height: 16 })
- .attr('transform', 'translate(-8, -8)');
-
- groups.attr('transform', pointTransform);
-
- // Selecting the following implicitly
- // sets the data (point entity) on the element
- groups.select('image').attr('xlink:href', iD.Style.pointImage);
- }
-
- function drawStrokes(ways, filter) {
- var strokes = drawLines(ways, filter, g.stroke, 'way line stroke');
-
- // Determine the lengths of oneway paths
- var lengths = {},
- oneways = strokes.filter(function (d) { return d.isOneWay(); }).each(function(d) {
- lengths[d.id] = Math.floor(this.getTotalLength() / alength);
- }).data();
-
- var uses = defs.selectAll('path')
- .filter(filter)
- .data(oneways, iD.Entity.key);
- uses.exit().remove();
- uses.enter().append('path');
- uses
- .attr('id', function(d) { return 'shadow-' + d.id; })
- .attr('d', getline);
-
- var labels = g.text.selectAll('text')
- .filter(filter)
- .data(oneways, iD.Entity.key);
- labels.exit().remove();
- var tp = labels.enter()
- .append('text').attr({ 'class': 'oneway', dy: 4 })
- .append('textPath').attr('class', 'textpath');
- g.text.selectAll('.textpath')
- .filter(filter)
- .attr('xlink:href', function(d, i) { return '#shadow-' + d.id; })
- .text(function(d) {
- return (new Array(Math.floor(lengths[d.id]))).join('►----');
- });
- }
-
function connectionLoad(err, result) {
history.merge(result);
redraw(Object.keys(result.entities));
diff --git a/js/id/svg.js b/js/id/svg.js
new file mode 100644
index 000000000..fd648f5db
--- /dev/null
+++ b/js/id/svg.js
@@ -0,0 +1,14 @@
+iD.svg = {
+ RoundProjection: function (projection) {
+ return function (d) {
+ return iD.util.geo.roundCoords(projection(d));
+ }
+ },
+
+ PointTransform: function (projection) {
+ projection = iD.svg.RoundProjection(projection);
+ return function (entity) {
+ return 'translate(' + projection(entity.loc) + ')';
+ }
+ }
+};
diff --git a/js/id/svg/areas.js b/js/id/svg/areas.js
new file mode 100644
index 000000000..ec77d95c2
--- /dev/null
+++ b/js/id/svg/areas.js
@@ -0,0 +1,42 @@
+iD.svg.Areas = function() {
+ return function(surface, graph, entities, filter, projection) {
+ var areas = [];
+
+ for (var i = 0; i < entities.length; i++) {
+ var entity = entities[i];
+ if (entity.geometry() === 'area') {
+ areas.push(entity);
+ }
+ }
+
+ var lineStrings = {};
+
+ function lineString(entity) {
+ return lineStrings[entity.id] || (lineStrings[entity.id] =
+ 'M' + _.pluck(entity.nodes, 'loc').map(iD.svg.RoundProjection(projection)).join('L'));
+ }
+
+ function drawPaths(group, areas, filter, classes) {
+ var paths = group.selectAll('path')
+ .filter(filter)
+ .data(areas, iD.Entity.key);
+
+ paths.enter()
+ .append('path')
+ .attr('class', classes);
+
+ paths
+ .order()
+ .attr('d', lineString)
+ .call(iD.Style.styleClasses());
+
+ paths.exit()
+ .remove();
+
+ return paths;
+ }
+
+ var fill = surface.select('.layer-fill'),
+ paths = drawPaths(fill, areas, filter, 'way area');
+ };
+};
diff --git a/js/id/svg/lines.js b/js/id/svg/lines.js
new file mode 100644
index 000000000..2f0d6cece
--- /dev/null
+++ b/js/id/svg/lines.js
@@ -0,0 +1,90 @@
+iD.svg.Lines = function() {
+ return function(surface, graph, entities, filter, projection) {
+ var arrow = surface.append('text').text('►----'),
+ alength = arrow.node().getComputedTextLength();
+
+ arrow.remove();
+
+ var lines = [];
+
+ for (var i = 0; i < entities.length; i++) {
+ var entity = entities[i];
+ if (entity.geometry() === 'line') {
+ lines.push(entity);
+ }
+ }
+
+ var lineStrings = {};
+
+ function lineString(entity) {
+ return lineStrings[entity.id] || (lineStrings[entity.id] =
+ 'M' + _.pluck(entity.nodes, 'loc').map(iD.svg.RoundProjection(projection)).join('L'));
+ }
+
+ function drawPaths(group, lines, filter, classes) {
+ var paths = group.selectAll('path')
+ .filter(filter)
+ .data(lines, iD.Entity.key);
+
+ paths.enter()
+ .append('path')
+ .attr('class', classes);
+
+ paths
+ .order()
+ .attr('d', lineString)
+ .call(iD.Style.styleClasses());
+
+ paths.exit()
+ .remove();
+
+ return paths;
+ }
+
+ var casing = surface.select('.layer-casing'),
+ stroke = surface.select('.layer-stroke'),
+ defs = surface.select('defs'),
+ text = surface.select('.layer-text'),
+ casings = drawPaths(casing, lines, filter, 'way line casing'),
+ strokes = drawPaths(stroke, lines, filter, 'way line stroke');
+
+ // Determine the lengths of oneway paths
+ var lengths = {},
+ oneways = strokes.filter(function (d) { return d.isOneWay(); }).each(function(d) {
+ lengths[d.id] = Math.floor(this.getTotalLength() / alength);
+ }).data();
+
+ var uses = defs.selectAll('path')
+ .filter(filter)
+ .data(oneways, iD.Entity.key);
+
+ uses.enter()
+ .append('path');
+
+ uses
+ .attr('id', function(d) { return 'shadow-' + d.id; })
+ .attr('d', lineString);
+
+ uses.exit()
+ .remove();
+
+ var labels = text.selectAll('text')
+ .filter(filter)
+ .data(oneways, iD.Entity.key);
+
+ var tp = labels.enter()
+ .append('text')
+ .attr({ 'class': 'oneway', dy: 4 })
+ .append('textPath')
+ .attr('class', 'textpath');
+
+ labels.exit().remove();
+
+ text.selectAll('.textpath')
+ .filter(filter)
+ .attr('xlink:href', function(d, i) { return '#shadow-' + d.id; })
+ .text(function(d) {
+ return (new Array(Math.floor(lengths[d.id]))).join('►----');
+ });
+ }
+};
diff --git a/js/id/svg/midpoints.js b/js/id/svg/midpoints.js
new file mode 100644
index 000000000..77bcaa264
--- /dev/null
+++ b/js/id/svg/midpoints.js
@@ -0,0 +1,36 @@
+iD.svg.Midpoints = function() {
+ return function(surface, graph, entities, filter, projection) {
+ var midpoints = [];
+
+ for (var i = 0; i < entities.length; i++) {
+ var entity = entities[i];
+
+ if (entity.type !== 'way')
+ continue;
+
+ for (var j = 0; j < entity.nodes.length - 1; j++) {
+ if (iD.util.geo.dist(entity.nodes[j].loc, entity.nodes[j + 1].loc) > 0.0001) {
+ midpoints.push({
+ loc: iD.util.geo.interp(entity.nodes[j].loc, entity.nodes[j + 1].loc, 0.5),
+ way: entity.id,
+ index: j + 1,
+ accuracy: true
+ });
+ }
+ }
+ }
+
+ var handles = surface.select('.layer-hit').selectAll('circle.accuracy-handle')
+ .filter(filter)
+ .data(midpoints, function (d) { return [d.way, d.index].join(","); });
+
+ handles.enter()
+ .append('circle')
+ .attr({ r: 3, 'class': 'accuracy-handle' });
+
+ handles.attr('transform', iD.svg.PointTransform(projection));
+
+ handles.exit()
+ .remove();
+ };
+};
diff --git a/js/id/svg/points.js b/js/id/svg/points.js
new file mode 100644
index 000000000..a92db711a
--- /dev/null
+++ b/js/id/svg/points.js
@@ -0,0 +1,42 @@
+iD.svg.Points = function() {
+ return function(surface, graph, entities, filter, projection) {
+ var points = [];
+
+ for (var i = 0; i < entities.length; i++) {
+ var entity = entities[i];
+ if (entity.geometry() === 'point') {
+ points.push(entity);
+ }
+ }
+
+ var groups = surface.select('.layer-hit').selectAll('g.point')
+ .filter(filter)
+ .data(points, iD.Entity.key);
+
+ var group = groups.enter()
+ .append('g')
+ .attr('class', 'node point');
+
+ group.append('circle')
+ .attr('class', 'stroke')
+ .attr({ r: 10 });
+
+ group.append('circle')
+ .attr('class', 'fill')
+ .attr({ r: 10 });
+
+ group.append('image')
+ .attr({ width: 16, height: 16 })
+ .attr('transform', 'translate(-8, -8)');
+
+ groups.attr('transform', iD.svg.PointTransform(projection));
+
+ // Selecting the following implicitly
+ // sets the data (point entity) on the element
+ groups.select('image')
+ .attr('xlink:href', iD.Style.pointImage);
+
+ groups.exit()
+ .remove();
+ };
+};
diff --git a/js/id/svg/vertices.js b/js/id/svg/vertices.js
new file mode 100644
index 000000000..f263dc1bf
--- /dev/null
+++ b/js/id/svg/vertices.js
@@ -0,0 +1,41 @@
+iD.svg.Vertices = function() {
+ return function(surface, graph, entities, filter, projection) {
+ var vertices = [];
+
+ for (var i = 0; i < entities.length; i++) {
+ var entity = entities[i];
+ if (entity.geometry() === 'vertex') {
+ vertices.push(entity);
+ }
+ }
+
+ var parentStructure = graph.parentStructure(vertices);
+
+ var groups = surface.select('.layer-hit').selectAll('g.vertex')
+ .filter(filter)
+ .data(vertices, iD.Entity.key);
+
+ var group = groups.enter()
+ .insert('g', ':first-child')
+ .attr('class', 'node vertex');
+
+ group.append('circle')
+ .attr('class', 'stroke')
+ .attr('r', 6);
+
+ group.append('circle')
+ .attr('class', 'fill')
+ .attr('r', 4);
+
+ groups.attr('transform', iD.svg.PointTransform(projection))
+ .classed('shared', function(d) { return parentStructure[d.id] > 1; });
+
+ // Selecting the following implicitly
+ // sets the data (vertix entity) on the elements
+ groups.select('circle.fill');
+ groups.select('circle.stroke');
+
+ groups.exit()
+ .remove();
+ };
+};
diff --git a/test/index.html b/test/index.html
index 5d85f04f7..be9c16b04 100644
--- a/test/index.html
+++ b/test/index.html
@@ -39,6 +39,14 @@
+
+
+
+
+
+
+
+