Optimize area rendering

Use iD.svg.Path for caching, and teach iD.svg.Path to
round coordinates.
This commit is contained in:
John Firebaugh
2013-05-05 10:38:05 -07:00
parent 16e05fd657
commit 9495f8f1c0
3 changed files with 53 additions and 25 deletions
+21 -2
View File
@@ -17,10 +17,29 @@ iD.svg = {
var cache = {},
path = d3.geo.path().projection(projection);
return function(entity) {
function result(entity) {
if (entity.id in cache) return cache[entity.id];
return cache[entity.id] = path(entity.asGeoJSON(graph));
var buffer = '';
path.context({
beginPath: function() {},
moveTo: function(x, y) { buffer += 'M' + Math.floor(x) + ',' + Math.floor(y); },
lineTo: function(x, y) { buffer += 'L' + Math.floor(x) + ',' + Math.floor(y); },
arc: function() {},
closePath: function() { buffer += 'Z'; }
});
path(entity.asGeoJSON(graph));
return cache[entity.id] = buffer;
}
result.area = function(entity) {
return path.area(entity.asGeoJSON(graph, true));
};
return result;
},
OneWaySegments: function(projection, graph, dt) {
+11 -12
View File
@@ -28,7 +28,7 @@ iD.svg.Areas = function(projection) {
}
return function drawAreas(surface, graph, entities, filter) {
var path = d3.geo.path().projection(projection),
var path = iD.svg.Path(projection, graph),
areas = {},
multipolygon;
@@ -39,25 +39,25 @@ iD.svg.Areas = function(projection) {
if (multipolygon = iD.geo.isSimpleMultipolygonOuterMember(entity, graph)) {
areas[multipolygon.id] = {
entity: multipolygon.mergeTags(entity.tags),
area: Math.abs(path.area(entity.asGeoJSON(graph, true)))
area: Math.abs(path.area(entity))
};
} else if (!areas[entity.id]) {
areas[entity.id] = {
entity: entity,
area: Math.abs(path.area(entity.asGeoJSON(graph, true)))
area: Math.abs(path.area(entity))
};
}
}
areas = d3.values(areas);
areas.sort(function(a, b) { return b.area - a.area; });
areas = d3.values(areas).filter(function hasPath(a) { return path(a.entity); });
areas.sort(function areaSort(a, b) { return b.area - a.area; });
areas = _.pluck(areas, 'entity');
var strokes = areas.filter(function(area) {
var strokes = areas.filter(function isWay(area) {
return area.type === 'way';
});
function drawPaths(areas, klass, closeWay) {
function drawPaths(areas, klass, path) {
var paths = surface.select('.layer-' + klass)
.selectAll('path.area')
.filter(filter)
@@ -78,7 +78,7 @@ iD.svg.Areas = function(projection) {
paths
.order()
.attr('d', function(entity) { return path(entity.asGeoJSON(graph, closeWay)); });
.attr('d', path);
if (klass === 'fill') paths.call(setPattern);
@@ -88,9 +88,8 @@ iD.svg.Areas = function(projection) {
return paths;
}
drawPaths(strokes, 'shadow');
drawPaths(strokes, 'stroke');
drawPaths(areas, 'fill', true);
drawPaths(strokes, 'shadow', path);
drawPaths(strokes, 'stroke', path);
drawPaths(areas, 'fill', path);
};
};
+21 -11
View File
@@ -9,20 +9,30 @@ describe("iD.svg.Areas", function () {
});
it("adds way and area classes", function () {
var area = iD.Way({tags: {area: 'yes'}}),
graph = iD.Graph([area]);
var graph = iD.Graph({
'a': iD.Node({id: 'a', loc: [0, 0]}),
'b': iD.Node({id: 'b', loc: [1, 0]}),
'c': iD.Node({id: 'c', loc: [1, 1]}),
'd': iD.Node({id: 'd', loc: [0, 1]}),
'w': iD.Way({id: 'w', tags: {building: 'yes'}, nodes: ['a', 'b', 'c', 'a']})
});
surface.call(iD.svg.Areas(projection), graph, [area], filter);
surface.call(iD.svg.Areas(projection), graph, [graph.entity('w')], filter);
expect(surface.select('path.way')).to.be.classed('way');
expect(surface.select('path.area')).to.be.classed('area');
});
it("adds tag classes", function () {
var area = iD.Way({tags: {area: 'yes', building: 'yes'}}),
graph = iD.Graph([area]);
var graph = iD.Graph({
'a': iD.Node({id: 'a', loc: [0, 0]}),
'b': iD.Node({id: 'b', loc: [1, 0]}),
'c': iD.Node({id: 'c', loc: [1, 1]}),
'd': iD.Node({id: 'd', loc: [0, 1]}),
'w': iD.Way({id: 'w', tags: {building: 'yes'}, nodes: ['a', 'b', 'c', 'a']})
});
surface.call(iD.svg.Areas(projection), graph, [area], filter);
surface.call(iD.svg.Areas(projection), graph, [graph.entity('w')], filter);
expect(surface.select('.area')).to.be.classed('tag-building');
expect(surface.select('.area')).to.be.classed('tag-building-yes');
@@ -47,8 +57,8 @@ describe("iD.svg.Areas", function () {
'b': iD.Node({id: 'b', loc: [1, 0]}),
'c': iD.Node({id: 'c', loc: [1, 1]}),
'd': iD.Node({id: 'd', loc: [0, 1]}),
's': iD.Way({area: true, tags: {building: 'yes'}, nodes: ['a', 'b', 'c', 'a']}),
'l': iD.Way({area: true, tags: {landuse: 'park'}, nodes: ['a', 'b', 'c', 'd', 'a']})
's': iD.Way({id: 's', tags: {building: 'yes'}, nodes: ['a', 'b', 'c', 'a']}),
'l': iD.Way({id: 'l', tags: {landuse: 'park'}, nodes: ['a', 'b', 'c', 'd', 'a']})
}),
areas = [graph.entity('s'), graph.entity('l')];
@@ -91,7 +101,7 @@ describe("iD.svg.Areas", function () {
b = iD.Node({loc: [2, 2]}),
c = iD.Node({loc: [3, 3]}),
w = iD.Way({tags: {area: 'yes'}, nodes: [a.id, b.id, c.id, a.id]}),
r = iD.Relation({members: [{id: w.id}], tags: {type: 'multipolygon', natural: 'wood'}}),
r = iD.Relation({members: [{id: w.id, type: 'way'}], tags: {type: 'multipolygon', natural: 'wood'}}),
graph = iD.Graph([a, b, c, w, r]);
surface.call(iD.svg.Areas(projection), graph, [w], filter);
@@ -105,7 +115,7 @@ describe("iD.svg.Areas", function () {
b = iD.Node({loc: [2, 2]}),
c = iD.Node({loc: [3, 3]}),
w = iD.Way({tags: {natural: 'wood'}, nodes: [a.id, b.id, c.id, a.id]}),
r = iD.Relation({members: [{id: w.id}], tags: {type: 'multipolygon'}}),
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], filter);
@@ -120,7 +130,7 @@ describe("iD.svg.Areas", function () {
b = iD.Node({loc: [2, 2]}),
c = iD.Node({loc: [3, 3]}),
w = iD.Way({tags: {natural: 'wood'}, nodes: [a.id, b.id, c.id, a.id]}),
r = iD.Relation({members: [{id: w.id}], tags: {type: 'multipolygon'}}),
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], filter);