Skip to content

Commit

Permalink
Merge pull request #43 from Datavisualiatie-UGent/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
jbvilla authored May 12, 2024
2 parents b1f7f3e + bd12e02 commit fb9d5e6
Show file tree
Hide file tree
Showing 19 changed files with 1,010 additions and 821 deletions.
62 changes: 62 additions & 0 deletions docs/aalst.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
---
title: Aalst
---

```js
const jaaroverzicht = FileAttachment("data/jaaroverzicht.csv").csv({typed: true});
const sites = FileAttachment("data/sites.csv").csv({typed: true});


import {overviewYearWeekday} from "./components/overviewYear.js";
import {createMap} from "./components/mapUtils.js";

```

# Aalst

```html
<style>
.center-map {
margin-left: auto;
margin-right: auto;
width: 100%;
}
.style-map {
border-radius: 25px;
border: 2px solid lightgray;
height: 300px;
}
</style>

<div class="grid grid-cols-2">
<div>
<p>
Laten we een diepgaande blik werpen op de gemeente Aalst, met name op de locatie Aalst 1. Deze bevindt zich langs een fietsautostrade (F414 Aalst – Zottegem). Aangezien dit een belangrijke verbindingsweg is, verwachten we dat deze locatie veel gebruikt wordt door fietsers.
</p>
<p>

</p>
</div>
<div class="center-map" style="width: 100%">
<div id="map" class="style-map"></div>
</div>
</div>
<hr>
```

```js
createMap(sites, 19);
```

