Short answer

Contours are isolines of constant elevation derived from a digital elevation model. In QGIS you generate them with Raster > Extraction > Contour (a wrapper around GDAL's gdal_contour) or the QGIS Contour processing algorithm: point them at a single-band elevation raster, set an interval, and they produce a line layer with an elevation attribute. The quality of the output is governed almost entirely by two upstream choices — the vertical accuracy of the DEM and the contour interval you pick. A fine interval on a coarse or noisy DEM produces dense, wandering lines that look detailed but represent grid artefacts, not terrain.

DEM first: what you contour determines what you get

Before contouring, know your DEM:

  • Source and resolution. A 1 m airborne LiDAR DTM, a 10 m national DEM, and a 30 m SRTM/Copernicus DEM are very different inputs. Cell size sets the smallest feature you can resolve; vertical accuracy (RMSE) sets the smallest interval that is meaningful.
  • DSM vs DTM. A Digital Surface Model includes buildings and canopy; contours from it will drape over trees. For terrain contours you want a Digital Terrain Model (bare earth).
  • CRS and vertical datum. Contour elevations carry the DEM's vertical reference (e.g. EGM2008 geoid heights vs ellipsoidal heights). The horizontal CRS should be projected and in metres so spacing is metric; reproject with gdalwarp -t_srs EPSG:2154 if needed.
  • No-data. Ensure the no-data value is set (gdalinfo to check) so the contour engine does not draw lines through voids or treat 0 as sea level.

A useful pre-step is hydrological conditioning if contours will feed terrain analysis: fill spurious pits (Processing > Fill sinks) so depressions are real, not sensor noise.

Choosing the interval

The interval is a vertical-accuracy decision, not an aesthetic one. A rule of thumb is that the contour interval should be a few times the DEM's vertical RMSE so that adjacent lines reflect real elevation change rather than error.

  • 1 m LiDAR DTM (RMSE ~0.1–0.3 m): 0.5 m or 1 m contours are honest; 2 m for a cleaner map.
  • 5–10 m DEM: 5 m or 10 m intervals.
  • 30 m SRTM / Copernicus GLO-30: 10–20 m; finer just draws noise.

Pair the base interval with index contours every 5th line (e.g. heavier lines at multiples of 50 m on a 10 m interval) for readability. In flat terrain, supplementary half-interval contours can be added selectively.

Generating contours

GUI: Raster > Extraction > Contour. Set Interval between contour lines (e.g. 10), the Attribute name for elevation (default ELEV), and optionally Offset to shift the base level. Output to a GeoPackage, not a shapefile, to avoid field-length and multipart limits.

Command line (gdal_contour): the most controllable route.

gdal_contour -a elev -i 10 -snodata -9999 dem.tif contours.gpkg
  • -a elev writes elevation to a field named elev.
  • -i 10 sets a 10 m interval; -off 5 shifts the base so lines fall on 5, 15, 25…
  • -fl 100 200 500 extracts only those specific levels instead of a regular interval — useful for a single index set or a flood elevation.
  • -p produces filled contour polygons instead of lines (bands between levels), good for hypsometric tinting.
  • -snodata -9999 tells GDAL the no-data value so voids are skipped.

QGIS Contour algorithm (gdal:contour) exposes the same options in the Processing dialog and integrates into models and batch runs.

Smoothing — and when not to

Raw contours stair-step along raster cell edges. There are two distinct cures, applied to different things:

  1. Smooth the DEM (changes the data). Apply a low-pass / Gaussian filter (e.g. SAGA Gaussian Filter, or gdal_calc/focal mean) before contouring to suppress per-cell noise. This produces genuinely smoother terrain isolines and is appropriate when the cell noise is not real.
  2. Smooth the lines (cartography only). Processing > Smooth (native:smoothgeometry) rounds the polylines for display. Use this purely for map appearance, with a modest iteration count; aggressive line smoothing pulls contours away from their true elevation positions, so never smooth lines that will be measured or used analytically.

Prefer DEM-level conditioning over heavy line smoothing when accuracy matters.

Styling and labelling for geological maps

  • Index vs intermediate. Use a rule-based style: "elev" % 50 = 0 for heavier index lines, else thin intermediate lines.
  • Labels. Label index contours only, with the elevation field, placement set to "On line" with curved labels following the contour. Suppress labels on short segments to avoid clutter (label only features longer than a threshold).
  • Hierarchy. Keep contours subordinate to geology — a mid-grey or brown hairline so faults, contacts, and units read first. On a hillshade base, contours add quantitative reference without competing.

Worked example: contours from a Copernicus 30 m DEM

  1. gdalinfo glo30.tif — confirm CRS (EPSG:4326) and no-data.
  2. Reproject to metric: gdalwarp -t_srs EPSG:32631 -r bilinear glo30.tif glo30_utm.tif.
  3. Fill sinks (Processing) to remove single-cell pits.
  4. gdal_contour -a elev -i 20 -off 0 glo30_utm.tif contours20.gpkg — 20 m interval, honest for 30 m data.
  5. Rule-based style: index at "elev" % 100 = 0.
  6. Curved labels on index lines, Smooth (1–2 iterations) for display only.

A 20 m interval here is deliberate: a 5 m interval on a 30 m DEM would draw three to four noisy lines for every one of real signal.

Common pitfalls and why they happen

  • Dense, wandering lines. Interval finer than the DEM can support; the lines trace noise. Coarsen the interval or smooth the DEM.
  • Contours over buildings/trees. You contoured a DSM. Use a bare-earth DTM.
  • Lines through voids or at 0 m. No-data not declared, so gaps were treated as elevation. Set -snodata.
  • Non-metric spacing. DEM in degrees (EPSG:4326); interval logic still works on values but horizontal context is distorted. Reproject to a projected CRS first.
  • Wrong elevations. Vertical datum mismatch (ellipsoidal vs geoid) — contours are internally consistent but offset from published heights. Document the datum.
  • Stair-step artefacts on coarse data. Inherent to grid contouring; light smoothing for display, but do not over-smooth analytical lines.

QA and validation

Cross-check contour elevations against the DEM: use the Identify tool to sample the raster at a few contour vertices — the value should match the line's elev within the DEM's noise. Compare against known spot heights or benchmarks if available. Verify lines do not cross each other (contours of the same surface never intersect) — crossings signal a corrupt DEM or mixed datums. Confirm the line layer's CRS and check that the interval and offset produced the intended levels by sorting the attribute table.

Bathyl perspective

Good contours are an honest portrait of the DEM, no finer than the data deserves. We choose intervals from vertical accuracy, condition the DEM rather than over-smoothing lines, and keep the vertical datum documented so elevations remain comparable across deliverables.

Related reading

Sources