BUILD_CONTOURS
Generates contour line vector tiles from a LAS/LAZ point cloud. The input is rasterized into a Digital Surface Model, smoothed to reduce noise, then passed through gdal_contour to extract isolines. The resulting lines are simplified, classified as major or minor, and smoothed again with a Chaikin corner-cutting pass to produce publication-quality contours.
The final output is a pair of vector tile packages — MBTiles and PMTiles v3 — ready for direct delivery to web mapping clients.
Typical use: generating contour maps from LiDAR or MBES surveys for web publishing, engineering deliverables, and GIS workflows.
Contract
| Type | BUILD_CONTOURS |
| Accepts | input_las: las |
| Produces | contours_mbtiles: mbtiles, contours_pmtiles: pmtiles |
| Params | all optional† |
† all parameters have executor defaults and may be omitted.
Inputs
| Slot | Type | Description |
|---|---|---|
input_las | las | Source point cloud dataset |
Outputs
| Slot | Type | Description |
|---|---|---|
contours_mbtiles | mbtiles | Vector tile package (MBTiles) — contours layer |
contours_pmtiles | pmtiles | Vector tile package (PMTiles v3) — contours layer |
Parameters
All parameters are optional. Omitting them applies the defaults shown below.
| Name | Type | Default | Description |
|---|---|---|---|
resolution | number | 1 | DSM raster cell size in CRS units |
output_type | string | "mean" | DSM height aggregation per cell — mean suppresses single-return outliers better than max; other options: min, idw, count, stdev, all |
smoothing_factor | number | 5 | Raster smoothing factor: the DSM is downsampled to resolution × smoothing_factor before contouring. Higher values produce smoother, more generalised contours |
contour_interval | number | 5 | Elevation interval between contour lines in CRS units |
major_interval_multiplier | number | 5 | Every Nth contour is classified as major. With contour_interval=5 and major_interval_multiplier=5, every 25-unit contour is major |
simplify_tolerance | number | 1 | ogr2ogr -simplify tolerance in CRS units — reduces vertex count while preserving shape |
chaikin_iterations | number | 3 | Number of Chaikin corner-cutting passes. Each pass doubles the vertex count and rounds corners. Values above 4–5 produce diminishing returns and significantly larger geometries |
Units note: all defaults with a horizontal or vertical dimension (
resolution,contour_interval,simplify_tolerance) are tuned for LAS data in US survey feet (ftUS). The executor does not auto-detect units — adjust these values when processing metric datasets.
Output layer schema
Both tile packages expose a single vector layer named contours with three fields:
| Field | Type | Description |
|---|---|---|
id | integer | Row number ordered by elevation |
elev | float | Contour elevation in CRS units |
type | string | "major" or "minor" — based on whether elev is divisible by contour_interval × major_interval_multiplier |
What it does internally
Downloads the input LAS from MinIO
Builds a PDAL pipeline using
readers.las→writers.gdalwith the configuredresolutionandoutput_type, writingraw_dsm.tif; stamps nodata withgdal_edit.py -a_nodata -9999Smooths the DSM:
gdalwarpdownsamples toresolution × smoothing_factorusing average resampling, then upsamples back toresolutionusing cubic spline →smoothed_dsm.tifRuns
gdal_contour -a elev -i {contour_interval} -f GPKGon the smoothed DSM →raw_contours.gpkgRuns
ogr2ogr -simplify {simplify_tolerance}with the SQLite dialect: assigns a stableidviaROW_NUMBER() OVER (ORDER BY elev), classifiestypeasmajor/minor, and simplifies geometry →contours.gpkgApplies Chaikin corner-cutting in-place (Python/OGR): replaces each segment with two points at 25% and 75%, repeated
chaikin_iterationstimes — handles bothLINESTRINGandMULTILINESTRINGgeometriesReprojects the smoothed contours to EPSG:4326 with
ogr2ogr -t_srs EPSG:4326 -lco RFC7946=YES -lco WRITE_BBOX=YES→contours_4326.geojsonRuns
tippecanoe -zg --drop-densest-as-needed --extend-zooms-if-still-dropping→contours.mbtilesThe bundled tippecanoe (v1.36.0) predates native PMTiles output and always writes MBTiles regardless of output extension. PMTiles conversion is handled in the next step.
Converts MBTiles → PMTiles v3 with
pmtiles convert(the go-pmtiles CLI). Verifies the output magic number (PMTiles) as a sanity check →contours.pmtilesUploads both files to MinIO and returns artifact descriptors with
md5sumhashes andogrinfometadata captured from the GeoJSON
Recipe usage
json
{
"id": "build_contours",
"type": "BUILD_CONTOURS",
"inputs": { "input_las": "job:input_las" },
"outputs": {
"contours_mbtiles": "step:build_contours.contours_mbtiles",
"contours_pmtiles": "step:build_contours.contours_pmtiles"
}
}With explicit params (all optional):
json
"params": {
"build_contours": {
"resolution": 1,
"output_type": "mean",
"smoothing_factor": 5,
"contour_interval": 5,
"major_interval_multiplier": 5,
"simplify_tolerance": 1,
"chaikin_iterations": 3
}
}Artifact storage paths
text
artifacts/job_{id}/build_contours/contours.mbtiles
artifacts/job_{id}/build_contours/contours.pmtilesRelated executors
BUILD_HILLSHADE_DSM— shares the same LAS-to-DSM rasterization first stage; produces a raster instead of vector tilesBUILD_COLORED_DSM— shares the same first stage; produces a color-relief rasterBUILD_COLORED_HILLSHADE_DSM— shares the same first stage; produces a colored shaded-relief raster