<div>
<div>
Het jaaroverzicht bevestigt het vermoeden dat deze fietsroute vaak wordt gebruikt, met dagelijkse aantallen variërend van ongeveer 100 tot 1000. Bovendien blijkt dat er tijdens de warmere maanden meer fietsers op de weg zijn, wat duidelijk wordt door de donkerdere plekken in het midden van het jaar. Echter, dit is niet het enige opvallende. Op 27/03/2022 is er aanzienlijk meer activiteit van fietsers dan op andere momenten. Een mogelijke verklaring hiervoor is een grootschalig wielerevenement, zoals blijkt uit een beetje onderzoek. Het blijkt dat op die dag de <a href="https://valckenier.be/nl/nieuws/1700-deelnemers-en-veel-sfeer-dit-was-de-valckenier-classic-2022-xxl">Valckenier Classic 2022 XXL</a> plaatsvond, waarvan het parcours ook door Aalst liep.
</div>
<div class="grid grid-cols-1">
<div class="card">
${resize((width) => overviewYearWeekday(jaaroverzicht, parseInt(19), width))}
</div>
</div>
</div>
16 changes: 9 additions & 7 deletions docs/components/dailyVolume.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ function calculateLabel(timeframe) {

export function doubleBarHorizontal(data, {width}) {
return Plot.plot({
title: "Inkomende en uitgaande fietsers.",
width: width,
y: {grid: true},
x: {
Expand All @@ -26,30 +27,31 @@ export function doubleBarHorizontal(data, {width}) {
marginLeft: 50,
color: {
scheme: "PiYg",
type: "ordinal"
type: "ordinal",
legend: true
},
marks: [
Plot.axisY({anchor: "left", label: "aantal fietsers"}),
Plot.rectY(
data,
{y: "in", x: "timeframe", fill: (d) => Math.sign(d.in)},
{y: "out", x: "timeframe", fill: (d) => "Uitgaand"}
),
Plot.rectY(
data,
{y: "out", x: "timeframe", fill: (d) => Math.sign(d.out)}
{y: "in", x: "timeframe", fill: (d) => "Inkomend"},
),
Plot.ruleY([0]),
Plot.tip(data, Plot.pointer({
y: "out",
x: "timeframe",
title: (d) => [`Hour: ${calculateLabel(new Date(d.timeframe))}`, `arrived: ${d.in}`, `departed: ${d.out}`].join("\n\n"),
fill: (d) => Math.sign(d.in)
title: (d) => [`Uur: ${calculateLabel(new Date(d.timeframe))}`, `Inkomend: ${d.in}`, `Uitgaand: ${d.out}`].join("\n\n"),
fill: (d) => "Uitgaand"
})),
Plot.tip(data, Plot.pointer({
y: "in",
x: "timeframe",
title: (d) => [`Hour: ${calculateLabel(new Date(d.timeframe))}`, `arrived: ${d.in}`, `departed: ${d.out}`].join("\n\n"),
fill: (d) => Math.sign(d.in)
title: (d) => [`Uur: ${calculateLabel(new Date(d.timeframe))}`, `Inkomend: ${d.in}`, `Uitgaand: ${d.out}`].join("\n\n"),
fill: (d) => "Inkomend"
})),
]
})
Expand Down
21 changes: 21 additions & 0 deletions docs/components/estimatedOverview.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import * as Plot from "npm:@observablehq/plot";

export function estimatedOverview(data, k, width, showY= true) {
return Plot.plot({
title: "Drukte benadering",
width: width,
x: {
label: "datum"
},
y: {
label: "drukte",
grid: true
},

marks: [
showY ? Plot.axisY() : Plot.axisY({ticks:1, textStroke:"black", textStrokeOpacity:1, textStrokeWidth: 1}),
Plot.ruleY([0], {stroke: "red"}),
Plot.lineY(data, Plot.windowY({k: k, reduce: "mean"}, {x: (d) => new Date(d.datum), y: "aantal", stroke: "grey", curve:"basis"})),
]
})
}
71 changes: 56 additions & 15 deletions docs/components/historyPlot.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,7 @@ function getMonth(startDate, index, withYear = true) {
* @param width the width of the plot
* @returns {*}
*/
export function plotNormalizedData(normalizedSiteCumulativeCountsGemeente, startDate, gemeentenActiveSince, totalMothsCount, {width} = {}, gemeenteActiveSince = undefined) {

export function plotNormalizedData(normalizedSiteCumulativeCountsGemeente, startDate, gemeentenActiveSince, totalMothsCount, {width} = {}, gemeenteActiveSince = undefined, minY = undefined, maxY = undefined) {
const lines = Array.from(Object.entries(normalizedSiteCumulativeCountsGemeente)).map(([gemeente, counts], i) => {
let data;
if (gemeenteActiveSince === undefined) {
Expand All @@ -45,7 +44,10 @@ export function plotNormalizedData(normalizedSiteCumulativeCountsGemeente, start
timeslot,
value,
gemeente
})).filter((value, timeslot) => timeslot >= gemeenteActiveSince)
}));
if (i === 0){
data = data.filter((value, timeslot) => timeslot >= gemeenteActiveSince)
}
}

return Plot.lineY(data, {
Expand All @@ -54,16 +56,26 @@ export function plotNormalizedData(normalizedSiteCumulativeCountsGemeente, start
stroke: "gemeente",
});
});

const minY = d3.min(lines, line => d3.min(line.data, d => d.value));
const maxY = d3.max(lines, line => d3.max(line.data, d => d.value));
if (minY === undefined) {
minY = d3.min(lines, line => d3.min(line.data, d => d.value));
}
if (maxY === undefined) {
maxY = d3.max(lines, line => d3.max(line.data, d => d.value));
}

const minX = d3.min(lines, line => d3.min(line.data, d => d.timeslot));
const maxX = d3.max(lines, line => d3.max(line.data, d => d.timeslot));

const stepSize = 0.1; // Pas dit aan aan uw behoeften
const ticks = Math.ceil((maxY - minY) / stepSize);

lines.push(
Plot.dot(
[{timeslot: minX, value: minY, gemeente: ""}, {timeslot: maxX, value: maxY, gemeente: ""}],
{x: "timeslot", y: "value", fillOpacity: 0, strokeOpacity: 0}
)
);

return Plot.plot({
width: width,
color: {legend: true},
Expand All @@ -85,7 +97,7 @@ export function plotNormalizedData(normalizedSiteCumulativeCountsGemeente, start
marks: [
...lines,
],
title: "Gemiddelde Cumulatieve Procentuele Verandering in Tellingen per Gemeente vanaf Maand Eén"
title: "Procentuele verandering van cumulatief gemiddelde ten opzichte van initiële maand"
});
}

Expand Down Expand Up @@ -125,21 +137,50 @@ export function getTrendCompareData(cumulatieveCounts, year, firstTrend, secondT
* @param secondTrend
* @returns {{secondTrendActiveSince: *, firstTrendsYears: {}, firstTrendActiveSince: *, secondTrendsYears: {}}}
*/
export function getFistAndSecondTrendYears(cumulatieveCounts, year, firstTrend, secondTrend) {
export function getFistAndSecondTrendYears(cumulatieveCounts, firstTrend, secondTrend) {
const firstTrendsYears = {}
const secondTrendsYears = {}
const indexYear = Object.keys(cumulatieveCounts.resultJSON).indexOf(year)
for (let i = indexYear; i < Object.keys(cumulatieveCounts.resultJSON).length; i++) {
firstTrendsYears[firstTrend + " " + Object.keys(cumulatieveCounts.resultJSON)[i]] = cumulatieveCounts.resultJSON[Object.keys(cumulatieveCounts.resultJSON)[i]].normalizedSiteCumulativeCountsGemeente[firstTrend]
secondTrendsYears[secondTrend + " " + Object.keys(cumulatieveCounts.resultJSON)[i]] = cumulatieveCounts.resultJSON[Object.keys(cumulatieveCounts.resultJSON)[i]].normalizedSiteCumulativeCountsGemeente[secondTrend]

let firstTrendActiveSince;
let secondTrendActiveSince;

// get the first and second trend of all active years
for (let i = 0; i < Object.keys(cumulatieveCounts.resultJSON).length; i++) {
if (cumulatieveCounts.resultJSON[Object.keys(cumulatieveCounts.resultJSON)[i]].normalizedSiteCumulativeCountsGemeente[firstTrend]) {
// get the first trend active since
if (firstTrendActiveSince === undefined) {
firstTrendActiveSince = cumulatieveCounts.resultJSON[Object.keys(cumulatieveCounts.resultJSON)[i]].gemeenteActiveSince[firstTrend]
}
firstTrendsYears[firstTrend + " " + Object.keys(cumulatieveCounts.resultJSON)[i]] = cumulatieveCounts.resultJSON[Object.keys(cumulatieveCounts.resultJSON)[i]].normalizedSiteCumulativeCountsGemeente[firstTrend]
}
if (cumulatieveCounts.resultJSON[Object.keys(cumulatieveCounts.resultJSON)[i]].normalizedSiteCumulativeCountsGemeente[secondTrend]) {
// get the second trend active since
if (secondTrendActiveSince === undefined) {
secondTrendActiveSince = cumulatieveCounts.resultJSON[Object.keys(cumulatieveCounts.resultJSON)[i]].gemeenteActiveSince[secondTrend]
}
secondTrendsYears[secondTrend + " " + Object.keys(cumulatieveCounts.resultJSON)[i]] = cumulatieveCounts.resultJSON[Object.keys(cumulatieveCounts.resultJSON)[i]].normalizedSiteCumulativeCountsGemeente[secondTrend]
}
}

// calculate minY and maxY
let allYValues = [];
for (let key in firstTrendsYears) {
allYValues.push(...firstTrendsYears[key]);
}
for (let key in secondTrendsYears) {
allYValues.push(...secondTrendsYears[key]);
}
const firstTrendActiveSince = cumulatieveCounts.resultJSON[year].gemeenteActiveSince[firstTrend]
const secondTrendActiveSince = cumulatieveCounts.resultJSON[year].gemeenteActiveSince[secondTrend]

let minY = d3.min(allYValues);
let maxY = d3.max(allYValues);

return {
firstTrendsYears,
secondTrendsYears,
firstTrendActiveSince,
secondTrendActiveSince
secondTrendActiveSince,
minY,
maxY,
totalMothsCount: 12
}
}
34 changes: 21 additions & 13 deletions docs/components/mapUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ function extractInstallationDate(d) {
* @returns {string} the popup HTML String
*/
function createPopUp(d) {
return 'Naam site: ' + d.naam + '<br>' +
'Naam gemeente: ' + d.gemeente + '<br>' +
'Interval tellingen: ' + d.interval + "minuten" + '<br>' +
extractInstallationDate(d)
return 'Site ID: ' + d.siteID + '<br>' +
'Naam site: ' + d.naam + '<br>' +
'Naam gemeente: ' + d.gemeente + '<br>' +
'Interval tellingen: ' + d.interval + "minuten" + '<br>' +
extractInstallationDate(d)
}

/**
Expand Down Expand Up @@ -70,23 +71,30 @@ function checkBounds(map, bounds) {
/**
* Creates a map with the given sites
* @param sites the sites to create the map with
* @param centerSite If needs to be centered on a site
*/
export function createMap(sites) {
export function createMap(sites, centerSite=null) {
// create map
const northEast = L.latLng(51.6, 6.05),
southWest = L.latLng(50.4, 2.5),
bounds = L.latLngBounds(southWest, northEast);

const centerLat = (southWest.lat + northEast.lat) / 2;
const centerLng = (southWest.lng + northEast.lng) / 2;

const northEast = L.latLng(51.6, 6.05)
const southWest = L.latLng(50.4, 2.5)
const bounds = L.latLngBounds(southWest, northEast)

let centerLat, centerLng;
if (centerSite == null) {
centerLat = (southWest.lat + northEast.lat) / 2
centerLng = (southWest.lng + northEast.lng) / 2
} else {
const site = sites.filter(d => d.siteID === centerSite)
centerLat = site[0].lat
centerLng = site[0].long
}

const map = L.map('map', {
center: [centerLat, centerLng],
bounds: bounds,
maxBoundsViscosity: 0.9,
zoomControl: false,
}).setView([centerLat, centerLng], 9);
}).setView([centerLat, centerLng], centerSite == null ? 9 : 14);
L.tileLayer('https://{s}.basemaps.cartocdn.com/rastertiles/voyager_labels_under/{z}/{x}/{y}{r}.png', {
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>, &copy; <a href="https://carto.com/attributions">CARTO</a>',
subdomains: 'abcd',
Expand Down
39 changes: 26 additions & 13 deletions docs/components/overviewYear.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import * as d3 from "npm:d3";
*/
function filterData(data, year=null, site=null) {
return data.filter((d) => {
const y = year !== null ? new Date(d.dag).getFullYear() === year : true
const y = year !== null ? new Date(d.datum).getFullYear() === year : true
const s = site !== null ? d.siteID === site : true
return y && s
}
Expand All @@ -35,11 +35,11 @@ export function overviewYearMonth(data, year, site, width) {
y: {tickFormat: Plot.formatMonth("nl", "short")}, // labels will be names instead of numbers
marks: [
Plot.cell(filtered, {
x: d => new Date(d.dag).getDate(),
y: d => new Date(d.dag).getMonth(),
x: d => new Date(d.datum).getDate(),
y: d => new Date(d.datum).getMonth(),
fill: "aantal",
channels: {
Datum: "dag",
Datum: "datum",
},
tip: {
format: {
Expand All @@ -57,10 +57,9 @@ export function overviewYearMonth(data, year, site, width) {

/**
* @param {*} data
* @param {number} year
* @param {*} site
*/
export function overviewYearWeekday(data, year, site, width) {
export function overviewYearWeekday(data, site, width) {
// Only use the data of the correct year and site
// const filtered = filterData(data, year, site)
const filtered = filterData(data, undefined, site)
Expand All @@ -70,24 +69,38 @@ export function overviewYearWeekday(data, year, site, width) {
padding: 0,
color: {type: "linear", scheme: "Greens"},
width: width,
x: {axis: null},
x: {
axis: "both",
tickSize: "0",
tickFormat: (x) => {
let d = new Date(new Date().getFullYear(), 0, 1);
d.setDate(d.getDate() + (parseInt(x))*7)

if (d.getDate() < 8 && d.getFullYear() === new Date().getFullYear()) {
return d.toLocaleString('nl-be',{month:'short'})
} else {
return ""
}
},
},
y: {tickFormat: Plot.formatWeekday("nl", "short"), tickSize: 0}, // labels will be names instead of numbers
fy: {tickFormat: ""},
fy: {tickFormat: "", padding:0.07},
marks: [
Plot.cell(filtered, {
x: (d) => d3.utcWeek.count(d3.timeYear(new Date(d.dag)), new Date(d.dag)),
y: d => new Date(d.dag).getDay() !== 0 ? new Date(d.dag).getDay() : 7,
fy: (d) => new Date(d.dag).getFullYear(),
x: (d) => d3.utcWeek.count(d3.timeYear(new Date(d.datum)), new Date(d.datum)),
y: d => new Date(d.datum).getDay() !== 0 ? new Date(d.datum).getDay() : 7,
fy: (d) => new Date(d.datum).getFullYear(),
channels: {
Datum: "dag",
Datum: "datum",
},
fill: "aantal",
tip: {
format: {
aantal: true,
channels: true,
x: false,
y: false
y: false,
fy: false
}
},
inset: 0.5
Expand Down
Loading

0 comments on commit fb9d5e6

Please sign in to comment.