D3 3.0’s projections use adaptive resampling to increase the accuracy of projected lines and polygons while still performing efficiently. On the left is no resampling, where obvious polygonal artifacts are visible due to projected lines becoming curves. Uniform resampling improves the appearance, but is inefficient because most of the resampling points are wasted, and areas of extreme distortion still exhibit artifacts. Adaptive resampling takes an idea from line simplification and resamples only where needed, producing high-quality results with low computational overhead.
-
-
Save mbostock/3795544 to your computer and use it in GitHub Desktop.
Adaptive Resampling
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
license: gpl-3.0 | |
redirect: https://observablehq.com/@d3/adaptive-sampling |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<meta charset="utf-8"> | |
<style> | |
.title { | |
font: bold 16px "Helvetica Neue"; | |
text-transform: capitalize; | |
} | |
.graticule { | |
fill: none; | |
stroke: #999; | |
stroke-width: .5px; | |
} | |
.resample { | |
stroke: #f00; | |
stroke-width: 1.5px; | |
fill: none; | |
} | |
</style> | |
<body> | |
<script src="//d3js.org/d3.v3.min.js"></script> | |
<script> | |
var width = 320, | |
height = 580, | |
velocity = .003, | |
time = Date.now(); | |
var projection = d3.geo.equirectangular() | |
.rotate([0, 0, 89]) | |
.translate([width / 2, height / 2]) | |
.scale(85) | |
.precision(0); | |
var path = d3.geo.path() | |
.projection(projection); | |
var graticule = d3.geo.graticule(); | |
d3.select("body") | |
.call(svg, "no resampling", [180].concat(d3.range(-90, 180, 90), [180]).map(function(x) { return [x, 0]; }), 0) | |
.call(svg, "uniform resampling", [180].concat(d3.range(-176, 180, 4), [180]).map(function(x) { return [x, 0]; }), 0) | |
.call(svg, "adaptive resampling", [[-180, 0], [-90, 0], [0, 0], [90, 0], [180, 0]], .5); | |
redraw(); | |
function svg(body, title, coordinates, precision) { | |
var svg = body.append("svg") | |
.datum({ | |
type: "LineString", | |
coordinates: coordinates, | |
precision: precision | |
}) | |
.attr("width", width) | |
.attr("height", height); | |
svg.append("text") | |
.attr("class", "title") | |
.attr("x", width / 2) | |
.attr("y", height - 50) | |
.style("text-anchor", "middle") | |
.text(title); | |
var g = svg.append("g") | |
.attr("transform", "rotate(90 " + projection.translate() + ")"); | |
g.append("path") | |
.attr("class", "resample"); | |
g.append("path") | |
.datum(graticule) | |
.attr("class", "graticule"); | |
} | |
function redraw() { | |
d3.selectAll("svg > g").each(function(d) { | |
var g = d3.select(this); | |
projection.precision(.3); | |
g.selectAll(".graticule").attr("d", path); | |
projection.precision(d.precision); | |
g.selectAll(".resample").attr("d", path); | |
// Override the path context to extract the resampled points. | |
path.context(bufferContext()); | |
path(d); | |
var points = path.context().buffer(); | |
path.context(null); | |
var point = g.selectAll(".point").data(points); | |
point.exit().remove(); | |
point.enter().append("circle").attr("class", "point resample").attr("r", 4.5); | |
point.attr("transform", function(d) { return "translate(" + d + ")"; }); | |
}); | |
} | |
d3.timer(function() { | |
var dt = Date.now() - time; | |
projection.rotate([velocity * dt, 0, 89]); | |
redraw(); | |
}); | |
function bufferContext() { | |
var buffer = []; | |
return { | |
moveTo: function(x, y) { buffer.push([x, y]); }, | |
lineTo: function(x, y) { buffer.push([x, y]); }, | |
closePath: function() {}, | |
buffer: function() { var _ = buffer; buffer = []; return _; } | |
}; | |
} | |
d3.select(self.frameElement).style("height", height + "px"); | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment