or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

aggregation-systems.mdcontour-layer.mdgrid-layer.mdheatmap-layer.mdhexagon-layer.mdindex.mdscreen-grid-layer.md
tile.json

contour-layer.mddocs/

Contour Layer

ContourLayer renders contour lines and bands from aggregated data using the marching squares algorithm. It creates isoline visualizations that show areas of equal value, making it ideal for topographic maps, temperature distributions, and other continuous field visualizations.

Capabilities

ContourLayer Class

Creates a layer that generates contour lines and filled contour bands from aggregated point data.

/**
 * Renders contour lines and bands from aggregated data
 * @param props - Configuration properties for the contour layer
 */
class ContourLayer<DataT = any> extends AggregationLayer<DataT, ContourLayerProps<DataT>> {
  constructor(props: ContourLayerProps<DataT>);
}

interface ContourLayerProps<DataT> extends LayerProps {
  /** Array of data objects to aggregate */
  data: DataT[];
  /** Function to extract position from data object */
  getPosition: Accessor<DataT, Position>;
  /** Function to extract weight value for aggregation */
  getWeight?: Accessor<DataT, number>;
  /** Size of grid cells for aggregation in meters */
  cellSize?: number;
  /** Origin point for grid alignment [x, y] */
  gridOrigin?: [number, number];
  /** Array of contour threshold definitions */
  contours?: Contour[];
  /** Z-offset for contour lines to avoid z-fighting */
  zOffset?: number;
  /** Whether to use GPU aggregation */
  gpuAggregation?: boolean;
  /** Aggregation operation to apply */
  aggregation?: AggregationOperation;
  /** Domain for data values [min, max] */
  domain?: [number, number] | null;
  /** Whether to render filled contour bands */
  filled?: boolean;
  /** Stroke width for contour lines */
  strokeWidth?: number;
  /** Whether contour lines should be stroked */
  stroked?: boolean;
}

interface ContourLayerPickingInfo extends PickingInfo {
  /** The contour definition that was picked */
  object: Contour | null;
  /** The threshold value of the picked contour */
  threshold?: number;
  /** Whether this is a contour line or filled area */
  isContourLine?: boolean;
}

interface Contour {
  /** Value threshold for the contour */
  threshold: number;
  /** Color for the contour line or fill */
  color?: Color;
  /** Stroke width for this specific contour */
  strokeWidth?: number;
  /** Z-offset for this specific contour */
  zOffset?: number;
}

Usage Examples:

import { ContourLayer } from "@deck.gl/aggregation-layers";

// Basic temperature contour map
const temperatureLayer = new ContourLayer({
  id: "temperature-contours",
  data: temperatureReadings,
  getPosition: (d) => [d.longitude, d.latitude],
  getWeight: (d) => d.temperature,
  cellSize: 1000, // 1km resolution
  contours: [
    { threshold: 0, color: [0, 0, 255, 128] },      // Blue for freezing
    { threshold: 10, color: [0, 255, 255, 128] },   // Cyan for cool
    { threshold: 20, color: [0, 255, 0, 128] },     // Green for mild
    { threshold: 30, color: [255, 255, 0, 128] },   // Yellow for warm
    { threshold: 40, color: [255, 0, 0, 128] }      // Red for hot
  ],
  pickable: true
});

// Elevation contour lines (topographic)
const elevationLayer = new ContourLayer({
  id: "elevation-contours",
  data: elevationPoints,
  getPosition: (d) => d.coordinates,
  getWeight: (d) => d.elevation,
  cellSize: 500,
  gridOrigin: [0, 0],
  contours: [
    { threshold: 100, color: [139, 69, 19], strokeWidth: 1 },   // 100m
    { threshold: 200, color: [160, 82, 45], strokeWidth: 1 },   // 200m
    { threshold: 500, color: [205, 133, 63], strokeWidth: 2 },  // 500m (major)
    { threshold: 1000, color: [222, 184, 135], strokeWidth: 3 }, // 1000m (major)
    { threshold: 1500, color: [245, 222, 179], strokeWidth: 2 },
    { threshold: 2000, color: [255, 248, 220], strokeWidth: 3 }
  ],
  filled: false,
  stroked: true,
  strokeWidth: 1,
  zOffset: 0.01,
  aggregation: 'MEAN',
  gpuAggregation: true,
  pickable: true
});

// Precipitation filled contours
const precipitationLayer = new ContourLayer({
  id: "precipitation",
  data: rainfallData,
  getPosition: (d) => [d.lng, d.lat],
  getWeight: (d) => d.rainfall,
  cellSize: 2000,
  contours: [
    { threshold: 0, color: [255, 255, 255, 0] },     // No rain (transparent)
    { threshold: 1, color: [173, 216, 230, 100] },   // Light rain
    { threshold: 5, color: [135, 206, 235, 130] },   // Moderate rain
    { threshold: 10, color: [70, 130, 180, 160] },   // Heavy rain
    { threshold: 25, color: [25, 25, 112, 190] },    // Very heavy rain
    { threshold: 50, color: [128, 0, 128, 220] }     // Extreme rain
  ],
  filled: true,
  aggregation: 'SUM',
  domain: [0, 100],
  pickable: true,
  onHover: (info) => {
    if (info.object) {
      console.log(`Rainfall threshold: ${info.threshold}mm`);
    }
  }
});

Contour Configuration

Controls the generation and appearance of contour lines and bands.

/** Array of contour threshold definitions */
contours?: Contour[];

/** Size of grid cells for aggregation in meters (default: 1000) */
cellSize?: number;

/** Origin point for grid alignment [x, y] (default: [0, 0]) */
gridOrigin?: [number, number];

/** Whether to render filled contour bands (default: true) */
filled?: boolean;

/** Whether contour lines should be stroked (default: false when filled=true) */
stroked?: boolean;

/** Stroke width for contour lines (default: 1) */
strokeWidth?: number;

/** Z-offset for contour lines to avoid z-fighting (default: 0.005) */
zOffset?: number;

Contour Definition

Individual contour specifications with threshold values and styling.

interface Contour {
  /** Value threshold for the contour (required) */
  threshold: number;
  /** Color for the contour line or fill (default: [255, 255, 255, 255]) */
  color?: Color;
  /** Stroke width for this specific contour (overrides layer strokeWidth) */
  strokeWidth?: number;
  /** Z-offset for this specific contour (overrides layer zOffset) */
  zOffset?: number;
}

// Contour utility types and functions
interface ContourLine {
  contour: Contour;
  vertices: number[][];
}

interface ContourPolygon {
  contour: Contour;
  vertices: number[][][];
}

function generateContours(options: {
  contours: Contour[];
  getValue: (x: number, y: number) => number;
  xRange: [number, number];
  yRange: [number, number];
}): {
  lines: ContourLine[];
  polygons: ContourPolygon[];
};

Aggregation Configuration

Controls how point data is aggregated before contour generation.

/** Function to extract weight value for aggregation */
getWeight?: Accessor<DataT, number>;

/** Aggregation operation to apply (default: 'SUM') */
aggregation?: AggregationOperation;

/** Whether to use GPU aggregation (default: true) */
gpuAggregation?: boolean;

/** Domain for data values [min, max] */
domain?: [number, number] | null;

Marching Squares Algorithm

The layer uses the marching squares algorithm for contour generation:

// Marching squares constants (from marching-squares-codes.ts)
const ISOLINES_CODE_OFFSET_MAP: Record<number, number[][]>;
const ISOBANDS_CODE_OFFSET_MAP: Record<number, number[][]>;

// Marching squares implementation
function marchingSquares(
  values: number[][],
  threshold: number,
  options: MarchingSquaresOptions
): ContourGeometry;

Performance Considerations

  • Cell Size: Smaller cells provide higher resolution but require more computation
  • Contour Count: More contours increase geometry complexity
  • GPU Aggregation: Recommended for large datasets (>50K points)
  • Grid Resolution: Balance between visual quality and performance
  • Filled vs Stroked: Filled contours require more GPU memory for polygon rendering

Common Patterns

// Weather visualization with multiple parameters
const weatherLayer = new ContourLayer({
  id: "weather",
  data: weatherStations,
  getPosition: (d) => [d.lon, d.lat],
  getWeight: (d) => d.pressure, // Atmospheric pressure
  cellSize: 5000,
  contours: [
    { threshold: 980, color: [255, 0, 0, 100] },   // Low pressure (red)
    { threshold: 1000, color: [255, 255, 0, 100] }, // Medium (yellow)
    { threshold: 1020, color: [0, 255, 0, 100] },  // High pressure (green)
    { threshold: 1040, color: [0, 0, 255, 100] }   // Very high (blue)
  ],
  filled: true,
  aggregation: 'MEAN'
});

// Ocean depth contours
const bathymetryLayer = new ContourLayer({
  id: "bathymetry",
  data: depthSoundings,
  getPosition: (d) => d.position,
  getWeight: (d) => -d.depth, // Negative for depth
  cellSize: 1000,
  contours: Array.from({length: 20}, (_, i) => ({
    threshold: -i * 100, // Every 100m depth
    color: [0, 100 + i * 8, 255 - i * 10, 150],
    strokeWidth: i % 5 === 0 ? 2 : 1 // Major contours every 500m
  })),
  filled: false,
  stroked: true,
  zOffset: 0.001
});

// Population density with smooth gradients
const populationLayer = new ContourLayer({
  id: "population-density",
  data: censusBlocks,
  getPosition: (d) => d.centroid,
  getWeight: (d) => d.population / d.area, // Density calculation
  cellSize: 2000,
  contours: [
    { threshold: 50, color: [255, 255, 204, 80] },   // Very low density
    { threshold: 100, color: [255, 237, 160, 100] },  // Low density
    { threshold: 250, color: [254, 217, 118, 120] },  // Medium-low
    { threshold: 500, color: [254, 178, 76, 140] },   // Medium
    { threshold: 1000, color: [253, 141, 60, 160] },  // Medium-high
    { threshold: 2000, color: [252, 78, 42, 180] },   // High density
    { threshold: 5000, color: [227, 26, 28, 200] }    // Very high density
  ],
  filled: true,
  aggregation: 'MEAN',
  domain: [0, 10000]
});

Integration with Other Layers

Contour layers work well in combination with other visualization layers:

// Combined visualization with base data and contours
const combinedLayers = [
  // Base points layer
  new ScatterplotLayer({
    id: "data-points",
    data: weatherData,
    getPosition: (d) => [d.lon, d.lat],
    getRadius: 500,
    getFillColor: [255, 140, 0],
    radiusMinPixels: 3
  }),
  
  // Contour overlay
  new ContourLayer({
    id: "temperature-contours",
    data: weatherData,
    getPosition: (d) => [d.lon, d.lat],
    getWeight: (d) => d.temperature,
    cellSize: 2000,
    contours: temperatureContours,
    filled: true
  })
];