Reprojection and CRS

A Coordinate Reference System (CRS) defines how coordinates in a dataset map to real-world locations. CRS mismatches are one of the most common sources of silent errors in GIS workflows: two layers may display correctly on screen (because QGIS reprojects them on-the-fly for display) while producing wrong results when passed to an analysis tool that expects matching CRS inputs.

This chapter explains how to identify, verify, and correct CRS issues in WbW-QGIS workflows.


Key Concepts

  • Geographic CRS (GCS): Coordinates in angular units (degrees of latitude and longitude). Common examples: WGS84 (EPSG:4326), NAD83 (EPSG:4269). Not suitable as a working CRS for distance/area calculations.
  • Projected CRS (PCS): Coordinates in linear units (metres or feet) on a flat map projection. Examples: UTM zones, Lambert Conformal Conic, Albers Equal Area.
  • EPSG code: A numeric registry identifier for a CRS. EPSG:4326 = WGS84; EPSG:32617 = UTM Zone 17N (WGS84); EPSG:3978 = Canada Atlas Lambert.
  • On-the-fly reprojection: QGIS displays all layers in the project CRS regardless of their native CRS. This is for display only — it does not change the file on disk.
  • Reproject (warp): Permanently transform raster or vector data to a new CRS, writing a new file. Required before passing data to analysis tools.
  • Z factor: A unit-conversion factor applied when DEM horizontal units (metres) differ from vertical units (feet), or vice versa.

Choosing a Working CRS

ScenarioRecommended CRS type
Global or continental analysisGeographic (WGS84 / EPSG:4326) for data exchange; Equal-Area projection for area measurements
Regional / national analysisNational projected CRS (e.g. Canada Atlas Lambert / EPSG:3978)
Local analysis (< 500 km extent)UTM zone covering the study area
Terrain analysis, hydrology, LiDARProjected CRS in metres (UTM recommended)
Slope / distance calculationsAlways use a projected CRS

Finding your UTM zone: The UTM zone number equals ⌊(longitude + 180) / 6⌋ + 1. For Ottawa, Canada (longitude ≈ –75.7°): zone 18, northern hemisphere → EPSG:32618.


Checking the CRS of a Layer

  1. Right-click a layer in the Layers panel → Properties.
  2. Select the Information tab.
  3. Read the CRS field. Confirm:
    • Authority and code (e.g. EPSG:32618)
    • Unit (metres vs degrees)
    • Datum (WGS84, NAD83, etc.)

Or in the Python Console:

from qgis.core import QgsProject

layer = QgsProject.instance().mapLayersByName('dem')[0]
crs = layer.crs()
print(crs.authid())        # e.g. "EPSG:32618"
print(crs.mapUnits())      # 0 = metres, 6 = degrees
print(crs.isGeographic())  # True if GCS

Setting the Project CRS

The project CRS controls the display and the default output CRS for tools that do not inherit CRS from their inputs.

Project → Properties → CRS tab → search by EPSG code or name → click OK.

Or use View → Panels → CRS Status at the bottom-right of the QGIS window to set the project CRS from any loaded layer.


Reprojecting a Raster

Use Reproject Raster to permanently transform a raster to a new CRS. This is required before any terrain analysis on a DEM stored in geographic (degree) coordinates.

Processing Toolbox → Whitebox Workflows → Raster → Reproject Raster

ParameterRecommended value
Input layerdem_wgs84.tif
Target EPSG code32618
Resampling methodbilinear (elevation surfaces)
Outputdem_utm18n.tif

The Resampling method parameter accepts any of the methods supported by WbW's raster engine:

MethodBest for
nearestCategorical / integer rasters (classification maps, stream grids)
bilinearContinuous surfaces (DEMs, slope, TWI, reflectance)
cubicHigh-quality continuous-surface resampling
lanczosHigh-quality sinc-window resampling
average3×3 mean statistic
min / max3×3 extremum statistics
mode3×3 majority-class (smoothed categorical)
median3×3 median statistic
stddev3×3 standard deviation
import processing

processing.run('whitebox_workflows:reproject_raster', {
    'input': '/data/dem_wgs84.tif',
    'epsg': 32618,
    'resample': 'bilinear',
    'output': '/data/dem_utm18n.tif',
})

After running, load the output and confirm in Layer Properties → Information: CRS shows EPSG:32618 and the extent is in metres.


Reprojecting a Vector Layer

Use Reproject Vector to transform a vector dataset to a new CRS.

Processing Toolbox → Whitebox Workflows → Vector → Reproject Vector

ParameterRecommended value
Input layerroads_wgs84.shp
Target EPSG code32618
Outputroads_utm18n.shp
import processing

processing.run('whitebox_workflows:reproject_vector', {
    'input': '/data/roads_wgs84.shp',
    'epsg': 32618,
    'output': '/data/roads_utm18n.shp',
})

Reprojecting a LiDAR Dataset

Use Reproject LiDAR to transform a point cloud to a new CRS.

Processing Toolbox → Whitebox Workflows → LiDAR → Reproject LiDAR

ParameterRecommended value
Input LiDAR filecloud_wgs84.laz
Target EPSG code32618
Outputcloud_utm18n.laz
import processing

processing.run('whitebox_workflows:reproject_lidar', {
    'input': '/data/cloud_wgs84.laz',
    'epsg': 32618,
    'output': '/data/cloud_utm18n.laz',
})

Assigning a Missing CRS

If a file has correct coordinates but missing or wrong CRS metadata (shown as "Unknown CRS" in Layer Properties), use one of the three Assign Projection tools to write the correct EPSG code into the file without moving any coordinates.

