
Qgis Core Coordinate Systems
- 8 installs
- 29 repo stars
- Updated July 8, 2026
- openaec-foundation/qgis-claude-skill-package
Helps with ai & agent building tasks.
About
qgis-core-coordinate-systems is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.
- qgis-core-coordinate-systems
- AI & Agent Building
- AI-coding skill
Qgis Core Coordinate Systems by the numbers
- 8 all-time installs (skills.sh)
- Ranked #12,335 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 2, 2026 (Skillselion catalog sync)
npx skills add https://github.com/openaec-foundation/qgis-claude-skill-package --skill qgis-core-coordinate-systemsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 8 |
|---|---|
| repo stars | ★ 29 |
| Last updated | July 8, 2026 |
| Repository | openaec-foundation/qgis-claude-skill-package ↗ |
What it does
Helps with ai & agent building tasks.
Files
qgis-core-coordinate-systems
Quick Reference
Core CRS Classes
| Class | Purpose | Key Methods |
|---|---|---|
QgsCoordinateReferenceSystem | Represents a spatial reference system | isValid(), authid(), isGeographic(), mapUnits() |
QgsCoordinateTransform | Transforms coordinates between two CRS | transform(), transformBoundingBox() |
QgsCoordinateTransformContext | Project-level datum transformation settings | Used as parameter for transforms |
QgsDistanceArea | Measures distances and areas in any CRS | measureLine(), measureArea(), setEllipsoid() |
CRS Creation Methods
| Input Format | Constructor Syntax | Example |
|---|---|---|
| EPSG code | QgsCoordinateReferenceSystem("EPSG:<code>") | "EPSG:4326" |
| PostGIS SRID | QgsCoordinateReferenceSystem("POSTGIS:<srid>") | "POSTGIS:4326" |
| Internal ID | QgsCoordinateReferenceSystem("INTERNAL:<srsid>") | "INTERNAL:3452" |
| Proj string | QgsCoordinateReferenceSystem("PROJ:<proj>") | "PROJ:+proj=longlat +datum=WGS84" |
| WKT string | QgsCoordinateReferenceSystem("WKT:<wkt>") | "WKT:GEOGCS[...]" |
CRS Property Methods
| Method | Returns | Description |
|---|---|---|
authid() | str | Authority identifier, e.g., "EPSG:4326" |
description() | str | Human-readable name, e.g., "WGS 84" |
isValid() | bool | Whether the CRS was successfully created |
isGeographic() | bool | True for geographic CRS (degrees), False for projected (meters) |
mapUnits() | Qgis.DistanceUnit | Unit enumeration for the CRS |
toProj() | str | Full Proj string representation |
toWkt() | str | Full WKT string representation |
postgisSrid() | int | PostGIS SRID value |
srsid() | int | QGIS internal ID |
projectionAcronym() | str | Projection type, e.g., "longlat" |
ellipsoidAcronym() | str | Ellipsoid identifier, e.g., "EPSG:7030" |
Common EPSG Codes
| Code | Name | Type | Units | Use Case |
|---|---|---|---|---|
| 4326 | WGS 84 | Geographic | Degrees | GPS coordinates, global data exchange |
| 3857 | Web Mercator | Projected | Meters | Web maps (OpenStreetMap, Google Maps) |
| 32631-32660 | UTM Zones 31N-60N | Projected | Meters | Accurate local measurements (Northern Hemisphere) |
| 32701-32760 | UTM Zones 1S-60S | Projected | Meters | Accurate local measurements (Southern Hemisphere) |
| 28992 | Amersfoort / RD New | Projected | Meters | Netherlands national grid |
| 27700 | OSGB 1936 | Projected | Meters | UK Ordnance Survey |
| 2154 | RGF93 / Lambert-93 | Projected | Meters | France national grid |
---
Critical Warnings
NEVER create a QgsCoordinateTransform without a QgsCoordinateTransformContext. The bare constructor ignores datum transformation preferences and produces silently inaccurate results.
NEVER assign a CRS to a layer as a substitute for reprojection. layer.setCrs() changes the interpretation of existing coordinates -- it does NOT transform the data. Use QgsCoordinateTransform to actually reproject.
NEVER assume QgsPointXY takes latitude first for EPSG:4326. QgsPointXY ALWAYS takes (x, y) = (longitude, latitude), regardless of the EPSG axis order specification.
NEVER use deprecated creation methods (createFromSrid(), createFromEpsg()). ALWAYS use the string-prefix constructor: QgsCoordinateReferenceSystem("EPSG:4326").
NEVER skip isValid() checks after CRS creation. An invalid CRS silently produces wrong results in ALL downstream operations (transforms, measurements, spatial queries).
NEVER run standalone PyQGIS scripts without QgsApplication.setPrefixPath() set correctly. Without it, QGIS cannot find the srs.db database and ALL CRS operations fail silently.
ALWAYS use QgsProject.instance().transformContext() instead of a bare QgsCoordinateTransformContext(). The project context includes user-configured datum transformation preferences.
ALWAYS check datum transformation grid availability before transforming between datums that require grid files. Missing grids cause silent loss of precision.
---
Decision Tree
Which CRS to Use?
Need to handle coordinates?
├── Receiving GPS/WGS84 data?
│ └── Use EPSG:4326 (geographic, degrees)
├── Displaying on a web map?
│ └── Use EPSG:3857 (Web Mercator, meters)
├── Measuring distances/areas accurately?
│ ├── Local area (< 6 degrees longitude)?
│ │ └── Use UTM zone for the area (EPSG:326xx for N, EPSG:327xx for S)
│ └── Large area or global?
│ └── Use EPSG:4326 + QgsDistanceArea with ellipsoidal measurement
├── Working with national data?
│ └── Use the country's national CRS (e.g., EPSG:28992 for NL)
└── Storing data for exchange?
└── Use EPSG:4326 (universal standard)How to Transform Coordinates?
Need to transform coordinates?
├── Single point?
│ └── QgsCoordinateTransform.transform(QgsPointXY)
├── Bounding box?
│ └── QgsCoordinateTransform.transformBoundingBox(QgsRectangle)
├── Full geometry?
│ └── geometry.transform(QgsCoordinateTransform)
├── Entire layer?
│ └── Use processing: native:reprojectlayer
└── Just for display?
└── Set project CRS — on-the-fly reprojection handles renderingEllipsoidal vs. Planimetric Measurement?
Need to measure distances or areas?
├── Source CRS is geographic (degrees)?
│ └── ALWAYS use QgsDistanceArea with ellipsoid set
├── Source CRS is projected (meters)?
│ ├── Need high precision over large areas?
│ │ └── Use QgsDistanceArea with ellipsoid set
│ └── Local area, moderate precision OK?
│ └── Planimetric (Cartesian) measurement is acceptable
└── Unsure?
└── ALWAYS use QgsDistanceArea with ellipsoid — it handles both cases---
Essential Patterns
Pattern 1: Create and Validate a CRS
from qgis.core import QgsCoordinateReferenceSystem
crs = QgsCoordinateReferenceSystem("EPSG:4326")
if not crs.isValid():
raise RuntimeError("Failed to create CRS: EPSG:4326")
print(crs.authid()) # "EPSG:4326"
print(crs.description()) # "WGS 84"
print(crs.isGeographic()) # TruePattern 2: Transform Coordinates Between CRS
from qgis.core import (
QgsCoordinateReferenceSystem,
QgsCoordinateTransform,
QgsProject,
QgsPointXY
)
crs_src = QgsCoordinateReferenceSystem("EPSG:4326")
crs_dest = QgsCoordinateReferenceSystem("EPSG:32633")
context = QgsProject.instance().transformContext()
xform = QgsCoordinateTransform(crs_src, crs_dest, context)
# Forward transform (source -> destination)
pt_utm = xform.transform(QgsPointXY(18.0, 5.0))
# Reverse transform (destination -> source)
pt_wgs = xform.transform(pt_utm, QgsCoordinateTransform.ReverseTransform)Pattern 3: Transform a Geometry
from qgis.core import (
QgsCoordinateReferenceSystem,
QgsCoordinateTransform,
QgsProject,
QgsGeometry,
QgsPointXY
)
geom = QgsGeometry.fromPointXY(QgsPointXY(5.0, 52.0))
xform = QgsCoordinateTransform(
QgsCoordinateReferenceSystem("EPSG:4326"),
QgsCoordinateReferenceSystem("EPSG:28992"),
QgsProject.instance().transformContext()
)
geom.transform(xform) # In-place transformationPattern 4: Measure Distance with QgsDistanceArea
from qgis.core import (
QgsDistanceArea,
QgsCoordinateReferenceSystem,
QgsPointXY,
QgsProject,
Qgis
)
d = QgsDistanceArea()
d.setSourceCrs(
QgsCoordinateReferenceSystem("EPSG:4326"),
QgsProject.instance().transformContext()
)
d.setEllipsoid("WGS84")
point1 = QgsPointXY(5.0, 52.0)
point2 = QgsPointXY(5.1, 52.1)
distance_m = d.measureLine(point1, point2)
# Convert to kilometers
distance_km = d.convertLengthMeasurement(distance_m, Qgis.DistanceUnit.Kilometers)Pattern 5: Set Project CRS
from qgis.core import QgsProject, QgsCoordinateReferenceSystem
# Set project CRS: all layers render in this CRS via on-the-fly reprojection
QgsProject.instance().setCrs(QgsCoordinateReferenceSystem("EPSG:3857"))---
Common Operations
Get CRS from an Existing Layer
layer = QgsProject.instance().mapLayersByName("my_layer")[0]
crs = layer.crs()
print(crs.authid()) # e.g., "EPSG:4326"Transform a Bounding Box
from qgis.core import QgsRectangle
bbox = QgsRectangle(4.0, 51.0, 6.0, 53.0) # xmin, ymin, xmax, ymax
xform = QgsCoordinateTransform(
QgsCoordinateReferenceSystem("EPSG:4326"),
QgsCoordinateReferenceSystem("EPSG:3857"),
QgsProject.instance().transformContext()
)
bbox_transformed = xform.transformBoundingBox(bbox)Measure Area of a Polygon
from qgis.core import QgsDistanceArea, QgsCoordinateReferenceSystem, QgsProject, Qgis
d = QgsDistanceArea()
d.setSourceCrs(layer.crs(), QgsProject.instance().transformContext())
d.setEllipsoid("WGS84")
for feature in layer.getFeatures():
area_sqm = d.measureArea(feature.geometry())
area_ha = d.convertAreaMeasurement(area_sqm, Qgis.AreaUnit.Hectares)Reproject a Layer via Processing
import processing
result = processing.run("native:reprojectlayer", {
"INPUT": layer,
"TARGET_CRS": QgsCoordinateReferenceSystem("EPSG:32632"),
"OUTPUT": "memory:"
})
reprojected_layer = result["OUTPUT"]---
Reference Links
- references/methods.md -- API signatures for QgsCoordinateReferenceSystem, QgsCoordinateTransform, QgsDistanceArea
- references/examples.md -- Working code examples for CRS operations
- references/anti-patterns.md -- CRS pitfalls and what NOT to do
Official Sources
- https://qgis.org/pyqgis/master/core/QgsCoordinateReferenceSystem.html
- https://qgis.org/pyqgis/master/core/QgsCoordinateTransform.html
- https://qgis.org/pyqgis/master/core/QgsDistanceArea.html
- https://docs.qgis.org/latest/en/docs/pyqgis_developer_cookbook/crs.html
Anti-Patterns (QGIS CRS)
1. Missing Transform Context
# WRONG: No transform context — ignores datum transformation preferences
xform = QgsCoordinateTransform(crs_src, crs_dest)
# CORRECT: ALWAYS provide the project transform context
xform = QgsCoordinateTransform(
crs_src, crs_dest,
QgsProject.instance().transformContext()
)WHY: Without a transform context, QGIS cannot apply user-configured datum transformation grids. The result may be off by meters or more, with no error or warning.
---
2. Confusing CRS Assignment with Reprojection
# WRONG: This does NOT reproject — it reinterprets existing coordinates
layer.setCrs(QgsCoordinateReferenceSystem("EPSG:32632"))
# If the layer was in EPSG:4326, all coordinates are now silently corrupted
# CORRECT: Use QgsCoordinateTransform or processing to reproject
import processing
result = processing.run("native:reprojectlayer", {
"INPUT": layer,
"TARGET_CRS": QgsCoordinateReferenceSystem("EPSG:32632"),
"OUTPUT": "memory:"
})
reprojected = result["OUTPUT"]WHY: setCrs() only changes the metadata label — it does not transform any coordinate values. A layer with longitude/latitude values labeled as UTM meters produces silently wrong results in every spatial operation.
---
3. Wrong Coordinate Order for EPSG:4326
# WRONG: Putting latitude in x and longitude in y
point = QgsPointXY(52.0, 5.0) # This is lat=52, lon=5 — REVERSED
# CORRECT: QgsPointXY ALWAYS takes (x=longitude, y=latitude)
point = QgsPointXY(5.0, 52.0) # lon=5, lat=52WHY: The EPSG standard defines EPSG:4326 with axis order latitude, longitude. However, QGIS (like most GIS software) uses x=longitude, y=latitude internally. QgsPointXY(x, y) ALWAYS means (longitude, latitude) for geographic CRS.
---
4. Skipping CRS Validity Check
# WRONG: Using a CRS without checking validity
crs = QgsCoordinateReferenceSystem("EPSG:99999")
xform = QgsCoordinateTransform(crs, other_crs, context)
# xform is invalid — transforms return garbage or original coordinates
# CORRECT: ALWAYS check isValid()
crs = QgsCoordinateReferenceSystem("EPSG:99999")
if not crs.isValid():
raise RuntimeError("Invalid CRS: EPSG:99999")WHY: An invalid CRS object does not raise an exception on creation. It silently propagates through transforms, measurements, and spatial operations, producing wrong results everywhere.
---
5. Using Deprecated Creation Methods
# WRONG: Deprecated methods from older QGIS versions
crs = QgsCoordinateReferenceSystem()
crs.createFromSrid(4326) # Deprecated
crs.createFromEpsg(4326) # Deprecated
# CORRECT: Use string-prefix constructor
crs = QgsCoordinateReferenceSystem("EPSG:4326")
# Or use static factory methods
crs = QgsCoordinateReferenceSystem.fromEpsgId(4326)WHY: Deprecated methods may be removed in future QGIS versions. The string-prefix constructor is the standard, forward-compatible approach.
---
6. Missing QgsApplication Init in Standalone Scripts
# WRONG: Using CRS without initializing QgsApplication
from qgis.core import QgsCoordinateReferenceSystem
crs = QgsCoordinateReferenceSystem("EPSG:4326")
# crs.isValid() returns False — srs.db not found
# CORRECT: Initialize QgsApplication first
from qgis.core import QgsApplication, QgsCoordinateReferenceSystem
qgs = QgsApplication([], False)
qgs.setPrefixPath("/usr", True) # Set correct path for your OS
qgs.initQgis()
crs = QgsCoordinateReferenceSystem("EPSG:4326")
assert crs.isValid() # Now worksWHY: QGIS needs the srs.db SQLite database (bundled with QGIS) to resolve EPSG codes to CRS definitions. Without setPrefixPath(), QGIS cannot locate this database. Within the QGIS desktop application or QGIS Python console, this is handled automatically.
---
7. Measuring with Geographic CRS Without Ellipsoid
# WRONG: Measuring distances on geographic CRS without setting ellipsoid
d = QgsDistanceArea()
d.setSourceCrs(QgsCoordinateReferenceSystem("EPSG:4326"), context)
# Missing: d.setEllipsoid("WGS84")
distance = d.measureLine(p1, p2)
# Returns distance in DEGREES, not meters
# CORRECT: ALWAYS set ellipsoid for geographic CRS
d = QgsDistanceArea()
d.setSourceCrs(QgsCoordinateReferenceSystem("EPSG:4326"), context)
d.setEllipsoid("WGS84")
distance = d.measureLine(p1, p2)
# Returns distance in meters (ellipsoidal calculation)WHY: Without an ellipsoid, QgsDistanceArea performs planimetric measurement in the CRS native units. For geographic CRS, native units are degrees — so the "distance" is in degrees, which is meaningless for real-world measurement.
---
8. Ignoring Datum Transformation Grid Availability
# WRONG: Assuming all datum transformations are available
xform = QgsCoordinateTransform(
QgsCoordinateReferenceSystem("EPSG:27700"), # OSGB 1936
QgsCoordinateReferenceSystem("EPSG:4326"), # WGS 84
context
)
# If OSTN15 grid is not installed, transform uses a less accurate fallback
# CORRECT: Check and warn about grid availability
xform = QgsCoordinateTransform(
QgsCoordinateReferenceSystem("EPSG:27700"),
QgsCoordinateReferenceSystem("EPSG:4326"),
context
)
# Log or warn if high-precision grids are required for your use case
# Install transformation grids via QGIS Settings > TransformationsWHY: Datum transformations between different datums (e.g., OSGB 1936 to WGS 84) can require grid files for centimeter-level accuracy. Without the grid, QGIS falls back to a less accurate Helmert transformation, which can be off by several meters. There is no runtime error — only silent precision loss.
---
9. Hardcoding CRS Without Context
# WRONG: Hardcoding a projected CRS for data that spans multiple zones
for layer in layers:
xform = QgsCoordinateTransform(
layer.crs(),
QgsCoordinateReferenceSystem("EPSG:32632"), # UTM 32N
context
)
# Fails silently for data outside UTM zone 32N — extreme distortion
# CORRECT: Choose the CRS based on the data extent
extent = layer.extent()
centroid_lon = (extent.xMinimum() + extent.xMaximum()) / 2
if layer.crs().isGeographic():
utm_zone = int((centroid_lon + 180) / 6) + 1
epsg = 32600 + utm_zone # Northern hemisphere
target_crs = QgsCoordinateReferenceSystem(f"EPSG:{epsg}")WHY: UTM zones cover 6 degrees of longitude. Using a UTM zone far from the data produces extreme distortion — distances and areas can be off by orders of magnitude.
---
10. Using setCrs() on Layers to "Fix" Coordinates
# WRONG: Trying to fix misaligned data by changing the CRS label
layer = QgsVectorLayer("data.shp", "my_layer", "ogr")
# Layer appears in wrong location on the map
layer.setCrs(QgsCoordinateReferenceSystem("EPSG:4326")) # "Fix" attempt
# This only changes the label — coordinates remain wrong
# CORRECT: Identify the actual CRS of the data and set it correctly
# If the data was created in RD New but has no .prj file:
layer.setCrs(QgsCoordinateReferenceSystem("EPSG:28992"))
# Now coordinates are interpreted correctly
# If you need the data in a different CRS, THEN reproject:
import processing
result = processing.run("native:reprojectlayer", {
"INPUT": layer,
"TARGET_CRS": QgsCoordinateReferenceSystem("EPSG:4326"),
"OUTPUT": "memory:"
})WHY: setCrs() is appropriate ONLY when you know the true CRS of the data and the file metadata is wrong or missing. It is NEVER appropriate as a way to reproject data. The distinction: setCrs() says "these coordinates ARE in this CRS"; reprojection says "convert these coordinates TO this CRS".
Working Code Examples (QGIS CRS)
Example 1: Create CRS from Various Sources
from qgis.core import QgsCoordinateReferenceSystem
# From EPSG code (most common)
crs_wgs84 = QgsCoordinateReferenceSystem("EPSG:4326")
assert crs_wgs84.isValid()
# From static factory
crs_utm = QgsCoordinateReferenceSystem.fromEpsgId(32632)
assert crs_utm.isValid()
# From WKT string
wkt = 'GEOGCS["WGS84", DATUM["WGS84", SPHEROID["WGS84", 6378137.0, 298.257223563]], PRIMEM["Greenwich", 0], UNIT["degree", 0.0174532925199433]]'
crs_from_wkt = QgsCoordinateReferenceSystem(f"WKT:{wkt}")
# From Proj string
crs_from_proj = QgsCoordinateReferenceSystem("PROJ:+proj=longlat +datum=WGS84 +no_defs")
# ALWAYS validate after creation
for crs in [crs_wgs84, crs_utm, crs_from_wkt, crs_from_proj]:
if not crs.isValid():
raise RuntimeError(f"Invalid CRS: {crs.authid()}")---
Example 2: Inspect CRS Properties
from qgis.core import QgsCoordinateReferenceSystem
crs = QgsCoordinateReferenceSystem("EPSG:32633")
print(f"Auth ID: {crs.authid()}") # "EPSG:32633"
print(f"Description: {crs.description()}") # "WGS 84 / UTM zone 33N"
print(f"Geographic: {crs.isGeographic()}") # False
print(f"Map units: {crs.mapUnits()}") # Qgis.DistanceUnit.Meters
print(f"Projection: {crs.projectionAcronym()}") # "utm"
print(f"Ellipsoid: {crs.ellipsoidAcronym()}") # "EPSG:7030"
print(f"PostGIS SRID: {crs.postgisSrid()}") # 32633---
Example 3: Transform a Point Between CRS
from qgis.core import (
QgsCoordinateReferenceSystem,
QgsCoordinateTransform,
QgsProject,
QgsPointXY
)
crs_wgs84 = QgsCoordinateReferenceSystem("EPSG:4326")
crs_utm33 = QgsCoordinateReferenceSystem("EPSG:32633")
context = QgsProject.instance().transformContext()
xform = QgsCoordinateTransform(crs_wgs84, crs_utm33, context)
# Forward: WGS84 -> UTM 33N
# QgsPointXY takes (x=longitude, y=latitude) for geographic CRS
pt_wgs = QgsPointXY(18.0, 5.0)
pt_utm = xform.transform(pt_wgs)
print(f"UTM: {pt_utm.x():.2f}, {pt_utm.y():.2f}")
# Reverse: UTM 33N -> WGS84
pt_back = xform.transform(pt_utm, QgsCoordinateTransform.ReverseTransform)
print(f"WGS84: {pt_back.x():.6f}, {pt_back.y():.6f}")---
Example 4: Transform a Geometry In-Place
from qgis.core import (
QgsCoordinateReferenceSystem,
QgsCoordinateTransform,
QgsProject,
QgsGeometry,
QgsPointXY
)
# Create a polygon in WGS84
polygon = QgsGeometry.fromPolygonXY([[
QgsPointXY(5.0, 52.0),
QgsPointXY(5.1, 52.0),
QgsPointXY(5.1, 52.1),
QgsPointXY(5.0, 52.1),
QgsPointXY(5.0, 52.0) # Close the ring
]])
# Transform to Dutch national CRS (RD New)
xform = QgsCoordinateTransform(
QgsCoordinateReferenceSystem("EPSG:4326"),
QgsCoordinateReferenceSystem("EPSG:28992"),
QgsProject.instance().transformContext()
)
# transform() modifies the geometry in-place
result = polygon.transform(xform)
# result == 0 means success (Qgis.GeometryOperationResult.Success)
print(f"Transform result: {result}")
print(f"Transformed extent: {polygon.boundingBox()}")---
Example 5: Transform a Bounding Box
from qgis.core import (
QgsCoordinateReferenceSystem,
QgsCoordinateTransform,
QgsProject,
QgsRectangle
)
# Bounding box for the Netherlands in WGS84
bbox = QgsRectangle(3.37, 50.75, 7.21, 53.47)
xform = QgsCoordinateTransform(
QgsCoordinateReferenceSystem("EPSG:4326"),
QgsCoordinateReferenceSystem("EPSG:28992"),
QgsProject.instance().transformContext()
)
bbox_rd = xform.transformBoundingBox(bbox)
print(f"RD New extent: {bbox_rd}")---
Example 6: Measure Distance Between Two Points
from qgis.core import (
QgsDistanceArea,
QgsCoordinateReferenceSystem,
QgsPointXY,
QgsProject,
Qgis
)
d = QgsDistanceArea()
d.setSourceCrs(
QgsCoordinateReferenceSystem("EPSG:4326"),
QgsProject.instance().transformContext()
)
d.setEllipsoid("WGS84")
# Amsterdam to Rotterdam (approximate)
amsterdam = QgsPointXY(4.9, 52.37)
rotterdam = QgsPointXY(4.48, 51.92)
distance_m = d.measureLine(amsterdam, rotterdam)
distance_km = d.convertLengthMeasurement(distance_m, Qgis.DistanceUnit.Kilometers)
print(f"Distance: {distance_m:.0f} m = {distance_km:.1f} km")---
Example 7: Measure Area of Features
from qgis.core import (
QgsDistanceArea,
QgsCoordinateReferenceSystem,
QgsProject,
Qgis
)
layer = QgsProject.instance().mapLayersByName("parcels")[0]
d = QgsDistanceArea()
d.setSourceCrs(layer.crs(), QgsProject.instance().transformContext())
d.setEllipsoid("WGS84")
for feature in layer.getFeatures():
area_sqm = d.measureArea(feature.geometry())
area_ha = d.convertAreaMeasurement(area_sqm, Qgis.AreaUnit.Hectares)
perimeter_m = d.measurePerimeter(feature.geometry())
print(f"Feature {feature.id()}: {area_ha:.2f} ha, perimeter {perimeter_m:.0f} m")---
Example 8: Measure Distance Along a Polyline
from qgis.core import (
QgsDistanceArea,
QgsCoordinateReferenceSystem,
QgsPointXY,
QgsProject,
Qgis
)
d = QgsDistanceArea()
d.setSourceCrs(
QgsCoordinateReferenceSystem("EPSG:4326"),
QgsProject.instance().transformContext()
)
d.setEllipsoid("WGS84")
# Route: Amsterdam -> Utrecht -> Eindhoven
route = [
QgsPointXY(4.9, 52.37), # Amsterdam
QgsPointXY(5.12, 52.09), # Utrecht
QgsPointXY(5.47, 51.44), # Eindhoven
]
total_m = d.measureLine(route)
total_km = d.convertLengthMeasurement(total_m, Qgis.DistanceUnit.Kilometers)
print(f"Route distance: {total_km:.1f} km")---
Example 9: Set and Read Project CRS
from qgis.core import QgsProject, QgsCoordinateReferenceSystem
project = QgsProject.instance()
# Read current project CRS
current_crs = project.crs()
print(f"Current project CRS: {current_crs.authid()}")
# Set project CRS (enables on-the-fly reprojection for all layers)
project.setCrs(QgsCoordinateReferenceSystem("EPSG:3857"))
print(f"New project CRS: {project.crs().authid()}")---
Example 10: Display Layer Names with CRS
from qgis.core import QgsProject
for layer in QgsProject.instance().mapLayers().values():
crs = layer.crs()
print(f"{layer.name()} -> {crs.authid()} ({crs.description()})")
if crs.isGeographic():
print(f" Warning: geographic CRS (degrees) — measurements need ellipsoidal calculation")---
Example 11: Reproject Layer Using Processing
import processing
from qgis.core import QgsCoordinateReferenceSystem, QgsProject
layer = QgsProject.instance().mapLayersByName("input_layer")[0]
result = processing.run("native:reprojectlayer", {
"INPUT": layer,
"TARGET_CRS": QgsCoordinateReferenceSystem("EPSG:32632"),
"OUTPUT": "memory:"
})
reprojected = result["OUTPUT"]
QgsProject.instance().addMapLayer(reprojected)
print(f"Reprojected layer CRS: {reprojected.crs().authid()}")---
Example 12: Standalone Script CRS Setup
import sys
from qgis.core import (
QgsApplication,
QgsCoordinateReferenceSystem,
QgsCoordinateTransform,
QgsCoordinateTransformContext,
QgsPointXY
)
# Initialize QGIS application (REQUIRED for standalone scripts)
qgs = QgsApplication([], False)
qgs.setPrefixPath("/usr", True) # Adjust path for your OS
qgs.initQgis()
# Now CRS operations work
crs = QgsCoordinateReferenceSystem("EPSG:4326")
if not crs.isValid():
raise RuntimeError("CRS creation failed — check QgsApplication prefix path")
# In standalone scripts without a project, use a bare context
context = QgsCoordinateTransformContext()
xform = QgsCoordinateTransform(
QgsCoordinateReferenceSystem("EPSG:4326"),
QgsCoordinateReferenceSystem("EPSG:3857"),
context
)
pt = xform.transform(QgsPointXY(5.0, 52.0))
print(f"Web Mercator: {pt.x():.2f}, {pt.y():.2f}")
# Cleanup
qgs.exitQgis()API Signatures Reference (QGIS CRS)
QgsCoordinateReferenceSystem
Represents a coordinate reference system (CRS). Immutable after creation.
Constructors
# From string identifier (preferred method)
QgsCoordinateReferenceSystem(definition: str) -> QgsCoordinateReferenceSystem
# Supported prefixes: "EPSG:", "POSTGIS:", "INTERNAL:", "PROJ:", "WKT:"
# Empty (invalid) CRS
QgsCoordinateReferenceSystem() -> QgsCoordinateReferenceSystem
# Static factory methods
QgsCoordinateReferenceSystem.fromEpsgId(epsg: int) -> QgsCoordinateReferenceSystem
QgsCoordinateReferenceSystem.fromWkt(wkt: str) -> QgsCoordinateReferenceSystem
QgsCoordinateReferenceSystem.fromProj(proj: str) -> QgsCoordinateReferenceSystem
QgsCoordinateReferenceSystem.fromOgcWmsCrs(ogc: str) -> QgsCoordinateReferenceSystemValidation
crs.isValid() -> bool
# Returns True if the CRS was successfully resolved. ALWAYS check after creation.Identity and Description
crs.authid() -> str
# Authority identifier, e.g., "EPSG:4326". Returns empty string if no authority match.
crs.description() -> str
# Human-readable name, e.g., "WGS 84"
crs.srsid() -> int
# QGIS internal SRS ID (different from EPSG code)
crs.postgisSrid() -> int
# PostGIS SRID value, e.g., 4326Properties
crs.isGeographic() -> bool
# True for geographic CRS (units are degrees), False for projected CRS (units are meters/feet)
crs.mapUnits() -> Qgis.DistanceUnit
# Returns the native unit: Qgis.DistanceUnit.Degrees, .Meters, .Feet, etc.
crs.projectionAcronym() -> str
# Projection type acronym, e.g., "longlat", "utm", "tmerc"
crs.ellipsoidAcronym() -> str
# Ellipsoid identifier, e.g., "EPSG:7030"Export
crs.toProj() -> str
# Full Proj string representation
crs.toWkt(variant: Qgis.CrsWktVariant = Qgis.CrsWktVariant.Wkt2_2019) -> str
# Full WKT string. Default is WKT2:2019 format.Comparison
crs1 == crs2 # Compares by authority ID
crs1 != crs2---
QgsCoordinateTransform
Transforms coordinates between two CRS. Requires a transform context.
Constructor
QgsCoordinateTransform(
source: QgsCoordinateReferenceSystem,
destination: QgsCoordinateReferenceSystem,
context: QgsCoordinateTransformContext
) -> QgsCoordinateTransform
QgsCoordinateTransform(
source: QgsCoordinateReferenceSystem,
destination: QgsCoordinateReferenceSystem,
project: QgsProject
) -> QgsCoordinateTransform
# NEVER use the bare constructor without context:
QgsCoordinateTransform() -> QgsCoordinateTransform # Creates invalid transformTransform Methods
xform.transform(point: QgsPointXY) -> QgsPointXY
# Transforms a single point. Default direction: ForwardTransform.
xform.transform(
point: QgsPointXY,
direction: QgsCoordinateTransform.TransformDirection
) -> QgsPointXY
# Direction: QgsCoordinateTransform.ForwardTransform or .ReverseTransform
xform.transform(x: float, y: float) -> QgsPointXY
# Transforms raw x, y coordinates.
xform.transformBoundingBox(
rect: QgsRectangle,
direction: QgsCoordinateTransform.TransformDirection = ForwardTransform,
handle180Crossover: bool = False
) -> QgsRectangle
# Transforms a bounding box. Set handle180Crossover=True for boxes crossing the antimeridian.Geometry Transformation
# QgsGeometry has its own transform method that takes a QgsCoordinateTransform
geometry.transform(xform: QgsCoordinateTransform) -> Qgis.GeometryOperationResult
# Transforms the geometry IN-PLACE. Returns 0 (Success) on success.Properties
xform.sourceCrs() -> QgsCoordinateReferenceSystem
xform.destinationCrs() -> QgsCoordinateReferenceSystem
xform.isValid() -> bool
xform.isShortCircuited() -> bool
# Returns True if source and destination CRS are identical (no transformation needed)Transform Direction Enum
QgsCoordinateTransform.ForwardTransform # source -> destination
QgsCoordinateTransform.ReverseTransform # destination -> source---
QgsCoordinateTransformContext
Holds project-level datum transformation settings. Determines which datum transformation pipeline to use for CRS pairs.
Getting the Context
# ALWAYS use project context (includes user preferences)
context = QgsProject.instance().transformContext()
# Bare constructor — ONLY for standalone scripts with no project
context = QgsCoordinateTransformContext()Methods
context.addCoordinateOperation(
source: QgsCoordinateReferenceSystem,
destination: QgsCoordinateReferenceSystem,
operation: str,
allowFallback: bool = True
) -> bool
# Register a specific coordinate operation (Proj pipeline) for a CRS pair.
context.removeCoordinateOperation(
source: QgsCoordinateReferenceSystem,
destination: QgsCoordinateReferenceSystem
) -> None
# Remove a registered operation for a CRS pair.
context.hasTransform(
source: QgsCoordinateReferenceSystem,
destination: QgsCoordinateReferenceSystem
) -> bool
# Check if a specific operation is registered for this CRS pair.
context.calculateCoordinateOperation(
source: QgsCoordinateReferenceSystem,
destination: QgsCoordinateReferenceSystem
) -> str
# Get the Proj pipeline string for a CRS pair.---
QgsDistanceArea
Calculates distances and areas, supporting both ellipsoidal and planimetric measurement.
Setup
d = QgsDistanceArea()
d.setSourceCrs(
crs: QgsCoordinateReferenceSystem,
context: QgsCoordinateTransformContext
) -> None
# Set the CRS of the geometries being measured.
d.setEllipsoid(ellipsoid: str) -> bool
# Set the ellipsoid for calculations. Common values: "WGS84", "GRS80", "Sketchy".
# Returns True if ellipsoid was set successfully.
# ALWAYS set this for accurate measurements on geographic CRS.Distance Measurement
d.measureLine(p1: QgsPointXY, p2: QgsPointXY) -> float
# Distance between two points in meters (ellipsoidal) or CRS units (planimetric).
d.measureLine(points: list[QgsPointXY]) -> float
# Total distance along a polyline.
d.measureLineProjected(p: QgsPointXY, distance: float, azimuth: float) -> float
# Measure distance from a point along an azimuth. Returns the geodesic distance.Area Measurement
d.measureArea(geometry: QgsGeometry) -> float
# Area of a polygon geometry in square meters (ellipsoidal) or CRS square units (planimetric).
d.measurePerimeter(geometry: QgsGeometry) -> float
# Perimeter of a polygon geometry.Unit Conversion
d.convertLengthMeasurement(length: float, toUnit: Qgis.DistanceUnit) -> float
# Convert a length measurement to the target unit.
# Units: Qgis.DistanceUnit.Meters, .Kilometers, .Feet, .NauticalMiles, .Yards, .Miles
d.convertAreaMeasurement(area: float, toUnit: Qgis.AreaUnit) -> float
# Convert an area measurement to the target unit.
# Units: Qgis.AreaUnit.SquareMeters, .SquareKilometers, .Hectares, .Acres, .SquareFeet
d.lengthUnits() -> Qgis.DistanceUnit
# Returns the unit of length measurements based on current CRS and ellipsoid settings.
d.areaUnits() -> Qgis.AreaUnit
# Returns the unit of area measurements based on current CRS and ellipsoid settings.Properties
d.sourceCrs() -> QgsCoordinateReferenceSystem
d.ellipsoid() -> str
d.ellipsoidSemiMajor() -> float
d.ellipsoidSemiMinor() -> float
d.ellipsoidInverseFlattening() -> float---
Qgis.DistanceUnit Enum
Qgis.DistanceUnit.Meters
Qgis.DistanceUnit.Kilometers
Qgis.DistanceUnit.Feet
Qgis.DistanceUnit.NauticalMiles
Qgis.DistanceUnit.Yards
Qgis.DistanceUnit.Miles
Qgis.DistanceUnit.Degrees
Qgis.DistanceUnit.Centimeters
Qgis.DistanceUnit.Millimeters
Qgis.DistanceUnit.Inches
Qgis.DistanceUnit.UnknownQgis.AreaUnit Enum
Qgis.AreaUnit.SquareMeters
Qgis.AreaUnit.SquareKilometers
Qgis.AreaUnit.SquareFeet
Qgis.AreaUnit.SquareYards
Qgis.AreaUnit.SquareMiles
Qgis.AreaUnit.Hectares
Qgis.AreaUnit.Acres
Qgis.AreaUnit.SquareNauticalMiles
Qgis.AreaUnit.SquareDegrees
Qgis.AreaUnit.SquareCentimeters
Qgis.AreaUnit.SquareMillimeters
Qgis.AreaUnit.SquareInches
Qgis.AreaUnit.Unknown