[{"data":1,"prerenderedAt":696},["ShallowReactive",2],{"doc:\u002Fspatial-data-processing-automation\u002Fcoordinate-reference-systems\u002Fhandling-missing-crs-in-pyqgis":3},{"id":4,"title":5,"body":6,"description":689,"extension":690,"meta":691,"navigation":89,"path":692,"seo":693,"stem":694,"__hash__":695},"docs\u002Fspatial-data-processing-automation\u002Fcoordinate-reference-systems\u002Fhandling-missing-crs-in-pyqgis\u002Findex.md","Handling missing CRS in PyQGIS",{"type":7,"value":8,"toc":684},"minimark",[9,13,36,41,44,381,387,413,417,420,456,482,499,525,529,603,608,622,658,677,680],[10,11,5],"h1",{"id":12},"handling-missing-crs-in-pyqgis",[14,15,16,17,22,23,27,28,31,32,35],"p",{},"When a layer loads without a defined ",[18,19,21],"a",{"href":20},"\u002Fspatial-data-processing-automation\u002Fcoordinate-reference-systems\u002F","Coordinate Reference System",", PyQGIS assigns an invalid ",[24,25,26],"code",{},"QgsCoordinateReferenceSystem"," object. This immediately breaks spatial joins, buffers, and distance calculations. Fix it by detecting the undefined state with ",[24,29,30],{},"isValid()",", validating your target projection, and assigning it via ",[24,33,34],{},"layer.setCrs()"," before any geometry processing runs.",[37,38,40],"h3",{"id":39},"core-detection-assignment-workflow","Core Detection & Assignment Workflow",[14,42,43],{},"The following PyQGIS 3.x script safely iterates through loaded layers, flags missing projections, and applies a known CRS without altering raw coordinates.",[45,46,51],"pre",{"className":47,"code":48,"language":49,"meta":50,"style":50},"language-python shiki shiki-themes github-dark","from qgis.core import (\n QgsProject, QgsVectorLayer, QgsCoordinateReferenceSystem, QgsMessageLog\n)\n\nTARGET_CRS = \"EPSG:4326\" # Replace with your known projection\nLOG_CATEGORY = \"CRS_Fixer\"\n\ndef fix_missing_crs():\n layers = QgsProject.instance().mapLayers().values()\n fixed_count = 0\n\n for layer in layers:\n if not isinstance(layer, QgsVectorLayer):\n continue\n\n if not layer.crs().isValid():\n QgsMessageLog.logMessage(f\"Missing CRS on: {layer.name()}\", LOG_CATEGORY)\n \n target = QgsCoordinateReferenceSystem(TARGET_CRS)\n if target.isValid():\n # Updates metadata only; does NOT transform coordinates\n layer.setCrs(target)\n fixed_count += 1\n else:\n QgsMessageLog.logMessage(f\"Invalid target CRS: {TARGET_CRS}\", LOG_CATEGORY)\n\n QgsMessageLog.logMessage(f\"Fixed {fixed_count} layers.\", LOG_CATEGORY)\n return fixed_count\n\nfix_missing_crs()\n","python","",[24,52,53,72,78,84,91,109,120,125,138,150,161,166,181,196,202,207,217,248,254,269,277,283,289,300,309,330,335,361,370,375],{"__ignoreMap":50},[54,55,58,62,66,69],"span",{"class":56,"line":57},"line",1,[54,59,61],{"class":60},"snl16","from",[54,63,65],{"class":64},"s95oV"," qgis.core ",[54,67,68],{"class":60},"import",[54,70,71],{"class":64}," (\n",[54,73,75],{"class":56,"line":74},2,[54,76,77],{"class":64}," QgsProject, QgsVectorLayer, QgsCoordinateReferenceSystem, QgsMessageLog\n",[54,79,81],{"class":56,"line":80},3,[54,82,83],{"class":64},")\n",[54,85,87],{"class":56,"line":86},4,[54,88,90],{"emptyLinePlaceholder":89},true,"\n",[54,92,94,98,101,105],{"class":56,"line":93},5,[54,95,97],{"class":96},"sDLfK","TARGET_CRS",[54,99,100],{"class":60}," =",[54,102,104],{"class":103},"sU2Wk"," \"EPSG:4326\"",[54,106,108],{"class":107},"sAwPA"," # Replace with your known projection\n",[54,110,112,115,117],{"class":56,"line":111},6,[54,113,114],{"class":96},"LOG_CATEGORY",[54,116,100],{"class":60},[54,118,119],{"class":103}," \"CRS_Fixer\"\n",[54,121,123],{"class":56,"line":122},7,[54,124,90],{"emptyLinePlaceholder":89},[54,126,128,131,135],{"class":56,"line":127},8,[54,129,130],{"class":60},"def",[54,132,134],{"class":133},"svObZ"," fix_missing_crs",[54,136,137],{"class":64},"():\n",[54,139,141,144,147],{"class":56,"line":140},9,[54,142,143],{"class":64}," layers ",[54,145,146],{"class":60},"=",[54,148,149],{"class":64}," QgsProject.instance().mapLayers().values()\n",[54,151,153,156,158],{"class":56,"line":152},10,[54,154,155],{"class":64}," fixed_count ",[54,157,146],{"class":60},[54,159,160],{"class":96}," 0\n",[54,162,164],{"class":56,"line":163},11,[54,165,90],{"emptyLinePlaceholder":89},[54,167,169,172,175,178],{"class":56,"line":168},12,[54,170,171],{"class":60}," for",[54,173,174],{"class":64}," layer ",[54,176,177],{"class":60},"in",[54,179,180],{"class":64}," layers:\n",[54,182,184,187,190,193],{"class":56,"line":183},13,[54,185,186],{"class":60}," if",[54,188,189],{"class":60}," not",[54,191,192],{"class":96}," isinstance",[54,194,195],{"class":64},"(layer, QgsVectorLayer):\n",[54,197,199],{"class":56,"line":198},14,[54,200,201],{"class":60}," continue\n",[54,203,205],{"class":56,"line":204},15,[54,206,90],{"emptyLinePlaceholder":89},[54,208,210,212,214],{"class":56,"line":209},16,[54,211,186],{"class":60},[54,213,189],{"class":60},[54,215,216],{"class":64}," layer.crs().isValid():\n",[54,218,220,223,226,229,232,235,238,241,244,246],{"class":56,"line":219},17,[54,221,222],{"class":64}," QgsMessageLog.logMessage(",[54,224,225],{"class":60},"f",[54,227,228],{"class":103},"\"Missing CRS on: ",[54,230,231],{"class":96},"{",[54,233,234],{"class":64},"layer.name()",[54,236,237],{"class":96},"}",[54,239,240],{"class":103},"\"",[54,242,243],{"class":64},", ",[54,245,114],{"class":96},[54,247,83],{"class":64},[54,249,251],{"class":56,"line":250},18,[54,252,253],{"class":64}," \n",[54,255,257,260,262,265,267],{"class":56,"line":256},19,[54,258,259],{"class":64}," target ",[54,261,146],{"class":60},[54,263,264],{"class":64}," QgsCoordinateReferenceSystem(",[54,266,97],{"class":96},[54,268,83],{"class":64},[54,270,272,274],{"class":56,"line":271},20,[54,273,186],{"class":60},[54,275,276],{"class":64}," target.isValid():\n",[54,278,280],{"class":56,"line":279},21,[54,281,282],{"class":107}," # Updates metadata only; does NOT transform coordinates\n",[54,284,286],{"class":56,"line":285},22,[54,287,288],{"class":64}," layer.setCrs(target)\n",[54,290,292,294,297],{"class":56,"line":291},23,[54,293,155],{"class":64},[54,295,296],{"class":60},"+=",[54,298,299],{"class":96}," 1\n",[54,301,303,306],{"class":56,"line":302},24,[54,304,305],{"class":60}," else",[54,307,308],{"class":64},":\n",[54,310,312,314,316,319,322,324,326,328],{"class":56,"line":311},25,[54,313,222],{"class":64},[54,315,225],{"class":60},[54,317,318],{"class":103},"\"Invalid target CRS: ",[54,320,321],{"class":96},"{TARGET_CRS}",[54,323,240],{"class":103},[54,325,243],{"class":64},[54,327,114],{"class":96},[54,329,83],{"class":64},[54,331,333],{"class":56,"line":332},26,[54,334,90],{"emptyLinePlaceholder":89},[54,336,338,340,342,345,347,350,352,355,357,359],{"class":56,"line":337},27,[54,339,222],{"class":64},[54,341,225],{"class":60},[54,343,344],{"class":103},"\"Fixed ",[54,346,231],{"class":96},[54,348,349],{"class":64},"fixed_count",[54,351,237],{"class":96},[54,353,354],{"class":103}," layers.\"",[54,356,243],{"class":64},[54,358,114],{"class":96},[54,360,83],{"class":64},[54,362,364,367],{"class":56,"line":363},28,[54,365,366],{"class":60}," return",[54,368,369],{"class":64}," fixed_count\n",[54,371,373],{"class":56,"line":372},29,[54,374,90],{"emptyLinePlaceholder":89},[54,376,378],{"class":56,"line":377},30,[54,379,380],{"class":64},"fix_missing_crs()\n",[14,382,383],{},[384,385,386],"strong",{},"Key behavior notes:",[388,389,390,400,407],"ul",{},[391,392,393,395,396,399],"li",{},[24,394,34],{}," only updates layer metadata. To physically reproject geometries, use ",[24,397,398],{},"QgsCoordinateTransform",".",[391,401,402,403,406],{},"Always validate ",[24,404,405],{},"target.isValid()"," before assignment to prevent silent failures.",[391,408,409,412],{},[24,410,411],{},"QgsMessageLog"," outputs directly to the QGIS Log Messages panel for audit trails.",[37,414,416],{"id":415},"fallback-strategies-for-unidentified-projections","Fallback Strategies for Unidentified Projections",[14,418,419],{},"When source files lack embedded metadata (common with legacy Shapefiles, CSV exports, or CAD conversions), automatic detection fails. Apply this structured fallback:",[421,422,423,440,450],"ol",{},[391,424,425,428,429,432,433,436,437,439],{},[384,426,427],{},"Check sidecar files:"," Look for ",[24,430,431],{},".prj"," or ",[24,434,435],{},".xml"," files. PyQGIS reads ",[24,438,431],{}," automatically, but corrupted files require manual WKT injection.",[391,441,442,445,446,449],{},[384,443,444],{},"Inspect raw bounds with GDAL:"," Run ",[24,447,448],{},"ogrinfo -al -so \u003Cfile>"," to extract coordinate extents. Match ranges to known regional datums.",[391,451,452,455],{},[384,453,454],{},"Force project-level CRS:"," If per-layer assignment is impractical, set the project CRS and enable on-the-fly transformation:",[45,457,459],{"className":47,"code":458,"language":49,"meta":50,"style":50},"project = QgsProject.instance()\nproject.setCrs(QgsCoordinateReferenceSystem(\"EPSG:3857\"))\n",[24,460,461,471],{"__ignoreMap":50},[54,462,463,466,468],{"class":56,"line":57},[54,464,465],{"class":64},"project ",[54,467,146],{"class":60},[54,469,470],{"class":64}," QgsProject.instance()\n",[54,472,473,476,479],{"class":56,"line":74},[54,474,475],{"class":64},"project.setCrs(QgsCoordinateReferenceSystem(",[54,477,478],{"class":103},"\"EPSG:3857\"",[54,480,481],{"class":64},"))\n",[421,483,484],{"start":86},[391,485,486,489,490,494,495,498],{},[384,487,488],{},"Skip & log unverified layers:"," In automated ",[18,491,493],{"href":492},"\u002Fspatial-data-processing-automation\u002F","Spatial Data Processing & Automation"," pipelines, wrap assignments in ",[24,496,497],{},"try\u002Fexcept"," blocks. Log the layer path and skip processing until manual verification occurs. Never guess projections; incorrect assumptions corrupt topology and invalidate downstream analysis.",[14,500,501,505,506,509,510,513,514,509,517,520,521,524],{},[502,503,504],"em",{},"Quick heuristic:"," Coordinates between ",[24,507,508],{},"-180"," to ",[24,511,512],{},"180"," and ",[24,515,516],{},"-90",[24,518,519],{},"90"," typically indicate WGS84 (",[24,522,523],{},"EPSG:4326","). Large positive integers usually signal UTM or State Plane systems.",[37,526,528],{"id":527},"compatibility-environment-notes","Compatibility & Environment Notes",[530,531,532,548],"table",{},[533,534,535],"thead",{},[536,537,538,542,545],"tr",{},[539,540,541],"th",{},"Component",[539,543,544],{},"Minimum Version",[539,546,547],{},"Notes",[549,550,551,567,578,592],"tbody",{},[536,552,553,557,560],{},[554,555,556],"td",{},"QGIS",[554,558,559],{},"3.28 LTS",[554,561,562,563,566],{},"Avoid deprecated ",[24,564,565],{},"QgsCRSCache"," in scripts.",[536,568,569,572,575],{},[554,570,571],{},"Python",[554,573,574],{},"3.9+",[554,576,577],{},"Legacy Python 2 bindings are removed.",[536,579,580,583,586],{},[554,581,582],{},"GDAL",[554,584,585],{},"3.2+",[554,587,588,589,591],{},"Required for ",[24,590,431],{}," parsing and WKT2 conversion.",[536,593,594,597,600],{},[554,595,596],{},"PROJ",[554,598,599],{},"7.2+",[554,601,602],{},"Handles modern datum transformations.",[14,604,605],{},[384,606,607],{},"Critical pitfalls:",[388,609,610,619],{},[391,611,612,614,615,618],{},[24,613,34],{}," does not trigger a canvas refresh. Call ",[24,616,617],{},"layer.triggerRepaint()"," if UI updates are needed.",[391,620,621],{},"Standalone scripts must initialize the application context before loading layers:",[45,623,625],{"className":47,"code":624,"language":49,"meta":50,"style":50},"from qgis.core import QgsApplication\nQgsApplication.setPrefixPath(\"\u002Fpath\u002Fto\u002Fqgis\", True)\nQgsApplication.initQgis()\n",[24,626,627,638,653],{"__ignoreMap":50},[54,628,629,631,633,635],{"class":56,"line":57},[54,630,61],{"class":60},[54,632,65],{"class":64},[54,634,68],{"class":60},[54,636,637],{"class":64}," QgsApplication\n",[54,639,640,643,646,648,651],{"class":56,"line":74},[54,641,642],{"class":64},"QgsApplication.setPrefixPath(",[54,644,645],{"class":103},"\"\u002Fpath\u002Fto\u002Fqgis\"",[54,647,243],{"class":64},[54,649,650],{"class":96},"True",[54,652,83],{"class":64},[54,654,655],{"class":56,"line":80},[54,656,657],{"class":64},"QgsApplication.initQgis()\n",[388,659,660,667],{},[391,661,662,663,666],{},"Database layers (PostGIS, GeoPackage) inherit CRS from the backend. Query ",[24,664,665],{},"layer.dataProvider().crs()"," before overriding.",[391,668,669,670,673,674,399],{},"If ",[24,671,672],{},"QgsCoordinateReferenceSystem.createFromUserInput()"," fails, validate the EPSG\u002FWKT syntax against the official registry or test with ",[24,675,676],{},"projinfo",[14,678,679],{},"Always test CRS assignment on dataset copies. Projection mismatches silently distort geometries, and recovery requires re-importing the original source.",[681,682,683],"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 .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}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);}",{"title":50,"searchDepth":74,"depth":74,"links":685},[686,687,688],{"id":39,"depth":80,"text":40},{"id":415,"depth":80,"text":416},{"id":527,"depth":80,"text":528},"When a layer loads without a defined Coordinate Reference System, PyQGIS assigns an invalid QgsCoordinateReferenceSystem object. This immediately breaks spatial joins, buffers, and distance calculations. Fix it by detecting the undefined state with isValid(), validating your target projection, and assigning it via layer.setCrs() before any geometry processing runs.","md",{},"\u002Fspatial-data-processing-automation\u002Fcoordinate-reference-systems\u002Fhandling-missing-crs-in-pyqgis",{"title":5,"description":689},"spatial-data-processing-automation\u002Fcoordinate-reference-systems\u002Fhandling-missing-crs-in-pyqgis\u002Findex","5Fh5zAfzadscSNsfPmsYoun-Htk4b1ywYrjwPyNQjpU",1777824788945]