TL;DR: If your QGIS buffer is the wrong size, your CRS is EPSG:4326 (degrees) — switch to a projected CRS (e.g. a local UTM zone) before buffering, or enable the Geodesic Distance option in the QGIS 3.28+ Buffer dialog.
The QGIS Buffer tool has one requirement that is easy to miss: the distance you enter is in the unit of the layer’s CRS, not the unit of your project CRS, and not any intuitive real-world unit you might have in mind.
If your layer is in EPSG:4326 (WGS 84, latitude/longitude), the distance unit is degrees. Enter 1000 and you get a buffer 1000 degrees wide — roughly covering 28 times the circumference of the Earth along a meridian. The tool does not warn you. The result is geometrically valid but geographically absurd.
This is the most common cause of the “my buffer covers the entire map” report on GIS Stack Exchange. It is not a QGIS bug. It is a unit mismatch that the tool correctly (if silently) enforces.
What Euclidean buffer actually does
The standard QGIS Buffer algorithm — Processing Toolbox → Vector Geometry → Buffer — uses Cartesian (planar) math. It takes the input geometry as a set of X, Y coordinates, offsets every edge by the specified distance, and rounds the caps. It has no concept of the ellipsoid shape of the Earth.
This is spelled out in the QGIS API: QgsGeometry objects have no concept of geodesy, and spatial operations including buffer() are calculated using strictly Cartesian mathematics. The QgsDistanceArea class is what you’d use if you needed ellipsoidal calculations — but the Buffer processing algorithm does not use it.
The implication: for a Euclidean buffer to give a result in meters, your layer’s CRS must have meters as its unit. UTM zones do. EPSG:4326 does not.
The classic failure mode
A user downloads a dataset of worldwide airport locations from Natural Earth or OpenStreetMap. It comes in EPSG:4326 (practically everything does by default). The project CRS is also set to 4326. They run Buffer → Distance = 10000, thinking they’re specifying 10 km to find all airports within 10 km of a city.
The buffer distance is 10000 degrees. Since 1 degree of latitude ≈ 111 km, 10000 degrees ≈ 1.11 million km — several times the distance to the Moon. Every polygon from the buffer overlaps every other feature on Earth.
The unit indicator is visible in the dialog (“Map units” is the default) but it does not say “degrees” in plain language, and the connection between “map units” and “WGS 84 degrees” is non-obvious to anyone not already thinking about CRS.
When Euclidean buffers on projected data are fine
For most local and regional work, reprojecting to a suitable UTM zone and running a Euclidean buffer there is indistinguishable from a geodesic buffer at any resolution you’d measure in practice.
A UTM zone covers a 6° longitudinal band. Within that band:
- Scale distortion at the zone edges is less than 0.1%
- A 1000 m Euclidean buffer in UTM is accurate to within 1 m of a geodesic 1000 m buffer
- For features entirely within a single UTM zone, just reproject, buffer, reproject back
The standard workflow for most GIS work:
# PyQGIS: reproject to UTM, buffer in meters, reproject back
import processing
# Step 1: Reproject layer to appropriate UTM
reproj = processing.run("native:reprojectlayer", {
'INPUT': layer,
'TARGET_CRS': 'EPSG:32632', # UTM zone 32N — pick your zone
'OUTPUT': 'TEMPORARY_OUTPUT'
})['OUTPUT']
# Step 2: Buffer in meters (now CRS unit = meters)
buffered = processing.run("native:buffer", {
'INPUT': reproj,
'DISTANCE': 500, # 500 meters, exact
'SEGMENTS': 25,
'END_CAP_STYLE': 0,
'JOIN_STYLE': 0,
'MITER_LIMIT': 2,
'DISSOLVE': False,
'OUTPUT': 'TEMPORARY_OUTPUT'
})['OUTPUT']
# Step 3: Reproject back to EPSG:4326 for output
result = processing.run("native:reprojectlayer", {
'INPUT': buffered,
'TARGET_CRS': 'EPSG:4326',
'OUTPUT': 'TEMPORARY_OUTPUT'
})['OUTPUT']
This covers most use cases for municipal planning, environmental analysis, infrastructure siting, and anything confined to a single country or region.
The UTM zone selector is simple: find the zone for your area of interest at the WGS 84 / UTM zone chart — EPSG codes run from 32601 (UTM zone 1N) to 32660 (UTM zone 60N) for the northern hemisphere, and 32701–32760 for the southern hemisphere. For data spanning multiple zones, use a national grid (OSGB 36 for the UK, RD New for the Netherlands, etc.) or a continental equal-area projection (ETRS89-LAEA for Europe, ESRI:102008 for North America).
When the difference actually matters
Features near the poles. At latitude 75°N, 1 degree of longitude ≈ 28 km rather than the equatorial 111 km. A buffer applied in degrees at high latitudes produces dramatically elongated east-west shapes compared to the circular buffer on the ground. Any polar data analysis (Arctic shipping routes, Antarctic research stations, northern tundra ecology) is wrong in a degree-based buffer.
Large buffer radii on continental-scale datasets. A 500 km buffer around a feature on a Lambert Conformal Conic or Mercator projection will distort at the projection edges. The distortion becomes significant (>1%) once the buffer radius exceeds roughly 50 km on a standard UTM, or 200 km on a continental projection.
Features spanning multiple UTM zones. A pipeline route or transmission line crossing from UTM zone 32 to 33 cannot be accurately buffered in either zone alone. You need a geodesic approach.
Ocean-scale analysis. Fish exclusion zones, exclusive economic zones (EEZs), and shipping lane buffers are specified in nautical miles on the ellipsoid. Applying these in a Mercator projection introduces enough distortion to create legal and navigational errors.
Geodesic buffer in PyQGIS: the per-point azimuthal equidistant approach
QGIS does not have a native geodesic buffer tool. The workaround — first published by Spatial Thoughts and now standard practice — uses a custom Azimuthal Equidistant projection centered on each feature.
An azimuthal equidistant projection has the property that all points on the map are at the correct distance from the center point. Buffer the feature in that projection and the result is a true geodesic circle on the ground.
from qgis.core import (
QgsCoordinateReferenceSystem,
QgsCoordinateTransform,
QgsProject,
QgsGeometry,
QgsPointXY
)
def geodesic_buffer(point_geom, distance_m, segments=64):
"""
Create a geodesic buffer around a point geometry.
point_geom: QgsGeometry (point, in any CRS)
distance_m: buffer radius in meters
segments: approximation quality (higher = smoother)
Returns a QgsGeometry in EPSG:4326
"""
# Transform to WGS 84 first
wgs84 = QgsCoordinateReferenceSystem('EPSG:4326')
transform_to_wgs84 = QgsCoordinateTransform(
point_geom.sourceCrs() if hasattr(point_geom, 'sourceCrs') else wgs84,
wgs84,
QgsProject.instance()
)
geom_wgs84 = QgsGeometry(point_geom)
geom_wgs84.transform(transform_to_wgs84)
pt = geom_wgs84.asPoint()
lon, lat = pt.x(), pt.y()
# Build a custom Azimuthal Equidistant CRS centered on this point
aeqd_wkt = f"""PROJCS["AEqD",
GEOGCS["GCS_WGS_1984",
DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],
PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]],
PROJECTION["Azimuthal_Equidistant"],
PARAMETER["False_Easting",0],PARAMETER["False_Northing",0],
PARAMETER["Central_Meridian",{lon}],PARAMETER["Latitude_Of_Origin",{lat}],
UNIT["Meter",1]]"""
aeqd_crs = QgsCoordinateReferenceSystem()
aeqd_crs.createFromWkt(aeqd_wkt)
# Transform point to AEQD (will be at origin: 0, 0)
to_aeqd = QgsCoordinateTransform(wgs84, aeqd_crs, QgsProject.instance())
geom_aeqd = QgsGeometry.fromPointXY(QgsPointXY(0, 0))
# Buffer in AEQD = true geodesic circle on the ground
buffered_aeqd = geom_aeqd.buffer(distance_m, segments)
# Transform result back to WGS 84
from_aeqd = QgsCoordinateTransform(aeqd_crs, wgs84, QgsProject.instance())
buffered_aeqd.transform(from_aeqd)
return buffered_aeqd
For polygon and line features, the same principle applies but you build the AEQD CRS centered on the feature centroid. The error grows as you move away from the centroid, so for large polygons (>100 km across) you should consider decomposing into sub-features or using a different approach.
For point datasets with hundreds or thousands of features, this is computationally intensive (one custom CRS per point). The Lat Lon Buffer QGIS plugin implements this algorithm with C++ bindings and runs significantly faster than the pure Python version above.
The distance unit dropdown in QGIS 3.x
As of QGIS 3.x, the Buffer processing algorithm dialog shows a “Distance units” dropdown. The default is “Map units” (the CRS unit). You can change it to “Meters”, “Kilometers”, “Miles”, etc.
When you change this, QGIS converts your distance to the layer’s CRS unit using an approximate factor before running the Cartesian buffer. For EPSG:4326, it divides by roughly 111,000 m/degree at the equator. This avoids the “1000 degree buffer” mistake, but it is still a Cartesian buffer — it just applies an approximate unit conversion first. At the equator the result is close; at latitude 60°N the conversion factor is wrong by ~50% (1 degree of longitude ≈ 55 km, not 111 km).
Treat the “Meters” option in the QGIS buffer dialog as a convenience for rough work in moderate latitudes. For precise work, reproject to UTM first.
Choosing the right approach
| Scenario | CRS situation | Recommended approach |
|---|---|---|
| City-scale analysis, single country | Local projected CRS or UTM | Buffer directly — already correct |
| Regional analysis, <500 km extent | EPSG:4326 data | Reproject to UTM → buffer → reproject back |
| Global dataset, moderate latitudes | EPSG:4326 | Reproject to continental equal-area → buffer |
| Global dataset, near poles or large radii | EPSG:4326 | Per-point AEQD approach or Lat Lon Buffer plugin |
| Cross-UTM-zone linear feature | EPSG:4326 | Per-point AEQD or dissolve-reproject-buffer-union |
| Quick rough estimate, any scale | Any | QGIS “Meters” dropdown (adds ~10–50% lat-dependent error) |
The CRS check that prevents this in the first place
The simplest safeguard is to inspect the CRS unit before running any distance-based operation. In PyQGIS:
from qgis.core import QgsUnitTypes
crs = layer.crs()
unit = crs.mapUnits()
if unit == QgsUnitTypes.DistanceDegrees:
print(f"WARNING: Layer CRS '{crs.authid()}' uses degrees. "
"Reproject to a metric CRS before buffering.")
elif unit == QgsUnitTypes.DistanceMeters:
print(f"Layer CRS '{crs.authid()}' uses meters. Buffer distance is in meters.")
Add this to any processing script that accepts a distance parameter and you eliminate the entire class of accidental degree-scale buffers.
CRS issues in format conversion
This problem doesn’t stop at buffers. The same unit confusion affects distance joins, nearest-neighbour analysis, kernel density estimation, and any QGIS processing algorithm where a distance parameter is involved.
It also matters for format conversion. When you convert a GeoJSON dataset (typically in EPSG:4326) to a Shapefile for use in ArcGIS or QGIS analysis tools, the CRS is embedded in the .prj file. If downstream analysis applies distance operations without reprojecting, you’ll get the same degree-unit errors. GeoConvert preserves the source CRS in conversion output, which means if your GeoJSON is in 4326, the Shapefile will be in 4326 — correct for data fidelity, but you still need to reproject before buffering.
The pattern of CRS issues causing silently wrong results in QGIS also shows up in DWG files imported at the wrong scale and in coordinate-system pitfalls when exporting to Rhino via DXF. The common thread: QGIS uses the coordinates as given and applies the operation mathematically correctly — the error is in the assumption that the CRS unit matches the intended real-world unit.
For automating GIS workflows with PyQGIS, the CRS unit check above is worth building into any reusable processing function that takes a distance argument.
Frequently Asked Questions
Why is my QGIS buffer the wrong size?
Your buffer distance is being interpreted in the layer’s CRS unit, not meters. If the layer is in EPSG:4326 (WGS 84), the unit is degrees — entering 1000 means 1000 degrees, not 1000 meters. A 1000-degree buffer covers roughly 111,000 km (nearly three times around the Earth), which is why it appears to cover everything on your map. Fix: reproject the layer to a metric CRS (a local UTM zone) before buffering.
How do I fix incorrect units in the QGIS buffer on EPSG:4326?
Two options: (1) Reproject the layer to a metric CRS — use Processing > Reproject Layer, choose the appropriate UTM zone (e.g. EPSG:32618 for the eastern US, EPSG:32632 for central Europe), run the buffer in meters, then reproject back. (2) In QGIS 3.28+, the Buffer dialog has a Geodesic Distance checkbox — enabling it computes a true ellipsoidal buffer regardless of CRS. This is accurate for radii up to a few hundred kilometres at moderate latitudes.
What is the difference between Euclidean and geodesic buffer in QGIS?
A Euclidean buffer uses flat-plane (Cartesian) math. It offsets coordinates by the specified distance in the layer’s CRS unit — fast and exact for projected data, wrong for EPSG:4326 degree data. A geodesic buffer uses the shape of the Earth (the WGS 84 ellipsoid), measuring distances along the surface rather than in Cartesian coordinate space. Geodesic buffers are more accurate for large radii, high-latitude data, and cross-UTM-zone features, but they require more computation.