Public
Edited
Jun 14, 2023
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
seattleNeighborhoodsTopojson = FileAttachment("City_Clerk_Neighborhoods.topojson").json();
Insert cell
Insert cell
vl.markGeoshape({fill: '#B0B0B0', stroke: '#F8F8F8', opacity: 0.5, strokewidth: 1})
.data(vl.topojson(seattleNeighborhoodsTopojson).feature('City_Clerk_Neighborhoods'))
.project(vl.projection('mercator'))
.title('Seattle Neighborhoods')
.render()
Insert cell
Insert cell
seattleNeighborhoodsGeojson = FileAttachment("City_Clerk_Neighborhoods.geojson").json();
Insert cell
vl.markGeoshape({fill: '#B0B0B0', stroke: '#F8F8F8', opacity: 0.5, strokewidth: 1})
.data(vl.json(rewind(seattleNeighborhoodsGeojson)).property('features'))
.project(vl.projection('mercator'))
.title('Seattle Neighborhoods')
.render()
Insert cell
Insert cell
seattleGeojson = FileAttachment("2016_seattle_city.geojson").json();
Insert cell
vl.markGeoshape({fill: '#B0B0B0', stroke: '#F8F8F8', opacity: 0.5, strokewidth: 1})
.data(vl.json(seattleGeojson).property('features'))
.project(vl.projection('mercator'))
.title('Seattle Boundary')
.render()
Insert cell
Insert cell
kingCountyBoundaryGeojson = FileAttachment("King_County_with_Natural_Shoreline_for_Puget_Sound_and_Lake_Washington___kingsh_area.geojson").json();
Insert cell
vl.markGeoshape({fill: '#B0B0B0', stroke: '#F8F8F8', opacity: 0.5, strokewidth: 1})
.data(vl.json(rewind(kingCountyBoundaryGeojson)).property("features"))
.project(vl.projection('mercator'))
.title('King County')
.render()
Insert cell
Insert cell
waStateCountiesTopojson = FileAttachment("WA-53-washington-counties.json").json();
Insert cell
Insert cell
vl.markGeoshape({fill: '#B0B0B0', stroke: '#F8F8F8', opacity: 0.5, strokewidth: 1})
.data(vl.topojson(waStateCountiesTopojson).feature('cb_2015_washington_county_20m'))
.project(vl.projection('mercator'))
.title('WA State Counties')
.render()
Insert cell
Insert cell
{
const waCountyMap = vl.markGeoshape({stroke: '#F8F8F8', opacity: 0.5, strokewidth: 1})
.title('WA State Counties')
.encode(
// Highlight King County since that's our county of focus
vl.color().if("datum.properties.NAME == 'King'", vl.value('lightblue')).value('#B0B0B0')
)

const waCountyLabels = vl.markText()
.encode(
vl.longitude().fieldQ('centroidX'),
vl.latitude().fieldQ('centroidY'),
vl.text().fieldN('properties.NAME')
);

return vl.layer(waCountyMap, waCountyLabels)
.data(vl.topojson(waStateCountiesTopojson).feature('cb_2015_washington_county_20m'))
.transform(
// We're going to calculate the centroid of the neighborhood geometry
// and use this to position the county label locations
// See: https://github.com/vega/vega-lite/issues/3829#issuecomment-516662465
vl.calculate("geoCentroid(null, datum.geometry)").as('centroid'),
vl.calculate("datum.centroid[0]").as('centroidX'),
vl.calculate("datum.centroid[1]").as('centroidY'),
)
.project(vl.projection('mercator').scale(4800).center([-120, 47.3]))
.width(900).height(480).render();
}
Insert cell
Insert cell
kingCountyBusStopLocs = FileAttachment("Transit_Stops_for_King_County_Metro___transitstop_point.csv").csv({typed: true})
Insert cell
Insert cell
printTable(kingCountyBusStopLocs.slice(0,7))
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
seattleNeighborhoodBusStopTable = FileAttachment("seattleNeighborhoodBusStopTable.csv").csv({typed: true})
Insert cell
Insert cell
Insert cell
Insert cell
kingCountyBusStopTooltipFields = [{field: 'STOP_ID', type: 'nominal', title: 'Stop ID'},
{field: 'ROUTE_LIST', type: 'nominal', title: 'Bus Route(s)'},
{field: 'ON_STREET_NAME', type: 'nominal', title: 'Street Name'},
{field: 'CF_CROSS_STREETNAME', type: 'nominal', title: 'Cross Street Name'},
{field: 'ACCESSIBILITY_DECAL', type: 'nominal', title: 'Accessibility Decal'},
{field: 'NUM_SHELTERS', type: 'nominal', title: 'Num Shelters'},
{field: 'SIGNPOST', type: 'nominal', title: 'Sign Post'},
{field: 'SIGN_MOUNTING_DIR', type: 'nominal', title: 'Sign Mounting Dir'},
{field: 'SIGNPOST_ANCHOR', type: 'nominal', title: 'Sign Post Anchor'},
{field: 'FRDOORLANDING', type: 'nominal', title: 'Front Door Landing'},
{field: 'FRDOORSIDEWALKWIDTH', type: 'nominal', title: 'Front Door Sidewalk Width'},
{field: 'STOP_STATUS', type: 'nominal', title: 'Stop Status'},
{field: 'CURB', type: 'nominal', title: 'Curb'},
{field: 'CURB_HEIGHT_FRONTDOOR', type: 'nominal', title: 'Curb Height Front Door'},
{field: 'CURBPAINT', type: 'nominal', title: 'Curb Paint'},
{field: 'BIKE_RACK', type: 'nominal', title: 'Bike Rack'},
{field: 'CF_DIST_FROM_INTERSECTION', type: 'nominal', title: 'Dist from Intersection'},
{field: 'DATE_CREATED', type: 'temporal', title: 'Date Created'},
{field: 'EFFECTIVE_BEGINDATE', type: 'temporal', title: 'Effective Begin Date'}
]
Insert cell
Insert cell
// Constructing dynamic streetview url from lat, long
// Example: http://maps.google.com/?cbll=40.714103,-74.006206&cbp=12,20.09,,0,5&layer=c
// See: https://stackoverflow.com/a/2759916/388117
kingCountyGsvHref = vl.href().value({expr: "'http://maps.google.com/?cbll=' + datum.Y + ', ' + datum.X + '&cbp=12,20.09,,0,5&layer=c'"})
Insert cell
Insert cell
{
const seattleMap = vl.markGeoshape({fill: '#B0B0B0', stroke: '#F8F8F8', opacity: 0.5, strokewidth: 1})
.data(vl.topojson(seattleNeighborhoodsTopojson).feature('City_Clerk_Neighborhoods'));
const kingCountyMap = vl.markGeoshape({fill: '#F0F0F0', stroke: '#F8F8F8', strokewidth: 1})
.data(vl.json(rewind(kingCountyBoundaryGeojson)).property("features"));

const busStopPoints = vl.markCircle()
.data(kingCountyBusStopLocs)
.encode(
vl.longitude().fieldQ('X'), // longitude encoding
vl.latitude().fieldQ('Y'), // latitude encoding
vl.color().fieldN('STOP_STATUS'),
kingCountyGsvHref,
vl.tooltip(kingCountyBusStopTooltipFields),
);
return vl.layer(kingCountyMap, seattleMap, busStopPoints)
.project(vl.projection('mercator').scale(30000).center([-121.9, 47.5]))
.width(700).height(700)
.render();
}
Insert cell
Insert cell
{
const waCountyMap = vl.markGeoshape({stroke: '#F8F8F8', opacity: 0.5, strokewidth: 1})
.title('WA State Counties')
.encode(
// Highlight King County since that's our county of focus
vl.color().if("datum.properties.NAME == 'King'", vl.value('lightblue')).value('#B0B0B0')
)

const waCountyLabels = vl.markText()
.encode(
vl.longitude().fieldQ('centroidX'),
vl.latitude().fieldQ('centroidY'),
vl.text().fieldN('properties.NAME')
);

const busStopPoints = vl.markCircle()
.data(kingCountyBusStopLocs)
.encode(
vl.longitude().fieldQ('X'), // longitude encoding
vl.latitude().fieldQ('Y'), // latitude encoding
vl.color().fieldN('STOP_STATUS'),
kingCountyGsvHref,
vl.tooltip(kingCountyBusStopTooltipFields),
);

return vl.layer(waCountyMap, waCountyLabels, busStopPoints)
.data(vl.topojson(waStateCountiesTopojson).feature('cb_2015_washington_county_20m'))
.transform(
// We're going to calculate the centroid of the neighborhood geometry
// and use this to position the county label locations
// See: https://github.com/vega/vega-lite/issues/3829#issuecomment-516662465
vl.calculate("geoCentroid(null, datum.geometry)").as('centroid'),
vl.calculate("datum.centroid[0]").as('centroidX'),
vl.calculate("datum.centroid[1]").as('centroidY'),
)
.project(vl.projection('mercator').scale(17800).center([-121.9, 47.5]))
.width(900).height(480).render();
}
Insert cell
Insert cell
{
const seattleMap = vl.markGeoshape({fill: '#B0B0B0', stroke: '#F8F8F8', opacity: 0.5, strokewidth: 1})
.data(vl.topojson(seattleNeighborhoodsTopojson).feature('City_Clerk_Neighborhoods'));
const kingCountyMap = vl.markGeoshape({fill: '#F0F0F0', stroke: '#F8F8F8', strokewidth: 1})
.data(vl.json(rewind(kingCountyBoundaryGeojson)).property("features"));

const busStopPoints = vl.markCircle()
.data(kingCountyBusStopLocs)
.transform(
vl.filter("datum.STOP_STATUS == 'ACT'")
).encode(
vl.longitude().fieldQ('X'), // longitude encoding
vl.latitude().fieldQ('Y'), // latitude encoding
vl.color().fieldN('STOP_STATUS'),
kingCountyGsvHref,
vl.tooltip(kingCountyBusStopTooltipFields),
);
return vl.layer(kingCountyMap, seattleMap, busStopPoints)
.project(vl.projection('mercator').scale(30000).center([-121.9, 47.5]))
.width(700).height(700)
.render();
}
Insert cell
Insert cell
{
const seattleMap = vl.markGeoshape({fill: '#B0B0B0', stroke: '#F8F8F8', opacity: 0.5, strokewidth: 1})
.data(vl.topojson(seattleNeighborhoodsTopojson).feature('City_Clerk_Neighborhoods'));
const kingCountyMap = vl.markGeoshape({fill: '#F0F0F0', stroke: '#F8F8F8', strokewidth: 1})
.data(vl.json(rewind(kingCountyBoundaryGeojson)).property("features"));

const busStopPoints = vl.markCircle({opacity: 0.2, size: 5})
.data(kingCountyBusStopLocs)
.encode(
vl.longitude().fieldQ('X'), // longitude encoding
vl.latitude().fieldQ('Y'), // latitude encoding
vl.color().fieldN('FRDOORLANDING'),
kingCountyGsvHref,
vl.tooltip(kingCountyBusStopTooltipFields),
);
return vl.layer(kingCountyMap, seattleMap, busStopPoints)
.project(vl.projection('mercator').scale(30000).center([-121.9, 47.5]))
.width(700).height(700)
.render();
}
Insert cell
Insert cell
{
const seattleMap = vl.markGeoshape({fill: '#B0B0B0', stroke: '#F8F8F8', opacity: 0.5, strokewidth: 1})
.data(vl.topojson(seattleNeighborhoodsTopojson).feature('City_Clerk_Neighborhoods'));
const kingCountyMap = vl.markGeoshape({fill: '#F0F0F0', stroke: '#F8F8F8', strokewidth: 1})
.data(vl.json(rewind(kingCountyBoundaryGeojson)).property("features"));

const busStopPoints = vl.markCircle({opacity: 0.2})
.data(kingCountyBusStopLocs)
.transform(
vl.filter("datum.FRDOORLANDING == 'ADA Access'")
)
.encode(
vl.longitude().fieldQ('X'), // longitude encoding
vl.latitude().fieldQ('Y'), // latitude encoding
vl.color().fieldN('FRDOORLANDING'),
kingCountyGsvHref,
vl.tooltip(kingCountyBusStopTooltipFields),
);
return vl.layer(kingCountyMap, seattleMap, busStopPoints)
.project(vl.projection('mercator').scale(30000).center([-121.9, 47.5]))
.width(700).height(700)
.render();
}
Insert cell
Insert cell
Insert cell
{
const seattleMap = vl.markGeoshape({fill: '#B0B0B0', stroke: '#F8F8F8', opacity: 0.5, strokewidth: 1})
.data(vl.topojson(seattleNeighborhoodsTopojson).feature('City_Clerk_Neighborhoods'));
const kingCountyMap = vl.markGeoshape({fill: '#F0F0F0', stroke: '#F8F8F8', strokewidth: 1})
.data(vl.json(rewind(kingCountyBoundaryGeojson)).property("features"));

const busStopPoints = vl.markCircle({opacity: 0.5, size: 8})
.data(kingCountyBusStopLocs)
.encode(
vl.longitude().fieldQ('X'), // longitude encoding
vl.latitude().fieldQ('Y'), // latitude encoding
vl.color().fieldT(selectDateField),
kingCountyGsvHref,
vl.tooltip(kingCountyBusStopTooltipFields),
);
return vl.layer(kingCountyMap, seattleMap, busStopPoints)
.project(vl.projection('mercator').scale(33000).center([-121.9, 47.55]))
.width(700).height(700)
.render();
}
Insert cell
Insert cell
Insert cell
mapBusStopObjectIDToSeattleNeighborhood = {
const mapBusStopIDToNeighborhood = new Map();
for(const busStop of seattleNeighborhoodBusStopTable){
mapBusStopIDToNeighborhood.set(busStop.BusStopObjectID, busStop.Neighborhood);
}
return mapBusStopIDToNeighborhood;
}
Insert cell
Insert cell
seattleBusStopLocs = {
const seattleBusStops = new Array();
for(const busStop of kingCountyBusStopLocs){
const busStopLoc = [busStop.X, busStop.Y];

// Filter to only bus stops inside the Seattle boundary
// We'll use d3's geoContains method for this:
// https://github.com/d3/d3-geo#geoContains
// From: https://stackoverflow.com/a/43490088
if(d3.geoContains(seattleGeojson, busStopLoc)){
// add in the neighborhood
busStop.Neighborhood = mapBusStopObjectIDToSeattleNeighborhood.get(busStop.OBJECTID);
seattleBusStops.push(busStop);
}
}
return seattleBusStops;
}
Insert cell
Insert cell
mapSeattleNeighborhoodsToBusStops = {
const mapSeattleNeighborhoodsToBusStops = new Map();
for(const busStop of seattleBusStopLocs){
if(!mapSeattleNeighborhoodsToBusStops.has(busStop.Neighborhood)){
mapSeattleNeighborhoodsToBusStops.set(busStop.Neighborhood, new Array());
}
mapSeattleNeighborhoodsToBusStops.get(busStop.Neighborhood).push(busStop);
}
return mapSeattleNeighborhoodsToBusStops;
}
Insert cell
Insert cell
seattleBusStopTooltipFields = {
const seattleBusStopTooltips = Array.from(kingCountyBusStopTooltipFields);
seattleBusStopTooltips.unshift({field: 'Neighborhood', type: 'nominal', title: 'Neighborhood'});
return seattleBusStopTooltips;
}
Insert cell
Insert cell
seattleNeighborhoodBusStopStats = {
const seattleNeighborhoodBusStopStats = new Array();
for(const [neighborhood, busStops] of mapSeattleNeighborhoodsToBusStops){
seattleNeighborhoodBusStopStats.push({
Neighborhood: neighborhood,
TotalStops: busStops.length,
ActiveStops: busStops.filter(busStop => busStop.STOP_STATUS === 'ACT').length,
StopsWithAwnings: busStops.filter(busStop => busStop.AWNING === 'YES').length,
ActiveStopsWithAwnings: busStops.filter(busStop => busStop.STOP_STATUS === 'ACT' && busStop.AWNING === 'YES').length,
StopsWithShelters: busStops.filter(busStop => busStop.NUM_SHELTERS > 0).length,
ActiveStopsWithShelters: busStops.filter(busStop => busStop.STOP_STATUS === 'ACT' && busStop.NUM_SHELTERS > 0).length,
});
}
return seattleNeighborhoodBusStopStats;
}
Insert cell
Insert cell
{
const seattleMap = vl.markGeoshape({fill: '#C0C0C0', stroke: '#F8F8F8', opacity: 0.5, strokewidth: 1})
.data(vl.topojson(seattleNeighborhoodsTopojson).feature('City_Clerk_Neighborhoods'));
const kingCountyMap = vl.markGeoshape({fill: '#F0F0F0', stroke: '#F8F8F8', strokewidth: 1})
.data(vl.json(rewind(kingCountyBoundaryGeojson)).property("features"));

const busStopPoints = vl.markCircle({opacity: 0.5, size: 10})
.data(seattleBusStopLocs)
.encode(
vl.longitude().fieldQ('X'), // longitude encoding
vl.latitude().fieldQ('Y'), // latitude encoding
vl.color().fieldN('STOP_STATUS'),
kingCountyGsvHref,
vl.tooltip(seattleBusStopTooltipFields),
);
return vl.layer(kingCountyMap, seattleMap, busStopPoints)
.project(vl.projection('mercator').scale(110000).center([-122.25, 47.615]))
.width(700).height(700)
.render();
}
Insert cell
Insert cell
{
const seattleMap = vl.markGeoshape({fill: '#C0C0C0', stroke: '#F8F8F8', opacity: 0.5, strokewidth: 1})
.data(vl.topojson(seattleNeighborhoodsTopojson).feature('City_Clerk_Neighborhoods'));
const kingCountyMap = vl.markGeoshape({fill: '#F0F0F0', stroke: '#F8F8F8', strokewidth: 1})
.data(vl.json(rewind(kingCountyBoundaryGeojson)).property("features"));

const busStopPoints = vl.markCircle({opacity: 0.5, size: 10})
.data(seattleBusStopLocs)
.transform(vl.filter("datum.STOP_STATUS == 'ACT'"))
.encode(
vl.longitude().fieldQ('X'), // longitude encoding
vl.latitude().fieldQ('Y'), // latitude encoding
vl.color().fieldN('STOP_STATUS'),
kingCountyGsvHref,
vl.tooltip(seattleBusStopTooltipFields),
);
return vl.layer(kingCountyMap, seattleMap, busStopPoints)
.project(vl.projection('mercator').scale(110000).center([-122.25, 47.615]))
.width(700).height(700)
.render();
}
Insert cell
Insert cell
{
const seattleMap = vl.markGeoshape({fill: '#E0E0E0', stroke: '#F8F8F8', opacity: 0.5, strokewidth: 1})
.data(vl.topojson(seattleNeighborhoodsTopojson).feature('City_Clerk_Neighborhoods'));
const kingCountyMap = vl.markGeoshape({fill: '#F0F0F0', stroke: '#F8F8F8', strokewidth: 1})
.data(vl.json(rewind(kingCountyBoundaryGeojson)).property("features"));

const busStopPoints = vl.markCircle({opacity: 0.8, size: 13})
.data(seattleBusStopLocs)
.transform(vl.filter("datum.STOP_STATUS == 'ACT' && datum.NUM_SHELTERS > 0"))
.encode(
vl.longitude().fieldQ('X'), // longitude encoding
vl.latitude().fieldQ('Y'), // latitude encoding
vl.color().fieldO('NUM_SHELTERS'), // color by number of shelters
kingCountyGsvHref,
vl.tooltip(seattleBusStopTooltipFields),
);
return vl.layer(kingCountyMap, seattleMap, busStopPoints)
.project(vl.projection('mercator').scale(110000).center([-122.25, 47.615]))
.width(700).height(700)
.render();
}
Insert cell
Insert cell
{
// We're going to calculate the centroid of the neighborhood geometry
// and use this to position the neighborhood labels
// See: https://github.com/vega/vega-lite/issues/3829#issuecomment-516662465
const sharedTransforms = [
vl.calculate("geoCentroid(null, datum.geometry)").as('centroid'),
vl.calculate("datum.centroid[0]").as('centroidX'),
vl.calculate("datum.centroid[1] + random()/200").as('centroidY'), // also added y jitter
vl.filter("datum.properties.S_HOOD != 'OOO' && length(trim(datum.properties.S_HOOD)) > 0")]; //filter out 'OOO' & empty neighborhoods
const seattleNeighborhoodsShape = vl.markGeoshape()
.transform(sharedTransforms)
.encode(
vl.color().fieldN('properties.S_HOOD')
.legend({columns: 3, symbolLimit: 100, title: "Neighborhoods"})
);

const seattleNeighborhoodsText = vl.markText({fontSize: 8})
.transform(sharedTransforms)
.encode(
vl.longitude().fieldQ('centroidX'),
vl.latitude().fieldQ('centroidY'),
vl.text().fieldN('properties.S_HOOD')
);

return vl.layer(seattleNeighborhoodsShape, seattleNeighborhoodsText)
.data(vl.topojson(seattleNeighborhoodsTopojson).feature('City_Clerk_Neighborhoods'))
.project(vl.projection('mercator'))
.width(500).height(800).render();
}
Insert cell
Insert cell
vl.markBar({tooltip: true})
.data(seattleBusStopLocs)
.encode(
vl.y().fieldN('Neighborhood')
.sort({encoding: 'x', order: 'descending'})
.axis({labelFontSize: 7}),
vl.x().fieldN('Neighborhood').count(),
vl.color().fieldN('STOP_STATUS')
).height(600).title("Num Seattle Bus Stops by Status").render();
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more