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 readsEPSG: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 reads0.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:
1if vertical and horizontal units are both metres;0.3048if 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:
1for metre/metre,0.3048for 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:
| Class | Range (deg) | Meaning |
|---|---|---|
| Flat | 0–5 | Buildable, low erosion |
| Gentle | 5–15 | Manageable |
| Moderate | 15–25 | Caution |
| Steep | 25–35 | Constrained |
| Very steep | >35 | High 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
-pflag 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
- Why DEM Slope Values Are Wrong in Geographic Coordinates
- What Is Aspect in GIS?
- Hillshade vs Slope
- Terrain intelligence