diff --git a/modules/renderer/tile_layer.js b/modules/renderer/tile_layer.js
index 843083e33..243314770 100644
--- a/modules/renderer/tile_layer.js
+++ b/modules/renderer/tile_layer.js
@@ -1,6 +1,7 @@
import * as d3 from 'd3';
import { d3geoTile } from '../lib/d3.geo.tile';
-import { prefixCSSProperty, functor } from '../util/index';
+import { prefixCSSProperty } from '../util/index';
+import { BackgroundSource } from './background_source.js';
export function TileLayer(context) {
var tileSize = 256,
@@ -10,7 +11,7 @@ export function TileLayer(context) {
tileOrigin,
z,
transformProp = prefixCSSProperty('Transform'),
- source = functor('');
+ source = BackgroundSource.None();
// blacklist overlay tiles around Null Island..
diff --git a/modules/svg/layers.js b/modules/svg/layers.js
index b2ff09e65..e43cc53f7 100644
--- a/modules/svg/layers.js
+++ b/modules/svg/layers.js
@@ -24,23 +24,25 @@ export function Layers(projection, context) {
svg = selection.selectAll('.surface')
.data([0]);
- svg.enter()
+ svg = svg.enter()
.append('svg')
.attr('class', 'surface')
+ .merge(svg);
+
+ svg
.append('defs');
var groups = svg.selectAll('.data-layer')
.data(layers);
- groups.enter()
- .append('g')
- .attr('class', function(d) { return 'data-layer data-layer-' + d.id; });
-
- groups
- .each(function(d) { d3.select(this).call(d.layer); });
-
groups.exit()
.remove();
+
+ groups.enter()
+ .append('g')
+ .attr('class', function(d) { return 'data-layer data-layer-' + d.id; })
+ .merge(groups)
+ .each(function(d) { d3.select(this).call(d.layer); });
}
drawLayers.all = function() {
diff --git a/modules/svg/midpoints.js b/modules/svg/midpoints.js
index 9eadb1037..f134d15c7 100644
--- a/modules/svg/midpoints.js
+++ b/modules/svg/midpoints.js
@@ -64,13 +64,16 @@ export function Midpoints(projection, context) {
if (midpoints[d.id])
return true;
- for (var i = 0; i < d.parents.length; i++)
- if (filter(d.parents[i]))
+ for (var i = 0; i < d.parents.length; i++) {
+ if (filter(d.parents[i])) {
return true;
+ }
+ }
return false;
}
+
var layer = selection.selectAll('.layer-hit');
var groups = layer
@@ -78,6 +81,9 @@ export function Midpoints(projection, context) {
.filter(midpointFilter)
.data(_.values(midpoints), function(d) { return d.id; });
+ groups.exit()
+ .remove();
+
var enter = groups.enter()
.insert('g', ':first-child')
.attr('class', 'midpoint');
@@ -90,11 +96,12 @@ export function Midpoints(projection, context) {
.attr('points', '-3,4 5,0 -3,-4')
.attr('class', 'fill');
- groups
+ groups = groups
+ .merge(enter)
.attr('transform', function(d) {
var translate = PointTransform(projection),
- a = context.entity(d.edge[0]),
- b = context.entity(d.edge[1]),
+ a = graph.entity(d.edge[0]),
+ b = graph.entity(d.edge[1]),
angleVal = Math.round(angle(a, b, projection) * (180 / Math.PI));
return translate(d) + ' rotate(' + angleVal + ')';
})
@@ -106,7 +113,5 @@ export function Midpoints(projection, context) {
groups.select('polygon.shadow');
groups.select('polygon.fill');
- groups.exit()
- .remove();
};
}
diff --git a/modules/svg/points.js b/modules/svg/points.js
index 700faa5c3..31f016028 100644
--- a/modules/svg/points.js
+++ b/modules/svg/points.js
@@ -60,7 +60,7 @@ export function Points(projection, context) {
groups.select('.icon')
.attr('xlink:href', function(entity) {
var preset = context.presets().match(entity, graph);
- return preset.icon ? '#' + preset.icon + '-12' : '';
+ return (preset && preset.icon) ? '#' + preset.icon + '-12' : '';
});
};
}
diff --git a/test/index.html b/test/index.html
index b8c2be03a..f425f2ccf 100644
--- a/test/index.html
+++ b/test/index.html
@@ -104,8 +104,10 @@
+
+
diff --git a/test/spec/svg/areas.js b/test/spec/svg/areas.js
index 2d9b49b69..36cb84901 100644
--- a/test/spec/svg/areas.js
+++ b/test/spec/svg/areas.js
@@ -1,13 +1,19 @@
describe('iD.svg.Areas', function () {
- var surface,
- projection = d3.geoProjection(function(x, y) { return [x, y]; })
+ var context, surface,
+ projection = d3.geoProjection(function(x, y) { return [x, -y]; })
+ .translate([0, 0])
+ .scale(180 / Math.PI)
.clipExtent([[0, 0], [Infinity, Infinity]]),
all = function() { return true; },
none = function() { return false; };
beforeEach(function () {
- surface = d3.select(document.createElementNS('http://www.w3.org/2000/svg', 'svg'))
- .call(iD.svg.Layers(projection, iD.Context(window)));
+ context = iD.Context(window);
+ d3.select(document.createElement('div'))
+ .attr('id', 'map')
+ .call(context.map());
+ surface = context.surface();
+
iD.setAreaKeys({
building: {},
landuse: {},
@@ -24,7 +30,7 @@ describe('iD.svg.Areas', function () {
iD.Way({id: 'w', tags: {building: 'yes'}, nodes: ['a', 'b', 'c', 'a']})
]);
- surface.call(iD.svg.Areas(projection), graph, [graph.entity('w')], none);
+ surface.call(iD.svg.Areas(projection, context), graph, [graph.entity('w')], none);
expect(surface.select('path.way')).to.be.classed('way');
expect(surface.select('path.area')).to.be.classed('area');
@@ -39,7 +45,7 @@ describe('iD.svg.Areas', function () {
iD.Way({id: 'w', tags: {building: 'yes'}, nodes: ['a', 'b', 'c', 'a']})
]);
- surface.call(iD.svg.Areas(projection), graph, [graph.entity('w')], none);
+ surface.call(iD.svg.Areas(projection, context), graph, [graph.entity('w')], none);
expect(surface.select('.area')).to.be.classed('tag-building');
expect(surface.select('.area')).to.be.classed('tag-building-yes');
@@ -55,10 +61,10 @@ describe('iD.svg.Areas', function () {
iD.Way({id: 'x', tags: {area: 'yes'}, nodes: ['a', 'b', 'd', 'a']})
]);
- surface.call(iD.svg.Areas(projection), graph, [graph.entity('x')], all);
+ surface.call(iD.svg.Areas(projection, context), graph, [graph.entity('x')], all);
graph = graph.remove(graph.entity('x')).remove(graph.entity('d'));
- surface.call(iD.svg.Areas(projection), graph, [graph.entity('w')], all);
+ surface.call(iD.svg.Areas(projection, context), graph, [graph.entity('w')], all);
expect(surface.select('.area').size()).to.equal(1);
});
@@ -77,30 +83,30 @@ describe('iD.svg.Areas', function () {
]);
it('stacks smaller areas above larger ones in a single render', function () {
- surface.call(iD.svg.Areas(projection), graph, [graph.entity('s'), graph.entity('l')], none);
+ surface.call(iD.svg.Areas(projection, context), graph, [graph.entity('s'), graph.entity('l')], none);
expect(surface.select('.area:nth-child(1)')).to.be.classed('tag-landuse-park');
expect(surface.select('.area:nth-child(2)')).to.be.classed('tag-building-yes');
});
it('stacks smaller areas above larger ones in a single render (reverse)', function () {
- surface.call(iD.svg.Areas(projection), graph, [graph.entity('l'), graph.entity('s')], none);
+ surface.call(iD.svg.Areas(projection, context), graph, [graph.entity('l'), graph.entity('s')], none);
expect(surface.select('.area:nth-child(1)')).to.be.classed('tag-landuse-park');
expect(surface.select('.area:nth-child(2)')).to.be.classed('tag-building-yes');
});
it('stacks smaller areas above larger ones in separate renders', function () {
- surface.call(iD.svg.Areas(projection), graph, [graph.entity('s')], none);
- surface.call(iD.svg.Areas(projection), graph, [graph.entity('l')], none);
+ surface.call(iD.svg.Areas(projection, context), graph, [graph.entity('s')], none);
+ surface.call(iD.svg.Areas(projection, context), graph, [graph.entity('l')], none);
expect(surface.select('.area:nth-child(1)')).to.be.classed('tag-landuse-park');
expect(surface.select('.area:nth-child(2)')).to.be.classed('tag-building-yes');
});
it('stacks smaller areas above larger ones in separate renders (reverse)', function () {
- surface.call(iD.svg.Areas(projection), graph, [graph.entity('l')], none);
- surface.call(iD.svg.Areas(projection), graph, [graph.entity('s')], none);
+ surface.call(iD.svg.Areas(projection, context), graph, [graph.entity('l')], none);
+ surface.call(iD.svg.Areas(projection, context), graph, [graph.entity('s')], none);
expect(surface.select('.area:nth-child(1)')).to.be.classed('tag-landuse-park');
expect(surface.select('.area:nth-child(2)')).to.be.classed('tag-building-yes');
@@ -116,7 +122,7 @@ describe('iD.svg.Areas', function () {
graph = iD.Graph([a, b, c, w, r]),
areas = [w, r];
- surface.call(iD.svg.Areas(projection), graph, areas, none);
+ surface.call(iD.svg.Areas(projection, context), graph, areas, none);
expect(surface.select('.fill')).to.be.classed('relation');
});
@@ -130,7 +136,7 @@ describe('iD.svg.Areas', function () {
graph = iD.Graph([a, b, c, w, r]),
areas = [w, r];
- surface.call(iD.svg.Areas(projection), graph, areas, none);
+ surface.call(iD.svg.Areas(projection, context), graph, areas, none);
expect(surface.selectAll('.stroke').size()).to.equal(0);
});
@@ -143,7 +149,7 @@ describe('iD.svg.Areas', function () {
r = iD.Relation({members: [{id: w.id, type: 'way'}], tags: {type: 'multipolygon'}}),
graph = iD.Graph([a, b, c, w, r]);
- surface.call(iD.svg.Areas(projection), graph, [w, r], none);
+ surface.call(iD.svg.Areas(projection, context), graph, [w, r], none);
expect(surface.selectAll('.way.fill').size()).to.equal(0);
expect(surface.selectAll('.relation.fill').size()).to.equal(1);
@@ -158,7 +164,7 @@ describe('iD.svg.Areas', function () {
r = iD.Relation({members: [{id: w.id, type: 'way'}], tags: {type: 'multipolygon'}}),
graph = iD.Graph([a, b, c, w, r]);
- surface.call(iD.svg.Areas(projection), graph, [w, r], none);
+ surface.call(iD.svg.Areas(projection, context), graph, [w, r], none);
expect(surface.selectAll('.stroke').size()).to.equal(0);
});
diff --git a/test/spec/svg/layers.js b/test/spec/svg/layers.js
new file mode 100644
index 000000000..ca79b45a2
--- /dev/null
+++ b/test/spec/svg/layers.js
@@ -0,0 +1,30 @@
+describe('iD.svg.Layers', function () {
+ var context, container,
+ projection = d3.geoProjection(function(x, y) { return [x, -y]; })
+ .translate([0, 0])
+ .scale(180 / Math.PI)
+ .clipExtent([[0, 0], [Infinity, Infinity]]);
+
+ beforeEach(function () {
+ context = iD.Context(window);
+ container = d3.select(document.createElement('div'));
+ });
+
+
+ it('creates a surface', function () {
+ container.call(iD.svg.Layers(projection, context));
+ expect(container.selectAll('svg')).to.be.classed('surface');
+ });
+
+ it('creates default data layers', function () {
+ container.call(iD.svg.Layers(projection, context));
+ var nodes = container.selectAll('svg .data-layer').nodes();
+ expect(nodes.length).to.eql(5);
+ expect(d3.select(nodes[0])).to.be.classed('data-layer-osm');
+ expect(d3.select(nodes[1])).to.be.classed('data-layer-gpx');
+ expect(d3.select(nodes[2])).to.be.classed('data-layer-mapillary-images');
+ expect(d3.select(nodes[3])).to.be.classed('data-layer-mapillary-signs');
+ expect(d3.select(nodes[4])).to.be.classed('data-layer-debug');
+ });
+
+});
diff --git a/test/spec/svg/lines.js b/test/spec/svg/lines.js
index bf848f1d4..65228d87a 100644
--- a/test/spec/svg/lines.js
+++ b/test/spec/svg/lines.js
@@ -1,15 +1,21 @@
describe('iD.svg.Lines', function () {
- var surface,
- projection = d3.geoProjection(function(x, y) { return [x, y]; })
+ var context, surface,
+ projection = d3.geoProjection(function(x, y) { return [x, -y]; })
+ .translate([0, 0])
+ .scale(180 / Math.PI)
.clipExtent([[0, 0], [Infinity, Infinity]]),
all = function() { return true; },
none = function() { return false; };
beforeEach(function () {
- surface = d3.select(document.createElementNS('http://www.w3.org/2000/svg', 'svg'))
- .call(iD.svg.Layers(projection, iD.Context(window)));
+ context = iD.Context(window);
+ d3.select(document.createElement('div'))
+ .attr('id', 'map')
+ .call(context.map());
+ surface = context.surface();
});
+
it('adds way and line classes', function () {
var a = iD.Node({loc: [0, 0]}),
b = iD.Node({loc: [1, 1]}),
diff --git a/test/spec/svg/midpoints.js b/test/spec/svg/midpoints.js
index 3b164c7da..dfb80778c 100644
--- a/test/spec/svg/midpoints.js
+++ b/test/spec/svg/midpoints.js
@@ -1,15 +1,20 @@
describe('iD.svg.Midpoints', function () {
- var surface,
- projection = Object,
- filter = function() { return true; },
- context;
+ var context, surface,
+ projection = d3.geoProjection(function(x, y) { return [x, -y]; })
+ .translate([0, 0])
+ .scale(180 / Math.PI)
+ .clipExtent([[0, 0], [Infinity, Infinity]]),
+ filter = function() { return true; };
beforeEach(function () {
context = iD.Context(window);
- surface = d3.select(document.createElementNS('http://www.w3.org/2000/svg', 'svg'))
- .call(iD.svg.Layers(projection, context));
+ d3.select(document.createElement('div'))
+ .attr('id', 'map')
+ .call(context.map());
+ surface = context.surface();
});
+
it('creates midpoint on segment completely within the extent', function () {
var a = iD.Node({loc: [0, 0]}),
b = iD.Node({loc: [50, 0]}),
@@ -21,7 +26,7 @@ describe('iD.svg.Midpoints', function () {
context.entity = function(id) { return graph.entity(id); };
surface.call(iD.svg.Midpoints(projection, context), graph, [line], filter, extent);
- expect(surface.select('.midpoint').datum().loc).to.eql([25, 0]);
+ expect(surface.selectAll('.midpoint').datum().loc).to.eql([25, 0]);
});
it('doesn\'t create midpoint on segment with pixel length less than 40', function () {
@@ -61,7 +66,7 @@ describe('iD.svg.Midpoints', function () {
context.entity = function(id) { return graph.entity(id); };
surface.call(iD.svg.Midpoints(projection, context), graph, [line], filter, extent);
- expect(surface.select('.midpoint').datum().loc).to.eql([100, 0]);
+ expect(surface.selectAll('.midpoint').datum().loc).to.eql([100, 0]);
});
it('doesn\'t create midpoint on extent edge for segment with pixel length less than 20', function () {
diff --git a/test/spec/svg/osm.js b/test/spec/svg/osm.js
new file mode 100644
index 000000000..5979d31de
--- /dev/null
+++ b/test/spec/svg/osm.js
@@ -0,0 +1,19 @@
+describe('iD.svg.Osm', function () {
+ var container;
+
+ beforeEach(function () {
+ container = d3.select(document.createElementNS('http://www.w3.org/2000/svg', 'svg'));
+ });
+
+ it('creates default osm layers', function () {
+ container.call(iD.svg.Osm());
+ var nodes = container.selectAll('.layer-osm').nodes();
+ expect(nodes.length).to.eql(5);
+ expect(d3.select(nodes[0])).to.be.classed('layer-areas');
+ expect(d3.select(nodes[1])).to.be.classed('layer-lines');
+ expect(d3.select(nodes[2])).to.be.classed('layer-hit');
+ expect(d3.select(nodes[3])).to.be.classed('layer-halo');
+ expect(d3.select(nodes[4])).to.be.classed('layer-label');
+ });
+
+});
diff --git a/test/spec/svg/points.js b/test/spec/svg/points.js
index 6ea1a9259..59379fdb6 100644
--- a/test/spec/svg/points.js
+++ b/test/spec/svg/points.js
@@ -1,14 +1,19 @@
describe('iD.svg.Points', function () {
- var surface,
- projection = Object,
- context;
+ var context, surface,
+ projection = d3.geoProjection(function(x, y) { return [x, -y]; })
+ .translate([0, 0])
+ .scale(180 / Math.PI)
+ .clipExtent([[0, 0], [Infinity, Infinity]]);
beforeEach(function () {
- context = iD.Context(window).presets(iD.data.presets);
- surface = d3.select(document.createElementNS('http://www.w3.org/2000/svg', 'svg'))
- .call(iD.svg.Layers(projection, context));
+ context = iD.Context(window);
+ d3.select(document.createElement('div'))
+ .attr('id', 'map')
+ .call(context.map());
+ surface = context.surface();
});
+
it('adds tag classes', function () {
var point = iD.Node({tags: {amenity: 'cafe'}, loc: [0, 0]}),
graph = iD.Graph([point]);
diff --git a/test/spec/svg/vertices.js b/test/spec/svg/vertices.js
index 9d48be59e..39e6ee695 100644
--- a/test/spec/svg/vertices.js
+++ b/test/spec/svg/vertices.js
@@ -1,14 +1,19 @@
describe('iD.svg.Vertices', function () {
- var surface,
- projection = Object,
- context;
+ var context, surface,
+ projection = d3.geoProjection(function(x, y) { return [x, -y]; })
+ .translate([0, 0])
+ .scale(180 / Math.PI)
+ .clipExtent([[0, 0], [Infinity, Infinity]]);
beforeEach(function () {
context = iD.Context(window);
- surface = d3.select(document.createElementNS('http://www.w3.org/2000/svg', 'svg'))
- .call(iD.svg.Layers(projection, context));
+ d3.select(document.createElement('div'))
+ .attr('id', 'map')
+ .call(context.map());
+ surface = context.surface();
});
+
it('adds the .shared class to vertices that are members of two or more ways', function () {
var node = iD.Node({loc: [0, 0]}),
way1 = iD.Way({nodes: [node.id], tags: {highway: 'residential'}}),