Skip to content

Instantly share code, notes, and snippets.

@ursulams
Last active July 9, 2020 19:26
Show Gist options
  • Save ursulams/7f4d93b0944c7c7d29ef3cc6060b808d to your computer and use it in GitHub Desktop.
Save ursulams/7f4d93b0944c7c7d29ef3cc6060b808d to your computer and use it in GitHub Desktop.
small multiples mapping nyc trash tonnage
license:mit
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<!-- <script src="d3.min.js"></script> -->
<script src="https://d3js.org/d3.v5.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/chroma-js/2.1.0/chroma.min.js"></script>
<title></title>
<style id="jsbin-css">
svg {
margin: 5px;
}
.cd {
stroke: #ada7b3;
stroke-width: 0.5;
fill: none;
}
.tooltip {
color: grey;
position: absolute;
text-align: left;
width: 150px;
height: 55px;
padding: 2px;
font-family: "Montserrat", sans-serif;
font-size: 12px;
background: white;
}
h3, .label
{
top: 30px;
font-family: "Montserrat", sans-serif;
font-size: 0.9em;
font-weight: 100;
color: grey;
}
</style>
</head>
<body>
<h3>2020 nyc total residential & school trash tonnage year over year change</h3>
<div id="chart"></div>
<script id="jsbin-source-javascript" type="text/javascript">
//each map svg size
var width = 270,
height = 320;
//map parameters
var projection = d3.geoMercator()
.scale(Math.pow(2, 14.66))
.translate([width / 1.8, height / 1.2])
.center([-73.94, 40.50]);
//community district outlines
var path = d3.geoPath()
.projection(projection);
var cd_url = "https://raw.githubusercontent.com/ursulakaczmarek/open.trash.lab/master/dsny.tonnage/nyccd.geojson";
var trash_url = "https://data.cityofnewyork.us/resource/ebb7-mvp5.json?$where=month IN('2019 / 03','2019 / 04', '2019 / 05', '2019 / 06', '2020 / 03','2020 / 04', '2020 / 05', '2020 / 06')";
Promise.all([d3.json(cd_url), d3.json(trash_url)])
.then(function(bothSets) {
var districts = bothSets[0];
var tonnage = bothSets[1];
var trashData = tonnage.filter(
row => new Date(row.month.split(" / ")[0] + "/" + row.month.split(" / ")[1] + "/01") > new Date("2019-02-28")).map(
row => [{
boroCD: row.borough_id.concat(row.communitydistrict),
date: new Date(row.month.split(" / ")[0] + "/" + row.month.split(" / ")[1] + "/01").toDateString().split(" ").slice(1).join(" "),
total: Object.entries(row).reduce(
(totals, type) => {
if (type[0].includes("tons"))
return totals + Number(type[1]);
else return +Number(totals).toFixed(3);
}, 0)}]).reduce(
(a, b) => a.concat(b));
console.log(trashData);
var yoy = trashData.reduce(
(a, b) => {
function diff(a, b) {
return +Number(100 * ((a - b) / Math.abs(b))).toFixed(3);
}
var idx = a.findIndex(
elem => elem.boroCD === b.boroCD &&
elem.date.substring(0, 3) === b.date.substring(0, 3));
if (~idx) {
a[idx].totalDiff = diff(b.total, a[idx].total);
a[idx].total2020 = b.total;
} else {
a.push(JSON.parse(JSON.stringify(b)));
}
return a
}, []);
var months = d3.nest()
.key(d => d.date)
.entries(yoy);
var totalDomain = [d3.min(months, month => d3.min(month.values, d => d.totalDiff)),
d3.max(months, month => d3.max(month.values, d => d.totalDiff))
];
var colorTotal = chroma.scale(["#5d537c", "#f28454"])
.domain(totalDomain);
var svgs = d3.select("#chart")
.selectAll("svg")
.data(months, d => d.key)
.enter()
.append("svg")
.attr("width", width)
.attr("height", height);
svgs.append("text")
.attr("class", "label")
.attr("x", 5)
.attr("y", 45)
.text(d => d.key.substring(0, 3));
//grouping element for drawing nyc in each svg
var gMaps = svgs.append("g");
//define legend
var legend = d3.select("#chart")
.append("svg")
.attr("class", "label");
//define tooltip
var tooltip = d3.select("body")
.append("div")
.attr("class", "tooltip");
//draw nyc choropleths
gMaps.selectAll(".cd")
.data(districts.features)
.enter()
.append("path")
.attr("class", "cd")
.attr("d", path);
gMaps.selectAll(".cd")
.data(d => d.values, d => (d.boroCD ? d.boroCD : d.properties.boro_cd))
.style("fill", d => colorTotal(d.totalDiff))
.on("mouseover", (d, i) => {
tooltip.transition().duration(750).style("visibility", "visible")
tooltip.html("community district: " + d.boroCD + "<br/> " + "2019 tons: " + d.total + "<br/> " + "2020 tons: " + d.total2020 + "<br/> " + "change: " + d.totalDiff + "%")
tooltip.style("top", (d3.event.pageY + 1) + "px")
tooltip.style("left", (d3.event.pageX + 1) + "px")
})
.on("click", d => {
lines(trashData, d[0], g, d);
})
.on("mouseout", (d, i) => {
tooltip.transition().duration(500).style("visibility", "hidden")
});
//map legend
legend.append("text")
.attr("class", "label")
.attr("x", 85)
.attr("y", 20)
.text("% change year over year");
legend.append("text")
.attr("class", "label")
.attr("x", 60)
.attr("y", 50)
.text(totalDomain[0] + "%");
legend.append("text")
.attr("class", "label")
.attr("x", 200)
.attr("y", 50)
.text(totalDomain[1] + "%");
var defs = legend.append("defs");
// append linearGradient element
var linearGradient = defs.append("linearGradient")
.attr("id", "linear-gradient");
linearGradient.selectAll("stop")
.data([{
offset: "0%",
color: "#5d537c"
}, {
offset: "25%",
color: "#875f73"
},
{
offset: "50%",
color: "#ac6b6a"
}, {
offset: "75%",
color: "#cf7760"
},
{
offset: "100%",
color: "#f28454"
}
])
.enter().append("stop")
.attr("offset", d => d.offset)
.attr("stop-color", d => d.color);
legend.append("rect")
.attr("width", 75)
.attr("height", 20)
.attr("x", 120)
.attr("y", 30)
.style("fill", "url(#linear-gradient)");
});
</script></body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment