or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

3d-tiles.mdanimation.mdindex.mdspatial-indexing.mdterrain.mdtile-layers.mdvector-tiles.md
tile.json

animation.mddocs/

Animated Path Visualization

Layer for animating trips and paths with temporal data support, including smooth path interpolation, time-based animation controls, and trail effects.

Capabilities

TripsLayer

Layer for animating trips/paths with temporal data, providing smooth movement visualization and configurable trail effects.

/**
 * Layer for animating trips and paths with temporal data
 * Renders animated paths with time-based progression and trail effects
 */
class TripsLayer<DataT = any> extends Layer<TripsLayerProps<DataT>> {
  constructor(props: TripsLayerProps<DataT>);
}

interface TripsLayerProps<DataT = unknown> extends LayerProps<DataT> {
  /** Accessor for trip path coordinates */
  getPath?: AccessorFunction<DataT, Position[]>;
  
  /** Accessor for timestamp array corresponding to path points */
  getTimestamps?: AccessorFunction<DataT, number[]>;
  
  /** Accessor for trip color */
  getColor?: AccessorFunction<DataT, Color>;
  
  /** Accessor for path width in pixels */
  getWidth?: AccessorFunction<DataT, number>;
  
  /** Current animation time */
  currentTime?: number;
  
  /** Length of trailing path in time units */
  trailLength?: number;
  
  /** Cap style for path ends ('round', 'square', 'butt') */
  capRounded?: boolean;
  
  /** Join style for path segments ('round', 'miter', 'bevel') */
  jointRounded?: boolean;
  
  /** Width units ('pixels', 'common', 'meters') */
  widthUnits?: string;
  
  /** Minimum width in pixels */
  widthMinPixels?: number;
  
  /** Maximum width in pixels */
  widthMaxPixels?: number;
  
  /** Width scaling factor */
  widthScale?: number;
  
  /** Fade trail over time */
  fadeTrail?: boolean;
}

type Position = [longitude: number, latitude: number] | [longitude: number, latitude: number, elevation: number];
type Color = [r: number, g: number, b: number] | [r: number, g: number, b: number, a: number];

Usage Examples:

import { TripsLayer } from "@deck.gl/geo-layers";

// Basic trip animation
const tripsLayer = new TripsLayer({
  id: 'trips',
  data: [
    {
      id: 'trip-1',
      path: [
        [-122.4, 37.8, 0],
        [-122.41, 37.81, 10], 
        [-122.42, 37.82, 5],
        [-122.43, 37.83, 0]
      ],
      timestamps: [0, 30, 60, 120], // Seconds
      color: [255, 0, 0],
      width: 8
    },
    {
      id: 'trip-2', 
      path: [
        [-122.45, 37.85, 0],
        [-122.44, 37.84, 15],
        [-122.43, 37.83, 20],
        [-122.42, 37.82, 5]
      ],
      timestamps: [10, 40, 80, 130],
      color: [0, 255, 0],
      width: 6
    }
  ],
  
  getPath: d => d.path,
  getTimestamps: d => d.timestamps,
  getColor: d => d.color,
  getWidth: d => d.width,
  
  currentTime: 0, // Start animation
  trailLength: 30, // 30 second trail
  
  capRounded: true,
  jointRounded: true,
  fadeTrail: true,
  
  pickable: true
});

// Animate the trips over time
function animateTrips() {
  let time = 0;
  const animate = () => {
    tripsLayer.setProps({
      currentTime: time
    });
    time += 1; // Advance 1 second
    
    if (time < 150) {
      requestAnimationFrame(animate);
    }
  };
  animate();
}

// Real-time GPS tracking visualization
const gpsTripsLayer = new TripsLayer({
  id: 'gps-trips',
  data: vehicleData, // Array of vehicle objects with GPS tracks
  
  getPath: d => d.gpsTrack.map(point => [point.lon, point.lat, point.altitude]),
  getTimestamps: d => d.gpsTrack.map(point => point.timestamp),
  getColor: d => {
    switch (d.vehicleType) {
      case 'bus': return [255, 165, 0]; // Orange
      case 'taxi': return [255, 255, 0]; // Yellow
      case 'delivery': return [139, 69, 19]; // Brown
      default: return [128, 128, 128]; // Gray
    }
  },
  getWidth: d => d.vehicleType === 'bus' ? 12 : 8,
  
  currentTime: Date.now() / 1000, // Current Unix timestamp
  trailLength: 300, // 5 minute trail
  
  widthUnits: 'pixels',
  widthMinPixels: 2,
  widthMaxPixels: 20,
  
  fadeTrail: true,
  capRounded: true,
  pickable: true,
  
  onHover: info => {
    if (info.object) {
      console.log(`Vehicle ${info.object.id}: ${info.object.vehicleType}`);
    }
  }
});

// Flight path visualization with altitude
const flightPathsLayer = new TripsLayer({
  id: 'flight-paths',
  data: flightData,
  
  getPath: d => d.waypoints.map(wp => [wp.longitude, wp.latitude, wp.altitude]),
  getTimestamps: d => d.waypoints.map(wp => wp.time),
  getColor: d => {
    const airline = d.airline;
    const colors = {
      'AA': [255, 0, 0],    // American - Red
      'UA': [0, 0, 255],    // United - Blue  
      'DL': [255, 0, 255],  // Delta - Magenta
      'SW': [255, 165, 0]   // Southwest - Orange
    };
    return colors[airline] || [128, 128, 128];
  },
  getWidth: d => Math.max(6, d.aircraft_size * 2),
  
  currentTime: animationTime,
  trailLength: 1800, // 30 minute trail
  
  // 3D path rendering
  extruded: true,
  
  widthUnits: 'meters',
  widthScale: 1000, // Scale for visibility at altitude
  
  fadeTrail: true,
  pickable: true
});

Animation Control

Time-based Animation

Control animation progression using time values and trail effects.

interface AnimationProps {
  /** Current animation time in same units as timestamps */
  currentTime?: number;
  
  /** Length of trailing path in time units */
  trailLength?: number;
  
  /** Fade trail opacity over time */
  fadeTrail?: boolean;
}

interface AnimationState {
  /** Calculate current position along path for given time */
  getCurrentPosition(path: Position[], timestamps: number[], currentTime: number): Position | null;
  
  /** Get trail segment for current time and trail length */
  getTrailSegment(path: Position[], timestamps: number[], currentTime: number, trailLength: number): {
    positions: Position[];
    colors: Color[];
  };
}

Animation Control Examples:

// Smooth animation with controls
class TripAnimationController {
  constructor(layer) {
    this.layer = layer;
    this.currentTime = 0;
    this.isPlaying = false;
    this.playbackSpeed = 1.0;
  }
  
  play() {
    if (this.isPlaying) return;
    this.isPlaying = true;
    this.animate();
  }
  
  pause() {
    this.isPlaying = false;
  }
  
  setSpeed(speed) {
    this.playbackSpeed = speed;
  }
  
  setTime(time) {
    this.currentTime = time;
    this.updateLayer();
  }
  
  animate() {
    if (!this.isPlaying) return;
    
    this.currentTime += this.playbackSpeed;
    this.updateLayer();
    
    requestAnimationFrame(() => this.animate());
  }
  
  updateLayer() {
    this.layer.setProps({
      currentTime: this.currentTime
    });
  }
}

// Usage
const controller = new TripAnimationController(tripsLayer);
controller.setSpeed(2.0); // 2x speed
controller.play();

// Time range controls
const timeControls = {
  startTime: 0,
  endTime: 3600, // 1 hour
  currentTime: 0,
  
  scrubToTime(time) {
    this.currentTime = Math.max(this.startTime, Math.min(this.endTime, time));
    tripsLayer.setProps({
      currentTime: this.currentTime
    });
  },
  
  getProgress() {
    return (this.currentTime - this.startTime) / (this.endTime - this.startTime);
  }
};

Interpolation and Smoothing

Control how positions are interpolated between timestamp points.

interface InterpolationOptions {
  /** Interpolation method for positions between timestamps */
  interpolation?: 'linear' | 'cubic' | 'great-circle';
  
  /** Smooth path curves at waypoints */
  smoothing?: boolean;
  
  /** Handle missing timestamps */
  fillGaps?: boolean;
}

Interpolation Examples:

// Linear interpolation (default)
const linearTrips = new TripsLayer({
  id: 'linear-trips',
  data: tripData,
  getPath: d => d.path,
  getTimestamps: d => d.timestamps,
  // Linear interpolation between waypoints
});

// Great circle interpolation for geographic accuracy
const greatCircleTrips = new TripsLayer({
  id: 'great-circle-trips', 
  data: longDistanceFlights,
  getPath: d => d.path,
  getTimestamps: d => d.timestamps,
  // Uses great circle arcs between waypoints for accuracy
});

// Handle irregular timestamps
const irregularTrips = new TripsLayer({
  id: 'irregular-trips',
  data: sensorData.map(d => ({
    ...d,
    // Fill missing timestamps with interpolation
    timestamps: fillTimestampGaps(d.timestamps, d.path)
  })),
  getPath: d => d.path,
  getTimestamps: d => d.timestamps
});

function fillTimestampGaps(timestamps, path) {
  // Interpolate missing timestamps based on path length
  const filled = [...timestamps];
  for (let i = 1; i < path.length; i++) {
    if (!timestamps[i]) {
      // Estimate timestamp based on distance and speed
      const dist = calculateDistance(path[i-1], path[i]);
      const timeDelta = dist / estimatedSpeed;
      filled[i] = filled[i-1] + timeDelta;
    }
  }
  return filled;
}

Styling and Appearance

Path Styling

Control the visual appearance of animated paths.

interface PathStyling {
  /** Path width in specified units */
  getWidth?: AccessorFunction<DataT, number>;
  
  /** Path color with optional alpha */
  getColor?: AccessorFunction<DataT, Color>;
  
  /** Cap style for path endpoints */
  capRounded?: boolean;
  
  /** Join style for path segments */
  jointRounded?: boolean;
  
  /** Width units and scaling */
  widthUnits?: 'pixels' | 'common' | 'meters';
  widthScale?: number;
  widthMinPixels?: number;
  widthMaxPixels?: number;
}

Styling Examples:

// Speed-based coloring
const speedColoredTrips = new TripsLayer({
  id: 'speed-colored',
  data: tripData,
  
  getPath: d => d.path,
  getTimestamps: d => d.timestamps,
  getColor: d => {
    const avgSpeed = calculateAverageSpeed(d.path, d.timestamps);
    if (avgSpeed > 60) return [255, 0, 0];     // Red for fast
    if (avgSpeed > 30) return [255, 255, 0];   // Yellow for medium
    return [0, 255, 0];                        // Green for slow
  },
  getWidth: d => Math.max(4, calculateAverageSpeed(d.path, d.timestamps) / 10),
  
  currentTime: animationTime,
  trailLength: 60,
  fadeTrail: true
});

// Route type styling
const routeStyledTrips = new TripsLayer({
  id: 'route-styled',
  data: deliveryRoutes,
  
  getPath: d => d.route,
  getTimestamps: d => d.schedule,
  getColor: d => {
    switch (d.priority) {
      case 'express': return [255, 0, 0, 200];     // Bright red
      case 'standard': return [0, 0, 255, 150];    // Blue
      case 'economy': return [128, 128, 128, 100]; // Gray
    }
  },
  getWidth: d => d.priority === 'express' ? 12 : 8,
  
  capRounded: true,
  jointRounded: true,
  
  widthUnits: 'pixels',
  widthMinPixels: 3,
  widthMaxPixels: 15,
  
  currentTime: currentDeliveryTime,
  trailLength: 300
});

Trail Effects

Control trail visualization and fading effects.

interface TrailEffects {
  /** Trail length in time units */
  trailLength?: number;
  
  /** Fade trail opacity over time */
  fadeTrail?: boolean;
  
  /** Custom trail opacity calculation */
  getTrailOpacity?: (segmentAge: number, maxAge: number) => number;
}

Trail Effect Examples:

// Custom trail fading
const customFadeTrips = new TripsLayer({
  id: 'custom-fade',
  data: tripData,
  
  getPath: d => d.path,
  getTimestamps: d => d.timestamps,
  getColor: d => [...d.baseColor, 255], // Full opacity base color
  
  currentTime: animationTime,
  trailLength: 120,
  fadeTrail: true,
  
  // Custom fade calculation
  updateTriggers: {
    getColor: [animationTime] // Recalculate colors when time changes
  }
});

// Pulsing trail effect
const pulsingTrails = new TripsLayer({
  id: 'pulsing-trails',
  data: tripData,
  
  getPath: d => d.path,
  getTimestamps: d => d.timestamps,
  getColor: d => {
    // Pulse effect based on current time
    const pulse = Math.sin(animationTime * 0.01) * 0.5 + 0.5;
    return [d.color[0], d.color[1], d.color[2], pulse * 255];
  },
  getWidth: d => {
    // Width pulse effect
    const pulse = Math.sin(animationTime * 0.02) * 0.3 + 0.7;
    return d.baseWidth * pulse;
  },
  
  currentTime: animationTime,
  trailLength: 90,
  
  updateTriggers: {
    getColor: [animationTime],
    getWidth: [animationTime]
  }
});