Skip to content

Instantly share code, notes, and snippets.

Last active Mar 3, 2017
What would you like to do?
Spherical extent
license: mit

Computing the extent for a spherical Voronoi diagram.

The pain point here was that we're filtering a GeoJSON object, and want the results to stay in spherical coordinates. However the clipping should reflect the way features will be printed on paper (or on screen), i.e. in the final projection (Mercator).

Next steps: make this a configurable function and one that preserves the features’ properties.

by Philippe Rivière.

forked from Fil's block: geoVoronoi polygons()

<!DOCTYPE html>
<meta charset="utf-8">
#sphere {
stroke: #444;
stroke-width: 2;
fill: #eee;
.polygons {
stroke: #444;
.sites {
stroke: black;
stroke-width: 0.5;
fill: white;
.sites .clipped {
fill: olive;
<script src=""></script>
<script src=""></script>
<script src=""></script>
var width = 960, height = 480,
svg ='body')
.attr('width', width)
.attr('height', height);
var center = [-60,35];
var points = {
type: "FeatureCollection",
features: d3.range(200).map(function(d,i) {
return {
type: "Feature",
geometry: {
type: "Point",
coordinates: [ center[0] + 25 * (Math.random() - Math.random()), center[1] + 10 * (Math.random() - Math.random()) ]
var padding = 30,
projection = d3.geoMercator()
.fitExtent([[padding,padding], [width-padding, height-padding]], points);
// Clip the features to a certain paper extent around a center.
var projection_invert = d3.geoTransform({
point: function(x, y) {
let u = projection.invert([x,y]);[0], u[1]);
var clip_padding = 70;
var clip2paper = d3.geoIdentity()
.clipExtent([[clip_padding,clip_padding], [width-clip_padding, height-clip_padding]])
var clip = function(d) {
d = d3.geoProject(d, projection);
d = d3.geoProject(d, clip2paper);
d = d3.geoProject(d, projection_invert);
return d;
// normally one would use the same projection for clipping and for display;
// here we change it only for the pleasure of the demo
var projection2 = d3.geoOrthographic()
.rotate([-center[0], -center[1]])
.fitExtent([[padding,padding], [960-padding, 500-padding]], points);
var _path = d3.geoPath().projection(projection2),
path = function(d) { return _path(clip(d)); };
// end clip code
var v = d3.geoVoronoi()(points),
features = v.polygons().features;
var svg ="svg");
.attr('class', 'polygons')
.attr('d', path)
.attr('fill', function(_,i) { return d3.schemeCategory10[i%10]; })
.attr('fill-opacity', 0.3)
.attr('class', 'sites')
.attr('d', _path) // display all sites (no clipping)
.classed('clipped', d => !path(d)) // mark those that would be clipped
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment