diff --git a/frontend/src/data_sockets/PandemosContext.tsx b/frontend/src/data_sockets/PandemosContext.tsx index b1f19f4c..e041c527 100644 --- a/frontend/src/data_sockets/PandemosContext.tsx +++ b/frontend/src/data_sockets/PandemosContext.tsx @@ -1,63 +1,22 @@ // SPDX-FileCopyrightText: 2024 German Aerospace Center (DLR) // SPDX-License-Identifier: Apache-2.0 -import React, {createContext, useCallback, useEffect, useState} from 'react'; +import React, {createContext, useCallback, useEffect, useMemo, 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'; - -export interface Agent { - /** ID of the agent (same as index) */ - agent_id: number; - /** Location ID of the agent's home */ - home_id: number; - /** Enum of the agent's age group (refer to key_info.md for more info) */ - age_group: number; -} - -export interface Location { - /** ID of the location (same as index) */ - location_id: number; - /** Enum of the location's type (refer to key_info.md for more info) */ - location_type: number; - /** Latitude of the location */ - lat: number; - /** Longitude of the location */ - lon: number; -} - -export interface Trip { - /** Timestep in which the trip took place */ - timestep: number; - /** ID of the agent who did the trip */ - agent_id: number; - /** ID of the trip (same as index) */ - trip_id: number; - /** ID of the start location */ - start_location: number; - /** ID of the end location */ - end_location: number; - /** Time in seconds when the trip started */ - start_time: number; - /** Time in seconds when the trip ended */ - end_time: number; - /** Enum of the mode of transportation used (refer to key_info.md for more info) */ - transport_mode: number; - /** Enum of the activity type at the end of this trip (refer to key_info.md for more info) */ - activity: number; - /** Enum of the infection state of this trip (refer to key_info.md for more info) */ - infection_state: number; -} +import {Agent, Location, Trip, TripExpanded} from '../types/pandemos'; /** Data context for the pandemos data. * Provides Crossfilter objects for agents, locations, and trips. * Use .all() to get raw array. */ export const PandemosContext = createContext<{ - agents: Crossfilter | undefined; - locations: Crossfilter | undefined; - trips: Crossfilter | undefined; + agents: Array | undefined; + locations: Array | undefined; + trips: Array | undefined; + expandedTrips: Crossfilter | undefined; tripChains: Map> | undefined; filteredTripChains: number[][] | undefined; setFilteredTripChains: ((value: number[][]) => void) | undefined; @@ -66,6 +25,7 @@ export const PandemosContext = createContext<{ agents: undefined, locations: undefined, trips: undefined, + expandedTrips: undefined, tripChains: undefined, filteredTripChains: undefined, setFilteredTripChains: undefined, @@ -73,13 +33,15 @@ export const PandemosContext = createContext<{ // Create provider component export const PandemosProvider = ({children}: {children: React.ReactNode}) => { - const [agents, setAgents] = useState>(); - const [locations, setLocations] = useState>(); - const [trips, setTrips] = useState>(); + const [agents, setAgents] = useState>(); + const [locations, setLocations] = useState>(); + const [trips, setTrips] = useState>(); const [tripChains, setTripChains] = useState>>(); const [filteredTripChains, setFilteredTripChains] = useState([]); + + // Effect to fetch the data useEffect(() => { Promise.all([ @@ -120,20 +82,20 @@ export const PandemosProvider = ({children}: {children: React.ReactNode}) => { // handle data on promises accept ([agents, locations, trips]: [Array, Array, Array]) => { // setup crossfilter objects for each - setAgents(crossfilter(agents)); - setLocations(crossfilter(locations)); - setTrips(crossfilter(trips)); + setAgents(agents); + setLocations(locations); + setTrips(trips); }, // on promises reject (reason) => console.error('Failed to parse Pandemos data.', reason) ); // This should only run once when the page loads - // TODO: Lazy load when the pandemos tab is selected + // TODO: Lazy load when the pandemos tab is selected? }, []); const getLocation = useCallback( (id: number) => { - return locations?.all().find((location) => location.location_id === id); + return locations?.find((location) => location.location_id === id); }, [locations] ); @@ -141,7 +103,7 @@ export const PandemosProvider = ({children}: {children: React.ReactNode}) => { useEffect(() => { const agentTrips = new Map>(); - for (const trip of trips?.all() ?? []) { + for (const trip of trips ?? []) { agentTrips.set(trip.agent_id, [...(agentTrips.get(trip.agent_id) ?? []), trip]); } @@ -158,6 +120,26 @@ export const PandemosProvider = ({children}: {children: React.ReactNode}) => { setTripChains(tripChains); }, [getLocation, locations, trips]); + // Preprocess a single crossfilter with information of agents & locations included in the trips + + const expandedTrips = useMemo>(() => { + if (trips && agents && locations) { + return crossfilter( + trips?.map((trip) => { + return ( + ({ + ...trip, + agent_age_group: agents[trip.agent_id].age_group, + start_location_type: locations[trip.start_location].location_type, + end_location_type: locations[trip.end_location].location_type, + } as TripExpanded) ?? {} + ); + }) + ); + } else { + return crossfilter([]); + } + }, [agents, locations, trips]); return ( { agents, locations, trips, + expandedTrips, tripChains, filteredTripChains, setFilteredTripChains, diff --git a/frontend/src/types/pandemos.ts b/frontend/src/types/pandemos.ts new file mode 100644 index 00000000..5f2cd522 --- /dev/null +++ b/frontend/src/types/pandemos.ts @@ -0,0 +1,142 @@ +// SPDX-FileCopyrightText: 2024 German Aerospace Center (DLR) +// SPDX-License-Identifier: Apache-2.0 + +/** Display names for the diffent enums of the pandemos data */ +export namespace KeyInfo { + /** Locations */ + export const location_type: Record = { + /** Home */ 0: '🏡', + /** School */ 1: '🏫', + /** Work */ 2: '🏭/🏢', + /** Social Event */ 3: '🏟', + /** Shopping */ 4: '🏪', + /** Hospital */ 5: '🏥❗', + /** ICU */ 6: '🏥‼', + /** Car */ 7: '🚘', + /** Public */ 8: '⛲', + /** Transport */ 9: '🚍', + /** Cemetery */ 10: '⚰', + }; + + /** Location types returning string and not icons. -Pawan */ + export const location_type_string: Record = { + /** Home */ 0: 'Home', + /** School */ 1: 'School', + /** Work */ 2: 'Work', + /** Social Event */ 3: 'Social Event', + /** Shopping */ 4: 'Shopping', + /** Hospital */ 5: 'Hospital', + /** ICU */ 6: 'ICU', + /** Car */ 7: 'Car', + /** Public */ 8: 'Public', + /** Transport */ 9: 'Transport', + /** Cemetery */ 10: 'Cemetery', + }; + /** Location types returning string and not icons. -Pawan */ + export const transport_mode: Record = { + /** Bike */ 0: '🚴‍♀️', + /** Car (Driver) */ 1: '🚘👤', + /** Car (Passenger) */ 2: '🚘👥', + /** Bus */ 3: '🚍', + /** Walking */ 4: '🚶‍♀️', + /** Other */ 5: '🛸', + /** Unknown */ 6: '❓', + }; + + export const transport_mode_string: Record = { + /** Bike */ 0: 'Bike', + /** Car (Driver) */ 1: 'Car_Driver', + /** Car (Passenger) */ 2: 'Car_Passenger', + /** Bus */ 3: 'Bus', + /** Walking */ 4: 'Walking', + /** Other */ 5: 'Other', + /** Unknown */ 6: 'Unknown', + }; + + export const activity: Record = { + /** Workplace */ 0: 'Workplace', + /** Education */ 1: 'Education', + /** Shopping */ 2: 'Shopping', + /** Leisure */ 3: 'Leisure', + /** Private Matters */ 4: 'Private Matters', + /** Other */ 5: 'Other', + /** Going Home */ 6: 'Going Home', + /** Unknown */ 7: 'Unknown', + }; + export const infection_state: Record = { + /** Susceptible */ 0: '🙂', + /** Infected with no symptoms */ 1: '🤔', + /** Infected with symptoms */ 2: '🤧', + /** Infected with severe symptoms */ 3: '🤒', + /** Infected with critical symptoms */ 4: '🤮', + /** Recovered */ 5: '😀', + /** Dead */ 6: '💀', + /** Unknown */ 7: '❓', + }; + + export const age: Record = { + 0: '0-4', + 1: '5-14', + 2: '15-34', + 3: '35-39', + 4: '60-79', + 5: '80+', + }; +} + +export interface Agent { + /** ID of the agent (same as index) */ + agent_id: number; + /** Location ID of the agent's home */ + home_id: number; + /** Enum of the agent's age group (refer to key_info.md for more info) */ + age_group: number; +} + +export interface Location { + /** ID of the location (same as index) */ + location_id: number; + /** Enum of the location's type (refer to key_info.md for more info) */ + location_type: number; + /** Enum of the location's type (refer to key_info.md for more info) */ + location_type_string: string; + /** Latitude of the location */ + lat: number; + /** Longitude of the location */ + lon: number; +} + +export interface Trip { + /** Timestep in which the trip took place */ + timestep: number; + /** ID of the agent who did the trip */ + agent_id: number; + /** ID of the trip (same as index) */ + trip_id: number; + /** ID of the start location */ + start_location: number; + /** ID of the end location */ + end_location: number; + /** Time in seconds when the trip started */ + start_time: number; + /** Time in seconds when the trip ended */ + end_time: number; + /** Enum of the mode of transportation used (refer to key_info.md for more info) */ + transport_mode: number; + /** Enum of the activity type at the end of this trip (refer to key_info.md for more info) */ + activity: number; + /** Enum of the infection state of this trip (refer to key_info.md for more info) */ + infection_state: number; +} + +export interface TripExpanded extends Trip { + agent_age_group: number; + start_location_type: number; + end_location_type: number; +} + +export interface TripChain { + chain_id: number; + agent_id: number; + trips: Array; +}