Skip to content

Instantly share code, notes, and snippets.

@tlfrd
Last active August 14, 2017 21:47
Show Gist options
  • Save tlfrd/5efbd1639276d58c904fd1f74508335f to your computer and use it in GitHub Desktop.
Save tlfrd/5efbd1639276d58c904fd1f74508335f to your computer and use it in GitHub Desktop.
Brush Minimap
license: mit

Using d3-brush to create an interactive minimap.

<!DOCTYPE html>
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/topojson.v2.min.js"></script>
<style>
body { margin: 0; position: fixed; top: 0; right: 0; bottom: 0; left: 0; }
.minimap {
fill: white;
stroke: black;
}
.land {
fill: green;
fill-opacity: 0.5;
}
.minimap-land {
fill: white;
stroke: black;
stroke-width: 0.5;
}
.exterior {
fill: none;
stroke: black;
stroke-width: 0.5;
}
.interior {
fill: none;
stroke: black;
stroke-opacity: 0.5;
stroke-width: 0.5;
}
.border {
fill: none;
stroke: black;
}
g.brush > .handle {
display: none;
}
.overlay {
pointer-events: none;
}
</style>
</head>
<body>
<script>
var margin = {top: 0, right: 0, bottom: 0, left: 0};
var width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var minimapMargin = {right: 20, bottom: 20};
var minimapWidth = width / 4,
minimapHeight = height / 4;
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var minimap = svg.append("g")
.attr("transform", "translate(" + (width - minimapWidth - minimapMargin.right) + "," + (height - minimapHeight- minimapMargin.bottom) + ")");
minimap.append("rect")
.attr("class", "minimap")
.attr("width", minimapWidth)
.attr("height", minimapHeight);
var brush = d3.brush()
.extent([[0, 0], [minimapWidth, minimapHeight]])
.on("start brush", brushed);
var projection = d3.geoEquirectangular();
var path = d3.geoPath().projection(projection);
var miniProjection = d3.geoEquirectangular();
var miniPath = d3.geoPath().projection(miniProjection);
var mapSource = "https://unpkg.com/world-atlas@1/world/110m.json"
d3.json(mapSource, function(error, world) {
if (error) throw error;
var land = topojson.feature(world, world.objects.land);
var exteriors = topojson.mesh(world, world.objects.countries, function(a, b) { return a == b; });
var interiors = topojson.mesh(world, world.objects.countries, function(a, b) { return a !== b; });
projection.fitSize([width, height], land);
miniProjection.fitSize([minimapWidth, minimapHeight], land);
var landMap = svg.append("g")
.append("path")
.datum(land)
.attr("class", "land")
.attr("d", path);
var exteriors = svg.append("g")
.append("path")
.datum(exteriors)
.attr("class", "exterior")
.attr("d", path);
var interiors = svg.append("g")
.append("path")
.datum(interiors)
.attr("class", "interior")
.attr("d", path);
minimap.raise();
minimap.append("g")
.append("path")
.datum(land)
.attr("class", "minimap-land")
.attr("d", miniPath);
minimap.append("g")
.attr("class", "brush")
.call(brush)
.call(brush.move, [[0, 0], [minimapWidth / 2, minimapHeight / 2]]);
});
function brushed() {
var s = d3.event.selection,
c0 = s[0],
c1 = s[1];
// convert to lat long
var mpc0 = miniProjection.invert(c0);
var mpc1 = miniProjection.invert(c1);
// convert to larger pixels
var pc0 = projection(mpc0);
var pc1 = projection(mpc1);
projection.center(mpc1);
svg.select(".land").attr("d", path);
svg.select(".interior").attr("d", path);
svg.select(".exterior").attr("d", path);
svg.select(".land").attr("transform", "scale(2)")
svg.select(".interior").attr("transform", "scale(2)")
svg.select(".exterior").attr("transform", "scale(2)")
}
</script>
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment