Skip to content

Instantly share code, notes, and snippets.

@patricksurry
Last active March 1, 2020 06:16
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save patricksurry/40e8e58359dfc852ee19 to your computer and use it in GitHub Desktop.
Save patricksurry/40e8e58359dfc852ee19 to your computer and use it in GitHub Desktop.
Circular inset maps for D3

An example of cirular inset maps using D3, based on the naive approach of redrawing a clipped version of the whole map for every inset.

<!DOCTYPE html>
<meta charset="utf-8">
<style>
svg {
background-color: #fff;
}
path {
fill: #eee;
stroke: #999;
stroke-width: .5px;
}
.insetmap circle.background {
fill: white;
}
.insetmap circle.blur {
filter: url(#blur);
fill: none;
stroke: white;
stroke-width: 10;
}
.insetmap circle.outline {
fill: none;
stroke: #999;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://d3js.org/topojson.v1.min.js"></script>
<script>
var width = 960,
height = 500,
rotate = 0, // so that [-60, 0] becomes initial center of projection
basescale = width/2/Math.PI;
var projection = d3.geo.mercator()
.rotate([rotate,0])
// .clipAngle(60) // circle clipping around center of projection via rotate
// .center([-60,0]) // this point will get translated to center of the viewport
.translate([0.5*width, 0.6*height])
.scale(basescale)
.precision(0.1);
var path = d3.geo.path()
.projection(projection);
var svg = d3.selectAll('body')
.append('svg')
.attr('width',width)
.attr('height',height);
var insets = [
{name: 'EDI', longitude: -3.2, latitude: 55.9, zoom: 3,
dx: -0.03, dy: -0.05, radius: 0.07*width},
{name: 'BOS', longitude: -71.1, latitude: 42.4, zoom: 3,
dx: 0.03, dy: 0.03, radius: 0.05*width},
{name: 'SIN', longitude: 104, latitude: 1.4, zoom: 3,
radius: 0.1*width }
];
insets.forEach(function(d) {
var xy = projection([d.longitude, d.latitude]);
d.x = xy[0] + (d.dx || 0)*width;
d.y = xy[1] + (d.dy || 0)*height;
d.path = d3.geo.path()
.projection(
d3.geo.mercator()
.rotate([rotate, 0])
.center([d.longitude - -rotate, d.latitude])
.translate([0,0])
.scale(basescale*d.zoom)
.precision(0.1)
);
})
d3.json("world-50m.json", function ready(error, world) {
var features = topojson.feature(world, world.objects.countries).features;
var defs = svg.append('defs');
defs.append('filter')
.attr('id','blur')
.append('feGaussianBlur')
.attr('stdDeviation',4);
defs.selectAll('clipPath')
.data(insets)
.enter().append('clipPath')
.attr('id', function(d) { return 'clip-inset-' + d.name; })
.append('circle')
.attr('r', function(d) { return d.radius; });
// draw a base map
svg.append('g')
.classed('basemap', true)
.selectAll('path')
.data(features)
.enter().append('path')
.attr('d', path);
var g = svg.selectAll('g.insetmap')
.data(insets)
.enter().append('g')
.classed('insetmap', true)
.attr('transform', function(d) {
return 'translate(' + [d.x, d.y] + ')'; })
.attr('id', function(d) {
return 'inset-' + d.name; })
.attr('clip-path', function(d) {
return 'url(#clip-inset-' + d.name + ')' });
g.append('circle')
.classed('background', true)
.attr('r', function(d) { return d.radius; });
g.selectAll('path')
.data(function(d) { return features.map(d.path); })
.enter().append('path')
.attr('d', function (d) { return d; });
g.append('circle')
.classed('blur', true)
.attr('r', function(d) { return d.radius; });
g.append('circle')
.classed('outline', true)
.attr('r', function(d) { return d.radius; });
});
</script>
</body>
</html>
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment