Skip to content

Commit

Permalink
Merge pull request #1164 from louisg1337/unify-base-mode
Browse files Browse the repository at this point in the history
Integrate unified base mode color and helper function
  • Loading branch information
shankari committed Aug 24, 2024
2 parents 1f1731d + a58e826 commit 02e0485
Show file tree
Hide file tree
Showing 9 changed files with 38 additions and 96 deletions.
2 changes: 1 addition & 1 deletion package.cordovabuild.json
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@
"cordova-custom-config": "^5.1.1",
"cordova-plugin-ibeacon": "git+https://github.com/louisg1337/cordova-plugin-ibeacon.git",
"core-js": "^2.5.7",
"e-mission-common": "github:JGreenlee/e-mission-common#semver:0.5.1",
"e-mission-common": "github:JGreenlee/e-mission-common#semver:0.5.4",
"enketo-core": "^6.1.7",
"enketo-transformer": "^4.0.0",
"fast-xml-parser": "^4.2.2",
Expand Down
2 changes: 1 addition & 1 deletion package.serve.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
"chartjs-adapter-luxon": "^1.3.1",
"chartjs-plugin-annotation": "^3.0.1",
"core-js": "^2.5.7",
"e-mission-common": "github:JGreenlee/e-mission-common#semver:0.5.1",
"e-mission-common": "github:JGreenlee/e-mission-common#semver:0.5.4",
"enketo-core": "^6.1.7",
"enketo-transformer": "^4.0.0",
"fast-xml-parser": "^4.2.2",
Expand Down
27 changes: 13 additions & 14 deletions www/__tests__/diaryHelper.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import {
getFormattedDateAbbr,
getFormattedTimeRange,
getDetectedModes,
getBaseModeByKey,
modeColors,
} from '../js/diary/diaryHelper';

import { base_modes } from 'e-mission-common';

import initializedI18next from '../js/i18nextInit';
window['i18next'] = initializedI18next;

Expand Down Expand Up @@ -38,20 +38,17 @@ it('returns a human readable time range', () => {
});

