Using d3-brush to create an interactive minimap.
forked from tlfrd's block: Brush Minimap
| license: mit |
Using d3-brush to create an interactive minimap.
forked from tlfrd's block: Brush 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> |