Skip to content

Commit

Permalink
Merge branch 'unprocessed-trip-sections' of https://github.com/JGreen…
Browse files Browse the repository at this point in the history
…lee/e-mission-phone into ble-ui-changes
  • Loading branch information
JGreenlee committed Apr 14, 2024
2 parents d61a4af + 826190a commit 8de3ca0
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 69 deletions.
120 changes: 75 additions & 45 deletions www/js/diary/timelineHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import {
CompositeTrip,
UnprocessedTrip,
BluetoothBleData,
SectionData,
CompositeTripLocation,
} from '../types/diaryTypes';
import { getLabelInputDetails, getLabelInputs } from '../survey/multilabel/confirmHelper';
import { LabelOptions } from '../types/labelTypes';
Expand Down Expand Up @@ -228,10 +230,10 @@ const location2GeojsonPoint = (locationPoint: Point, featureType: string): Featu
*/
function locations2GeojsonTrajectory(
trip: CompositeTrip,
locationList: Array<Point>,
locationList: CompositeTripLocation[],
trajectoryColor?: string,
) {
let sectionsPoints;
): Feature[] {
let sectionsPoints: CompositeTripLocation[][];
if (!trip.sections) {
// this is a unimodal trip so we put all the locations in one section
sectionsPoints = [locationList];
Expand All @@ -255,6 +257,9 @@ function locations2GeojsonTrajectory(
color for the sensed mode of this section, and fall back to dark grey */
color: trajectoryColor || getBaseModeByKey(section?.sensed_mode_str)?.color || '#333',
},
properties: {
feature_type: 'section_trajectory',
},
};
});
}
Expand Down Expand Up @@ -301,7 +306,10 @@ const dateTime2localdate = (currtime: DateTime, tz: string) => ({
second: currtime.second,
});

function points2TripProps(locationPoints: Array<BEMData<FilteredLocation>>) {
/**
* @description Given an array of location points, creates an UnprocessedTrip object.
*/
function points2UnprocessedTrip(locationPoints: Array<BEMData<FilteredLocation>>): UnprocessedTrip {
const startPoint = locationPoints[0];
const endPoint = locationPoints[locationPoints.length - 1];
const tripAndSectionId = `unprocessed_${startPoint.data.ts}_${endPoint.data.ts}`;
Expand Down Expand Up @@ -331,32 +339,63 @@ function points2TripProps(locationPoints: Array<BEMData<FilteredLocation>>) {
speed: speeds[i],
}));

return {
_id: { $oid: tripAndSectionId },
key: 'UNPROCESSED_trip',
origin_key: 'UNPROCESSED_trip',
additions: [],
confidence_threshold: 0,
// baseProps: these are the properties that are the same between the trip and its section
const baseProps = {
distance: dists.reduce((a, b) => a + b, 0),
duration: endPoint.data.ts - startPoint.data.ts,
end_fmt_time: endTime.toISO() || displayErrorMsg('end_fmt_time: invalid DateTime') || '',
end_loc: {
type: 'Point',
coordinates: [endPoint.data.longitude, endPoint.data.latitude],
} as Point,
end_local_dt: dateTime2localdate(endTime, endPoint.metadata.time_zone),
end_ts: endPoint.data.ts,
expectation: { to_label: true },
inferred_labels: [],
locations: locations,
source: 'unprocessed',
start_fmt_time: startTime.toISO() || displayErrorMsg('start_fmt_time: invalid DateTime') || '',
start_loc: {
type: 'Point',
coordinates: [startPoint.data.longitude, startPoint.data.latitude],
} as Point,
start_local_dt: dateTime2localdate(startTime, startPoint.metadata.time_zone),
start_ts: startPoint.data.ts,
} as const;

// section: baseProps + some properties that are unique to the section
const singleSection: SectionData = {
...baseProps,
_id: { $oid: `unprocessed_section_${tripAndSectionId}` },
cleaned_section: { $oid: `unprocessed_section_${tripAndSectionId}` },
key: 'UNPROCESSED_section',
origin_key: 'UNPROCESSED_section',
sensed_mode: 4, // MotionTypes.UNKNOWN (4)
sensed_mode_str: 'UNKNOWN',
trip_id: { $oid: tripAndSectionId },
};

// the complete UnprocessedTrip: baseProps + properties that are unique to the trip, including the section
return {
...baseProps,
_id: { $oid: tripAndSectionId },
additions: [],
confidence_threshold: 0,
expectation: { to_label: true },
inferred_labels: [],
key: 'UNPROCESSED_trip',
locations: locations,
origin_key: 'UNPROCESSED_trip',
sections: [singleSection],
user_input: {},
};
}

const tsEntrySort = (e1: BEMData<FilteredLocation>, e2: BEMData<FilteredLocation>) =>
e1.data.ts - e2.data.ts; // compare timestamps

function transitionTrip2TripObj(trip: Array<any>): Promise<UnprocessedTrip | undefined> {
/**
* @description Given an array of 2 transitions, queries the location data during that time and promises an UnprocessedTrip object.
* @param trip An array of transitions representing one trip; i.e. [start transition, end transition]
*/
function tripTransitions2UnprocessedTrip(trip: Array<any>): Promise<UnprocessedTrip | undefined> {
const tripStartTransition = trip[0];
const tripEndTransition = trip[1];
const tq = {
Expand Down Expand Up @@ -398,20 +437,7 @@ function transitionTrip2TripObj(trip: Array<any>): Promise<UnprocessedTrip | und
logDebug(`transitions: start = ${JSON.stringify(tripStartTransition.data)};
end = ${JSON.stringify(tripEndTransition.data)}`);
}

const tripProps = points2TripProps(filteredLocationList);

return {
...tripProps,
start_loc: {
type: 'Point',
coordinates: [tripStartPoint.data.longitude, tripStartPoint.data.latitude],
},
end_loc: {
type: 'Point',
coordinates: [tripEndPoint.data.longitude, tripEndPoint.data.latitude],
},
};
return points2UnprocessedTrip(filteredLocationList);
},
);
}
Expand Down Expand Up @@ -440,22 +466,26 @@ function isEndingTransition(transWrapper: BEMData<TripTransition>) {
// Logger.log("Returning false");
return false;
}
/*
* This is going to be a bit tricky. As we can see from
* https://github.com/e-mission/e-mission-phone/issues/214#issuecomment-286279163,
* when we read local transitions, they have a string for the transition
* (e.g. `T_DATA_PUSHED`), while the remote transitions have an integer
* (e.g. `2`).
* See https://github.com/e-mission/e-mission-phone/issues/214#issuecomment-286338606
*
* Also, at least on iOS, it is possible for trip end to be detected way
* after the end of the trip, so the trip end transition of a processed
* trip may actually show up as an unprocessed transition.
* See https://github.com/e-mission/e-mission-phone/issues/214#issuecomment-286279163
*
* Let's abstract this out into our own minor state machine.

/**
* @description Given an array of transitions, finds which transitions represent the start and end of a detected trip and returns them as pairs.
* @returns An 2D array of transitions, where each inner array represents one trip; i.e. [start transition, end transition]
*/
function transitions2Trips(transitionList: Array<BEMData<TripTransition>>) {
function transitions2TripTransitions(transitionList: Array<BEMData<TripTransition>>) {
/* This is going to be a bit tricky. As we can see from
* https://github.com/e-mission/e-mission-phone/issues/214#issuecomment-286279163,
* when we read local transitions, they have a string for the transition
* (e.g. `T_DATA_PUSHED`), while the remote transitions have an integer
* (e.g. `2`).
* See https://github.com/e-mission/e-mission-phone/issues/214#issuecomment-286338606
*
* Also, at least on iOS, it is possible for trip end to be detected way
* after the end of the trip, so the trip end transition of a processed
* trip may actually show up as an unprocessed transition.
* See https://github.com/e-mission/e-mission-phone/issues/214#issuecomment-286279163
*
* Let's abstract this out into our own minor state machine.
*/
let inTrip = false;
const tripList: [BEMData<TripTransition>, BEMData<TripTransition>][] = [];
let currStartTransitionIndex = -1;
Expand Down Expand Up @@ -533,12 +563,12 @@ export function readUnprocessedTrips(
return [];
} else {
logDebug(`Found ${transitionList.length} transitions. yay!`);
const tripsList = transitions2Trips(transitionList);
const tripsList = transitions2TripTransitions(transitionList);
logDebug(`Mapped into ${tripsList.length} trips. yay!`);
tripsList.forEach((trip) => {
logDebug(JSON.stringify(trip, null, 2));
});
const tripFillPromises = tripsList.map(transitionTrip2TripObj);
const tripFillPromises = tripsList.map(tripTransitions2UnprocessedTrip);
return Promise.all(tripFillPromises).then(
(rawTripObjs: (UnprocessedTrip | undefined)[]) => {
// Now we need to link up the trips. linking unprocessed trips
Expand Down
48 changes: 24 additions & 24 deletions www/js/types/diaryTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import { BaseModeKey, MotionTypeKey } from '../diary/diaryHelper';
import { MultilabelKey } from './labelTypes';
import { BEMData, LocalDt } from './serverData';
import { FeatureCollection, Feature, Geometry, Point } from 'geojson';
import { FeatureCollection, Feature, Geometry, Point, Position } from 'geojson';

type ObjectId = { $oid: string };

Expand Down Expand Up @@ -45,14 +45,9 @@ export type TripTransition = {
ts: number;
};

export type LocationCoord = {
type: string; // e.x., "Point"
coordinates: [number, number];
};

type CompTripLocations = {
export type CompositeTripLocation = {
loc: {
coordinates: number[]; // e.g. [1, 2.3]
coordinates: Position; // [lon, lat]
};
speed: number;
ts: number;
Expand All @@ -61,24 +56,27 @@ type CompTripLocations = {
// Used for return type of readUnprocessedTrips
export type UnprocessedTrip = {
_id: ObjectId;
additions: UserInputEntry[];
additions: []; // unprocessed trips won't have any matched processed inputs, so this is always empty
confidence_threshold: number;
distance: number;
duration: number;
end_fmt_time: string;
end_loc: Point;
end_local_dt: LocalDt;
expectation: any; // TODO "{to_label: boolean}"
inferred_labels: any[]; // TODO
key: string;
locations?: CompTripLocations[];
origin_key: string; // e.x., UNPROCESSED_trip
source: string;
end_ts: number;
expectation: { to_label: true }; // unprocessed trips are always expected to be labeled
inferred_labels: []; // unprocessed trips won't have inferred labels
key: 'UNPROCESSED_trip';
locations?: CompositeTripLocation[];
origin_key: 'UNPROCESSED_trip';
sections: SectionData[];
source: 'unprocessed';
start_fmt_time: string;
start_local_dt: LocalDt;
start_ts: number;
start_loc: Point;
starting_trip?: any;
user_input: UserInput;
user_input: {}; // unprocessed trips won't have any matched processed inputs, so this is always empty
};

/* These are the properties received from the server (basically matches Python code)
Expand All @@ -98,16 +96,16 @@ export type CompositeTrip = {
end_local_dt: LocalDt;
end_place: ObjectId;
end_ts: number;
expectation: any; // TODO "{to_label: boolean}"
expectation: { to_label: boolean };
expected_trip: ObjectId;
inferred_labels: InferredLabels;
inferred_section_summary: SectionSummary;
inferred_trip: ObjectId;
key: string;
locations: any[]; // TODO
locations: CompositeTripLocation[];
origin_key: string;
raw_trip: ObjectId;
sections: any[]; // TODO
sections: SectionData[];
source: string;
start_confirmed_place: BEMData<ConfirmedPlace>;
start_fmt_time: string;
Expand Down Expand Up @@ -199,23 +197,25 @@ export type Location = {
latitude: number;
fmt_time: string; // ISO
mode: number;
loc: LocationCoord;
loc: Point;
ts: number; // Unix
altitude: number;
distance: number;
};

// used in readAllCompositeTrips
export type SectionData = {
_id: ObjectId;
end_ts: number; // Unix time, e.x. 1696352498.804
end_loc: LocationCoord;
end_loc: Point;
start_fmt_time: string; // ISO time
end_fmt_time: string;
key: string;
origin_key: string;
trip_id: ObjectId;
sensed_mode: number;
source: string; // e.x., "SmoothedHighConfidenceMotion"
start_ts: number; // Unix
start_loc: LocationCoord;
start_loc: Point;
cleaned_section: ObjectId;
start_local_dt: LocalDt;
end_local_dt: LocalDt;
Expand All @@ -224,7 +224,7 @@ export type SectionData = {
distance: number;
};

// used in timelineHelper's `transitionTrip2TripObj`
// used in timelineHelper's `transitionTrip2UnprocessedTrip`
export type FilteredLocation = {
accuracy: number;
altitude: number;
Expand Down

0 comments on commit 8de3ca0

Please sign in to comment.