Published
Edited
Nov 10, 2019
1 fork
Insert cell
md`# 355 parks data`
Insert cell
d3 = require('d3@5')
Insert cell
VegaLite = require('vega-embed@5')
Insert cell
md`We are loading the gas prices here and the only 2 important fields are the year and the current gas prices so we decided to return them`
Insert cell
gasPrice = d3.csv("https://www.sfu.ca/~ridgez/ParkData/gas_price.txt", d => {
return {
year: d.year,
price: d.gas_current,
}
})
Insert cell
md`We are loading the relative income data here here and the only 2 important fields are the year and the annual income. Since a date format is given, we have decided to split the date and only retrieve the year.`
Insert cell
income = d3.csv("https://www.sfu.ca/~ridgez/ParkData/MEHOINUSA672N.txt", d => {
return {
year: d.DATE.split("/")[2],
income: +d["Income (2018 Adjusted Dollars)"]
}
})
Insert cell
md`We are loading the park data here and returning the year, state, region, type of park, the name of the park and the number of visitors.`
Insert cell
park = d3.csv("https://www.sfu.ca/~ridgez/ParkData/national_parks.txt", d => {
if (d["year"].includes("Total")) return undefined;

return {
year: d.year,
state: d.state,
region:d.region,
type: d.unit_type,
name:d.parkname,
visitors: d.visitors,
count: 1,
}
})
Insert cell
md`We are loading the population per state here and returning the year, the state, and the population.`
Insert cell
population = d3.csv("https://www.sfu.ca/~ridgez/ParkData/state_pop.txt", d => {
return {
year: d.year,
state: d.state,
pop: d.pop === "NA" ? 0 : d.pop,
}
})
Insert cell
md`We are loading inbound tourism data from the world to the united states and we are returning the year, country, and the population.`
Insert cell
tourismIn = d3.csv("https://www.sfu.ca/~ridgez/ParkData/TOURISM_INBOUND_09112019011654690.txt", d => {
if (d["VARIABLE"].includes("INB_")) return undefined;
return {
year: d.YEAR,
country: d.Variable,
population: +d.Value,
}
})
Insert cell
md`Using the same data as the previous set, we are returning the type of accommodataions that the tourism are staying in.`
Insert cell
tourismAccommodation = d3.csv("https://www.sfu.ca/~ridgez/ParkData/TOURISM_INBOUND_09112019011654690.txt", d => {
if (!d["VARIABLE"].includes("INB_")) return undefined;
return {
year: d.YEAR,
accommodation: d.Variable,
population: d.Value,
}
})
Insert cell
md`In order to create a concat view with data from different datasets, we must combine them into one data set which is what the following array is doing.`
Insert cell
allDataUnion = {
let array = [];
for (let year = 1900; year < 2020; year++) {
let gasPriceThisYear = gasPrice.filter(d => d.year == year);
if (gasPriceThisYear.length > 0) gasPriceThisYear = gasPriceThisYear[0].price;
else gasPriceThisYear = 0;
let parkVisits = park.filter(d => d.year == year);
if (parkVisits.length > 0) parkVisits = d3.sum(parkVisits.map(d => d.visitors))
else parkVisits = 0;
let incomeThisYear = income.filter(d => d.year == year);
if (incomeThisYear.length > 0) incomeThisYear = incomeThisYear[0].income;
else incomeThisYear = 0;
let populationThisYear = population.filter(d => d.year == year);
if (populationThisYear.length > 0) populationThisYear = d3.sum(populationThisYear.map(d => d.pop));
else populationThisYear = 0;
let tourismThisYear = tourismIn.filter(d => d.year == year);
if (tourismThisYear.length > 0) tourismThisYear = d3.sum(tourismThisYear.map(d => d.population));
else tourismThisYear = 0;
let numberOfParksThisYear = park.filter(d => d.year == year);
if (numberOfParksThisYear.length > 0) numberOfParksThisYear = d3.sum(numberOfParksThisYear.map(d => d.count));
else numberOfParksThisYear = 0;
let d = {
year: year,
parkVisits: parkVisits,
gasPrice: gasPriceThisYear,
income: incomeThisYear,
population: populationThisYear,
tourist: tourismThisYear,
numberOfParks: numberOfParksThisYear
}
array.push(d);
}
return array;
}
Insert cell
md`The graphs below are for us to see if the datasets are working and provide us with an idea of what is happening.`
Insert cell
VegaLite({
data: { values: tourismIn },
width: 800,
height: 300,
mark: { type: "bar"},
encoding: {
x: {
field: "country",
type: "nominal",
sort: "y"
},
y: {
field: "population", type: "quantitative",
},
}
})
Insert cell
VegaLite({
data: { values: tourismAccommodation },
width: 500,
height: 300,
mark: { type: "bar"},
encoding: {
x: {
field: "accommodation",
type: "nominal",
sort: {encoding: "y", order: "descending"}
},
y: {
field: "population", type: "quantitative",
},
}
})
Insert cell
VegaLite({
data: {values: gasPrice},
mark: "line",
encoding: {
x: {field: "year", type: "temporal", sort: {encoding: "x", order: "assending"}},
y: {field: "price", type: "quantitative"},
}
})
Insert cell
md`We have tried a horizon graph to visualize the population change for the states in the United States.`
Insert cell
{
let numOfLayers = 20;

// let maxPop = d3.max(population.map(d => d.pop));
let maxPop = 40000000;

let perLayerRange = maxPop / numOfLayers;

let layers = [];
let templateLayer = {
width: 300,
height: 50,
mark: { type: "area", line: true, clip: true, orient: "vertical" },
transform: [
{
calculate: "datum.pop - 50",
as: "popShifted"
}
],
encoding: {
x: {
field: "year",
type: "temporal",
scale: {
zero: false,
nice: false
},
axis: {
ticks: false,
title: null,
}
},
y: {
field: "popShifted", type: "quantitative",
scale: { domain: [0, 1000000] }
},
opacity: { value: .1 }
}
};

for (let i = 0; i < numOfLayers; i++) {
let newLayer = JSON.parse(JSON.stringify(templateLayer));
newLayer.encoding.y.scale.domain[0] = 0;
newLayer.encoding.y.scale.domain[1] = perLayerRange;
newLayer.transform[0].calculate = "datum.pop - " + perLayerRange * i;
layers.push(newLayer);
}
return VegaLite({
data: { values: population },
facet: {
row: {
field: "state",
type: "nominal",
}
},
spec: {
layer: layers
}

})
}
Insert cell
VegaLite({
data: { values: population },
width: 300,
height: 300,
mark: { type: "line"},
encoding: {
x: {
field: "year",
type: "temporal",
},
y: {
aggregate: "sum", field: "pop", type: "quantitative",
},
}
})
Insert cell
VegaLite({
data: { values: income },
width: 300,
height: 300,
mark: { type: "line"},
encoding: {
x: {
field: "year",
type: "temporal",
},
y: {
field: "income", type: "quantitative",
},
}
})
Insert cell
md`Type of Parks`
Insert cell
md`We have also decided to look into the number different types of parks as well.`
Insert cell
md`The slider below controls the follow 2 visualizations making them interactive and linked.`
Insert cell
viewof range = rangeSlider({
min: d3.min(allDataUnion, d => d.year),
max: d3.max(allDataUnion, d => d.year),
title: 'Year',
step: 1,
description: 'filtering by year'
})
Insert cell
{
let rangedPark = park.filter(d => d.year < range[1] && d.year > range[0])
let data = d3.nest().key(d => d.name).rollup(v => v[0].type).entries(rangedPark)
return VegaLite({
data: { values: data },
mark: { type: "bar" },
encoding: {
x: {
field: "value",
type: "nominal",
axis: {title: "Types of Parks"}
},
y: {
aggregate: "count",
field: "*",
axis: {title: "Number of Parks"}
},
}
})
}
Insert cell
md`This is our most important linked visualization where the slider for the year will control the years. The following data is selectable, can be filted through brushing, are linked and details can be displayed through the delection.`
Insert cell
VegaLite({
data: { values: allDataUnion },
width: 300,
height: 300,
columns:2,
vconcat: [
{
transform: [
{filter: {field: "year", lte: range[1]}},
{filter: {field: "year", gte: range[0]}},
],
selection: {
area: { type: "interval" }
},
mark: { type: "circle", clip: true },
encoding: {
x: {
field: "gasPrice",
type: "quantitative",
},
y: {
field: "parkVisits",
type: "quantitative",
},
size: {
condition: {
selection: "area",
value: 20,
},
value: 5
},
color: {
condition: {
selection: "area",
value: "green",
},
value: "lightgray"
},
}
},
{
transform: [
{filter: {field: "year", lte: range[1]}},
{filter: {field: "year", gte: range[0]}},
],
selection: {
area: { type: "interval" }
},
mark: { type: "circle", clip: true },
encoding: {
x: {
field: "income",
type: "quantitative",
scale: {
domain: [40000, 80000]
}
},
y: {
field: "parkVisits",
type: "quantitative",
scale: {
domain: [200000000, 400000000]
}
},
size: {
condition: {
selection: "area",
value: 20,
},
value: 5
},
color: {
condition: {
selection: "area",
value: "green",
},
value: "lightgray"
},
}
}
,
{
transform: [
{filter: {field: "year", lte: range[1]}},
{filter: {field: "year", gte: range[0]}},
],
selection: {
area: { type: "interval" }
},
mark: { type: "circle", clip: true },
encoding: {
x: {
field: "tourist",
type: "quantitative",
scale: {
domain: [350000000, 500000000]
}
},
y: {
field: "parkVisits",
type: "quantitative",
scale: {
domain: [200000000, 400000000]
}
},
size: {
condition: {
selection: "area",
value: 20,
},
value: 5
},
color: {
condition: {
selection: "area",
value: "green",
},
value: "lightgray"
},
}
}
,
{
transform: [
{filter: {field: "year", lte: range[1]}},
{filter: {field: "year", gte: range[0]}},
],
selection: {
area: { type: "interval" }
},
mark: { type: "circle", clip: true },
encoding: {
x: {
field: "population",
type: "quantitative",
scale: {
domain: [200000000, 350000000]
}
},
y: {
field: "parkVisits",
type: "quantitative",
scale: {
domain: [200000000, 400000000]
}
},
size: {
condition: {
selection: "area",
value: 20,
},
value: 5
},
color: {
condition: {
selection: "area",
value: "green",
},
value: "lightgray"
},
}
}
,
{
transform: [
{filter: {field: "year", lte: range[1]}},
{filter: {field: "year", gte: range[0]}},
],
selection: {
area: { type: "interval" }
},
mark: { type: "circle", clip: true },
encoding: {
y: {
field: "parkVisits",
type: "quantitative",
scale: {
domain: [1000000, 400000000]
}
},
x: {
field: "numberOfParks",
type: "quantitative",
scale: {
domain: [0, 400]
}
},
size: {
condition: {
selection: "area",
value: 20,
},
value: 5
},
color: {
condition: {
selection: "area",
value: "green",
},
value: "lightgray"
},
}
}
]
})
Insert cell
md`After looking at the previous visualization, we think if would be interesting to see how number of parks change over time and so on so we decided to create another visualization set.`
Insert cell
viewof range2 = rangeSlider({
min: d3.min(allDataUnion, d => d.year),
max: d3.max(allDataUnion, d => d.year),
title: 'Year',
step: 1,
description: 'filtering by year'
})
Insert cell
VegaLite({
data: { values: allDataUnion },
width: 300,
height: 300,
columns:2,
vconcat: [
{
transform: [
{filter: {field: "year", lte: range2[1]}},
{filter: {field: "year", gte: range2[0]}},
],
selection: {
area: { type: "interval" }
},
mark: { type: "bar", clip: true },
encoding: {
x: {
field: "year",
type: "quantitative",
scale: {
domain: [1900, 2018]
}
},
y: {
field: "numberOfParks",
type: "quantitative",
scale: {
domain: [0, 400]
}
},
size: {
condition: {
selection: "area",
value: 20,
},
value: 5
},
color: {
condition: {
selection: "area",
value: "green",
},
value: "lightgray"
},
}
}
,
{
transform: [
{filter: {field: "year", lte: range2[1]}},
{filter: {field: "year", gte: range2[0]}},
],
selection: {
area: { type: "interval" }
},
mark: { type: "bar", clip: true },
encoding: {
x: {
field: "year",
type: "quantitative",
scale: {
domain: [1900, 2018]
}
},
y: {
field: "gasPrice",
type: "quantitative",
},
size: {
condition: {
selection: "area",
value: 20,
},
value: 5
},
color: {
condition: {
selection: "area",
value: "green",
},
value: "lightgray"
},
}
}
,
{
transform: [
{filter: {field: "year", lte: range2[1]}},
{filter: {field: "year", gte: range2[0]}},
],
selection: {
area: { type: "interval" }
},
mark: { type: "bar", clip: true },
encoding: {
x: {
field: "year",
type: "quantitative",
scale: {
domain: [1900, 2018]
}
},
y: {
field: "population",
type: "quantitative",
},
size: {
condition: {
selection: "area",
value: 20,
},
value: 5
},
color: {
condition: {
selection: "area",
value: "green",
},
value: "lightgray"
},
}
}
,
]
})
Insert cell
md`Sources`
Insert cell
md`https://data.world/inform8n/us-national-parks-visitation-1904-2016-with-boundaries
https://github.com/rfordatascience/tidytuesday/tree/master/data/2019/2019-09-17
https://stats.oecd.org/Index.aspx?DataSetCode=TOURISM_INBOUND
https://fred.stlouisfed.org/series/MEHOINUSA672N
`
Insert cell
md`Libraries`
Insert cell
md`We used the d3 and Vegalite library.`
Insert cell
import {rangeSlider} from '@mootari/range-slider'
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