Skip to content

Instantly share code, notes, and snippets.

@arrayjam
Last active August 29, 2015 14:01
Show Gist options
  • Save arrayjam/5c4288aa30de1b949433 to your computer and use it in GitHub Desktop.
Save arrayjam/5c4288aa30de1b949433 to your computer and use it in GitHub Desktop.
Non-Overlapping Geographic Circles

When creating many geographic circles (e.g. for visualizing graduating radii on a map), calling d3.geo.circle multiple times will create overlapping great circles, which may not be desired.

Here we simply subtract out the smaller great circles from the bigger ones, adding them as holes to the GeoJSON Polygon object. Note: The holes must be in reverse order to the great circle.

Compare with Overlapping Geographic Circles.

<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
background: #fcfcfa;
}
.stroke {
fill: none;
stroke: #000;
stroke-width: 3px;
}
.fill {
fill: #fff;
}
.graticule {
fill: none;
stroke: #777;
stroke-width: .5px;
stroke-opacity: .5;
}
.land {
fill: #222;
}
.boundary {
fill: none;
stroke: #fff;
stroke-width: .5px;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://d3js.org/d3.geo.projection.v0.min.js"></script>
<script src="http://d3js.org/topojson.v1.min.js"></script>
<script>
var width = 960,
height = 500;
var projection = d3.geo.naturalEarth()
.scale(167)
.translate([width / 2, height / 2])
.precision(.1);
var path = d3.geo.path()
.projection(projection);
var graticule = d3.geo.graticule();
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
svg.append("defs").append("path")
.datum({type: "Sphere"})
.attr("id", "sphere")
.attr("d", path);
svg.append("use")
.attr("class", "stroke")
.attr("xlink:href", "#sphere");
svg.append("use")
.attr("class", "fill")
.attr("xlink:href", "#sphere");
svg.append("path")
.datum(graticule)
.attr("class", "graticule")
.attr("d", path);
var circleWithAngle = (function() {
var circle = d3.geo.circle();
return function (angle) {
return circle.angle(angle)();
};
})();
var createNonOverlappingCircles = function(angles) {
var circles = [];
circles.push(circleWithAngle(angles[0]));
for (var i = 1; i < angles.length; i++) {
var circleGeo = circleWithAngle(angles[i]), // Generate a circle with the given angle
holeCircleGeo = circleWithAngle(angles[i - 1]); // And a circle with a previous angle
// Holes in GeoJSON Polygons are specified in the coordinates array in the 1 to nth index in reverse winding order
circleGeo.coordinates.push(holeCircleGeo.coordinates[0].reverse());
circles.push(circleGeo);
}
console.log(circles, "YUP");
return circles;
};
var circleAngles = [30, 50, 80, 120];
var circles = createNonOverlappingCircles(circleAngles);
var category = d3.scale.category10();
d3.json("/mbostock/raw/4090846/world-50m.json", function(error, world) {
svg.insert("path", ".graticule")
.datum(topojson.feature(world, world.objects.land))
.attr("class", "land")
.attr("d", path);
svg.insert("path", ".graticule")
.datum(topojson.mesh(world, world.objects.countries, function(a, b) { return a !== b; }))
.attr("class", "boundary")
.attr("d", path);
svg.selectAll("path.circle")
.data(circles)
.enter().append("path")
.attr("class", "circle")
.attr("d", path)
.style("fill", function(d, i) { return category(i); })
.style("opacity", 0.3);
});
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