Skip to content

Instantly share code, notes, and snippets.

@emeeks
Last active August 29, 2015 14:06
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save emeeks/1a75ec54489368b514f8 to your computer and use it in GitHub Desktop.
Save emeeks/1a75ec54489368b514f8 to your computer and use it in GitHub Desktop.
Feature Clustering - d3.carto

Quadtree-based feature clustering in d3.carto.map.

This is an early example of how using the quadtrees generated with each layer could also be used with feature layers. This allows you to dynamically aggregate features based on zoom. Polygons are colored by their shared grouping at different zoom levels. This version uses centroids--the final version will likely either use bounding box solely or give the option for both.

See also simple point clustering and more advanced point clustering that leverages circle-packing.

This also uses a custom formatter for the infoboxes to give you the name of an unclustered feature and the number of features in a clustered region.

path,circle,rect,polygon,ellipse,line {
vector-effect: non-scaling-stroke;
}
svg, canvas {
top: 0;
}
#d3MapZoomBox {
position: absolute;
z-index: 10;
height: 100px;
width: 25px;
top: 10px;
right: 50px;
}
#d3MapZoomBox > button {
height:25px;
width: 25px;
line-height: 25px;
}
.d3MapControlsBox > button {
font-size:22px;
font-weight:900;
border: none;
height:25px;
width:25px;
background: rgba(35,31,32,.85);
color: white;
padding: 0;
cursor: pointer;
}
.d3MapControlsBox > button:hover {
background: black;
}
#d3MapPanBox {
position: absolute;
z-index: 10;
height: 100px;
width: 25px;
top: 60px;
right: 50px;
}
#d3MapPanBox > button {
height:25px;
width: 25px;
line-height: 25px;
}
#d3MapPanBox > button#left {
position: absolute;
left: -25px;
top: 10px;
}
#d3MapPanBox > button#right {
position: absolute;
right: -25px;
top: 10px;
}
#d3MapLayerBox {
position: relative;
z-index: 10;
height: 100px;
width: 120px;
top: 10px;
left: 10px;
overflow: auto;
color: white;
background: rgba(35,31,32,.85);
}
#d3MapLayerBox > div {
margin: 5px;
border: none;
}
#d3MapLayerBox ul {
list-style: none;
padding: 0;
margin: 0;
cursor: pointer;
}
#d3MapLayerBox li {
list-style: none;
padding: 0;
}
#d3MapLayerBox li:hover {
font-weight:700;
}
#d3MapLayerBox li input {
cursor: pointer;
}
div.d3MapModal {
position: absolute;
z-index: 11;
background: rgba(35,31,32,.90);
top: 50px;
left: 50px;
color: white;
max-width: 400px;
}
div.d3MapModalContent {
width:100%;
height: 100%;
overflow: auto;
}
div.d3MapModalContent > p {
padding: 0px 20px;
margin: 5px 0;
}
div.d3MapModalContent > h1 {
padding: 0px 20px;
font-size: 20px;
}
div.d3MapModalArrow {
content: "";
width: 0;
height: 0;
border-left: 20px solid transparent;
border-right: 20px solid transparent;
border-top: 20px solid rgba(35,31,32,.90);
position: absolute;
bottom: -20px;
left: 33px;
}
#d3MapSVG {
}
rect.minimap-extent {
fill: rgba(200,255,255,0.35);
stroke: black;
stroke-width: 2px;
stroke-dasharray: 5 5;
}
circle.newpoints {
fill: black;
stroke: red;
stroke-width: 2px;
}
path.newfeatures {
fill: steelblue;
fill-opacity: .5;
stroke: pink;
stroke-width: 2px;
}
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Quadtree-based Feature Clustering - d3.carto</title>
<meta charset="utf-8" />
<link type="text/css" rel="stylesheet" href="d3map.css" />
<link type="text/css" rel="stylesheet" href="https://raw.githubusercontent.com/emeeks/d3-carto-map/master/examples/example.css" />
</head>
<style>
html,body {
height: 100%;
width: 100%;
margin: 0;
}
#map {
height: 100%;
width: 100%;
position: absolute;
}
.country {
stroke: white;
}
.clustered {
stroke: red;
fill: orange;
}
</style>
<script>
function makeSomeMaps() {
pathSource = 0;
map = d3.carto.map();
d3.select("#map").call(map);
map.centerOn([-88,39],"latlong");
map.setScale(2);
clusterModal = d3.carto.modal();
clusterModal.formatter(clusterFormatter);
regionLayer = d3.carto.layer.geojson();
regionLayer
.path("world.geojson")
.label("Countries")
.cssClass("country")
.renderMode("svg")
.on("recluster", recolorClusters)
.cluster(true);
map.addCartoLayer(regionLayer);
map.refresh();
function recolorClusters() {
regionLayer.clusterLayer()
.clickableFeatures(true)
.modal(clusterModal);
var clusterColor = d3.scale.category20b();
regionLayer.clusterLayer().g()
.selectAll("path")
.style("fill", function(d,i) {return clusterColor(i%20)})
.style("stroke", function(d,i) {return d3.rgb(clusterColor(i%20)).darker()})
}
function clusterFormatter(d) {
var content = "";
if (d.properties.node) {
var content = "<h1>Clustered Region</h1><p> ("+d.properties.node._d3MapQuad.size + " Countries)</p>"
}
else {
content = "<h1>Country</h1><p>Name: " + d.properties.name + "</p>"
}
return content;
}
}
</script>
<body onload="makeSomeMaps()">
<div id="map">
</div>
<footer>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8" type="text/javascript"></script>
<script src="http://d3js.org/topojson.v1.min.js" type="text/javascript">
</script>
<script src="http://d3js.org/d3.geo.projection.v0.min.js" type="text/javascript">
</script>
<script src="http://bl.ocks.org/emeeks/raw/f3105fda25ff785dc5ed/tile.js" type="text/javascript">
</script>
<script src="http://bl.ocks.org/emeeks/raw/f3105fda25ff785dc5ed/d3.quadtiles.js" type="text/javascript">
</script>
<script src="http://bl.ocks.org/emeeks/raw/f3105fda25ff785dc5ed/d3.geo.raster.js" type="text/javascript">
</script>
<script src="https://rawgit.com/emeeks/d3-carto-map/master/d3.carto.map.js" type="text/javascript">
</script>
</footer>
</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