Visualizer work
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

165 lines
5.7 KiB

export function findRadarFrameIndexForTime(targetTimeMs, vizData) {
if (!vizData || vizData.radarFrames.length === 0) return -1;
// Initialize low, high, and answer variables for binary search
// 'ans' will store the index of the closest frame found so far
// 'low' and 'high' define the search range
let low = 0,
high = vizData.radarFrames.length - 1,
ans = 0;
// Perform binary search to find the radar frame whose timestamp is closest to, but not exceeding, the target time
while (low <= high) {
let mid = Math.floor((low + high) / 2);
// If the current frame's timestamp is less than or equal to the target time,
// it's a potential answer, and we try to find a more recent one in the right half.
if (vizData.radarFrames[mid].timestampMs <= targetTimeMs) {
ans = mid;
low = mid + 1;
} else {
// If the current frame's timestamp is greater than the target time,
// we need to look in the left half.
high = mid - 1;
}
}
// Return the index of the found radar frame.
return ans;
}
export function extractTimestampInfo(filename) {
// Return null if filename is not provided
if (!filename) return null;
// Try to match the old JSON filename pattern: "Tracks_YYYYMMDD_HHMMSS.ms"
let match = filename.match(/Tracks_(\d{8}_\d{6}\.\d{3})/);
if (match) return { timestampStr: match[1], format: "json" };
// NEW: Add this block to match the new filename pattern
match = filename.match(/fHist_(\d{8}_\d{6}\.\d{3})/);
if (match) return { timestampStr: match[1], format: "json" };
// Try to match video filename pattern (e.g., from GoPro): "WIN_YYYYMMDD_HH_MM_SS"
match = filename.match(/WIN_(\d{8})_(\d{2})_(\d{2})_(\d{2})/);
if (match) {
const timestamp = `${match[1]}_${match[2]}${match[3]}${match[4]}`;
return { timestampStr: timestamp, format: "video" };
}
// Try to match another common video filename pattern: "video_YYYYMMDD_HHMMSS"
match = filename.match(/video_(\d{8}_\d{6})/);
if (match)
return {
timestampStr: match[1],
format: "video",
};
// If no pattern matches, return null
return null;
}
export function parseTimestamp(timestampStr, format) {
// Return null if timestamp string or format is not provided.
if (!timestampStr || !format) return null;
let day,
month,
year,
hour,
minute,
second,
millisecond = 0;
// Parse video timestamp format: YYYYMMDD_HH_MM_SS
// Example: 20231027_10_30_00
if (format === "video") {
[year, month, day] = [
timestampStr.substring(0, 4),
timestampStr.substring(4, 6),
timestampStr.substring(6, 8),
];
[hour, minute, second] = [
timestampStr.substring(9, 11),
timestampStr.substring(11, 13),
timestampStr.substring(13, 15),
];
}
else if (format === "json") {
// Parse JSON timestamp format: DDMMYYYY_HHMMSS.ms
[day, month, year] = [
timestampStr.substring(0, 2),
timestampStr.substring(2, 4),
timestampStr.substring(4, 8),
];
[hour, minute, second, millisecond] = [
timestampStr.substring(9, 11),
timestampStr.substring(11, 13),
timestampStr.substring(13, 15),
parseInt(timestampStr.substring(16, 19)),
];
} else {
// Return null for unsupported formats
return null;
} // Create a Date object using UTC to avoid timezone issues
const date = new Date(
Date.UTC(year, month - 1, day, hour, minute, second, millisecond)
);
// Check if the created Date object is valid.
// If getTime() returns NaN, the date is invalid.
return isNaN(date.getTime()) ? null : date;
}
/**
* Creates a throttled function that only invokes the provided function
* at most once per every `delay` milliseconds.
* @param {Function} func The function to throttle.
* @param {number} delay The number of milliseconds to throttle invocations to.
* @returns {Function} Returns the new throttled function.
*/
export function throttle(func, delay) {
// `lastCall` keeps track of the timestamp of the last successful invocation.
let lastCall = 0;
// Return a new function that, when called, will throttle the execution of the original function
return function (...args) {
// Get the current timestamp.
const now = new Date().getTime();
// If the time since the last call is less than the delay, do not execute the function
if (now - lastCall < delay) {
return;
}
// Otherwise, update the last call time and execute the original function
lastCall = now;
return func(...args); // Apply the original function with its arguments.
};
}
/**
* Formats milliseconds into a MM:SS.ms string.
* @param {number} milliseconds The time in milliseconds.
* @returns {string} The formatted time string.
*/
export function formatTime(milliseconds) {
if (isNaN(milliseconds) || milliseconds < 0) {
return "00:00.000";
}
const totalSeconds = milliseconds / 1000;
const minutes = Math.floor(totalSeconds / 60);
const seconds = Math.floor(totalSeconds % 60);
const ms = Math.round(milliseconds % 1000);
return `${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}.${String(ms).padStart(3, '0')}`;
}
/**
* Formats a Date object into a HH:MM:SS.ms UTC string.
* @param {Date} date The date object to format.
* @returns {string} The formatted time string.
*/
export function formatUTCTime(date) {
if (!date || isNaN(date.getTime())) {
return "00:00:00.000";
}
const hours = String(date.getUTCHours()).padStart(2, '0');
const minutes = String(date.getUTCMinutes()).padStart(2, '0');
const seconds = String(date.getUTCSeconds()).padStart(2, '0');
const milliseconds = String(date.getUTCMilliseconds()).padStart(3, '0');
return `${hours}:${minutes}:${seconds}.${milliseconds}`;
}