Assign vs. Reproject: Assigning a CRS only updates the metadata label. Use it when coordinates are already in the target system but the file has no CRS tag. If the coordinate values themselves need to change, use the Reproject tools above instead.

Assign Projection to a Raster

Processing Toolbox → Whitebox Workflows → Raster → Assign Projection Raster

ParameterValue
Input layerdem_no_crs.tif
EPSG code to assign32618
import processing

processing.run('whitebox_workflows:assign_projection_raster', {
    'input': '/data/dem_no_crs.tif',
    'epsg': 32618,
})

Assign Projection to a Vector

Processing Toolbox → Whitebox Workflows → Vector → Assign Projection Vector

ParameterValue
Input layerroads_no_crs.shp
EPSG code to assign32618
import processing

processing.run('whitebox_workflows:assign_projection_vector', {
    'input': '/data/roads_no_crs.shp',
    'epsg': 32618,
})

Assign Projection to a LiDAR File

Processing Toolbox -> Whitebox Workflows -> LiDAR -> Assign Projection LiDAR

ParameterValue
Input LiDAR filecloud_no_crs.laz
EPSG code to assign32618
import processing

processing.run('whitebox_workflows:assign_projection_lidar', {
  'input': '/data/cloud_no_crs.laz',
  'epsg': 32618,
})

Georeferencing from Control Points

Use Georeference Raster From Control Points when the raster has no valid georeferencing and you have control points that relate pixel coordinates to map coordinates.

Processing Toolbox -> Whitebox Workflows -> Raster -> Georeference Raster From Control Points

ParameterRecommended value
Input rasterhistorical_scan.tif
Control points CSVhistorical_scan_gcps.csv
Destination EPSG code32618
Resampling methodbilinear (continuous) or nearest (categorical)
Georeferenced raster outputhistorical_scan_georef.tif
Diagnostics report outputhistorical_scan_georef_report.json (optional)

Control-points CSV fields must include source image coordinates and target map coordinates:

  • source_col
  • source_row
  • target_x
  • target_y
import processing

processing.run('whitebox_workflows:georeference_raster_from_control_points', {
  'input': '/data/historical_scan.tif',
  'control_points': '/data/historical_scan_gcps.csv',
  'epsg': 32618,
  'resample': 'bilinear',
  'output': '/data/historical_scan_georef.tif',
  'report': '/data/historical_scan_georef_report.json',
})

Assign Projection to a LiDAR Dataset

Processing Toolbox → Whitebox Workflows → LiDAR → Assign Projection LiDAR

ParameterValue
Input LiDAR filecloud_no_crs.laz
EPSG code to assign32618
import processing

processing.run('whitebox_workflows:assign_projection_lidar', {
    'input': '/data/cloud_no_crs.laz',
    'epsg': 32618,
})

The Z Factor for Terrain Tools

When a DEM's horizontal units differ from its vertical units, slope and curvature calculations are incorrect unless a Z factor is applied.

Horizontal unitVertical unitZ factor
MetresMetres1.0 (no conversion needed)
MetresFeet0.3048
FeetFeet1.0
Degrees (geographic)MetresDo not use — reproject first

All WbW terrain tools that accept a Z factor parameter apply the conversion as: slope = atan(rise × z_factor / run).

Best practice: Reproject the DEM to a projected CRS in metres before running any terrain analysis. Set Z factor to 1.0 after reprojection if vertical units are also metres.


Batch Reprojection via Python Console

Reproject all GeoPackages in a folder to EPSG:32618 using the Whitebox vector reprojection tool:

import processing
from pathlib import Path

src_dir = Path('/data/raw_vectors')
out_dir = Path('/data/projected')
out_dir.mkdir(exist_ok=True)

for gpkg in src_dir.glob('*.gpkg'):
    out = out_dir / gpkg.name
    processing.run('whitebox_workflows:reproject_vector', {
        'input': str(gpkg),
        'epsg': 32618,
        'output': str(out),
    })
    print(f"Reprojected: {gpkg.name}")

print("Batch reprojection complete.")

Reproject a folder of LiDAR files in the same pattern:

import processing
from pathlib import Path

src_dir = Path('/data/raw_lidar')
out_dir = Path('/data/projected_lidar')
out_dir.mkdir(exist_ok=True)

for las in src_dir.glob('*.laz'):
    out = out_dir / las.name
    processing.run('whitebox_workflows:reproject_lidar', {
        'input': str(las),
        'epsg': 32618,
        'output': str(out),
    })
    print(f"Reprojected: {las.name}")

print("Batch LiDAR reprojection complete.")

Common CRS Problems

ProblemLikely causeFix
Layers display in wrong locationLayer has incorrect assigned CRSAssign correct CRS (do not reproject)
Slope values in hundreds of degreesDEM in geographic CRS (degrees) — cell size << 1°Reproject DEM to metres before running slope
Area calculations wildly wrongLayer CRS is geographic (degrees)Reproject to equal-area projected CRS
Watershed does not close properlyRaster and vector inputs in different CRSReproject all inputs to same CRS before processing
WbW tool silently returns NoData everywhereCRS mismatch causes spatial extents not to overlapVerify all inputs share the same CRS and extent
"Datum transform not found" warning in QGISDatum shift grid file not installedInstall proj-data package, or accept approximate transform

Validation Checklist

  • Project CRS is set to the intended working CRS before analysis.
  • All raster inputs share the same CRS, extent, and cell size.
  • All vector inputs share the same CRS as the raster grid.
  • DEM CRS is projected (linear units — metres or feet), not geographic.
  • Z factor is set to 1.0 when both horizontal and vertical units are metres.
  • Reprojected outputs have been inspected (extent in metres, CRS code confirmed).
  • No layers show "Unknown CRS" in the Layers panel.