Layer for rendering 3D terrain meshes from elevation data with texture mapping, material support, and optimized mesh generation using Martini triangulation.
Layer for rendering 3D terrain from heightmap data with optional texture overlays and advanced material properties.
/**
* Layer for rendering 3D terrain meshes from elevation data
* Uses Martini triangulation for efficient mesh generation from heightmaps
*/
class TerrainLayer extends TileLayer<MeshAttributes> {
constructor(props: TerrainLayerProps);
}
interface TerrainLayerProps extends TileLayerProps<MeshAttributes> {
/** Image URL template that encodes height data */
elevationData: URLTemplate;
/** Image URL template to use as texture overlay */
texture?: URLTemplate;
/** Martini error tolerance in meters, smaller number means more detailed mesh */
meshMaxError?: number;
/** Bounding box of the terrain image [minX, minY, maxX, maxY] in world coordinates */
bounds?: number[] | null;
/** Color to use if texture is unavailable */
color?: Color;
/** Object to decode height data from RGB values to height in meters */
elevationDecoder?: ElevationDecoder;
/** Supply URL to local terrain worker bundle (for offline use) */
workerUrl?: string;
/** Render terrain as wireframe */
wireframe?: boolean;
/** Material properties for terrain rendering */
material?: Material | boolean;
/** Data loaders for processing terrain data */
loaders?: any[];
}
interface ElevationDecoder {
/** Red channel multiplier */
rScaler: number;
/** Green channel multiplier */
gScaler: number;
/** Blue channel multiplier */
bScaler: number;
/** Height offset in meters */
offset: number;
}
type MeshAttributes = {
positions: Float32Array;
normals?: Float32Array;
texCoords?: Float32Array;
indices?: Uint32Array;
};Usage Examples:
import { TerrainLayer } from "@deck.gl/geo-layers";
// Basic terrain layer with elevation data
const terrainLayer = new TerrainLayer({
id: 'terrain',
minZoom: 0,
maxZoom: 15,
strategy: 'no-overlap',
// Elevation data (e.g., Mapbox terrain-rgb tiles)
elevationData: 'https://api.mapbox.com/v4/mapbox.terrain-rgb/{z}/{x}/{y}.pngraw?access_token={token}',
// Texture overlay (e.g., satellite imagery)
texture: 'https://api.mapbox.com/v4/mapbox.satellite/{z}/{x}/{y}@2x.png?access_token={token}',
// Mapbox terrain-rgb elevation decoder
elevationDecoder: {
rScaler: 6553.6, // (2^16 - 1) / 10
gScaler: 25.6, // (2^8 - 1) / 10
bScaler: 0.1, // 1 / 10
offset: -10000 // Offset for below-sea-level areas
},
// Mesh quality (lower = more detailed)
meshMaxError: 4.0,
// Styling
color: [255, 255, 255]
});
// High-resolution terrain with custom elevation data
const highResTerrainLayer = new TerrainLayer({
id: 'high-res-terrain',
minZoom: 10,
maxZoom: 16,
// Custom elevation data source
elevationData: 'https://elevation-tiles.example.com/{z}/{x}/{y}.png',
// Custom elevation decoder for 16-bit grayscale heightmaps
elevationDecoder: {
rScaler: 0.1, // Red channel as primary height
gScaler: 0, // No green channel contribution
bScaler: 0, // No blue channel contribution
offset: 0 // No offset
},
// Higher mesh quality for detailed terrain
meshMaxError: 1.0,
// Wireframe visualization
wireframe: true,
// Custom material properties
material: {
ambient: 0.35,
diffuse: 0.6,
shininess: 32,
specularColor: [255, 255, 255]
}
});
// Terrain with bounds and custom styling
const boundedTerrainLayer = new TerrainLayer({
id: 'bounded-terrain',
elevationData: 'https://tiles.example.com/elevation/{z}/{x}/{y}.png',
texture: 'https://tiles.example.com/imagery/{z}/{x}/{y}.jpg',
// Constrain to specific geographic bounds
bounds: [-122.5, 37.7, -122.3, 37.9], // San Francisco area
elevationDecoder: {
rScaler: 1.0,
gScaler: 0,
bScaler: 0,
offset: 0
},
// Fallback color when texture fails to load
color: [139, 169, 19], // Olive green
meshMaxError: 2.0,
// Enable material lighting
material: true
});Configure how RGB pixel values are converted to elevation heights.
interface ElevationDecoder {
/** Multiplier for red channel (0-255) */
rScaler: number;
/** Multiplier for green channel (0-255) */
gScaler: number;
/** Multiplier for blue channel (0-255) */
bScaler: number;
/** Constant offset added to final height */
offset: number;
}
/**
* Final height calculation:
* height = (r * rScaler) + (g * gScaler) + (b * bScaler) + offset
*/Common Elevation Encodings:
// Mapbox Terrain-RGB encoding
const mapboxDecoder: ElevationDecoder = {
rScaler: 6553.6, // Red: 256 * 256 / 10
gScaler: 25.6, // Green: 256 / 10
bScaler: 0.1, // Blue: 1 / 10
offset: -10000 // Support below-sea-level
};
// 16-bit grayscale heightmap
const grayscaleDecoder: ElevationDecoder = {
rScaler: 0.1, // Red channel only
gScaler: 0, // Ignore green
bScaler: 0, // Ignore blue
offset: 0 // No offset
};
// Custom encoding with centimeter precision
const centimeterDecoder: ElevationDecoder = {
rScaler: 65.536, // Red: 256 * 256 / 1000
gScaler: 0.256, // Green: 256 / 1000
bScaler: 0.001, // Blue: 1 / 1000
offset: 0
};
// USGS 1/3 arc-second DEM encoding
const usgsDecoder: ElevationDecoder = {
rScaler: 1.0, // Direct height values
gScaler: 0,
bScaler: 0,
offset: 0
};TerrainLayer uses Martini algorithm for efficient terrain mesh generation from regular heightmaps.
interface MeshGenerationProps {
/**
* Martini error tolerance in meters
* Lower values create more detailed meshes with more triangles
* Higher values create simpler meshes with fewer triangles
*/
meshMaxError?: number;
/**
* Geographic bounds for terrain tiles
* Used for accurate mesh projection and coordinate transformation
*/
bounds?: number[] | null;
}
interface MeshAttributes {
/** Vertex positions as [x, y, z] coordinates */
positions: Float32Array;
/** Normal vectors for lighting calculations */
normals?: Float32Array;
/** Texture coordinates for UV mapping */
texCoords?: Float32Array;
/** Triangle indices for mesh connectivity */
indices?: Uint32Array;
}Mesh Quality Examples:
// High quality terrain (more triangles)
const highQuality = new TerrainLayer({
id: 'high-quality',
elevationData: 'https://tiles.example.com/{z}/{x}/{y}.png',
meshMaxError: 0.5, // Very low error tolerance
// Results in detailed mesh with many triangles
});
// Medium quality terrain (balanced)
const mediumQuality = new TerrainLayer({
id: 'medium-quality',
elevationData: 'https://tiles.example.com/{z}/{x}/{y}.png',
meshMaxError: 4.0, // Default error tolerance
// Good balance of detail vs performance
});
// Low quality terrain (fewer triangles)
const lowQuality = new TerrainLayer({
id: 'low-quality',
elevationData: 'https://tiles.example.com/{z}/{x}/{y}.png',
meshMaxError: 20.0, // High error tolerance
// Simplified mesh for better performance
});Apply imagery textures over the terrain mesh for realistic visualization.
interface TextureProps {
/** URL template for texture imagery */
texture?: URLTemplate;
/** Fallback color when texture is unavailable */
color?: Color;
/** Material properties affecting texture appearance */
material?: Material | boolean;
}
interface Material {
/** Ambient light coefficient (0-1) */
ambient?: number;
/** Diffuse light coefficient (0-1) */
diffuse?: number;
/** Specular shininess factor */
shininess?: number;
/** Specular highlight color */
specularColor?: Color;
}Texture Examples:
// Satellite imagery texture
const satelliteLayer = new TerrainLayer({
id: 'satellite-terrain',
elevationData: 'https://elevation.example.com/{z}/{x}/{y}.png',
texture: 'https://satellite.example.com/{z}/{x}/{y}.jpg',
material: {
ambient: 0.4, // Some ambient lighting
diffuse: 0.8, // Strong diffuse lighting
shininess: 16, // Moderate shininess
specularColor: [255, 255, 255]
}
});
// Color-coded elevation without texture
const coloredLayer = new TerrainLayer({
id: 'colored-terrain',
elevationData: 'https://elevation.example.com/{z}/{x}/{y}.png',
// No texture prop - uses color instead
color: [70, 120, 70], // Forest green
material: true // Enable basic material lighting
});
// Multiple texture sources
const multiTextureLayer = new TerrainLayer({
id: 'multi-texture',
elevationData: 'https://elevation.example.com/{z}/{x}/{y}.png',
// Use different textures based on zoom level
texture: [
'https://low-res.example.com/{z}/{x}/{y}.jpg',
'https://high-res.example.com/{z}/{x}/{y}.jpg'
],
color: [200, 200, 200], // Fallback gray
material: true
});TerrainLayer uses web workers for mesh generation to avoid blocking the main thread.
interface WorkerProps {
/**
* URL to custom terrain worker bundle
* Only needed for offline use or custom worker builds
*/
workerUrl?: string;
}Worker Configuration:
// Use custom worker for offline applications
const offlineLayer = new TerrainLayer({
id: 'offline-terrain',
elevationData: './local-tiles/{z}/{x}/{y}.png',
workerUrl: './terrain-worker.js', // Local worker bundle
meshMaxError: 4.0
});
// Default configuration uses CDN worker
const onlineLayer = new TerrainLayer({
id: 'online-terrain',
elevationData: 'https://tiles.example.com/{z}/{x}/{y}.png',
// workerUrl not specified - uses default CDN worker
meshMaxError: 4.0
});