Short answer

A correct slope map in QGIS is mostly about what you do before you press run: confirm the DEM is in a projected, metre-based CRS, set the z-factor so vertical and horizontal units agree, and decide whether you want degrees or percent. Then run Processing → Raster terrain analysis → Slope (or the GDAL Slope algorithm), classify the output with a Singleband pseudocolor renderer, and check a few values against terrain you understand. The color ramp is the last 5 percent of the job.

Slope is a measurement layer, not a picture. If the underlying units are wrong, the prettiest ramp in the world is still reporting nonsense.

Step 1 — Verify the DEM before anything else

Load the DEM and open Layer Properties → Information. You are looking for three things:

  • CRS. It should be a projected system in metres — for example EPSG:32630 (UTM zone 30N) or a national grid. If it reads EPSG:4326 (WGS84) or any geographic CRS, the units are degrees and slope will come out wrong. Reproject first (see step 2).
  • Pixel size. Should read something like 30, -30 (metres). If it reads 0.000277..., -0.000277..., that is degrees — another sign you are in a geographic CRS.
  • Vertical units. Confirm elevation is metres. US data is often in feet, which changes the z-factor.

Also confirm the file is genuine elevation, not a hillshade image someone saved as a GeoTIFF. The Identify tool should return elevation values (e.g. 412.7), not RGB triplets (e.g. 188, 188, 188).

Step 2 — Reproject if needed

If the DEM is geographic, reproject it. Raster → Projections → Warp (Reproject), or in Processing run GDAL → Warp:

  • Target CRS: your local UTM zone, e.g. EPSG:32630.
  • Resampling method: Bilinear for elevation (never Nearest neighbour — it creates stair-step artefacts that become fake slope spikes; Cubic is also fine).
  • Output resolution: set a clean pixel size, e.g. 30 m.

The equivalent on the command line:

gdalwarp -t_srs EPSG:32630 -r bilinear -tr 30 30 dem.tif dem_utm.tif

Step 3 — Run the Slope algorithm

Two good options live in the Processing toolbox:

QGIS native: Raster terrain analysis → Slope. It outputs slope in degrees, applies a z-factor, and uses the Horn method on a 3×3 window. Set:

  • Z factor: 1 if vertical and horizontal units are both metres; 0.3048 if elevation is in feet and the grid is in metres.

GDAL Slope (GDAL → Raster analysis → Slope) gives more control:

  • "Slope expressed as percent instead of degrees" — tick this for percent rise.
  • "Scale (ratio of vert. units to horiz.)" — this is the z-factor in disguise: 1 for metre/metre, 0.3048 for feet/metre.
  • "Compute edges" — enable to avoid a NoData border one pixel wide.

Command-line equivalent:

gdaldem slope dem_utm.tif slope_deg.tif        # degrees
gdaldem slope -p dem_utm.tif slope_pct.tif     # percent

Degrees vs percent — pick deliberately

These measure the same thing on different scales and are easy to confuse:

  • Degrees range 0–90. Intuitive for hazard thresholds (e.g. landslide-susceptible slopes often flagged above 25–35 degrees) and for talking to non-GIS stakeholders.
  • Percent rise is (rise / run) × 100. A 45-degree slope is 100 percent, not 100 degrees; a 100 percent grade is steep, not vertical. Common in road and drainage engineering.

Conversion: degrees = atan(percent / 100) × 180 / π. Whatever you choose, write the unit in the legend title — "Slope (degrees)" — so the next reader cannot misinterpret it.

Step 4 — Classify and style

Right-click the slope layer → Properties → Symbology → Singleband pseudocolor. Rather than a continuous ramp, use discrete or classified breaks tied to the decision the map supports. A common terrain-stability scheme in degrees:

ClassRange (deg)Meaning
Flat0–5Buildable, low erosion
Gentle5–15Manageable
Moderate15–25Caution
Steep25–35Constrained
Very steep>35High hazard

Discrete classes communicate thresholds far better than a smooth blue-to-red ramp, which hides where the meaningful break sits.

Worked QA pass

Before the map leaves your machine:

  • Histogram check. In Symbology, load min/max and view the histogram. For most landscapes the bulk sits below 40 degrees with a long tail. A tall spike at 89–90 degrees means a units or CRS error.
  • Spot-check a known slope. Use the Identify tool on a feature you can verify — a quarry face, a graded road embankment, a flat lake margin (should read near 0).
  • Look at the edges, not just the middle. Tile seams, NoData borders, and void-filled patches in SRTM/lidar produce ring or stripe artefacts that only show at the margins.
  • Watch for noise on high-resolution lidar. A 1 m DEM amplifies micro-relief (vegetation returns, vehicle ruts). If you want landform-scale slope, resample to a coarser grid or apply a light low-pass filter before computing slope, and say so in the metadata.

Common pitfalls and why they happen

  • Slope pinned near 90 everywhere. DEM is in a geographic CRS — degrees as the run. Reproject to UTM. This is by far the most frequent failure.
  • Slope too shallow by a constant factor. Vertical units are feet but the z-factor is left at 1. Set z-factor 0.3048.
  • Speckled, noisy slope. High-resolution lidar with real micro-features, or nearest-neighbour resampling during reproject. Use bilinear resampling and consider smoothing.
  • Legend reads "percent" but values look like degrees (or vice versa). The GDAL -p flag was toggled inconsistently between runs. Re-run and confirm the unit.
  • Treating a hillshade as slope. Hillshade is a shaded-relief visualisation lit from an angle; it is not a measurement and cannot be classified into slope thresholds.

Bathyl perspective

We build slope rasters as evidence layers, which means the CRS, z-factor, source resolution, and unit choice are recorded alongside the file, not held in someone's head. A slope map is trustworthy only when another analyst can see exactly how it was produced and reproduce the same numbers from the same DEM.

Related Bathyl reading

Sources