it('returns a Base Mode for a given key', () => {
expect(getBaseModeByKey('WALKING')).toEqual({
name: 'WALKING',
expect(base_modes.get_base_mode_by_key('WALKING')).toMatchObject({
icon: 'walk',
color: modeColors.blue,
color: base_modes.mode_colors['blue'],
});
expect(getBaseModeByKey('MotionTypes.WALKING')).toEqual({
name: 'WALKING',
expect(base_modes.get_base_mode_by_key('MotionTypes.WALKING')).toMatchObject({
icon: 'walk',
color: modeColors.blue,
color: base_modes.mode_colors['blue'],
});
expect(getBaseModeByKey('I made this type up')).toEqual({
name: 'UNKNOWN',
expect(base_modes.get_base_mode_by_key('I made this type up')).toMatchObject({
icon: 'help',
color: modeColors.grey,
color: base_modes.mode_colors['grey'],
});
});

Expand Down Expand Up @@ -87,11 +84,13 @@ let myFakeTrip2 = {
};

let myFakeDetectedModes = [
{ mode: 'BICYCLING', icon: 'bike', color: modeColors.green, pct: 89 },
{ mode: 'WALKING', icon: 'walk', color: modeColors.blue, pct: 11 },
{ mode: 'BICYCLING', icon: 'bike', color: base_modes.mode_colors['green'], pct: 89 },
{ mode: 'WALKING', icon: 'walk', color: base_modes.mode_colors['blue'], pct: 11 },
];

let myFakeDetectedModes2 = [{ mode: 'BICYCLING', icon: 'bike', color: modeColors.green, pct: 100 }];
let myFakeDetectedModes2 = [
{ mode: 'BICYCLING', icon: 'bike', color: base_modes.mode_colors['green'], pct: 100 },
];

it('returns the detected modes, with percentages, for a trip', () => {
expect(getDetectedModes(myFakeTrip)).toEqual(myFakeDetectedModes);
Expand Down
1 change: 0 additions & 1 deletion www/js/components/charting.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import color from 'color';
import { getBaseModeByKey } from '../diary/diaryHelper';
import { readableLabelToKey } from '../survey/multilabel/confirmHelper';
import { logDebug } from '../plugin/logger';

Expand Down
4 changes: 2 additions & 2 deletions www/js/diary/cards/ModesIndicator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import { View, StyleSheet } from 'react-native';
import color from 'color';
import TimelineContext from '../../TimelineContext';
import { logDebug } from '../../plugin/logger';
import { getBaseModeByKey, getBaseModeByValue } from '../diaryHelper';
import { Text, Icon, useTheme } from 'react-native-paper';
import { useTranslation } from 'react-i18next';
import { base_modes } from 'e-mission-common';

const ModesIndicator = ({ trip, detectedModes }) => {
const { t } = useTranslation();
Expand All @@ -18,7 +18,7 @@ const ModesIndicator = ({ trip, detectedModes }) => {
let modeViews;
const confirmedModeForTrip = confirmedModeFor(trip);
if (labelOptions && confirmedModeForTrip?.value) {
const baseMode = getBaseModeByKey(confirmedModeForTrip.baseMode);
const baseMode = base_modes.get_base_mode_by_key(confirmedModeForTrip.baseMode);
indicatorBorderColor = baseMode.color;
logDebug(`TripCard: got baseMode = ${JSON.stringify(baseMode)}`);
modeViews = (
Expand Down
6 changes: 3 additions & 3 deletions www/js/diary/details/TripSectionsDescriptives.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import React, { useContext } from 'react';
import { View, StyleSheet } from 'react-native';
import { Icon, Text, useTheme } from 'react-native-paper';
import useDerivedProperties from '../useDerivedProperties';
import { getBaseModeByKey, getBaseModeByValue } from '../diaryHelper';
import TimelineContext from '../../TimelineContext';
import { base_modes } from 'e-mission-common';

const TripSectionsDescriptives = ({ trip, showConfirmedMode = false }) => {
const { labelOptions, labelFor, confirmedModeFor } = useContext(TimelineContext);
Expand All @@ -24,9 +24,9 @@ const TripSectionsDescriptives = ({ trip, showConfirmedMode = false }) => {
if ((showConfirmedMode && confirmedModeForTrip) || !trip.sections?.length) {
let baseMode;
if (showConfirmedMode && labelOptions && confirmedModeForTrip) {
baseMode = getBaseModeByKey(confirmedModeForTrip.baseMode);
baseMode = base_modes.get_base_mode_by_key(confirmedModeForTrip.baseMode);
} else {
baseMode = getBaseModeByKey('UNPROCESSED');
baseMode = base_modes.get_base_mode_by_key('UNPROCESSED');
}
sections = [
{
Expand Down
76 changes: 8 additions & 68 deletions www/js/diary/diaryHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,9 @@ import { LocalDt } from '../types/serverData';
import humanizeDuration from 'humanize-duration';
import { AppConfig } from '../types/appConfigTypes';
import { ImperialConfig } from '../config/useImperialConfig';
import { base_modes } from 'e-mission-common';

export const modeColors = {
pink: '#c32e85', // oklch(56% 0.2 350) // e-car
red: '#c21725', // oklch(52% 0.2 25) // car
orange: '#bf5900', // oklch(58% 0.16 50) // air, hsr
green: '#008148', // oklch(53% 0.14 155) // bike, e-bike, moped
blue: '#0074b7', // oklch(54% 0.14 245) // walk
periwinkle: '#6356bf', // oklch(52% 0.16 285) // light rail, train, tram, subway
magenta: '#9240a4', // oklch(52% 0.17 320) // bus
grey: '#555555', // oklch(45% 0 0) // unprocessed / unknown
taupe: '#7d585a', // oklch(50% 0.05 15) // ferry, trolleybus, user-defined modes
};

type BaseMode = {
name: string;
icon: string;
color: string;
};
export type BaseModeKey = string; // TODO figure out how to get keyof typeof base_modes.BASE_MODES

// parallels the server-side MotionTypes enum: https://github.com/e-mission/e-mission-server/blob/94e7478e627fa8c171323662f951c611c0993031/emission/core/wrapper/motionactivity.py#L12
export type MotionTypeKey =
Expand All @@ -42,54 +27,9 @@ export type MotionTypeKey =
| 'STOPPED_WHILE_IN_VEHICLE'
| 'AIR_OR_HSR';

const BaseModes: { [k: string]: BaseMode } = {
// BEGIN MotionTypes
IN_VEHICLE: { name: 'IN_VEHICLE', icon: 'speedometer', color: modeColors.red },
BICYCLING: { name: 'BICYCLING', icon: 'bike', color: modeColors.green },
ON_FOOT: { name: 'ON_FOOT', icon: 'walk', color: modeColors.blue },
UNKNOWN: { name: 'UNKNOWN', icon: 'help', color: modeColors.grey },
WALKING: { name: 'WALKING', icon: 'walk', color: modeColors.blue },
AIR_OR_HSR: { name: 'AIR_OR_HSR', icon: 'airplane', color: modeColors.orange },
// END MotionTypes
CAR: { name: 'CAR', icon: 'car', color: modeColors.red },
E_CAR: { name: 'E_CAR', icon: 'car-electric', color: modeColors.pink },
E_BIKE: { name: 'E_BIKE', icon: 'bicycle-electric', color: modeColors.green },
E_SCOOTER: { name: 'E_SCOOTER', icon: 'scooter-electric', color: modeColors.periwinkle },
MOPED: { name: 'MOPED', icon: 'moped', color: modeColors.green },
TAXI: { name: 'TAXI', icon: 'taxi', color: modeColors.red },
BUS: { name: 'BUS', icon: 'bus-side', color: modeColors.magenta },
AIR: { name: 'AIR', icon: 'airplane', color: modeColors.orange },
LIGHT_RAIL: { name: 'LIGHT_RAIL', icon: 'train-car-passenger', color: modeColors.periwinkle },
TRAIN: { name: 'TRAIN', icon: 'train-car-passenger', color: modeColors.periwinkle },
TRAM: { name: 'TRAM', icon: 'fas fa-tram', color: modeColors.periwinkle },
SUBWAY: { name: 'SUBWAY', icon: 'subway-variant', color: modeColors.periwinkle },
FERRY: { name: 'FERRY', icon: 'ferry', color: modeColors.taupe },
TROLLEYBUS: { name: 'TROLLEYBUS', icon: 'bus-side', color: modeColors.taupe },
UNPROCESSED: { name: 'UNPROCESSED', icon: 'help', color: modeColors.grey },
OTHER: { name: 'OTHER', icon: 'pencil-circle', color: modeColors.taupe },
};

export type BaseModeKey = keyof typeof BaseModes;
/**
* @param motionName A string like "WALKING" or "MotionTypes.WALKING"
* @returns A BaseMode object containing the name, icon, and color of the motion type
*/
export function getBaseModeByKey(
motionName: BaseModeKey | MotionTypeKey | `MotionTypes.${MotionTypeKey}`,
) {
const key = ('' + motionName).toUpperCase();
const pop = key.split('.').pop(); // if "MotionTypes.WALKING", then just take "WALKING"
return (pop && BaseModes[pop]) || BaseModes.UNKNOWN;
}

export function getBaseModeByValue(value: string, labelOptions: LabelOptions) {
const modeOption = labelOptions?.MODE?.find((opt) => opt.value == value);
return getBaseModeByKey(modeOption?.baseMode || 'OTHER');
}

export function getBaseModeByText(text: string, labelOptions: LabelOptions) {
const modeOption = labelOptions?.MODE?.find((opt) => opt.text == text);
return getBaseModeByKey(modeOption?.baseMode || 'OTHER');
return base_modes.get_base_mode_by_key(modeOption?.baseMode || 'OTHER');
}

/**
Expand Down Expand Up @@ -172,11 +112,11 @@ export function getDetectedModes(trip: CompositeTrip) {
if (!sectionSummary?.distance) return [];

return Object.entries(sectionSummary.distance)
.sort(([modeA, distA], [modeB, distB]) => distB - distA) // sort by distance (highest first)
.sort(([modeA, distA]: [string, number], [modeB, distB]: [string, number]) => distB - distA) // sort by distance (highest first)
.map(([mode, dist]: [MotionTypeKey, number]) => ({
mode,
icon: getBaseModeByKey(mode)?.icon,
color: getBaseModeByKey(mode)?.color || 'black',
icon: base_modes.get_base_mode_by_key(mode)?.icon,
color: base_modes.get_base_mode_by_key(mode)?.color || 'black',
pct: Math.round((dist / trip.distance) * 100) || '<1', // if rounds to 0%, show <1%
}));
}
Expand All @@ -187,8 +127,8 @@ export function getFormattedSectionProperties(trip: CompositeTrip, imperialConfi
duration: getFormattedTimeRange(s.start_fmt_time, s.end_fmt_time),
distance: imperialConfig.getFormattedDistance(s.distance),
distanceSuffix: imperialConfig.distanceSuffix,
icon: getBaseModeByKey(s.sensed_mode_str)?.icon,
color: getBaseModeByKey(s.sensed_mode_str)?.color || '#333',
icon: base_modes.get_base_mode_by_key(s.sensed_mode_str)?.icon,
color: base_modes.get_base_mode_by_key(s.sensed_mode_str)?.color || '#333',
}));
}

Expand Down
11 changes: 7 additions & 4 deletions www/js/diary/timelineHelper.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { displayError, displayErrorMsg, logDebug } from '../plugin/logger';
import { getBaseModeByKey, getBaseModeByValue } from './diaryHelper';
import { getUnifiedDataForInterval } from '../services/unifiedDataLoader';
import { getRawEntries } from '../services/commHelper';
import { ServerResponse, BEMData } from '../types/serverData';
Expand Down Expand Up @@ -28,7 +27,7 @@ import {
} from '../survey/enketo/enketoHelper';
import { AppConfig } from '../types/appConfigTypes';
import { Point, Feature } from 'geojson';
import { ble_matching } from 'e-mission-common';
import { ble_matching, base_modes } from 'e-mission-common';

const cachedGeojsons: Map<string, GeoJSONData> = new Map();

Expand All @@ -42,7 +41,8 @@ export function useGeojsonForTrip(trip: CompositeTrip, baseMode?: string) {
return cachedGeojsons.get(gjKey);
}

const trajectoryColor = (baseMode && getBaseModeByKey(baseMode)?.color) || undefined;
const trajectoryColor =
(baseMode && base_modes.get_base_mode_by_key(baseMode)?.color) || undefined;

logDebug("Reading trip's " + trip.locations.length + ' location points at ' + new Date());
const features = [
Expand Down Expand Up @@ -266,7 +266,10 @@ function locations2GeojsonTrajectory(
style: {
/* If a color was passed as arg, use it for the whole trajectory. Otherwise, use the
color for the sensed mode of this section, and fall back to dark grey */
color: trajectoryColor || getBaseModeByKey(section?.sensed_mode_str)?.color || '#333',
color:
trajectoryColor ||
base_modes.get_base_mode_by_key(section?.sensed_mode_str)?.color ||
'#333',
},
properties: {
feature_type: 'section_trajectory',
Expand Down
5 changes: 3 additions & 2 deletions www/js/metrics/MetricsCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ import {
import ToggleSwitch from '../components/ToggleSwitch';
import { cardStyles } from './MetricsTab';
import { labelKeyToRichMode, labelOptions } from '../survey/multilabel/confirmHelper';
import { getBaseModeByKey, getBaseModeByText, modeColors } from '../diary/diaryHelper';
import { getBaseModeByText } from '../diary/diaryHelper';
import { useTranslation } from 'react-i18next';
import { GroupingField, MetricName } from '../types/appConfigTypes';
import { useImperialConfig } from '../config/useImperialConfig';
import { base_modes } from 'e-mission-common';

type Props = {
metricName: MetricName;
Expand Down Expand Up @@ -115,7 +116,7 @@ const MetricsCard = ({
// All other modes are colored according to their base mode
const getColorForLabel = (label: string) => {
if (label == 'Unlabeled') {
const unknownModeColor = getBaseModeByKey('UNKNOWN').color;
const unknownModeColor = base_modes.get_base_mode_by_key('UNKNOWN').color;
return colorLib(unknownModeColor).alpha(0.15).rgb().string();
}
return getBaseModeByText(label, labelOptions).color;
Expand Down

0 comments on commit 02e0485

Please sign in to comment.