Skip to content

Commit

Permalink
🔧 Pre-compute trip chains.
Browse files Browse the repository at this point in the history
  • Loading branch information
JonasGilg committed Jul 24, 2024
1 parent f2b09da commit 51ad658
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 1 deletion.
16 changes: 16 additions & 0 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"i18next-browser-languagedetector": "^7.2.0",
"i18next-http-backend": "^2.4.2",
"json5": "^2.2.3",
"object-hash": "^3.0.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-i18next": "^13.5.0",
Expand All @@ -74,6 +75,7 @@
"@testing-library/user-event": "^14.5.1",
"@types/country-flag-icons": "^1.2.2",
"@types/node-fetch": "^2.6.9",
"@types/object-hash": "^3.0.6",
"@types/react": "^18.2.45",
"@types/react-dom": "^18.2.17",
"@types/react-lazyload": "^3.2.3",
Expand Down
118 changes: 117 additions & 1 deletion frontend/src/data_sockets/PandemosContext.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
// SPDX-FileCopyrightText: 2024 German Aerospace Center (DLR)
// SPDX-License-Identifier: Apache-2.0

import React, {createContext, useEffect, useState} from 'react';
import React, {createContext, useCallback, useEffect, useState} from 'react';
import crossfilter, {Crossfilter} from 'crossfilter2';
import agentList from '../../assets/pandemos/agents_lookup.json?url';
import locationList from '../../assets/pandemos/locations_lookup.json?url';
import trajectories from '../../assets/pandemos/trajectories.json?url';
import hash from 'object-hash';

export interface Agent {
/** ID of the agent (same as index) */
Expand Down Expand Up @@ -58,11 +59,15 @@ export const PandemosContext = createContext<{
agents: Crossfilter<Agent> | undefined;
locations: Crossfilter<Location> | undefined;
trips: Crossfilter<Trip> | undefined;
tripChains: Map<number, Array<Trip>> | undefined;
tripChainsByOccurrence: number[][] | undefined;
}>({
// default values should be undefined or null
agents: undefined,
locations: undefined,
trips: undefined,
tripChains: undefined,
tripChainsByOccurrence: undefined,
});

// Create provider component
Expand All @@ -71,6 +76,9 @@ export const PandemosProvider = ({children}: {children: React.ReactNode}) => {
const [locations, setLocations] = useState<Crossfilter<Location>>();
const [trips, setTrips] = useState<Crossfilter<Trip>>();

const [tripChains, setTripChains] = useState<Map<number, Array<Trip>>>();
const [tripChainsByOccurrence, setTripChainsByOccurrence] = useState<Array<number[]>>();

// Effect to fetch the data
useEffect(() => {
Promise.all([
Expand Down Expand Up @@ -122,15 +130,123 @@ export const PandemosProvider = ({children}: {children: React.ReactNode}) => {
// TODO: Lazy load when the pandemos tab is selected
}, []);

const getLocation = useCallback(
(id: number) => {
return locations?.all().find((location) => location.location_id === id);
},
[locations]
);

useEffect(() => {
const tripMap = new Map<string, Array<number>>();
const agentTrips = new Map<number, Array<Trip>>();

for (const trip of trips?.all() ?? []) {
agentTrips.set(trip.agent_id, [...(agentTrips.get(trip.agent_id) ?? []), trip]);
}

let tripId = 0;
const tripChains = new Map<number, Array<Trip>>();
for (const tripChain of agentTrips.values()) {
let start = 0;
tripChain.forEach((trip, index) => {
if (getLocation(trip.start_location)?.location_type === 0) start = index;
if (getLocation(trip.end_location)?.location_type === 0)
tripChains.set(tripId++, tripChain.slice(start, index + 1));
});
}

setTripChains(tripChains);

for (const [id, tripChain] of tripChains) {
const hashed: string = hash(
tripChain.map((trip) => ({
activity: trip.activity,
transportMode: trip.transport_mode,
start: getLocation(trip.start_location)?.location_type,
end: getLocation(trip.end_location)?.location_type,
}))
);

tripMap.set(hashed, [...(tripMap.get(hashed) ?? []), id]);
}

const sortedTrips = [...tripMap.values()].sort((a, b) => b.length - a.length);
setTripChainsByOccurrence(sortedTrips);

// TODO: Just print out the top 10 trip chains.
for (const chains of sortedTrips.slice(0, 10)) {
if (locations && chains.length > 0) {
console.log(chains.length);
printTripChain(tripChains.get(chains[0])!, locations.all());
}
}
}, [getLocation, locations, trips]);

return (
<PandemosContext.Provider
value={{
agents,
locations,
trips,
tripChains,
tripChainsByOccurrence: tripChainsByOccurrence,
}}
>
{children}
</PandemosContext.Provider>
);
};

const locationNames: Record<number, string> = {
0: '🏡', // Home
1: '🏫', // School
2: '🏭/🏢', // Work
3: '💃', // Social Event
4: '🛒', // Shopping
5: '🏥🤒', // Hospital
6: '🏥🤮', // ICU
7: '🚘', // Car
8: '⛲', // Public
9: '🚍', // Transport
10: '⚰', // Cemetery
};

const transportNames: Record<number, string> = {
0: '🚴‍♀️', // Bike
1: '🚘D', // Car (Driver)
2: 'P🚘', // Car (Passenger)
3: '🚍', // Bus
4: '🚶‍♀️', // Walking
5: 'Other', // Other
6: '❓', // Unknown
};

const activityNames: Record<number, string> = {
0: 'Workplace',
1: 'Education',
2: 'Shopping',
3: 'Leisure',
4: 'Private Matters',
5: 'Other',
6: 'Going Home',
7: 'Unknown',
};

function printTripChain(tripChain: Array<Trip>, locations: Readonly<Array<Location>>) {
const getLocation = (id: number) => {
return locationNames[locations.find((location) => location.location_id === id)?.location_type ?? -1] ?? 'unknown';
};

console.log(`Agent: ${tripChain[0].agent_id}, Trips: ${tripChain.length}`);
const chainString = tripChain.reduce((previousValue: string, trip: Trip) => {
return (
previousValue +
` —(${transportNames[trip.transport_mode]})⇾ ` +
getLocation(trip.end_location) +
` [${activityNames[trip.activity]}]`
);
}, getLocation(tripChain[0].start_location));

console.log(chainString);
}

0 comments on commit 51ad658

Please sign in to comment.