Short answer
The z-factor is a scalar multiplier applied to the elevation (z) values of a DEM before slope, aspect, or hillshade is calculated. Its only purpose is to make the vertical units commensurate with the horizontal cell spacing so that the gradient, which is fundamentally a rise over run ratio, is dimensionally consistent. When x, y, and z are all in metres, the z-factor is 1. When the units disagree, the z-factor carries the conversion. Get it wrong and your slope angles, ruggedness indices, and shaded relief are all quietly scaled by the wrong amount.
This is one of the most common sources of "the terrain looks flat" or "the slope values are absurd" bug reports in GIS, and almost all of it comes down to a single misunderstood number.
Why a z-factor exists at all
Every first-order terrain derivative is built from the local gradient. For a cell, software fits a plane (or a finite-difference stencil) to a 3x3 window and estimates the partial derivatives dz/dx and dz/dy. Slope is then:
slope_radians = atan( z_factor * sqrt( (dz/dx)^2 + (dz/dy)^2 ) )
The dz/dx term divides an elevation difference (rise) by a ground distance (run). If the rise is in metres and the run is in metres, the ratio is unitless and the arctangent gives a true angle. But the run is derived from the cell size, which is expressed in the horizontal CRS units. If those units differ from the elevation units, the ratio is scaled by a constant, and atan of a wrongly scaled number is a wrong angle. The z-factor z_factor is the correction that puts rise and run back into the same unit system before the arctangent is taken.
The same gradient drives hillshade. Hillshade computes how much an illuminating sun vector aligns with the local surface normal, and that normal is built from dz/dx and dz/dy. So the z-factor controls how "steep" the surface appears to the virtual light, which is exactly why analysts also (deliberately) abuse it for vertical exaggeration.
The unit-matching rule
The decision is mechanical once you know your three unit systems:
| Horizontal (x/y) | Vertical (z) | z-factor |
|---|---|---|
| metres | metres | 1 |
| metres | feet | 0.3048 |
| feet | metres | 3.28084 |
| feet | feet | 1 |
The principle: the z-factor converts the vertical unit into the horizontal unit. If z is in feet and x/y are in metres, multiply z by 0.3048 so both are metres. If z is in metres and x/y are in US survey feet, multiply z by 3.28084 (more precisely 3.2808333... for US survey feet versus 3.280839895 for international feet, a distinction that matters at survey-grade precision).
A practical mnemonic: z_factor = (vertical unit in metres) / (horizontal unit in metres).
The hard case: geographic coordinates (degrees)
The situation that breaks naive workflows is a DEM stored in a geographic CRS such as EPSG:4326 (WGS 84), where x and y are decimal degrees and z is metres. Examples include unprojected SRTM, Copernicus GLO-30, and many ASTER GDEM tiles.
Here there is no single correct z-factor, because a degree is not a fixed ground distance. One degree of latitude is roughly constant (~111,320 m), but one degree of longitude is 111,320 x cos(latitude) metres, collapsing toward zero at the poles. Treating a degree as if it were a metre (z-factor 1) makes slope wildly understated, because the "run" is inflated by a factor of about 111,000.
There are two defensible approaches.
Reproject first (preferred). Warp the DEM into a metric projected CRS appropriate to the area, then use z-factor 1:
gdalwarp -t_srs EPSG:32633 -tr 30 30 -r bilinear \
-dstnodata -9999 srtm_4326.tif srtm_utm33n.tif
Pick the UTM zone (or a national grid such as EPSG:25832 / ETRS89-UTM32N) that covers your extent, then run gdaldem slope on the projected raster with the default z-factor of 1. This is the cleanest option because slope and aspect are genuinely better defined on an equal-area or conformal grid, and it removes the latitude dependence entirely.
Apply a latitude-corrected z-factor (quick approximation). If you must compute on the geographic grid, approximate:
z_factor ≈ 1 / (111320 * cos(latitude))
evaluated at the dataset's mid-latitude. At 45 degrees north this is 1 / (111320 x 0.7071) ≈ 1.27e-5. ArcGIS Pro documents exactly this style of latitude lookup table for its Slope and Hillshade tools when the input is in geographic coordinates. Be honest about its limits: it uses a single latitude for the whole tile and ignores the latitude-versus-longitude asymmetry, so over a tall north-south extent it introduces a north-to-south bias. For anything beyond a quick visual, reproject.
Worked example
Suppose a survey hands you a DEM where elevation is in feet but the project CRS is EPSG:25831 (ETRS89 / UTM 31N, metres). The cell size is 1 m. Run slope naively and the terrain looks improbably gentle, because each foot of rise is being divided by a metre of run, understating the true rise by a factor of 1/0.3048 ≈ 3.28.
The fix is one flag in GDAL:
gdaldem slope -s 0.3048 -compute_edges dem_ft.tif slope_deg.tif
In gdaldem, the scale parameter -s is the ratio of vertical to horizontal units, so -s 0.3048 is the z-factor that converts feet to metres. (When x/y and z are both metres on a projected grid, omit it; the default is 1.) For hillshade the same correction applies, and gdaldem hillshade also exposes a -z argument used purely for vertical exaggeration on top of unit correction:
gdaldem hillshade -z 1.0 -az 315 -alt 45 dem_m.tif hs.tif
In QGIS the same concept appears as the Z factor input on Raster > Analysis > Slope and on the Hillshade algorithm in the Processing toolbox (qgis:slope, qgis:hillshade). In ArcGIS Pro it is the Z factor parameter on the Slope, Aspect, and Hillshade Spatial Analyst tools; recent versions can read the z-unit from the raster and apply a sensible default, but you should always confirm it rather than trust the auto value.
Z-factor versus vertical exaggeration
These two uses share one parameter and are constantly confused.
- Unit correction is mandatory and objective. There is a correct value; using anything else makes slope and hillshade measurements wrong.
- Vertical exaggeration is a deliberate, subjective visualisation choice layered on top of unit correction. A z-factor of 2 or 3 on an otherwise unit-correct DEM stretches the relief so subtle landforms (palaeochannels, fault scarps, fluvial terraces) pop in a hillshade.
The rule of thumb: exaggerate freely for hillshade you intend only to look at, but never let an exaggerated z-factor leak into a slope, aspect, ruggedness (TRI/TPI), or curvature layer that someone will measure. A 3x exaggeration turns a true 20-degree slope into a reported ~50-degree slope. If you exaggerate for cartography, keep a separate, unexaggerated derivative for analysis and label both.
Common pitfalls and why they happen
- Slope of a few degrees on obviously steep terrain. Almost always a geographic-CRS DEM (degrees) run with z-factor 1. The run is ~111,000x too large. Reproject or apply the latitude correction.
- Slopes that are too steep everywhere. The reciprocal mistake: x/y in feet but z in metres, with z-factor 1, overstating rise by 3.28x. Set the z-factor to
3.28084. - Hillshade contrast changes but slope numbers look fine, or vice versa. People assume the z-factor only affects shading. It affects every gradient-based derivative equally; if a hillshade needed correcting, your slope did too.
- US survey feet treated as international feet. A ~2 ppm difference, negligible for shading but real for high-precision engineering DEMs. Confirm which foot the source uses (NAD83 State Plane data is frequently US survey feet).
- Mixed-unit mosaics. Tiles delivered in different vertical datums or units, merged before checking. Inspect
gdalinfoon each tile; normalise units before mosaicking, not after.
QA and validation
Before a terrain derivative goes into a report or model, run a few cheap checks:
- Read the metadata explicitly.
gdalinfo dem.tifshows the CRS, pixel size, and units; confirm horizontal units from the CRS and vertical units from the source documentation (they are rarely embedded reliably). - Sanity-check the slope histogram. Natural terrain rarely exceeds the angle of repose (~30-35 degrees) over large areas. If most cells read under 1 degree on mountainous ground, your z-factor is too small; if everything is near vertical, it is too large.
- Spot-check against known values. Pick two adjacent contours or a surveyed road grade and compute rise/run by hand; compare to the raster's slope at that point.
- Compare projected versus geographic. For a degree-based DEM, reproject a small clip, compute slope both ways, and confirm the projected result is the credible one.
Bathyl perspective
We treat the z-factor as a hard correctness check, not a styling slider. In our terrain pipelines, any DEM in a geographic CRS is reprojected to an appropriate metric grid before derivatives are computed, and any exaggeration used for cartographic hillshade is kept strictly separate from the analytical slope, aspect, and ruggedness layers we hand to clients. The documented z-factor and CRS travel with the output so the numbers can be reproduced and audited.
Related reading
- DEM Resolution for Terrain Analysis
- Contours From DEM Data
- Why Your GIS Layers Do Not Line Up
- Terrain intelligence