Skip to content

Commit

Permalink
Merge pull request #20 from Datavisualiatie-UGent/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
milachae authored May 10, 2024
2 parents e9a82e6 + fb203a2 commit b1f7f3e
Show file tree
Hide file tree
Showing 18 changed files with 1,452 additions and 180 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ jobs:
with:
node-version: 20
- run: npm install
- run: node docs/data/preprocessingData.js
- run: npm run build
- run: node docs/data/allFilteredData.js
- run: npm run build:mem
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -263,3 +263,4 @@ dist
# Eigen toevoegingen

*csv
/fietstellingen/
56 changes: 56 additions & 0 deletions docs/components/dailyVolume.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import * as Plot from "npm:@observablehq/plot";

function calculateLabel(timeframe) {
let hour = timeframe.getUTCHours().toString().padStart(2, "0")
let quarter = ["00", "15", "30", "45"][Math.floor(timeframe.getUTCMinutes() / 15)]
return `${hour}:${quarter}`
}

export function doubleBarHorizontal(data, {width}) {
return Plot.plot({
width: width,
y: {grid: true},
x: {
label: null,
tickFormat: (x) => {
let minutes = new Date(x).getUTCMinutes()
if(minutes === 0){
return `${new Date(x).getUTCHours().toString().padStart(2, "0")}:00`
}
return ""
},
type:"band"
},
marginRight: 20,
marginBottom: 20,
marginLeft: 50,
color: {
scheme: "PiYg",
type: "ordinal"
},
marks: [
Plot.axisY({anchor: "left", label: "aantal fietsers"}),
Plot.rectY(
data,
{y: "in", x: "timeframe", fill: (d) => Math.sign(d.in)},
),
Plot.rectY(
data,
{y: "out", x: "timeframe", fill: (d) => Math.sign(d.out)}
),
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)
})),
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)
})),
]
})
}
145 changes: 145 additions & 0 deletions docs/components/historyPlot.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import * as Plot from "npm:@observablehq/plot";
import * as d3 from "npm:d3";

/**
* This function calculates the month when adding an index to a start date.
* @param startDate
* @param index
* @param withYear
* @returns {string}
*/
function getMonth(startDate, index, withYear = true) {
const monthNames = Array.from({length: 12}, (_, i) => new Date(0, i + 1, 0).toLocaleString('default', {month: 'short'}));

let newDate = new Date(startDate);

newDate.setMonth(newDate.getMonth() + index);
if (!withYear) {
return monthNames[newDate.getMonth()];
}
return monthNames[newDate.getMonth()] + ' ' + newDate.getFullYear();
}

/**
* This function plots the normalized data.
* @param normalizedSiteCumulativeCountsGemeente object containing per gemeente the normalized the cumulative counts normalized by month 0
* @param startDate the start date of the data
* @param gemeentenActiveSince
* @param gemeenteActiveSince the date when the gemeente started
* @param totalMothsCount the total number of months
* @param width the width of the plot
* @returns {*}
*/
export function plotNormalizedData(normalizedSiteCumulativeCountsGemeente, startDate, gemeentenActiveSince, totalMothsCount, {width} = {}, gemeenteActiveSince = undefined) {

const lines = Array.from(Object.entries(normalizedSiteCumulativeCountsGemeente)).map(([gemeente, counts], i) => {
let data;
if (gemeenteActiveSince === undefined) {
data = counts.map((value, timeslot) => ({
timeslot,
value,
gemeente
})).filter((value, timeslot) => timeslot >= gemeentenActiveSince[gemeente])
} else {
data = counts.map((value, timeslot) => ({
timeslot,
value,
gemeente
})).filter((value, timeslot) => timeslot >= gemeenteActiveSince)
}

return Plot.lineY(data, {
x: "timeslot",
y: "value",
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));

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);

return Plot.plot({
width: width,
color: {legend: true},
y: {
percent: true,
grid: true,
ticks: ticks,
label: "Percentage (%)",
},
x: {
ticks: Math.ceil(maxX) - Math.floor(minX),
label: "Maanden",
tickFormat: (d => {
if (gemeenteActiveSince === undefined) return getMonth(startDate, d);
else return getMonth(startDate, d, false);
}),
grid: true,
},
marks: [
...lines,
],
title: "Gemiddelde Cumulatieve Procentuele Verandering in Tellingen per Gemeente vanaf Maand Eén"
});
}


/**
* this function gets the trend compare data.
* @param cumulatieveCounts
* @param year
* @param firstTrend
* @param secondTrend
* @returns {{filteredObj: {[p: string]: *}, totalMothsCount: (*|number), gemeenteActiveSince: (*|{[p: string]: any}), startDate: *}}
*/
export function getTrendCompareData(cumulatieveCounts, year, firstTrend, secondTrend) {
const startDate = cumulatieveCounts.resultJSON[year].startDate;
const gemeenteActiveSince = cumulatieveCounts.resultJSON[year].gemeenteActiveSince;
const totalMothsCount = cumulatieveCounts.resultJSON[year].totalMothsCount;
const compare = cumulatieveCounts.resultJSON[year].normalizedSiteCumulativeCountsGemeente

const filteredObj = Object.fromEntries(
Object.entries(compare).filter(([key, value]) => (key === firstTrend || key === secondTrend))
);

return {
filteredObj,
startDate,
gemeenteActiveSince,
totalMothsCount
}
}


/**
* This function gets the first and second trend years.
* @param cumulatieveCounts
* @param year
* @param firstTrend
* @param secondTrend
* @returns {{secondTrendActiveSince: *, firstTrendsYears: {}, firstTrendActiveSince: *, secondTrendsYears: {}}}
*/
export function getFistAndSecondTrendYears(cumulatieveCounts, year, 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]
}
const firstTrendActiveSince = cumulatieveCounts.resultJSON[year].gemeenteActiveSince[firstTrend]
const secondTrendActiveSince = cumulatieveCounts.resultJSON[year].gemeenteActiveSince[secondTrend]

