Skip to content

Conversation

felixpalmer
Copy link
Collaborator

Background

In order to visualize trajectory data, a new TrajectoryTileLayer is added

Screen.Recording.2025-08-29.at.15.44.59.mov

The underlying rendering is performed by TripsLayer with the data format being the same as that used for point data in VectorTileLayer. It has a number of features to make it easier to work with:

  • Automatic conversion of point data to trajectory data, using a specific per-point property as a timestamp
  • Tiled data support without seams
  • Automatic speed property generation
  • Automatic min/max time detection to avoid common precision issues with timestamps
  • Styling of line per trajectory and per-vertex

Change List

  • Add TrajectoryTileLayer and supporting files
  • New test app to demonstrate usage
  • Fix re-render issue in VectorTileLayer

@felixpalmer felixpalmer force-pushed the felix/trajectory-poc branch from d89322f to b3494ce Compare August 29, 2025 13:56
in float vTime;
`,
// Drop the segments outside of the time window
// Drop the segments outside of the time window, unless highlighted
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: don't make changes in the TripsLayer

The code here makes the autohighlighting work with the full trajectory

Screen.Recording.2025-08-29.at.16.12.59.mov

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be a cool feature for the core layer too, especially if we don't have a highlight behavior today

Copy link
Collaborator

@donmccurdy donmccurdy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks great! Playback is perfectly smooth when testing here.

Only issues I ran into were related to picking:

  1. Picking on the sample dataset takes ~20ms and introduces some visible stuttering, mostly in a call to createRenderPipeline() each time.
  2. If I pause playback and then start picking segments, there's some visible inconsistency with the location of the mouse vs. the highlighted segment. In the screenshot below my mouse was at the center of the circle, but the orange "L" is the highlighted path:
Image

Not sure if that indicates a glitch in rendering, in picking the right trip, or something else!

return deg * (Math.PI / 180);
}

function distanceBetweenPoints([lon1, lat1, lon2, lat2]: number[]): number {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are the units of the return value of distanceBetweenPoints? Might also be a good addition to @math.gl/* at some point.

import type {ProcessedGeometry} from './trajectory-utils';

// Helper functions for speed calculation
function deg2rad(deg: number): number {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional: There's toRadians in @math.gl/core, but it doesn't seem to be used elsewhere in the codebase so no strong preference here.

Comment on lines +41 to +46
/**
* Rendering mode for trajectories.
* - 'paths': Static path rendering
* - 'trips': Animated trip rendering with time controls
*/
renderMode?: 'paths' | 'trips';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional: I slightly prefer your static | animated naming from the comment over paths | trips, I think it might be clearer.

Comment on lines +100 to +109
if (!trajectoryIdColumn) {
throw new Error(
'TrajectoryTileLayer: trajectoryIdColumn is required in data source configuration'
);
}
if (!timestampColumn) {
throw new Error(
'TrajectoryTileLayer: timestampColumn is required in data source configuration'
);
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional - sliiightly more concise/readable with an assert() here maybe?

Comment on lines +107 to +112
const maxReasonableSpeed = 50000; // Max speed to prevent UI issues
const minReasonableSpeed = 10; // Min speed for very short datasets
const clampedSpeed = Math.max(
minReasonableSpeed,
Math.min(maxReasonableSpeed, calculatedSpeed)
);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could use @math.gl/core here, already a dependency?

Suggested change
const maxReasonableSpeed = 50000; // Max speed to prevent UI issues
const minReasonableSpeed = 10; // Min speed for very short datasets
const clampedSpeed = Math.max(
minReasonableSpeed,
Math.min(maxReasonableSpeed, calculatedSpeed)
);
// Clamp min speed for short datasets, and max speed to prevent UI issues.
const clampedSpeed = clamp(calculatedSpeed, 10, 50000);


// CARTO API configuration
const API_CONFIG = {
apiBaseUrl: 'https://gcp-us-east1-19.dev.api.carto.com',
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not urgent but would be great if we could include a demo dataset in a production env when merging this!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants