Skip to content

Instantly share code, notes, and snippets.

@ursulams
Last active December 19, 2023 15:11
Show Gist options
  • Save ursulams/531690907823120f73293a0398221d23 to your computer and use it in GitHub Desktop.
Save ursulams/531690907823120f73293a0398221d23 to your computer and use it in GitHub Desktop.
points on a map with slider update
license: mit
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<script src="https://d3js.org/d3.v5.min.js"></script>
<script src="https://d3js.org/d3-time-format.v2.min.js"></script>
<title>nyc auto smash-and-grabs</title>
<style>
h1 {
font: 100 22px "Montserrat", sans-serif;
color: #969696;
}
h2 {
font: 100 16px "Montserrat", sans-serif;
color: #969696;
}
p,
.ticks {
font: 100 12px "Montserrat", sans-serif;
color: #969696;
}
.label {
font: 500 16px "Montserrat", sans-serif;
fill: #ddd;
}
.pct {
stroke-width: 0.2;
}
.track {
stroke: #000;
stroke-opacity: 0.3;
stroke-width: 10px;
stroke-linecap: round;
}
.track-inset {
stroke: #dcdcdc;
stroke-width: 8px;
stroke-linecap: round;
}
.track-overlay {
pointer-events: stroke;
stroke-width: 25px;
stroke: transparent;
cursor: crosshair;
}
.handle {
fill: #fff;
stroke: #000;
stroke-opacity: 0.5;
stroke-width: 1px;
}
#button {
position: absolute;
top: 140px;
left: 20px;
background: #5e81a3;
border: none;
color: white;
padding: 0px 5px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 14px;
cursor: pointer;
width: 80px;
height: 30px;
}
#button:hover {
background-color: #00528b;
}
</style>
</head>
<body>
<h1>NYC auto smash-and-grabs over the past year</h1>
<h2></h2>
<button id="button" style="vertical-align:middle"><span>start </span></button>
<div id="chart">
</div>
<script>
var pctUrl = "https://data.cityofnewyork.us/resource/5rqd-h5ci.geojson";
//use app token to avoid throttling:"https://data.cityofnewyork.us/resource/qgea-i56i.json?$$app_token=YOUR_TOKEN&$where=pd_cd IN('321.0','421.0') AND rpt_dt>'2019-3-31' AND crm_atpt_cptd_cd='COMPLETED' AND lat_lon IS NOT NULL&$limit=100000"
var histUrl = "https://data.cityofnewyork.us/resource/qgea-i56i.json?$where=pd_cd IN('321.0','421.0') AND rpt_dt>'2019-4-30' AND crm_atpt_cptd_cd='COMPLETED' AND lat_lon IS NOT NULL&$limit=100000";
var ytdUrl = "https://data.cityofnewyork.us/resource/5uac-w243.json?$where=pd_cd IN('321.0','421.0')AND crm_atpt_cptd_cd='COMPLETED' AND lat_lon IS NOT NULL&$limit=100000";
var formatDateYear = d3.timeFormat("%b %Y")
Promise.all([d3.json(pctUrl), d3.json(histUrl), d3.json(ytdUrl)])
.then(function(allSets) {
var precincts = allSets[0];
var histCrimes = allSets[1];
var ytdCrimes = allSets[2];
var combinedData = d3.merge([histCrimes, ytdCrimes]);
var crimeData = combinedData.map(
row => [{
number: row.cmplnt_num,
reportDate: new Date(row.rpt_dt),
offenseCode: row.pd_cd,
offense: row.pd_desc,
lat: +Number(row.latitude),
long: +Number(row.longitude)
}]).flat().sort((a, b) => a.reportDate - b.reportDate);;
var startDate = crimeData.map(d => d.reportDate)[0],
endDate = crimeData.map(d => d.reportDate).reverse()[0];
var margin = {
top: 0,
right: 10,
bottom: 5,
left: 25
},
width = 800 - margin.left - margin.right,
height = 650 - margin.top - margin.bottom;
//create top-level SVG
var svg = d3.select("#chart").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
var projection = d3.geoMercator()
.scale(60000)
.translate([width / 1.5, height])
.center([-73.94, 40.50]);
//nypd pct outlines
var path = d3.geoPath()
.projection(projection);
var targetValue = width - 25,
inputValue = 0;
var x = d3.scaleTime()
.domain([startDate, endDate])
.range([0, targetValue])
.clamp(true);
var slider = svg.append("g")
.attr("class", "slider")
.attr("transform", "translate(" + margin.left + "," + height / 20 + ")");
var startButton = d3.select("#button"),
moving = false;
slider.append("line")
.attr("class", "track")
.attr("x1", x.range()[0])
.attr("x2", x.range()[1])
.select(function() {
return this.parentNode.appendChild(this.cloneNode(true));
})
.attr("class", "track-inset")
.select(function() {
return this.parentNode.appendChild(this.cloneNode(true));
})
.attr("class", "track-overlay")
.call(d3.drag()
.on("start.interrupt", function() { slider.interrupt(); })
.on("start drag", function() {
inputValue = d3.event.x;
update(x.invert(inputValue));
})
);
slider.insert("g", ".track-overlay")
.attr("class", "ticks")
.attr("transform", "translate(30," + margin.left + ")")
.selectAll("text")
.data(x.ticks(9))
.enter()
.append("text")
.attr("x", x)
.attr("y", 10)
.attr("text-anchor", "middle")
.text(d => formatDateYear(d));
startButton.on("click", function() {
var button = d3.select(this);
if (button.text() == "pause") {
moving = false;
clearInterval(timer);
button.text("start");
} else {
moving = true;
timer = setInterval(step, 100);
button.text("pause");
}
})
function step() {
update(x.invert(inputValue));
inputValue = inputValue + (targetValue / 11);
if (inputValue > targetValue) {
moving = false;
inputValue = 0;
clearInterval(timer);
playButton.text("start");
}
}
var handle = slider.insert("circle", ".track-overlay")
.attr("class", "handle")
.attr("r", 9);
var label = slider.append("text")
.attr("class", "label")
.attr("text-anchor", "middle")
.text(formatDateYear(startDate))
.attr("transform", "translate(0," + (-20) + ")");
svg.selectAll(".pct")
.data(precincts.features)
.enter().append("path")
.attr("class", "pct")
.style("fill", "#ddd")
.style("stroke", "#969696")
.attr("d", path);
draw(crimeData, startDate);
function draw(data, date) {
svg.selectAll("locations").remove();
var locations = svg.selectAll("locations")
.data(data)
.enter().append("circle")
.attr("cx", d => projection([d.long, d.lat])[0])
.attr("cy", d => projection([d.long, d.lat])[1])
.attr("r", 2);
locations.style("fill", function(d, i) {
var d = formatDateYear(d.reportDate);
if (d === formatDateYear(date)) {
this.parentElement.appendChild(this);
return "#ff0000";
} else {
return "lightgrey";
};
})
.style("stroke", "#9696")
.style("opacity", 0.8);
}
function update(h) {
handle.attr("cx", x(h));
label.attr("x", x(h))
.text(formatDateYear(h));
draw(crimeData, h);
}
});
</script>
</body>
<footer>
<p>data <a href="https://data.cityofnewyork.us/resource/qgea-i56i" style="color:#969;text-decoration:none">nyc open data </a>
created by <a href="https://ursulams.github.io/" style="color:#969;text-decoration:none">ursula kaczmarek</a></p>
</footer>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment