问题
Last year I made a couple experiments on web maps using the Mapnik library (server-side, bitmap/tiling). Now I'm trying to replicate the same experiments using the vector, client-side approach with d3.js.
I have a map (~680 shapes) where zoom
is slow and pan
is sluggish (this example by Mike Bostock works well). I suspect the problem is in the zoommove
callback, the selectAll("path").attr("d", path)
takes too long.
function zoommove() {
projection.translate(d3.event.translate).scale(d3.event.scale);
mapa.selectAll("path").attr("d", path);
console.log('zoommove fired...');
}
Questions:
- Am I doing something wrong here?
- what can I do in order to optmize performance?
The map is this (jsfiddle here):
The datasource is in topojson format. It was simplified, may be already too much, because some shapes are not closing:
[UPDATE]
Looks like the problem with open geometries occurs even when running topojson without simplification flags, I'm still investigating. I would appreciate any clues here, the documentation is not very detailed.
回答1:
(I'm not super sure what's going on under the hood here, this might be totally wrong).
mapa.selectAll("path").attr("d", path);
Redraws the map from scratch. That works fine for 50 states but starts to get pretty slow with 600+ shapes. You might have better luck if you left the paths in place and just transformed the entire svg:
function zoommove() {
svg.attr("transform",
"translate("+d3.event.translate+")"
+ " scale("+d3.event.scale+")");
}
Which I've used to create a county level map of the US (~500 shapes) that zooms and pans smoothly.
回答2:
Answering my own question here, If you find this helpful, please upvote Adam's answer instead, he deserves the credit.
What worked for me:
var bg = svg.append('g')
.call(zoom);
var map = bg.append("g")
.attr("transform", "translate(0,0) scale(1)");
...
function zoommove() {
var t1 = projection.translate(),
t2 = d3.event.translate,
t = [t2[0]-t1[0], t2[1]-t1[1]];
map.attr("transform",
"translate("+t+") " +
"scale("+(d3.event.scale/s)+")"
);
console.log(map.attr("transform"));
}
Some tips:
- if
projection.translate()
is not [0, 0], you have to take it in account, otherwise a big bump will occur on the first time you try to pan/zoom (only the first time). - if
projection.scale()
is not 1, you have to take it in account. - the
.call(zoom)
must be in the maps parent element, otherwise panning/zooming becomes bumpy.
回答3:
I've come across similar issues, and a rescaling was not suitable for my solution as i did not want the svg elements themselves to be scaled. What I did instead was optimize it such that elements out of the visible area were not recalculated. This means that when all the elements are in the view performance is still bad, but when you zoom in it's much better.
ode sample:
clippedArea.selectAll("circle")
.style("visibility", d => pointInDomain(d, domain) ? "visible" : "hidden")
.filter(d => pointInDomain(d, domain))
.attr("cx", d => xz(d.x));
JSFiddle
来源:https://stackoverflow.com/questions/17093614/d3-geo-performance-on-pan-zoom