return {
firstTrendsYears,
secondTrendsYears,
firstTrendActiveSince,
secondTrendActiveSince
}
}
111 changes: 111 additions & 0 deletions docs/components/mapUtils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import L from 'npm:leaflet';


/**
* Extracts the installation date from a site
* @param d the site
* @returns {string} the installation date
*/
function extractInstallationDate(d) {
const date = new Date(d.datum_van);
return 'Datum eerste telling: ' + String(date.getDate()).padStart(2, '0') + '/' + String((date.getMonth() + 1)).padStart(2, '0') + '/' + date.getFullYear();
}

/**
* Creates a popup for a site
* @param d the site
* @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)
}

/**
* Returns a smaller bounds
* @param bounds the bounds to make smaller
* @param shrinkAmount the amount to shrink the bounds
* @returns the smaller bounds
*/
function makeSmallerBounds(bounds, shrinkAmount) {
const northEast = bounds.getNorthEast();
const southWest = bounds.getSouthWest();

const smallerNorthEast = L.latLng(northEast.lat - shrinkAmount, northEast.lng - shrinkAmount);
const smallerSouthWest = L.latLng(southWest.lat + shrinkAmount, southWest.lng + shrinkAmount);

return L.latLngBounds(smallerSouthWest, smallerNorthEast);
}


/**
* Limits a point to a bounds
* @param point the point to limit
* @param bounds the bounds to limit to
* @returns the point limited to the bounds
*/
function limitToBounds(point, bounds) {
const lat = Math.max(Math.min(point.lat, bounds.getNorth()), bounds.getSouth());
const lng = Math.max(Math.min(point.lng, bounds.getEast()), bounds.getWest());
return L.latLng(lat, lng);
}


/**
* Checks if the map is within the bounds and moves it back if it is not
* @param map the map to check
* @param bounds the bounds to check
*/
function checkBounds(map, bounds) {
let newCenter = map.getCenter();
if (!bounds.contains(newCenter)) {
newCenter = limitToBounds(newCenter, makeSmallerBounds(bounds, 0.5));
map.panTo(newCenter, {animate: true, duration: 1});
}
}


/**
* Creates a map with the given sites
* @param sites the sites to create the map with
*/
export function createMap(sites) {
// 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 map = L.map('map', {
center: [centerLat, centerLng],
bounds: bounds,
maxBoundsViscosity: 0.9,
zoomControl: false,
}).setView([centerLat, centerLng], 9);
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',
minZoom: 9
}).addTo(map);

const markers = [];

// add markers
sites.forEach((d) => {
const marker = L.marker([d.lat, d.long]).addTo(map)
.bindPopup(createPopUp(d))
.bindTooltip(d.naam);
markers.push(marker);
});


// check bounds
map.on('moveend', () => {
checkBounds(map, bounds);
});
}
Loading

0 comments on commit b1f7f3e

Please sign in to comment.