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> |
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