Skip to content

Instantly share code, notes, and snippets.

@Fil
Last active March 3, 2017 09:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Fil/6128aae082c04eef06422f953d0f593f to your computer and use it in GitHub Desktop.
Save Fil/6128aae082c04eef06422f953d0f593f to your computer and use it in GitHub Desktop.
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">
<style>
#sphere {
stroke: #444;
stroke-width: 2;
fill: #eee;
}
.polygons {
stroke: #444;
}
.sites {
stroke: black;
stroke-width: 0.5;
fill: white;
}
.sites .clipped {
fill: olive;
}
</style>
<body></body>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://unpkg.com/d3-geo-voronoi"></script>
<script src="https://d3js.org/d3-geo-projection.v1.min.js"></script>
<script>
var width = 960, height = 480,
svg = d3.select('body')
.append('svg')
.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]);
this.stream.point(u[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 = d3.select("svg");
svg.append('g')
.attr('class', 'polygons')
.selectAll('path')
.data(features)
.enter()
.append('path')
.attr('d', path)
.attr('fill', function(_,i) { return d3.schemeCategory10[i%10]; })
.attr('fill-opacity', 0.3)
svg.append('g')
.attr('class', 'sites')
.selectAll('path')
.data(points.features)
.enter()
.append('path')
.attr('d', _path) // display all sites (no clipping)
.classed('clipped', d => !path(d)) // mark those that would be clipped
;
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment