UNPKG

@lsteiner/map_module

Version:

a module for outputting locus-branded chloropleths

167 lines (155 loc) 5.27 kB
const d3 = require('d3') const topojson = require('topojson') const geo = require ('./shapefile') const scales = require ('./colorScales') const draw = (config) => { const defaults = { id : "body", data: [], title: "a new datavisualization", colorScheme: "green", numBuckets: 8, valueKey: "value", countyKey: "GEO", countyNameKey: "GEO_NAME", pointInTime: true, } const blend = {...defaults, ...config} const {id, data, title, colorScheme, numBuckets, valueKey, countyKey, countyNameKey, pointInTime} = blend //page settings const margin = {top: 0, right: 0, bottom: 0, left: 0}, width = 960 - margin.left - margin.right, height = 800 - margin.top - margin.bottom; //svg furniture set up const svg = d3.select(id).append('svg') svg.attr("width", width) .attr("height", height) .append('g') .attr('class', 'map'); const path = d3.geoPath(); if(pointInTime) { //create bucketed domain const max = d3.max(data, d => d[valueKey]) const domainArr = [d3.min(data, d => d[valueKey])] for (let i = 1; i <= numBuckets; i++ ){ domainArr.push((max/numBuckets) * i) } const color = d3.scaleQuantile().range(scales[colorScheme][numBuckets]).domain(domainArr) const tooltip = d3.select("body").append("div") .attr("class", "tooltip") .style("opacity", 0) //drawing map svg.append('title').text(title); svg.append("g") .attr("class", "counties") .selectAll("path") .data(topojson.feature(geo, geo.objects.counties).features) .enter().append("path") .attr("d", path) .attr('fill', function(d) { const value = data.find(el => el[countyKey] === d.id) if (value){ return color(value[valueKey])} return 'black' }) .style('tab-index', '0') .attr('stroke', 'black') .on("mouseover", function(d) { tooltip.transition() .duration(250) .style("opacity", 1); const obj = data.find(el => el[countyKey] === d.id) if(!obj) return; const value = obj[valueKey] const name = obj[countyNameKey] tooltip.html( "<p><strong>" + name + ",</strong></p>" + "<table><tbody><tr><td class='wide'>Population in 2017:</td><td>" + d3.format(',')(value) + "</td></tr>" ) .style("left", (d3.event.pageX + 15) + "px") .style("top", (d3.event.pageY - 28) + "px") .style('position', 'absolute') .style('padding', '7px') .style('font-size', '0.9em') .style('pointer-events', 'none') .style('background', '#fff') .style('border', '1px solid #ccc') .style('border-radius', '4px') .style('-moz-box-shadow', '3px 3px 10px 0px rgba(0, 0, 0, 0.25)') .style('-webkit-box-shadow', '3px 3px 10px 0px rgba(0, 0, 0, 0.25)') .style('box-shadow', '3px 3px 10px 0px rgba(0, 0, 0, 0.25)'); }) .on("mouseout", function(d) { tooltip.transition() .duration(250) .style("opacity", 0); }) .on('focus', (d) => { const element = document.activeElement const length = element.getTotalLength(); const point = element.getPointAtLength(length/2) tooltip.transition() .duration(250) .style("opacity", 1); const obj = data.find(el => el[countyKey] === d.id) if(!obj) return; const value = obj[valueKey] const name = obj[countyNameKey] tooltip.html( "<p><strong>" + name + ",</strong></p>" + "<table><tbody><tr><td class='wide'>Population in 2017:</td><td>" + d3.format(',')(value) + "</td></tr>" ) .style("left", (point.x + 15) + "px") .style("top", (point.y - 28) + "px") .style('position', 'absolute') .style('padding', '7px') .style('font-size', '0.9em') .style('pointer-events', 'none') .style('background', '#fff') .style('border', '1px solid #ccc') .style('border-radius', '4px') .style('-moz-box-shadow', '3px 3px 10px 0px rgba(0, 0, 0, 0.25)') .style('-webkit-box-shadow', '3px 3px 10px 0px rgba(0, 0, 0, 0.25)') .style('box-shadow', '3px 3px 10px 0px rgba(0, 0, 0, 0.25)'); }) .on('focusout', () => { tooltip.transition() .duration(250) .style("opacity", 0); }); //drawing legend const x = d3.scaleLinear() .domain([d3.min(data, (d) => d[valueKey]), d3.max(data, (d) => d[valueKey])]) .rangeRound([400, 860]); var g = svg.append("g") .attr("class", "key") .attr("transform", "translate(0,15)"); g.selectAll("rect") .data(color.range().map(function(d) { d = color.invertExtent(d); if (d[0] == null) d[0] = x.domain()[0]; if (d[1] == null) d[1] = x.domain()[1]; return d; })) .enter().append("rect") .attr("height", 8) .attr("x", function(d) { return x(d[0]); }) .attr("width", function(d) { return x(d[1]) - x(d[0]); }) .attr("fill", function(d) { return color(d[0]); }); g.call(d3.axisBottom(x) .tickSize(13) .tickFormat(function(x) { return d3.format(',')(parseInt(x))}) .tickValues(color.domain())) g.append("text") .attr("class", "caption") .attr("x", x.range()[0]) .attr("y", -6) .attr("fill", "#000") .attr("text-anchor", "start") .attr("font-weight", "bold") .text(title); //uncomment to remove top border of legend // .select(".domain") // .remove(); } } module.exports = {draw}