Published
Edited
Apr 13, 2020
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
map = new mapboxgl.Map({
container: 'map',
center: [ -73.897753, 40.754693], // nyc [ -73.986753, 40.754693]
zoom: 10.6,
maxZoom: 18.5, // how close you can zoom in
minZoom: 6, // how far out you can zoom out
style: 'mapbox://styles/mapbox/dark-v9',
});

Insert cell
Insert cell
Insert cell
{
// Load GeoJSON from a data URL (instead of holding it in a JavaScript object and passing to a Mapbox GL GeoJSON Source ==> reduces client memory overhead)
var nav = new mapboxgl.NavigationControl();
map.addControl(nav, 'bottom-left');
map.on('load', function() {
// Subway LINES
var url = 'https://gist.githubusercontent.com/the-observables/7fd9846f47fad8d98a6bafbb91704c85/raw/fbbe2a2270f9f1d6a8e7d73500094c81839e7cd3/subway-lines.geojson';
map.addSource('subway_line_data', { type: 'geojson', data: url});
map.addLayer({
'id': 'subway_lines',
'type': 'line',
'source': 'subway_line_data',
'paint': { // want: 'line-color': palette.get(color(['get', 'rt_symbol'])),
'line-color': [
'match',
['get', 'rt_symbol'],
// IND Eighth Avenue Line: Dark Blue (A-C-E)
'A', '#0096e5', 'C', '#0096e5', 'E', '#0096e5',
// IND Sixth Avenue Line: Orange (B-D-F-M)
'B', '#ff9600', 'D', '#ff9600', 'F', '#ff9600','M', '#ff9600',
// IND Crosstown Line: Light Green or "Lime" (G)
'G', '#80b62d',
// BMT Canarsie Line: Light Gray (L)
'L', '#bebebe',
// BMT Nassau Street Line: Brown (J-Z)
'J', '#b47e1e', 'Z', '#b47e1e',
// BMT Broadway Line: Yellow (N-Q-R-W)
'N', '#ffd200', 'Q', '#ffd200', 'R', '#ffd200', 'W', '#ffd200',
// IRT Broadway – Seventh Avenue Line: Red (1-2-3)
'1', '#ff553c', '2', '#ff553c', '3', '#ff553c',
// IRT Lexington Avenue Line: Green (4-5-6)
'4', '#00b95b', '5', '#00b95b', '6', '#00b95b',
// IRT Flushing Line: Purple (7)
'7', '#c373d2',
// Shuttles: Gray
'S', '#9c9d9f',
// Other:
'#ccc'
],
'line-width': 2,
'line-opacity': 0.75
}
});
// STATIONS
map.addSource('subway_station_data', {
type: 'geojson',
data: march_10_2020,
cluster: true,
clusterRadius: 45 // Radius of each cluster when clustering points (defaults to 50)
});
// TODO: Scale by volume of entries&exits in cluster instead of cluster count
/*
Steps/To-Do List for Clustering:
https://blog.mapbox.com/clustering-properties-with-mapbox-and-html-markers-bb353c8662ba
1. Scale cluster by the aggregated volume of all the stations in the cluster
2. Scale stations by each one's volume of daily activity
3. Possible: on-click reveal bar graph of that station's hourly break down of entries/exits
- different endpoint
*/
map.addLayer({
id: 'clusters',
type: 'circle',
source: 'subway_station_data',
filter: ['has', 'point_count'], // # in cluster
paint: { // https://docs.mapbox.com/mapbox-gl-js/style-spec/#expressions-step)
'circle-color': [
'step',
['get', 'point_count'], // switch to gradient based off exit-to-entry ratio
'#51bbd6', 20, // blue if cluster < 20 exit/entry points
'#f1f075', 50, // yellow if cluster 20 < x < 50 exit/entry points
'#f1f075', 300, // yellow if cluster of 50 < x < 300 exit/entry points
'#f28cb1' // pink if cluster of >= 300 exit/entry points
],
'circle-radius': [
'step',
['get', 'point_count'],
9, 20, // radius = 9px if cluster < 20 exit/entry points
12, 50, // radius = 12px if cluster 20 < x < 50 exit/entry points
18, 300, // radius = 818px if cluser 50 < x < 300 exit/entry points
30 // radius = 30px if cluster of >= 300 exit/entry points
]
}
});
map.addLayer({
id: 'cluster-count',
type: 'symbol',
source: 'subway_station_data',
filter: ['has', 'point_count'],
layout: {
'text-field': '{point_count_abbreviated}',
'text-font': ['DIN Offc Pro Medium', 'Arial Unicode MS Bold'],
'text-size': 12
}
}); // ['!', ['has','point_count']]
map.addLayer({
'id': 'station',
'type': 'circle',
'source': 'subway_station_data',
'filter': ['!', ['has','point_count']],
'paint': {
'circle-color': 'white',
'circle-radius': 4,
'circle-stroke-color': 'blue',
'circle-stroke-width': 1
}
});
//pseudo voroni (increase region user can click to show pop-up)
map.addLayer({
'id': 'select-station-region',
'type': 'circle',
'source': 'subway_station_data',
'filter': ['!', ['has','point_count']],
'paint': {
'circle-color': 'rgba(0,0,0,0)', // transparent
'circle-stroke-color': 'rgba(0,0,0,0)',
'circle-radius': 10 // change to scale by volume
}
});
// Station Pop-up on-click
map.on('click', 'select-station-region', function(e) {
var coordinates = e.features[0].geometry.coordinates.slice();
var name = e.features[0].properties.STATION.toUpperCase();
console.log(name)
if(station_lines.has(name)){
new mapboxgl.Popup()
.setLngLat(coordinates)
.setHTML(build_subway_sign(name))
.addTo(map);
}else{
new mapboxgl.Popup()
.setLngLat(coordinates)
.setHTML(`<div> Station: ${name}</div>`)
.addTo(map);
}
});
map.on('mouseenter', 'select-station-region', function() {
map.getCanvas().style.cursor = 'pointer';
});
map.on('mouseleave', 'select-station-region', function() {
map.getCanvas().style.cursor = '';
});
});
}
Insert cell
Insert cell
Insert cell
slider = html`
<div class="slider container">
<div class="inner">
<h2>Transit Exits and Entries: ${date_selected}</h2>
<label id="day"></label>
<input id="slider" type="range" min="0" max="31" step="1" value="0" />
</div>
</div>
`
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function build_subway_sign(station){
var lines = station_lines.get(station)
var subway_sign = ""
var lines_html = ""
for (let subway_line of lines.values()){
var line_color = palette.get(color(subway_line)) // background circle
var text_color = (color(subway_line) === "yellow" ? "#000" : "#fff") // black or white
lines_html += `
<div class="line" style="background-color: ${line_color}">
<p style="color: ${text_color};">${subway_line}</p>
</div>`
}
subway_sign = (`<br>
<div class="subway-sign">
<div class="rule"></div>
<div class="inner">
<p class="name"> ${station} </p>
${lines_html}
</div>
</div>`
)
return subway_sign;
}
Insert cell
color = d3.scaleOrdinal()
.domain([
...["B", "D", "F", "M"],
...["A", "C", "E"],
"G",
...["J", "Z"],
"L",
...["N", "Q", "R", "W"],
"S",
...["1", "2", "3"],
...["4", "5", "6"],
"7"
])
.range([
...Array(4).fill("orange"),
...Array(3).fill("blue"),
"lime",
...Array(2).fill("brown"),
"light-grey",
...Array(4).fill("yellow"),
"medium-grey",
...Array(3).fill("red"),
...Array(3).fill("green"),
"purple"
])
Insert cell
palette = {
// http://web.mta.info/developers/resources/line_colors.htm
const p = new Map();
p.set("blue", "#0096e5"); // A,C,E (prev: 0039A6)
p.set("orange", "#f56600") // B,D,F,M (prev: FF6319)
p.set("lime", "#80b62d"); // G (prev: 6CBE45)
p.set("brown", "#b47e1e"); // J,Z (prev: 996633)
p.set("light-grey", "#bebebe"); // L (prev: A7A9AC)
p.set("yellow", "#ffd200"); // N,Q,R,W (prev: FCCC0A)
p.set("medium-grey", "#808183"); // S
p.set("red", "#EE352E"); // 1,2,3
p.set("green", "#00933C"); // 4,5,6
p.set("purple", "#B933AD"); // 7
return p;
}
Insert cell
station_lines = {
const data = await d3.csv("https://gist.githubusercontent.com/clhenrick/a585388c95c14074ebe512458e359197/raw/189cfaa73aa6dc0676e33fed17c22158b40394fe/nyc_subway_names_lines.csv")
console.log(data);
return data
.sort((a, b) => {
const nameA = a.name.toUpperCase();
const nameB = b.name.toUpperCase();
return nameA.localeCompare(nameB, "en", {numeric: true, sensitivity: 'base'});
})
.reduce((acc, cur, i) => {
const re = RegExp("Express")
const name = cur.name.toUpperCase();
let line = cur.line
.split("-")
.sort()
.filter(d => d.indexOf("Express") === -1);
if (acc.get(name)) {
acc.set(`${name}%${i}`, new Set([...line]))
} else {
acc.set(name, new Set(line));
}
return acc;
}, new Map());
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more