Reputation: 2893
I am drawing an SVG map with D3 using the d3.geo.mercator()
projection.
I also use a zoom behaviour with the map which applies a transform
to the <g>
object holding all paths of the map.
After looking at examples of dynamic simplification by Mike Bostock (http://bl.ocks.org/mbostock/6252418) I wonder whether I can apply such an algorithm in my case to redraw the geometry with fewer points when it's zoomed out?
In all examples I've seen, there is a simplify
function which skips negligible points and plots the rest as it is, and that function is used in var path = d3.geo.path().projection(simplify)
. I can't use it like that since I need it to be applied on top of already existing projection = d3.geo.mercator().scale(*).translate([*,*])
.
How should I use dynamic simplification with existing projection?
Upvotes: 3
Views: 1046
Reputation: 17481
According to the example you quoted, Dynamic Simplification II
The simplify
function would be something like
var simplify = d3.geo.transform({
point: function(x, y, z) {
if (z >= area) {
this.stream.point(x, y);
}
}
});
Where area
is a treshold variable that you can set beforehand or modify dinamically according to zoom.
Then you would use it on the projection
method of d3.geo.path()
like
var path = d3.geo.path()
.projection(simplify);
That's more or less the situation you described in your answer. Now, according to Dynamic Simplification IV, the projection method could also be defined as
var path = d3.geo.path()
.projection({
stream: function(s) {
return simplify.stream(s);
}
});
This is exactly the same as before. It's just "expanding" the default methods. d3.geo.path
always calls the projection stream
method, so you can declare your own stream and forward it to simplify.stream
.
Now, you say you need to reproject your path using d3.geo.mercator().
var mercatorProjection = d3.geo.mercator().scale(*).translate([*,*]);
No problem: the streams are chainable. You can do:
var path = d3.geo.path()
.projection({
stream: function(s) {
return simplify.stream(mercatorProjection.stream(s));
}
});
As well as:
var path = d3.geo.path()
.projection({
stream: function(s) {
return mercatorProjection.stream(simplify.stream(s));
}
});
The only difference being that the treshold area would have to be calculated differently if you're dealing with WGS84, pixels or another coordinate system.
Important Caveat, the z
parameter in the simplify
function isn't the altitude. It is the area of the triangle defined by each point, a precalculated value that's part of TopoJSON sweetness.
I'm afraid this means you can't rely on this example to simplify regular geoJSON. You'll have to add your own logic to calculate each point's related area (if you want to apply Visvalingam's algorithm) or the distance to the closest point (if you want to apply Douglas-Peucker algorithm) or implement your own algorithm.
Good luck.
Upvotes: 4