Map created with CARTO's new 4.0 JS (beta) library. Data sourced from County Health Rankings and Roadmaps
Last active
June 30, 2019 05:55
-
-
Save rgdonohue/000b9f743327c5c4a45edc3fa1916dbe to your computer and use it in GitHub Desktop.
CARTO 4.0 with D3 Histogram
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>CARTO 4.0 with D3 Histogram</title> | |
<meta name="viewport" content="initial-scale=1.0"> | |
<meta charset="utf-8"> | |
<link href="https://unpkg.com/[email protected]/dist/leaflet.css" rel="stylesheet"> | |
<link href='https://fonts.googleapis.com/css?family=Lato:300,400,600,700' rel='stylesheet' type='text/css'> | |
<style> | |
* { | |
margin: 0; | |
padding: 0; | |
} | |
/* Mobile first styles b/c that's how we roll */ | |
html { | |
box-sizing: border-box; | |
height: 100%; | |
} | |
body { | |
background: #f2f6f9; | |
color: #374C70; | |
font-family: Lato, sans-serif; | |
position: relative; | |
} | |
h1, h2 { | |
font-weight: normal; | |
margin-bottom: 18px; | |
} | |
#map { | |
width: 100%; | |
height: 480px; | |
margin: 12px; | |
background: rgb(63, 150, 179); | |
} | |
#widgets { | |
margin: 12px; | |
} | |
.widget { | |
background: white; | |
padding: 10px; | |
margin-bottom: 10px; | |
} | |
#histogram { | |
width: 100%; | |
} | |
div.tooltip { | |
position: absolute; | |
min-width: 60px; | |
min-height: 28px; | |
padding: 6px 12px; | |
color: whitesmoke; | |
background: #374C70; | |
border-radius: 2px; | |
pointer-events: none; | |
z-index: 9999; | |
} | |
/* CSS rules for larger devices */ | |
@media (min-width: 900px) { | |
#container { | |
display: flex; | |
} | |
#map { | |
flex: 1.618; | |
margin-right: 0; | |
} | |
#widgets { | |
flex: 1; | |
} | |
} | |
</style> | |
</head> | |
<body> | |
<div id="container"> | |
<div id="map"></div> | |
<div id="widgets"> | |
<div class="widget"> | |
<h2>2017 uninsured adults by county</h2> | |
<div id="histogram"> | |
<svg></svg> | |
</div> | |
</div> | |
</div> | |
</div> | |
<script src="https://unpkg.com/[email protected]/dist/leaflet.js"></script> | |
<script src="https://cartodb-libs.global.ssl.fastly.net/carto.js/v4.0.0-beta.20/carto.min.js"></script> | |
<!-- Import babel standalone so the ES6 JS works --> | |
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script> | |
<script src="https://d3js.org/d3.v5.js"></script> | |
<script type="text/babel"> | |
// create a new Leaflet map | |
const map = L.map('map').setView([-5, 2], 4); | |
// Create div for the tooltip and hide with opacity | |
const div = d3.select("body").append("div") | |
.attr("class", "tooltip") | |
.style("opacity", 0); | |
// position the tooltip when moving over the map | |
// we'll handle the show/hide stuff with CARTO's method below | |
d3.select("#map") | |
.on("mousemove", event => { | |
div.style("left", (d3.event.pageX + 10) + "px") | |
.style("top", (d3.event.pageY - 30) + "px"); | |
}) | |
// create new carto client tied to account | |
const client = new carto.Client({ | |
apiKey: 'default_public', | |
username: 'rgdonohue' | |
}); | |
// graticule clipped to North America, reprojected | |
const graticuleSource = new carto.source.SQL( | |
` | |
SELECT ST_Transform(the_geom, 2163) | |
AS the_geom_webmercator, cartodb_id | |
FROM na_grat_clipped | |
` | |
); | |
// request the land, reprojecting the geometries | |
const landSource = new carto.source.SQL( | |
` | |
SELECT ST_Transform(the_geom, 2163) | |
AS the_geom_webmercator, cartodb_id | |
FROM ne_50m_land | |
` | |
); | |
// request our data layer | |
const uninsuredSource = new carto.source.SQL( | |
` | |
SELECT ST_Transform(the_geom, 2163) | |
AS the_geom_webmercator, cartodb_id, adults_percent, county | |
FROM uninsured_counties_2017 | |
` | |
); | |
// styles for graticule | |
const graticuleStyle = new carto.style.CartoCSS( | |
` | |
#layer{ | |
line-width: 1.8; | |
line-color: #c7d4e8; | |
line-opacity: 0.43; | |
} | |
` | |
); | |
// styles for land | |
// based off https://carto.com/learn/guides/styling/styling-a-custom-basemap/#custom-fill-properties | |
const landStyle = new carto.style.CartoCSS( | |
` | |
#layer { | |
polygon-fill: #d6d4ca; | |
polygon-opacity: 1; | |
line-color: #979797; | |
line-width: 0.25; | |
line-opacity: 0.5; | |
::shadow{ | |
polygon-fill: #d6d4ca; | |
image-filters: agg-stack-blur(10,10); | |
} | |
::fill { | |
line-color: #979797; | |
line-width: 0.25; | |
line-opacity: 0.5; | |
polygon-opacity: 1; | |
polygon-fill: #d6d4ca; | |
} | |
} | |
` | |
) | |
// styles for datalayer | |
const uninsuredStyle = new carto.style.CartoCSS( | |
` | |
#layer { | |
polygon-fill: ramp([adults_percent], cartocolor(OrYel), jenks(7)); | |
line-width: .7; | |
line-color: #FFF; | |
line-opacity: 0.4; | |
} | |
` | |
) | |
// create the carto layers using the source and styles | |
const graticuleLayer = new carto.layer.Layer(graticuleSource, graticuleStyle); | |
const landLayer = new carto.layer.Layer(landSource, landStyle); | |
const uninsuredLayer = new carto.layer.Layer(uninsuredSource, uninsuredStyle, { | |
// designate which attribute columns are accessible with interaction | |
featureOverColumns: ['adults_percent', 'county'] | |
}); | |
// add the layers to the client | |
client.addLayers([graticuleLayer, landLayer, uninsuredLayer]); | |
// translate layers to Leaflet map | |
client.getLeafletLayer().addTo(map); | |
// are there any errors? | |
client.on('error', clientError => { | |
console.error(clientError.message); | |
}); | |
// display and update tooltip on mouse over | |
uninsuredLayer.on('featureOver', e => { | |
div.style("opacity", 1).html(`${e.data.county} County<br> ${e.data.adults_percent} % uninsured`) | |
}); | |
// hide the tooltip | |
uninsuredLayer.on('featureOut', e => { | |
div.style("opacity", 0) | |
}); | |
// Create a histogram with x bins | |
const histogram = new carto.dataview.Histogram(uninsuredSource, 'adults_percent', { | |
bins: 7 | |
}); | |
// Set up a callback to render the histogram data every time new data is obtained | |
histogram.on('dataChanged', histoData => { | |
const data = histoData.bins.map(val => { | |
var d = {}; | |
d.min = +val.min; | |
d.max = +val.max; | |
d.freq = +val.freq; | |
return d; | |
}); | |
drawHistogramChart(data); | |
}); | |
// Add the histogram to the client | |
client.addDataview(histogram); | |
function drawHistogramChart(data) { | |
// 7 class color OrYel scheme from | |
// from https://github.com/CartoDB/CartoColor/blob/master/cartocolor.js | |
// would be nice to derive colors and class breaks from TurboCARTO results above | |
const colors = [ | |
"#ecda9a", | |
"#efc47e", | |
"#f3ad6a", | |
"#f7945d", | |
"#f97b57", | |
"#f66356", | |
"#ee4d5a" | |
] | |
const margin = { | |
top: 10, | |
right: 0, | |
bottom: 30, | |
left: 50 | |
} | |
// select target div | |
const chartDiv = d3.select("#widgets"); | |
// width and height of chart based on rendered page | |
const width = chartDiv.node().offsetWidth, | |
height = chartDiv.node().offsetHeight - 100; | |
// dynamically provide width/height | |
const svg = d3.select("#histogram svg") | |
.style('width', width + 'px') | |
.style('height', height + margin.bottom + 'px') | |
// define x axis range | |
const x = d3.scaleBand() | |
.domain(data.map(d => d.max)) | |
.range([margin.left, width - margin.right]) | |
.padding(0.1) | |
// define y axis range | |
const y = d3.scaleLinear() | |
.domain([0, d3.max(data, d => d.freq)]).nice() | |
.range([height - margin.bottom, margin.top]) | |
// draw x axis | |
const xAxis = g => g | |
.attr("transform", `translate(0,${height - margin.bottom})`) | |
.call(d3.axisBottom(x).tickSizeOuter(0)) | |
// draw y axis | |
const yAxis = g => g | |
.attr("transform", `translate(${margin.left},0)`) | |
.call(d3.axisLeft(y)) | |
.call(g => g.select(".domain").remove()) | |
// draw histogram bars | |
svg.append("g") | |
.selectAll("rect") | |
.data(data).enter() | |
.append("rect") | |
.attr("fill", function(d,i) { | |
return colors[i]; | |
}) | |
.attr("x", d => x(d.max)) | |
.attr("y", d => y(d.freq)) | |
.attr("height", d => y(0) - y(d.freq)) | |
.attr("width", x.bandwidth()); | |
svg.append("g") | |
.call(xAxis); | |
svg.append("g") | |
.call(yAxis); | |
// x-axis label | |
svg.append("text") | |
.attr("transform", `translate(${width/2},${height})`) | |
.style("text-anchor", "middle") | |
.attr("font", "16px sans-serif") | |
.attr("fill", "#374C70") | |
.attr("dy", "1.3em") | |
.text("% uninsured in county"); | |
// y-axis label | |
svg.append("text") | |
.attr("transform", "rotate(-90)") | |
.attr("y", 0) | |
.attr("x", -height / 2) | |
.attr("dy", "0.65em") | |
.attr("font", "16px sans-serif") | |
.attr("fill", "#374C70") | |
.style("text-anchor", "middle") | |
.text("# of counties"); | |
} | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment