Published
Edited
Dec 3, 2020
8 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
data = {
let finished = false
let output = []
let fetchUrl = CORS_PROXY + INITIAL_URL
const domparser = new DOMParser()
const dateParser = d3.timeParse('%-m/%-d/%Y')

while (!finished){
// Fetch the HTML document and parse it
const rawHtml = await (await fetch(fetchUrl)).text()
const htmlDoc = domparser.parseFromString(rawHtml, 'text/html')
// Parse rows from the table into object form
const rows = Array.from(htmlDoc.querySelectorAll('.views-table tbody tr')).map(row => ({
date: dateParser(row.querySelector('.views-field-field-today-date').innerHTML.trim()),
thisYear: parseInt(
row.querySelector('.views-field-field-this-year').innerHTML.trim().replace(/,/g,''),
10
),
lastYear: parseInt(
row.querySelector('.views-field-field-last-year').innerHTML.trim().replace(/,/g,''),
10
)
}))
output = output.concat(rows)
// If next link exists, swipe the querystring and append it to the initial URL to
// paginate to the next page. Otherwise, terminate the loop
const nextLink = htmlDoc.querySelector('.pager__link--next')
if (nextLink !== null) {
fetchUrl = CORS_PROXY + INITIAL_URL + '?' + nextLink.href.split('?')[1]
} else {
finished = true
}
}
// If we get exactly 200 rows, only the first page of data loaded, most likely
if (output.length === 200) throw('Data failed to completely load. Try refreshing.')
return output
}
Insert cell
Insert cell
averageLine = vl.markLine()
.encode(
vl.opacity().value('1'),
vl.x().fieldT('date').title(null),
vl.y().fieldQ('average').axis({ format: '.2s' }).title('Travelers'),
vl.color().fieldN('year').title('Year'),
)
Insert cell
travelerPoints = vl.markPoint({ filled: true })
.encode(
vl.opacity().value('0.35'),
vl.x().fieldT('date').title(null),
vl.y().fieldQ('travelers').title('Travelers'),
vl.color().fieldN('year').title('Year')
)
Insert cell
comparisonChart = vl.layer(averageLine, travelerPoints)
.data(data)
.transform(
vl.fold(['thisYear', 'lastYear']).as(['year', 'travelers']),
vl.calculate('datum.year === "thisYear" ? 2020 : 2019').as('year'),
vl.window(vl.average('travelers').as('average'))
.sort(
vl.fieldT('date'),
vl.order('ascending')
)
.groupby('year')
.frame([-6, 0])
)
.title('Travelers passing through TSA checkpoints 2020 and 2019')
.width(width * 0.8)
.height(width * 0.8 * 0.333)
Insert cell
Insert cell
percentAverageLine = vl.markLine()
.encode(
vl.color().value('#0C8'),
vl.x().fieldT('date').title(null),
vl.y().fieldQ('percentageAverage').title('Percentage of Baseline')
)
Insert cell
percentPoints = vl.markPoint({ filled: true })
.encode(
vl.opacity().value('0.35'),
vl.color().value('#0C8'),
vl.x().fieldT('date').title(null),
vl.y().fieldQ('percentage').axis({ format: '.0%' }).title('Percentage of Baseline')
)
Insert cell
percentageChart = vl.layer(percentAverageLine, percentPoints)
.data(data)
.transform(
vl.calculate('datum.thisYear / datum.lastYear').as('percentage'),
vl.window(vl.average('percentage').as('percentageAverage'))
.sort(vl.fieldT('date'), vl.order('ascending'))
.frame([-6, 0])
)
.title('2020 travelers compared to 2019 baseline')
.width(width * 0.8)
.height(width * 0.8 * 0.333)
Insert cell
Insert cell
html`
<style type="text/css">
/* make charts responsive */
svg.marks {
width: 100% !important;
height: auto !important;
}
</style>
`
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