[{"data":1,"prerenderedAt":2045},["ShallowReactive",2],{"doc:\u002Fspatial-data-processing-automation\u002Fcoordinate-reference-systems":3},{"id":4,"title":5,"body":6,"description":2033,"extension":2034,"meta":2035,"navigation":302,"path":2041,"seo":2042,"stem":2043,"__hash__":2044},"docs\u002Fspatial-data-processing-automation\u002Fcoordinate-reference-systems\u002Findex.md","Coordinate Reference Systems in PyQGIS",{"type":7,"value":8,"toc":2014},"minimark",[9,14,42,185,190,193,233,236,240,267,270,402,545,550,557,775,783,787,790,1010,1024,1028,1047,1199,1212,1216,1228,1390,1418,1422,1425,1429,1432,1447,1451,1454,1458,1483,1555,1559,1562,1569,1573,1576,1589,1593,1596,1723,1726,1733,1736,1740,1795,1799,1813,1829,1844,1854,1872,1894,1900,1906,1920,1926,1932,1948,1954,1968,1972,2010],[10,11,13],"h1",{"id":12},"coordinate-reference-systems-in-qgis-and-pyqgis","Coordinate Reference Systems in QGIS and PyQGIS",[15,16,17,18,23,24,28,29,32,33,28,37,41],"p",{},"Spatial data relies on precise mathematical frameworks to translate the Earth's three-dimensional surface into two-dimensional representations. Coordinate Reference Systems (CRS) define these frameworks, establishing how coordinates map to real-world locations through datums, projections, and measurement units. When working within the QGIS ecosystem, managing these systems programmatically via PyQGIS ensures reproducible, accurate results across diverse datasets and automated pipelines. This guide sits inside the broader ",[19,20,22],"a",{"href":21},"\u002Fspatial-data-processing-automation\u002F","Spatial Data Processing & Automation"," workflow: it covers how QGIS models a projection, how to inspect and validate the CRS on a layer, the crucial difference between ",[25,26,27],"em",{},"assigning"," and ",[25,30,31],{},"transforming"," coordinates, and how to fold reprojection into repeatable scripts. Two focused recipes extend it — ",[19,34,36],{"href":35},"\u002Fspatial-data-processing-automation\u002Fcoordinate-reference-systems\u002Fhandling-missing-crs-in-pyqgis\u002F","handling missing CRS",[19,38,40],{"href":39},"\u002Fspatial-data-processing-automation\u002Fcoordinate-reference-systems\u002Fbatch-reprojecting-raster-datasets\u002F","batch reprojecting raster datasets"," — and are linked at the relevant points below.",[15,43,44],{},[45,46,51,55,59,66,76,85,91,95,99,106,112,116,120,123,127,131,133,136,139,155,162,165,171,176,180],"svg",{"viewBox":47,"role":48,"ariaLabel":49,"xmlns":50},"0 0 760 320","img","How PyQGIS transforms coordinates from a source CRS to a target CRS using QgsCoordinateTransform","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg",[52,53,54],"title",{},"PyQGIS coordinate transformation flow",[56,57,58],"desc",{},"A source CRS and a target CRS, each a QgsCoordinateReferenceSystem, feed a QgsCoordinateTransform that uses a project transform context and PROJ to convert geographic coordinates to projected coordinates.",[60,61],"rect",{"x":62,"y":62,"width":63,"height":64,"fill":65},"0","760","320","#f6f3ea",[60,67],{"x":68,"y":69,"width":70,"height":71,"rx":72,"fill":73,"stroke":74,"style":75},"24","40","200","110","10","#fffdf7","#2563eb","stroke-width:2.5",[77,78,84],"text",{"x":79,"y":80,"style":81,"fill":82,"textAnchor":83},"124","70","text-anchor:middle;font-size:15px;font-weight:bold;font-family:sans-serif","#17211d","middle","Source CRS",[77,86,90],{"x":79,"y":87,"style":88,"fill":89,"textAnchor":83},"94","text-anchor:middle;font-size:12px;font-family:sans-serif","#2f3b35","QgsCoordinateReferenceSystem",[77,92,94],{"x":79,"y":93,"style":88,"fill":74,"textAnchor":83},"116","geographic · EPSG:4326",[77,96,98],{"x":79,"y":97,"style":88,"fill":89,"textAnchor":83},"134","lat\u002Flon, degrees",[60,100],{"x":101,"y":102,"width":70,"height":103,"rx":72,"fill":104,"stroke":105,"style":75},"280","30","130","#26322d","#0f766e",[77,107,111],{"x":108,"y":109,"style":81,"fill":110,"textAnchor":83},"380","62","#d9f99d","QgsCoordinateTransform",[77,113,115],{"x":108,"y":114,"style":88,"fill":110,"textAnchor":83},"88","transform context",[77,117,119],{"x":108,"y":118,"style":88,"fill":110,"textAnchor":83},"108","+ PROJ datum grids",[77,121,122],{"x":108,"y":97,"style":88,"fill":110,"textAnchor":83},".transform(point)",[60,124],{"x":125,"y":69,"width":70,"height":71,"rx":72,"fill":73,"stroke":126,"style":75},"536","#15803d",[77,128,130],{"x":129,"y":80,"style":81,"fill":82,"textAnchor":83},"636","Target CRS",[77,132,90],{"x":129,"y":87,"style":88,"fill":89,"textAnchor":83},[77,134,135],{"x":129,"y":93,"style":88,"fill":126,"textAnchor":83},"projected · EPSG:32633",[77,137,138],{"x":129,"y":97,"style":88,"fill":89,"textAnchor":83},"x\u002Fy, metres",[140,141,142],"defs",{},[143,144,151],"marker",{"id":145,"viewBox":146,"refX":147,"refY":148,"markerWidth":149,"markerHeight":149,"orient":150},"crsarrow","0 0 10 10","8","5","7","auto-start-reverse",[152,153],"path",{"d":154,"fill":89},"M0 0 L10 5 L0 10 z",[156,157],"line",{"x1":158,"y1":159,"x2":160,"y2":159,"stroke":89,"style":161},"224","95","278","stroke-width:2.5;marker-end:url(#crsarrow)",[156,163],{"x1":125,"y1":159,"x2":164,"y2":159,"stroke":89,"style":161},"482",[60,166],{"x":167,"y":168,"width":101,"height":169,"rx":72,"fill":73,"stroke":170,"style":75},"240","220","64","#b45309",[77,172,175],{"x":108,"y":173,"style":174,"fill":82,"textAnchor":83},"248","text-anchor:middle;font-size:13px;font-weight:bold;font-family:sans-serif","QgsProject.instance()",[77,177,179],{"x":108,"y":178,"style":88,"fill":89,"textAnchor":83},"270","supplies the transform context",[156,181],{"x1":108,"y1":182,"x2":108,"y2":183,"stroke":170,"style":184},"160","218","stroke-width:2.5;stroke-dasharray:5 4;marker-end:url(#crsarrow)",[186,187,189],"h2",{"id":188},"prerequisites","Prerequisites",[15,191,192],{},"Before implementing CRS operations in PyQGIS, verify your environment meets the following baseline requirements:",[194,195,196,204,215,221,227],"ul",{},[197,198,199,203],"li",{},[200,201,202],"strong",{},"QGIS 3.34 LTR"," (or newer) installed with a functional Python 3.x environment. The examples here target the 3.34 long-term release; where an API name changed in an earlier version it is called out inline.",[197,205,206,209,210,214],{},[200,207,208],{},"PyQGIS API access"," via the QGIS Python Console, standalone scripts, or custom plugins. If you have not set this up yet, start with ",[19,211,213],{"href":212},"\u002Fpyqgis-fundamentals-environment-setup\u002F","PyQGIS Fundamentals & Environment Setup",".",[197,216,217,220],{},[200,218,219],{},"Basic understanding"," of geographic (lat\u002Flon) versus projected (metres\u002Ffeet) coordinate systems.",[197,222,223,226],{},[200,224,225],{},"Sample datasets"," containing known EPSG codes for testing validation and transformation routines.",[197,228,229,232],{},[200,230,231],{},"PROJ library"," properly configured within your QGIS installation (required for datum transformations and grid shifts).",[15,234,235],{},"Familiarity with QGIS layer management and basic Python syntax will streamline implementation.",[186,237,239],{"id":238},"how-qgis-models-a-crs-geographic-vs-projected","How QGIS models a CRS: geographic vs projected",[15,241,242,243,246,247,250,251,254,255,258,259,262,263,266],{},"Every CRS in QGIS is an instance of ",[244,245,90],"code",{},", but they fall into two families that behave very differently in analysis. A ",[200,248,249],{},"geographic CRS"," — the archetype being WGS84, ",[244,252,253],{},"EPSG:4326"," — stores positions as latitude and longitude in ",[25,256,257],{},"degrees"," on an ellipsoid. A ",[200,260,261],{},"projected CRS"," — such as a UTM zone or a national grid — flattens that ellipsoid onto a plane and stores ",[25,264,265],{},"eastings and northings in metres",". The distinction matters because degrees are not a length: a buffer, distance, or area computed in a geographic CRS is mathematically meaningless.",[15,268,269],{},"PyQGIS exposes this classification directly, so scripts can branch on it instead of guessing from the EPSG number:",[271,272,277],"pre",{"className":273,"code":274,"language":275,"meta":276,"style":276},"language-python shiki shiki-themes github-dark","from qgis.core import QgsCoordinateReferenceSystem\n\n# Geographic CRS: coordinates in degrees on an ellipsoid\nwgs84 = QgsCoordinateReferenceSystem(\"EPSG:4326\")\nprint(wgs84.isGeographic())   # True\nprint(wgs84.mapUnits())       # Qgis.DistanceUnit.Degrees\n\n# Projected CRS: coordinates in metres on a plane\nutm33 = QgsCoordinateReferenceSystem(\"EPSG:32633\")\nprint(utm33.isGeographic())   # False\nprint(utm33.mapUnits())       # Qgis.DistanceUnit.Meters\n","python","",[244,278,279,297,304,311,330,343,354,359,365,380,391],{"__ignoreMap":276},[280,281,283,287,291,294],"span",{"class":156,"line":282},1,[280,284,286],{"class":285},"snl16","from",[280,288,290],{"class":289},"s95oV"," qgis.core ",[280,292,293],{"class":285},"import",[280,295,296],{"class":289}," QgsCoordinateReferenceSystem\n",[280,298,300],{"class":156,"line":299},2,[280,301,303],{"emptyLinePlaceholder":302},true,"\n",[280,305,307],{"class":156,"line":306},3,[280,308,310],{"class":309},"sjoCn","# Geographic CRS: coordinates in degrees on an ellipsoid\n",[280,312,314,317,320,323,327],{"class":156,"line":313},4,[280,315,316],{"class":289},"wgs84 ",[280,318,319],{"class":285},"=",[280,321,322],{"class":289}," QgsCoordinateReferenceSystem(",[280,324,326],{"class":325},"sU2Wk","\"EPSG:4326\"",[280,328,329],{"class":289},")\n",[280,331,333,337,340],{"class":156,"line":332},5,[280,334,336],{"class":335},"sDLfK","print",[280,338,339],{"class":289},"(wgs84.isGeographic())   ",[280,341,342],{"class":309},"# True\n",[280,344,346,348,351],{"class":156,"line":345},6,[280,347,336],{"class":335},[280,349,350],{"class":289},"(wgs84.mapUnits())       ",[280,352,353],{"class":309},"# Qgis.DistanceUnit.Degrees\n",[280,355,357],{"class":156,"line":356},7,[280,358,303],{"emptyLinePlaceholder":302},[280,360,362],{"class":156,"line":361},8,[280,363,364],{"class":309},"# Projected CRS: coordinates in metres on a plane\n",[280,366,368,371,373,375,378],{"class":156,"line":367},9,[280,369,370],{"class":289},"utm33 ",[280,372,319],{"class":285},[280,374,322],{"class":289},[280,376,377],{"class":325},"\"EPSG:32633\"",[280,379,329],{"class":289},[280,381,383,385,388],{"class":156,"line":382},10,[280,384,336],{"class":335},[280,386,387],{"class":289},"(utm33.isGeographic())   ",[280,389,390],{"class":309},"# False\n",[280,392,394,396,399],{"class":156,"line":393},11,[280,395,336],{"class":335},[280,397,398],{"class":289},"(utm33.mapUnits())       ",[280,400,401],{"class":309},"# Qgis.DistanceUnit.Meters\n",[15,403,404],{},[45,405,408,411,414,417,422,428,432,468,471,475,478,483,513,519,526,530,534,537,539,542],{"viewBox":406,"role":48,"ariaLabel":407,"xmlns":50},"0 0 760 372","Side-by-side comparison of a geographic CRS with a curved lat\u002Flon graticule in degrees versus a projected CRS with a flat metric grid in metres, showing that distance, area and buffer are valid only in the projected system",[52,409,410],{},"Geographic CRS versus projected CRS",[56,412,413],{},"On the left, a geographic CRS (EPSG:4326) draws latitude and longitude as a curved graticule on an ellipsoid measured in degrees, where distance, area and buffer are meaningless. On the right, a projected CRS (EPSG:32633) draws eastings and northings as a flat, evenly spaced grid measured in metres, where those measurements are geometrically valid.",[60,415],{"x":62,"y":62,"width":63,"height":416,"fill":65},"372",[60,418],{"x":68,"y":419,"width":420,"height":421,"rx":72,"fill":73,"stroke":74,"style":75},"52","336","214",[77,423,427],{"x":424,"y":425,"style":426,"fill":82,"textAnchor":83},"192","82","text-anchor:middle;font-size:16px;font-weight:bold;font-family:sans-serif","Geographic CRS",[77,429,431],{"x":424,"y":430,"style":88,"fill":74,"textAnchor":83},"104","lat \u002F lon in degrees · EPSG:4326",[433,434,437,443,447,450,453,456,459,462,465],"g",{"fill":435,"stroke":74,"style":436},"none","stroke-width:1.5",[438,439],"ellipse",{"cx":424,"cy":440,"rx":441,"ry":442},"184","100","60",[156,444],{"x1":445,"y1":440,"x2":446,"y2":440},"92","292",[152,448],{"d":449},"M105 154 Q192 145 279 154",[152,451],{"d":452},"M105 214 Q192 223 279 214",[156,454],{"x1":424,"y1":79,"x2":424,"y2":455},"244",[152,457],{"d":458},"M192 124 Q112 184 192 244",[152,460],{"d":461},"M192 124 Q152 184 192 244",[152,463],{"d":464},"M192 124 Q272 184 192 244",[152,466],{"d":467},"M192 124 Q232 184 192 244",[60,469],{"x":470,"y":419,"width":420,"height":421,"rx":72,"fill":73,"stroke":126,"style":75},"400",[77,472,474],{"x":473,"y":425,"style":426,"fill":82,"textAnchor":83},"568","Projected CRS",[77,476,477],{"x":473,"y":430,"style":88,"fill":126,"textAnchor":83},"easting \u002F northing in metres · EPSG:32633",[60,479],{"x":480,"y":481,"width":69,"height":102,"fill":126,"fillOpacity":482},"468","152",0.14,[433,484,485,487,490,493,496,499,502,504,507,509,511],{"fill":435,"stroke":126,"style":436},[156,486],{"x1":480,"y1":79,"x2":480,"y2":455},[156,488],{"x1":489,"y1":79,"x2":489,"y2":455},"508",[156,491],{"x1":492,"y1":79,"x2":492,"y2":455},"548",[156,494],{"x1":495,"y1":79,"x2":495,"y2":455},"588",[156,497],{"x1":498,"y1":79,"x2":498,"y2":455},"628",[156,500],{"x1":501,"y1":79,"x2":501,"y2":455},"668",[156,503],{"x1":480,"y1":79,"x2":501,"y2":79},[156,505],{"x1":480,"y1":506,"x2":501,"y2":506},"154",[156,508],{"x1":480,"y1":440,"x2":501,"y2":440},[156,510],{"x1":480,"y1":421,"x2":501,"y2":421},[156,512],{"x1":480,"y1":455,"x2":501,"y2":455},[77,514,518],{"x":515,"y":173,"style":516,"fill":126,"textAnchor":517},"678","text-anchor:start;font-size:11px;font-family:sans-serif","start","x",[77,520,525],{"x":521,"y":522,"style":523,"fill":126,"textAnchor":524},"461","122","text-anchor:end;font-size:11px;font-family:sans-serif","end","y",[60,527],{"x":68,"y":528,"width":420,"height":109,"rx":72,"fill":73,"stroke":170,"style":529},"290","stroke-width:2",[77,531,533],{"x":424,"y":532,"style":174,"fill":170,"textAnchor":83},"317","✗ Distance, area & buffer",[77,535,536],{"x":424,"y":420,"style":88,"fill":89,"textAnchor":83},"meaningless — degrees are not a length",[60,538],{"x":470,"y":528,"width":420,"height":109,"rx":72,"fill":73,"stroke":126,"style":529},[77,540,541],{"x":473,"y":532,"style":174,"fill":126,"textAnchor":83},"✓ Distance, area & buffer",[77,543,544],{"x":473,"y":420,"style":88,"fill":89,"textAnchor":83},"valid — metres measured on a plane",[546,547,549],"h3",{"id":548},"choosing-a-projected-crs-for-a-study-area","Choosing a projected CRS for a study area",[15,551,552,553,556],{},"The most common practical decision is ",[25,554,555],{},"which"," projected system to reproject into. For most local and regional analysis a UTM zone is the safe default: it is metric, globally defined, and minimises distortion within its 6° band. Rather than hard-coding a zone, derive it from the data's own extent so the same script works anywhere on Earth:",[271,558,560],{"className":273,"code":559,"language":275,"meta":276,"style":276},"from qgis.core import (\n    QgsProject,\n    QgsCoordinateReferenceSystem,\n    QgsCoordinateTransform,\n)\n\n\ndef suggest_utm_crs(layer):\n    \"\"\"Return the UTM CRS whose zone contains the layer's centroid.\"\"\"\n    to_wgs84 = QgsCoordinateTransform(\n        layer.crs(),\n        QgsCoordinateReferenceSystem(\"EPSG:4326\"),\n        QgsProject.instance(),\n    )\n    centre = to_wgs84.transform(layer.extent().center())\n    zone = int((centre.x() + 180) \u002F 6) + 1\n    # 326xx = northern hemisphere, 327xx = southern\n    epsg = (32600 if centre.y() >= 0 else 32700) + zone\n    return QgsCoordinateReferenceSystem(f\"EPSG:{epsg}\")\n",[244,561,562,573,578,583,588,592,596,600,612,617,627,632,643,649,655,666,702,708,747],{"__ignoreMap":276},[280,563,564,566,568,570],{"class":156,"line":282},[280,565,286],{"class":285},[280,567,290],{"class":289},[280,569,293],{"class":285},[280,571,572],{"class":289}," (\n",[280,574,575],{"class":156,"line":299},[280,576,577],{"class":289},"    QgsProject,\n",[280,579,580],{"class":156,"line":306},[280,581,582],{"class":289},"    QgsCoordinateReferenceSystem,\n",[280,584,585],{"class":156,"line":313},[280,586,587],{"class":289},"    QgsCoordinateTransform,\n",[280,589,590],{"class":156,"line":332},[280,591,329],{"class":289},[280,593,594],{"class":156,"line":345},[280,595,303],{"emptyLinePlaceholder":302},[280,597,598],{"class":156,"line":356},[280,599,303],{"emptyLinePlaceholder":302},[280,601,602,605,609],{"class":156,"line":361},[280,603,604],{"class":285},"def",[280,606,608],{"class":607},"svObZ"," suggest_utm_crs",[280,610,611],{"class":289},"(layer):\n",[280,613,614],{"class":156,"line":367},[280,615,616],{"class":325},"    \"\"\"Return the UTM CRS whose zone contains the layer's centroid.\"\"\"\n",[280,618,619,622,624],{"class":156,"line":382},[280,620,621],{"class":289},"    to_wgs84 ",[280,623,319],{"class":285},[280,625,626],{"class":289}," QgsCoordinateTransform(\n",[280,628,629],{"class":156,"line":393},[280,630,631],{"class":289},"        layer.crs(),\n",[280,633,635,638,640],{"class":156,"line":634},12,[280,636,637],{"class":289},"        QgsCoordinateReferenceSystem(",[280,639,326],{"class":325},[280,641,642],{"class":289},"),\n",[280,644,646],{"class":156,"line":645},13,[280,647,648],{"class":289},"        QgsProject.instance(),\n",[280,650,652],{"class":156,"line":651},14,[280,653,654],{"class":289},"    )\n",[280,656,658,661,663],{"class":156,"line":657},15,[280,659,660],{"class":289},"    centre ",[280,662,319],{"class":285},[280,664,665],{"class":289}," to_wgs84.transform(layer.extent().center())\n",[280,667,669,672,674,677,680,683,686,689,692,695,697,699],{"class":156,"line":668},16,[280,670,671],{"class":289},"    zone ",[280,673,319],{"class":285},[280,675,676],{"class":335}," int",[280,678,679],{"class":289},"((centre.x() ",[280,681,682],{"class":285},"+",[280,684,685],{"class":335}," 180",[280,687,688],{"class":289},") ",[280,690,691],{"class":285},"\u002F",[280,693,694],{"class":335}," 6",[280,696,688],{"class":289},[280,698,682],{"class":285},[280,700,701],{"class":335}," 1\n",[280,703,705],{"class":156,"line":704},17,[280,706,707],{"class":309},"    # 326xx = northern hemisphere, 327xx = southern\n",[280,709,711,714,716,719,722,725,728,731,734,737,740,742,744],{"class":156,"line":710},18,[280,712,713],{"class":289},"    epsg ",[280,715,319],{"class":285},[280,717,718],{"class":289}," (",[280,720,721],{"class":335},"32600",[280,723,724],{"class":285}," if",[280,726,727],{"class":289}," centre.y() ",[280,729,730],{"class":285},">=",[280,732,733],{"class":335}," 0",[280,735,736],{"class":285}," else",[280,738,739],{"class":335}," 32700",[280,741,688],{"class":289},[280,743,682],{"class":285},[280,745,746],{"class":289}," zone\n",[280,748,750,753,755,758,761,764,767,770,773],{"class":156,"line":749},19,[280,751,752],{"class":285},"    return",[280,754,322],{"class":289},[280,756,757],{"class":285},"f",[280,759,760],{"class":325},"\"EPSG:",[280,762,763],{"class":335},"{",[280,765,766],{"class":289},"epsg",[280,768,769],{"class":335},"}",[280,771,772],{"class":325},"\"",[280,774,329],{"class":289},[15,776,777,778,782],{},"Deriving the target CRS this way keeps distance and area operations honest downstream — before running any buffer or spatial join in a ",[19,779,781],{"href":780},"\u002Fspatial-data-processing-automation\u002Fvector-data-manipulation\u002F","Vector Data Manipulation"," routine, reproject inputs into a metric CRS like the one this helper returns.",[186,784,786],{"id":785},"inspecting-and-validating-a-layers-crs","Inspecting and validating a layer's CRS",[15,788,789],{},"Before any spatial operation, verify the existing CRS definition attached to your dataset. QGIS layers store projection metadata independently of project settings, and mismatched definitions cause silent misalignment. Always validate before processing.",[271,791,793],{"className":273,"code":792,"language":275,"meta":276,"style":276},"from qgis.core import QgsProject, QgsCoordinateReferenceSystem\n\n# Safely retrieve the layer by name\nlayers = QgsProject.instance().mapLayersByName(\"sample_vector\")\nif not layers:\n    raise ValueError(\"Layer not found in current project.\")\nlayer = layers[0]\n\nlayer_crs = layer.crs()\n\n# Validate and extract key properties\nif layer_crs.isValid():\n    print(f\"Auth ID: {layer_crs.authid()}\")\n    print(f\"Description: {layer_crs.description()}\")\n    print(f\"Map units: {layer_crs.mapUnits()}\")\n    print(f\"Geographic: {layer_crs.isGeographic()}\")\nelse:\n    print(\"Warning: undefined or invalid CRS detected.\")\n",[244,794,795,806,810,815,830,841,857,872,876,886,890,895,902,925,947,969,991,999],{"__ignoreMap":276},[280,796,797,799,801,803],{"class":156,"line":282},[280,798,286],{"class":285},[280,800,290],{"class":289},[280,802,293],{"class":285},[280,804,805],{"class":289}," QgsProject, QgsCoordinateReferenceSystem\n",[280,807,808],{"class":156,"line":299},[280,809,303],{"emptyLinePlaceholder":302},[280,811,812],{"class":156,"line":306},[280,813,814],{"class":309},"# Safely retrieve the layer by name\n",[280,816,817,820,822,825,828],{"class":156,"line":313},[280,818,819],{"class":289},"layers ",[280,821,319],{"class":285},[280,823,824],{"class":289}," QgsProject.instance().mapLayersByName(",[280,826,827],{"class":325},"\"sample_vector\"",[280,829,329],{"class":289},[280,831,832,835,838],{"class":156,"line":332},[280,833,834],{"class":285},"if",[280,836,837],{"class":285}," not",[280,839,840],{"class":289}," layers:\n",[280,842,843,846,849,852,855],{"class":156,"line":345},[280,844,845],{"class":285},"    raise",[280,847,848],{"class":335}," ValueError",[280,850,851],{"class":289},"(",[280,853,854],{"class":325},"\"Layer not found in current project.\"",[280,856,329],{"class":289},[280,858,859,862,864,867,869],{"class":156,"line":356},[280,860,861],{"class":289},"layer ",[280,863,319],{"class":285},[280,865,866],{"class":289}," layers[",[280,868,62],{"class":335},[280,870,871],{"class":289},"]\n",[280,873,874],{"class":156,"line":361},[280,875,303],{"emptyLinePlaceholder":302},[280,877,878,881,883],{"class":156,"line":367},[280,879,880],{"class":289},"layer_crs ",[280,882,319],{"class":285},[280,884,885],{"class":289}," layer.crs()\n",[280,887,888],{"class":156,"line":382},[280,889,303],{"emptyLinePlaceholder":302},[280,891,892],{"class":156,"line":393},[280,893,894],{"class":309},"# Validate and extract key properties\n",[280,896,897,899],{"class":156,"line":634},[280,898,834],{"class":285},[280,900,901],{"class":289}," layer_crs.isValid():\n",[280,903,904,907,909,911,914,916,919,921,923],{"class":156,"line":645},[280,905,906],{"class":335},"    print",[280,908,851],{"class":289},[280,910,757],{"class":285},[280,912,913],{"class":325},"\"Auth ID: ",[280,915,763],{"class":335},[280,917,918],{"class":289},"layer_crs.authid()",[280,920,769],{"class":335},[280,922,772],{"class":325},[280,924,329],{"class":289},[280,926,927,929,931,933,936,938,941,943,945],{"class":156,"line":651},[280,928,906],{"class":335},[280,930,851],{"class":289},[280,932,757],{"class":285},[280,934,935],{"class":325},"\"Description: ",[280,937,763],{"class":335},[280,939,940],{"class":289},"layer_crs.description()",[280,942,769],{"class":335},[280,944,772],{"class":325},[280,946,329],{"class":289},[280,948,949,951,953,955,958,960,963,965,967],{"class":156,"line":657},[280,950,906],{"class":335},[280,952,851],{"class":289},[280,954,757],{"class":285},[280,956,957],{"class":325},"\"Map units: ",[280,959,763],{"class":335},[280,961,962],{"class":289},"layer_crs.mapUnits()",[280,964,769],{"class":335},[280,966,772],{"class":325},[280,968,329],{"class":289},[280,970,971,973,975,977,980,982,985,987,989],{"class":156,"line":668},[280,972,906],{"class":335},[280,974,851],{"class":289},[280,976,757],{"class":285},[280,978,979],{"class":325},"\"Geographic: ",[280,981,763],{"class":335},[280,983,984],{"class":289},"layer_crs.isGeographic()",[280,986,769],{"class":335},[280,988,772],{"class":325},[280,990,329],{"class":289},[280,992,993,996],{"class":156,"line":704},[280,994,995],{"class":285},"else",[280,997,998],{"class":289},":\n",[280,1000,1001,1003,1005,1008],{"class":156,"line":710},[280,1002,906],{"class":335},[280,1004,851],{"class":289},[280,1006,1007],{"class":325},"\"Warning: undefined or invalid CRS detected.\"",[280,1009,329],{"class":289},[15,1011,1012,1013,1016,1017,1020,1021,214],{},"A layer whose ",[244,1014,1015],{},"crs().isValid()"," returns ",[244,1018,1019],{},"False"," was loaded without projection metadata — a frequent situation with CAD exports and older shapefiles. That is a data-repair problem rather than a transformation one; the full diagnosis-and-fix routine lives in ",[19,1022,1023],{"href":35},"Handling missing CRS in PyQGIS",[186,1025,1027],{"id":1026},"assigning-versus-transforming-coordinates","Assigning versus transforming coordinates",[15,1029,1030,1031,1034,1035,1038,1039,1042,1043,1046],{},"This is the single most important distinction in CRS work, and confusing the two silently corrupts data. ",[200,1032,1033],{},"Assigning"," a CRS with ",[244,1036,1037],{},"layer.setCrs()"," only rewrites the metadata label; the stored coordinates are untouched. Use it ",[25,1040,1041],{},"only"," to correct a wrong or missing definition when you know the true CRS of the numbers already in the file. ",[200,1044,1045],{},"Transforming"," actually recomputes every coordinate from one system into another, and is what you want when you need the geometry to physically move.",[271,1048,1050],{"className":273,"code":1049,"language":275,"meta":276,"style":276},"from qgis.core import (\n    QgsCoordinateTransform,\n    QgsPointXY,\n    QgsCoordinateReferenceSystem,\n    QgsProject,\n)\n\n# Define the target CRS (UTM Zone 33N, EPSG:32633)\ntarget_crs = QgsCoordinateReferenceSystem(\"EPSG:32633\")\n\n# Build the transform with the project's transform context\ntransform = QgsCoordinateTransform(layer_crs, target_crs, QgsProject.instance())\n\nsource_point = QgsPointXY(12.4924, 41.8902)  # Rome, Italy (WGS84)\ntransformed_point = transform.transform(source_point)\nprint(f\"Transformed: {transformed_point.x():.2f}, {transformed_point.y():.2f}\")\n",[244,1051,1052,1062,1066,1071,1075,1079,1083,1087,1092,1105,1109,1114,1124,1128,1153,1163],{"__ignoreMap":276},[280,1053,1054,1056,1058,1060],{"class":156,"line":282},[280,1055,286],{"class":285},[280,1057,290],{"class":289},[280,1059,293],{"class":285},[280,1061,572],{"class":289},[280,1063,1064],{"class":156,"line":299},[280,1065,587],{"class":289},[280,1067,1068],{"class":156,"line":306},[280,1069,1070],{"class":289},"    QgsPointXY,\n",[280,1072,1073],{"class":156,"line":313},[280,1074,582],{"class":289},[280,1076,1077],{"class":156,"line":332},[280,1078,577],{"class":289},[280,1080,1081],{"class":156,"line":345},[280,1082,329],{"class":289},[280,1084,1085],{"class":156,"line":356},[280,1086,303],{"emptyLinePlaceholder":302},[280,1088,1089],{"class":156,"line":361},[280,1090,1091],{"class":309},"# Define the target CRS (UTM Zone 33N, EPSG:32633)\n",[280,1093,1094,1097,1099,1101,1103],{"class":156,"line":367},[280,1095,1096],{"class":289},"target_crs ",[280,1098,319],{"class":285},[280,1100,322],{"class":289},[280,1102,377],{"class":325},[280,1104,329],{"class":289},[280,1106,1107],{"class":156,"line":382},[280,1108,303],{"emptyLinePlaceholder":302},[280,1110,1111],{"class":156,"line":393},[280,1112,1113],{"class":309},"# Build the transform with the project's transform context\n",[280,1115,1116,1119,1121],{"class":156,"line":634},[280,1117,1118],{"class":289},"transform ",[280,1120,319],{"class":285},[280,1122,1123],{"class":289}," QgsCoordinateTransform(layer_crs, target_crs, QgsProject.instance())\n",[280,1125,1126],{"class":156,"line":645},[280,1127,303],{"emptyLinePlaceholder":302},[280,1129,1130,1133,1135,1138,1141,1144,1147,1150],{"class":156,"line":651},[280,1131,1132],{"class":289},"source_point ",[280,1134,319],{"class":285},[280,1136,1137],{"class":289}," QgsPointXY(",[280,1139,1140],{"class":335},"12.4924",[280,1142,1143],{"class":289},", ",[280,1145,1146],{"class":335},"41.8902",[280,1148,1149],{"class":289},")  ",[280,1151,1152],{"class":309},"# Rome, Italy (WGS84)\n",[280,1154,1155,1158,1160],{"class":156,"line":657},[280,1156,1157],{"class":289},"transformed_point ",[280,1159,319],{"class":285},[280,1161,1162],{"class":289}," transform.transform(source_point)\n",[280,1164,1165,1167,1169,1171,1174,1176,1179,1182,1184,1186,1188,1191,1193,1195,1197],{"class":156,"line":668},[280,1166,336],{"class":335},[280,1168,851],{"class":289},[280,1170,757],{"class":285},[280,1172,1173],{"class":325},"\"Transformed: ",[280,1175,763],{"class":335},[280,1177,1178],{"class":289},"transformed_point.x()",[280,1180,1181],{"class":285},":.2f",[280,1183,769],{"class":335},[280,1185,1143],{"class":325},[280,1187,763],{"class":335},[280,1189,1190],{"class":289},"transformed_point.y()",[280,1192,1181],{"class":285},[280,1194,769],{"class":335},[280,1196,772],{"class":325},[280,1198,329],{"class":289},[15,1200,1201,1202,1204,1205,1207,1208,1211],{},"The third argument — ",[244,1203,175],{}," — supplies the ",[25,1206,115],{},". This tells PROJ which datum-transformation path to take when several exist (for example between NAD27 and WGS84), and guarantees a scripted transform matches the settings a user would see in the GUI. Omitting it can silently introduce sub-metre shifts. On QGIS 3.x you can also pass a ",[244,1209,1210],{},"QgsCoordinateTransformContext"," directly instead of the project.",[186,1213,1215],{"id":1214},"reprojecting-whole-layers-in-processing-pipelines","Reprojecting whole layers in processing pipelines",[15,1217,1218,1219,1222,1223,1227],{},"Real-world projects rarely operate on isolated points. When transforming an entire dataset, do ",[200,1220,1221],{},"not"," iterate through features by hand — reach for a Processing algorithm, which rewrites geometry, preserves attributes, and updates CRS metadata in one optimised, memory-safe pass. Embedding a CRS guard directly into the routine prevents downstream errors: ",[19,1224,1226],{"href":1225},"\u002Fspatial-data-processing-automation\u002Fraster-analysis-workflows\u002F","Raster Analysis Workflows",", for instance, require strict alignment of grid origins, pixel sizes, and projections to avoid resampling artefacts.",[271,1229,1231],{"className":273,"code":1230,"language":275,"meta":276,"style":276},"from qgis import processing\nfrom qgis.core import QgsCoordinateReferenceSystem\n\n\ndef reproject_layer(layer, target_epsg):\n    \"\"\"Reproject a layer only if it does not already match the target EPSG.\"\"\"\n    target_crs = QgsCoordinateReferenceSystem(f\"EPSG:{target_epsg}\")\n    if layer.crs() == target_crs:\n        return layer  # already aligned, skip the work\n    params = {\n        \"INPUT\": layer,\n        \"TARGET_CRS\": target_crs,\n        \"OUTPUT\": \"TEMPORARY_OUTPUT\",\n    }\n    return processing.run(\"native:reprojectlayer\", params)[\"OUTPUT\"]\n",[244,1232,1233,1245,1255,1259,1263,1273,1278,1302,1316,1327,1337,1345,1353,1367,1372],{"__ignoreMap":276},[280,1234,1235,1237,1240,1242],{"class":156,"line":282},[280,1236,286],{"class":285},[280,1238,1239],{"class":289}," qgis ",[280,1241,293],{"class":285},[280,1243,1244],{"class":289}," processing\n",[280,1246,1247,1249,1251,1253],{"class":156,"line":299},[280,1248,286],{"class":285},[280,1250,290],{"class":289},[280,1252,293],{"class":285},[280,1254,296],{"class":289},[280,1256,1257],{"class":156,"line":306},[280,1258,303],{"emptyLinePlaceholder":302},[280,1260,1261],{"class":156,"line":313},[280,1262,303],{"emptyLinePlaceholder":302},[280,1264,1265,1267,1270],{"class":156,"line":332},[280,1266,604],{"class":285},[280,1268,1269],{"class":607}," reproject_layer",[280,1271,1272],{"class":289},"(layer, target_epsg):\n",[280,1274,1275],{"class":156,"line":345},[280,1276,1277],{"class":325},"    \"\"\"Reproject a layer only if it does not already match the target EPSG.\"\"\"\n",[280,1279,1280,1283,1285,1287,1289,1291,1293,1296,1298,1300],{"class":156,"line":356},[280,1281,1282],{"class":289},"    target_crs ",[280,1284,319],{"class":285},[280,1286,322],{"class":289},[280,1288,757],{"class":285},[280,1290,760],{"class":325},[280,1292,763],{"class":335},[280,1294,1295],{"class":289},"target_epsg",[280,1297,769],{"class":335},[280,1299,772],{"class":325},[280,1301,329],{"class":289},[280,1303,1304,1307,1310,1313],{"class":156,"line":361},[280,1305,1306],{"class":285},"    if",[280,1308,1309],{"class":289}," layer.crs() ",[280,1311,1312],{"class":285},"==",[280,1314,1315],{"class":289}," target_crs:\n",[280,1317,1318,1321,1324],{"class":156,"line":367},[280,1319,1320],{"class":285},"        return",[280,1322,1323],{"class":289}," layer  ",[280,1325,1326],{"class":309},"# already aligned, skip the work\n",[280,1328,1329,1332,1334],{"class":156,"line":382},[280,1330,1331],{"class":289},"    params ",[280,1333,319],{"class":285},[280,1335,1336],{"class":289}," {\n",[280,1338,1339,1342],{"class":156,"line":393},[280,1340,1341],{"class":325},"        \"INPUT\"",[280,1343,1344],{"class":289},": layer,\n",[280,1346,1347,1350],{"class":156,"line":634},[280,1348,1349],{"class":325},"        \"TARGET_CRS\"",[280,1351,1352],{"class":289},": target_crs,\n",[280,1354,1355,1358,1361,1364],{"class":156,"line":645},[280,1356,1357],{"class":325},"        \"OUTPUT\"",[280,1359,1360],{"class":289},": ",[280,1362,1363],{"class":325},"\"TEMPORARY_OUTPUT\"",[280,1365,1366],{"class":289},",\n",[280,1368,1369],{"class":156,"line":651},[280,1370,1371],{"class":289},"    }\n",[280,1373,1374,1376,1379,1382,1385,1388],{"class":156,"line":657},[280,1375,752],{"class":285},[280,1377,1378],{"class":289}," processing.run(",[280,1380,1381],{"class":325},"\"native:reprojectlayer\"",[280,1383,1384],{"class":289},", params)[",[280,1386,1387],{"class":325},"\"OUTPUT\"",[280,1389,871],{"class":289},[15,1391,1392,1393,1396,1397,1400,1401,1404,1405,1408,1409,28,1413,1417],{},"For vector data, ",[244,1394,1395],{},"native:reprojectlayer"," is the workhorse; for saving a reprojected copy to disk, ",[244,1398,1399],{},"QgsVectorFileWriter.writeAsVectorFormatV3()"," accepts a target CRS and transform context in one call. Raster reprojection follows the same principle with ",[244,1402,1403],{},"gdal:warpreproject"," — when a whole folder of rasters needs converting, the chunked, parallel pattern is covered in ",[19,1406,1407],{"href":39},"Batch reprojecting raster datasets",". Reprojection also slots naturally into larger ",[19,1410,1412],{"href":1411},"\u002Fspatial-data-processing-automation\u002Fbatch-processing-with-pyqgis\u002F","batch processing pipelines",[19,1414,1416],{"href":1415},"\u002Fspatial-data-processing-automation\u002Fchaining-processing-algorithms\u002F","chained Processing algorithms",", where CRS normalisation is usually the first step before analysis.",[186,1419,1421],{"id":1420},"common-crs-errors-and-fixes","Common CRS errors and fixes",[15,1423,1424],{},"Working with Coordinate Reference Systems introduces predictable failure points. Below are the most frequent issues encountered in PyQGIS environments and their tested resolutions.",[546,1426,1428],{"id":1427},"missing-or-undefined-crs-metadata","Missing or undefined CRS metadata",[15,1430,1431],{},"Datasets downloaded from legacy portals or exported from CAD software often lack embedded projection files. When loaded, QGIS assigns an unknown CRS, causing misalignment with other layers.",[15,1433,1434,1437,1438,1440,1441,1444,1445,214],{},[200,1435,1436],{},"Fix:"," Programmatically assign the correct CRS ",[25,1439,1041],{}," when you have verified the true coordinates, using ",[244,1442,1443],{},"layer.setCrs(QgsCoordinateReferenceSystem(\"EPSG:XXXX\"))",". For comprehensive diagnosis strategies, see ",[19,1446,1023],{"href":35},[546,1448,1450],{"id":1449},"transformation-failures-and-datum-shifts","Transformation failures and datum shifts",[15,1452,1453],{},"Converting between datums (e.g. NAD27 to WGS84, or ETRS89 to a local grid) sometimes fails if the required transformation grids are missing or QGIS cannot determine an optimal path.",[15,1455,1456],{},[200,1457,1436],{},[1459,1460,1461,1468],"ol",{},[197,1462,1463,1464,1467],{},"Verify that the ",[244,1465,1466],{},"proj-data"," packages are installed alongside your QGIS distribution.",[197,1469,1470,1471,1474,1475,1478,1479,1482],{},"Wrap ",[244,1472,1473],{},"transform.transform()"," in a ",[244,1476,1477],{},"try\u002Fexcept"," to catch ",[244,1480,1481],{},"QgsCsException"," and fall back gracefully or prompt for intervention:",[271,1484,1486],{"className":273,"code":1485,"language":275,"meta":276,"style":276},"from qgis.core import QgsCsException\n\ntry:\n    transformed = transform.transform(source_point)\nexcept QgsCsException as exc:\n    print(f\"Transformation failed: {exc}\")\n",[244,1487,1488,1499,1503,1510,1519,1533],{"__ignoreMap":276},[280,1489,1490,1492,1494,1496],{"class":156,"line":282},[280,1491,286],{"class":285},[280,1493,290],{"class":289},[280,1495,293],{"class":285},[280,1497,1498],{"class":289}," QgsCsException\n",[280,1500,1501],{"class":156,"line":299},[280,1502,303],{"emptyLinePlaceholder":302},[280,1504,1505,1508],{"class":156,"line":306},[280,1506,1507],{"class":285},"try",[280,1509,998],{"class":289},[280,1511,1512,1515,1517],{"class":156,"line":313},[280,1513,1514],{"class":289},"    transformed ",[280,1516,319],{"class":285},[280,1518,1162],{"class":289},[280,1520,1521,1524,1527,1530],{"class":156,"line":332},[280,1522,1523],{"class":285},"except",[280,1525,1526],{"class":289}," QgsCsException ",[280,1528,1529],{"class":285},"as",[280,1531,1532],{"class":289}," exc:\n",[280,1534,1535,1537,1539,1541,1544,1546,1549,1551,1553],{"class":156,"line":345},[280,1536,906],{"class":335},[280,1538,851],{"class":289},[280,1540,757],{"class":285},[280,1542,1543],{"class":325},"\"Transformation failed: ",[280,1545,763],{"class":335},[280,1547,1548],{"class":289},"exc",[280,1550,769],{"class":335},[280,1552,772],{"class":325},[280,1554,329],{"class":289},[546,1556,1558],{"id":1557},"batch-processing-bottlenecks","Batch processing bottlenecks",[15,1560,1561],{},"Manually reprojecting dozens of layers through the GUI is inefficient and error-prone, and naive scripted loops can exhaust memory.",[15,1563,1564,1566,1567,214],{},[200,1565,1436],{}," Use generator-based iteration and Processing algorithms, which run off the main thread and clean up temporary files automatically. For large raster collections, follow the chunked approach in ",[19,1568,1407],{"href":39},[546,1570,1572],{"id":1571},"on-the-fly-transformation-conflicts","On-the-fly transformation conflicts",[15,1574,1575],{},"QGIS renders layers on the fly in the project CRS by default, visually aligning them without changing the underlying data. Convenient for viewing, but relying on it during automated analysis produces incorrect distance, area, and overlay results.",[15,1577,1578,1580,1581,1584,1585,1588],{},[200,1579,1436],{}," In scripts, set the project CRS explicitly with ",[244,1582,1583],{},"QgsProject.instance().setCrs(target_crs)"," and physically reproject inputs before analysis. Confirm alignment with ",[244,1586,1587],{},"QgsGeometry.distance()"," or a spatial-index query.",[186,1590,1592],{"id":1591},"three-crss-are-in-play-at-once","Three CRSs are in play at once",[15,1594,1595],{},"Most CRS confusion comes from forgetting that a QGIS session holds several coordinate reference systems simultaneously, and that they are allowed to disagree.",[15,1597,1598],{},[45,1599,1602,1605,1608,1611,1618,1623,1627,1632,1636,1661,1664,1667,1670,1687,1690,1694,1697,1714,1719],{"viewBox":1600,"role":48,"ariaLabel":1601,"xmlns":50},"0 0 760 254","The three coordinate reference systems present in a session — each layer's own, the project's, and the one an export is rendered in — and which operations use which",[52,1603,1604],{},"Layer CRS, project CRS and destination CRS",[56,1606,1607],{},"Each layer stores coordinates in its own CRS, which is what geometry comparisons and measurements use. The project CRS is what the canvas draws in and what extents and click coordinates are expressed in. A render or export can specify a destination CRS independently of both. They frequently differ, and QGIS reprojects on the fly for display only.",[60,1609],{"x":62,"y":62,"width":63,"height":1610,"fill":65},"254",[140,1612,1613],{},[143,1614,1616],{"id":1615,"viewBox":146,"refX":147,"refY":148,"markerWidth":149,"markerHeight":149,"orient":150},"crsThreeArrow",[152,1617],{"d":154,"fill":105},[77,1619,1622],{"x":108,"y":1620,"style":1621,"fill":82,"textAnchor":83},"26","text-anchor:middle;font-size:14px;font-weight:bold;font-family:sans-serif","Three systems, and they are allowed to disagree",[60,1624],{"x":1625,"y":419,"width":1626,"height":440,"rx":72,"fill":73,"stroke":74,"style":75},"16","228",[77,1628,1631],{"x":103,"y":1629,"style":1630,"fill":74,"textAnchor":83},"76","text-anchor:middle;font-size:11.5px;font-weight:bold;font-family:sans-serif","each layer's CRS",[77,1633,1635],{"x":103,"y":441,"style":1634,"fill":89,"textAnchor":83},"text-anchor:middle;font-size:10.5px;font-family:monospace","layer.crs()",[433,1637,1639,1644,1648,1652,1656],{"style":1638},"font-size:10.5px;font-family:sans-serif",[77,1640,1643],{"x":103,"y":1641,"style":1642,"fill":89,"textAnchor":83},"132","text-anchor:middle","what the coordinates",[77,1645,1647],{"x":103,"y":1646,"style":1642,"fill":89,"textAnchor":83},"150","actually mean",[77,1649,1651],{"x":103,"y":1650,"style":1642,"fill":74,"textAnchor":83},"180","geometry comparisons",[77,1653,1655],{"x":103,"y":1654,"style":1642,"fill":74,"textAnchor":83},"198","and measurements",[77,1657,1660],{"x":103,"y":1658,"style":1642,"fill":1659,"textAnchor":83},"222","#59645f","different per layer",[60,1662],{"x":1663,"y":419,"width":1626,"height":440,"rx":72,"fill":73,"stroke":105,"style":75},"266",[77,1665,1666],{"x":108,"y":1629,"style":1630,"fill":105,"textAnchor":83},"the project CRS",[77,1668,1669],{"x":108,"y":441,"style":1634,"fill":89,"textAnchor":83},"QgsProject.crs()",[433,1671,1672,1675,1678,1681,1684],{"style":1638},[77,1673,1674],{"x":108,"y":1641,"style":1642,"fill":89,"textAnchor":83},"what the canvas",[77,1676,1677],{"x":108,"y":1646,"style":1642,"fill":89,"textAnchor":83},"draws in",[77,1679,1680],{"x":108,"y":1650,"style":1642,"fill":105,"textAnchor":83},"extents, click coordinates",[77,1682,1683],{"x":108,"y":1654,"style":1642,"fill":105,"textAnchor":83},"and the scale bar",[77,1685,1686],{"x":108,"y":1658,"style":1642,"fill":1659,"textAnchor":83},"one per project",[60,1688],{"x":1689,"y":419,"width":1626,"height":440,"rx":72,"fill":73,"stroke":170,"style":75},"516",[77,1691,1693],{"x":1692,"y":1629,"style":1630,"fill":170,"textAnchor":83},"630","the destination CRS",[77,1695,1696],{"x":1692,"y":441,"style":1634,"fill":89,"textAnchor":83},"settings.destinationCrs()",[433,1698,1699,1702,1705,1708,1711],{"style":1638},[77,1700,1701],{"x":1692,"y":1641,"style":1642,"fill":89,"textAnchor":83},"what a render or",[77,1703,1704],{"x":1692,"y":1646,"style":1642,"fill":89,"textAnchor":83},"export produces",[77,1706,1707],{"x":1692,"y":1650,"style":1642,"fill":170,"textAnchor":83},"independent of both",[77,1709,1710],{"x":1692,"y":1654,"style":1642,"fill":170,"textAnchor":83},"others",[77,1712,1713],{"x":1692,"y":1658,"style":1642,"fill":1659,"textAnchor":83},"per render job",[156,1715],{"x1":455,"y1":1716,"x2":1717,"y2":1716,"stroke":105,"style":1718},"144","262","stroke-width:2;stroke-dasharray:4 3;marker-end:url(#crsThreeArrow)",[156,1720],{"x1":1721,"y1":1716,"x2":1722,"y2":1716,"stroke":105,"style":1718},"494","512",[15,1724,1725],{},"On-the-fly reprojection is what makes the disagreement invisible. QGIS transforms each layer into the project CRS as it draws, so layers in five different systems align perfectly on screen while their stored coordinates remain completely different. That is a genuine convenience for viewing and an active hazard for analysis: a spatial predicate compares stored coordinates and knows nothing about the display transform, so two layers that overlap on screen can return no intersections at all.",[15,1727,1728,1729,1732],{},"The rule that follows is to be explicit about which of the three you are working in at every step. Reading a click position gives project-CRS coordinates. Reading ",[244,1730,1731],{},"layer.extent()"," gives layer-CRS coordinates. Comparing them without a transform is the single most common CRS bug, and it produces empty results rather than errors — which is exactly why it survives testing.",[15,1734,1735],{},"Standardising early is the cheapest defence. Reprojecting every input into one metric CRS at the start of a pipeline means every later step operates in a single coordinate space, distance and area are meaningful throughout, and there is nothing left to get wrong.",[186,1737,1739],{"id":1738},"key-takeaways","Key takeaways",[194,1741,1742,1751,1763,1774,1780,1789],{},[197,1743,1744,1747,1748,1750],{},[200,1745,1746],{},"A CRS is metadata plus a projection recipe."," ",[244,1749,90],{}," accepts EPSG codes, WKT, or PROJ strings; prefer stable EPSG codes over raw WKT, which drifts between QGIS and PROJ versions.",[197,1752,1753,1762],{},[200,1754,1755,1758,1759,1761],{},[244,1756,1757],{},"setCrs()"," labels, ",[244,1760,111],{}," moves."," Assigning fixes a wrong definition; transforming recomputes coordinates. Never use one where you meant the other.",[197,1764,1765,718,1768,1770,1771,1773],{},[200,1766,1767],{},"Always pass a transform context",[244,1769,175],{}," or a ",[244,1772,1210],{},") so datum paths are resolved consistently and reproducibly.",[197,1775,1776,1779],{},[200,1777,1778],{},"Never measure in degrees."," Reproject into a metric CRS — a UTM zone derived from the data's extent is a reliable default — before any buffer, distance, or area calculation.",[197,1781,1782,1785,1786,1788],{},[200,1783,1784],{},"Validate early, standardise once."," Check ",[244,1787,1015],{}," on ingest, define a project-wide target CRS, and reproject everything to it with Processing algorithms rather than hand-written feature loops.",[197,1790,1791,1794],{},[200,1792,1793],{},"Log the details."," Record source and target EPSG codes, transformation methods, and software versions so results stay auditable and reproducible.",[186,1796,1798],{"id":1797},"frequently-asked-questions","Frequently Asked Questions",[15,1800,1801,1804,1805,1808,1809,1812],{},[200,1802,1803],{},"What is the difference between a geographic and a projected CRS in PyQGIS?","\nA geographic CRS such as EPSG:4326 stores coordinates as latitude and longitude in degrees on an ellipsoid, while a projected CRS such as a UTM zone stores them as easting and northing in metres on a flat plane. You can check which one a layer uses with ",[244,1806,1807],{},"crs.isGeographic()"," or by inspecting ",[244,1810,1811],{},"crs.mapUnits()",". Distance, area, and buffer operations are only meaningful in a projected CRS.",[15,1814,1815,1825,1826,1828],{},[200,1816,1817,1818,1820,1821,1824],{},"Why does ",[244,1819,111],{}," require a ",[244,1822,1823],{},"QgsProject"," or transform context?","\nThe context tells PROJ which datum transformation path to use when more than one is available, for example when converting between NAD27 and WGS84. Passing ",[244,1827,175],{}," makes scripted transforms match the transformation settings configured in the GUI, ensuring consistent results. Without it, QGIS may pick a different path and introduce sub-metre shifts.",[15,1830,1831,1834,1835,1838,1839,1841,1842,214],{},[200,1832,1833],{},"How do I check whether a layer has a valid CRS before processing?","\nCall ",[244,1836,1837],{},"layer.crs().isValid()","; it returns ",[244,1840,1019],{}," for layers loaded without projection metadata. Validating early prevents silent misalignment in spatial joins and overlays. For a full diagnosis-and-repair routine, see the guide on ",[19,1843,36],{"href":35},[15,1845,1846,1849,1850,1853],{},[200,1847,1848],{},"Should I use EPSG codes or raw WKT strings to define a CRS?","\nPrefer EPSG codes like ",[244,1851,1852],{},"QgsCoordinateReferenceSystem(\"EPSG:32633\")"," because they are stable and concise. Raw WKT strings vary across QGIS and PROJ versions and are easy to corrupt when copied. Reserve WKT for custom or non-registered projections that have no EPSG identifier.",[15,1855,1856,1862,1863,1865,1866,1868,1869,214],{},[200,1857,1858,1859,1861],{},"Does ",[244,1860,1757],{}," reproject my data?","\nNo. ",[244,1864,1037],{}," only overwrites the metadata label and leaves the underlying coordinates untouched, so it is for correcting a wrong or missing definition, not for converting coordinates. To physically convert geometry, use ",[244,1867,111],{}," or ",[244,1870,1871],{},"processing.run(\"native:reprojectlayer\", ...)",[15,1873,1874,1877,1878,1881,1882,1885,1886,1889,1890,1893],{},[200,1875,1876],{},"How do I pick the right projected CRS automatically?","\nDerive it from the data's location rather than hard-coding a zone. Reproject the layer extent's centre to WGS84, compute the UTM zone from its longitude (",[244,1879,1880],{},"int((lon + 180) \u002F 6) + 1","), and build ",[244,1883,1884],{},"EPSG:326xx"," in the northern hemisphere or ",[244,1887,1888],{},"EPSG:327xx"," in the southern — the ",[244,1891,1892],{},"suggest_utm_crs()"," helper above does exactly this.",[15,1895,1896,1899],{},[200,1897,1898],{},"Which CRS should I standardise a project on?","\nA projected system whose units are metres and whose zone suits the data's extent — a UTM zone derived from the layer centroid is a reliable default. Standardising early means every later distance, area and overlay operation is meaningful without further thought.",[15,1901,1902,1905],{},[200,1903,1904],{},"Does on-the-fly reprojection change my data?","\nNo. It transforms coordinates for display only; the stored values are untouched. That is why layers can look perfectly aligned on screen while a spatial predicate between them returns nothing at all.",[15,1907,1908,1911,1016,1913,1916,1917,1919],{},[200,1909,1910],{},"How do I tell whether a CRS is geographic or projected?",[244,1912,1807],{},[244,1914,1915],{},"True"," for latitude-and-longitude systems and ",[244,1918,1019],{}," for metric ones. It is a far more reliable test than inspecting the EPSG number, and it is the guard worth putting in front of any distance or area calculation.",[15,1921,1922,1925],{},[200,1923,1924],{},"Why do my coordinates differ slightly from a colleague's?","\nDifferent datum transformation paths. When two datums are related by more than one published transformation, PROJ chooses one, and the choice can differ between installations depending on which grid files are present. Passing a transform context and logging the operation used makes the difference traceable.",[15,1927,1928,1931],{},[200,1929,1930],{},"Should I ever use setCrs to fix a misaligned layer?","\nOnly when you know the stored coordinates are correct and the label is wrong. If the coordinates themselves need to move, that is a transformation, and using assignment instead permanently mislabels the data in a way nothing downstream can detect.",[15,1933,1934,1937,1938,1941,1942,1944,1945,1947],{},[200,1935,1936],{},"What does an invalid CRS actually mean?","\nThat QGIS could not resolve the layer's projection definition at all — usually a missing ",[244,1939,1940],{},".prj"," file, or a definition it does not recognise. ",[244,1943,1015],{}," returning ",[244,1946,1019],{}," is the check worth running on ingest, because every later operation inherits the problem.",[15,1949,1950,1953],{},[200,1951,1952],{},"Can I define a CRS that has no EPSG code?","\nYes, from a WKT or PROJ string, and QGIS will store it as a user-defined system. Prefer an EPSG code wherever one exists, because raw definitions vary between PROJ versions and are easy to corrupt when copied by hand.",[15,1955,1956,1959,1960,1962,1963,1885,1965,1967],{},[200,1957,1958],{},"How do I find the right UTM zone for my data?","\nReproject the layer extent's centre to WGS84 and compute the zone from its longitude with ",[244,1961,1880],{},", then build ",[244,1964,1884],{},[244,1966,1888],{}," in the southern. Deriving it means the same script works anywhere.",[186,1969,1971],{"id":1970},"related","Related",[194,1973,1974,1980,1985,1990,1995,2000,2005],{},[197,1975,1976,1979],{},[19,1977,1978],{"href":21},"Spatial Data Processing & Automation with PyQGIS"," — the parent guide this page belongs to",[197,1981,1982],{},[19,1983,1984],{"href":780},"Vector Data Manipulation in PyQGIS",[197,1986,1987],{},[19,1988,1989],{"href":1225},"Raster Analysis Workflows in PyQGIS",[197,1991,1992],{},[19,1993,1994],{"href":1411},"Batch Processing with PyQGIS",[197,1996,1997],{},[19,1998,1999],{"href":1415},"Chaining Processing Algorithms in PyQGIS",[197,2001,2002],{},[19,2003,2004],{"href":35},"Handling Missing CRS in PyQGIS",[197,2006,2007],{},[19,2008,2009],{"href":39},"Batch Reprojecting Raster Datasets in PyQGIS",[2011,2012,2013],"style",{},"html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .sjoCn, html code.shiki .sjoCn{--shiki-default:#9AA79F}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}",{"title":276,"searchDepth":299,"depth":299,"links":2015},[2016,2017,2020,2021,2022,2023,2029,2030,2031,2032],{"id":188,"depth":299,"text":189},{"id":238,"depth":299,"text":239,"children":2018},[2019],{"id":548,"depth":306,"text":549},{"id":785,"depth":299,"text":786},{"id":1026,"depth":299,"text":1027},{"id":1214,"depth":299,"text":1215},{"id":1420,"depth":299,"text":1421,"children":2024},[2025,2026,2027,2028],{"id":1427,"depth":306,"text":1428},{"id":1449,"depth":306,"text":1450},{"id":1557,"depth":306,"text":1558},{"id":1571,"depth":306,"text":1572},{"id":1591,"depth":299,"text":1592},{"id":1738,"depth":299,"text":1739},{"id":1797,"depth":299,"text":1798},{"id":1970,"depth":299,"text":1971},"Work with coordinate reference systems in PyQGIS. Assign, transform, and validate CRS with QgsCoordinateReferenceSystem and reproject layers.","md",{"slug":2036,"type":2037,"breadcrumb":2038,"datePublished":2039,"dateModified":2040},"coordinate-reference-systems","guide","Coordinate Reference Systems","2025-03-04","2026-07-18","\u002Fspatial-data-processing-automation\u002Fcoordinate-reference-systems",{"title":5,"description":2033},"spatial-data-processing-automation\u002Fcoordinate-reference-systems\u002Findex","jBOaE8m7AQy2j_PVkGOYRkfdRoHWXkt6s_oxqMQvRgY",1787823363576]