[{"data":1,"prerenderedAt":1765},["ShallowReactive",2],{"doc:\u002Fspatial-data-processing-automation\u002Flayer-data-sources-and-formats\u002Fdecode-and-build-data-source-uris-pyqgis":3},{"id":4,"title":5,"body":6,"description":1754,"extension":1755,"meta":1756,"navigation":265,"path":1761,"seo":1762,"stem":1763,"__hash__":1764},"docs\u002Fspatial-data-processing-automation\u002Flayer-data-sources-and-formats\u002Fdecode-and-build-data-source-uris-pyqgis\u002Findex.md","Decode and Build Data Source URIs in PyQGIS",{"type":7,"value":8,"toc":1741},"minimark",[9,13,30,39,196,201,223,227,233,373,434,530,536,672,682,686,696,833,853,857,867,1009,1038,1042,1063,1255,1301,1305,1310,1380,1510,1522,1526,1553,1557,1613,1617,1623,1627,1635,1652,1661,1685,1703,1707,1737],[10,11,5],"h1",{"id":12},"decode-and-build-data-source-uris-in-pyqgis",[14,15,16,17,21,22,25,26,29],"p",{},"Every layer in QGIS is a provider key plus a source string. For a shapefile the string is a path; for a GeoPackage it is a path with ",[18,19,20],"code",{},"|layername="," glued on; for PostGIS it is a run of ",[18,23,24],{},"key='value'"," pairs; for a WMS it is URL-encoded query parameters. Scripts that need to know which file a layer reads, move a project to a new server or swap one table for another usually start by splitting that string with ",[18,27,28],{},"split(\"|\")"," — and break the first time a path contains a pipe, a quote or a space.",[14,31,32,33,38],{},"The provider registry already knows how to parse and assemble each provider's format. This recipe belongs to ",[34,35,37],"a",{"href":36},"\u002Fspatial-data-processing-automation\u002Flayer-data-sources-and-formats\u002F","Layer Data Sources & Formats in PyQGIS"," and covers reading a source apart, building one from parts, handling database connection strings, and repointing a live layer.",[14,40,41],{},[42,43,48,52,56,63,80,89,99,105,110,114,121,127,130,133,136,140,145,149,152,154,158,161,164,169,173,179,184,187,193],"svg",{"viewBox":44,"role":45,"ariaLabel":46,"xmlns":47},"0 0 760 300","img","A round trip through the provider registry: a source string is decoded into a dictionary of parts, one part is changed, and encodeUri assembles a new source string that setDataSource applies to the layer","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg",[49,50,51],"title",{},"Decode, change one part, encode",[53,54,55],"desc",{},"A layer's source string goes into decodeUri with the provider key and comes out as a dictionary with keys such as path and layerName. The script changes only the path. encodeUri turns the dictionary back into a correctly quoted source string, and setDataSource points the existing layer at it while keeping its style and id.",[57,58],"rect",{"x":59,"y":59,"width":60,"height":61,"fill":62},"0","760","300","#f6f3ea",[64,65,66],"defs",{},[67,68,75],"marker",{"id":69,"viewBox":70,"refX":71,"refY":72,"markerWidth":73,"markerHeight":73,"orient":74},"uriRoundArrow","0 0 10 10","8","5","7","auto-start-reverse",[76,77],"path",{"d":78,"fill":79},"M0 0 L10 5 L0 10 z","#2f3b35",[81,82,88],"text",{"x":83,"y":84,"style":85,"fill":86,"textAnchor":87},"380","28","text-anchor:middle;font-size:14px;font-weight:bold;font-family:sans-serif","#17211d","middle","Never edit the string — edit the parts",[57,90],{"x":91,"y":92,"width":93,"height":94,"rx":95,"fill":96,"stroke":97,"style":98},"24","56","220","92","10","#fffdf7","#59645f","stroke-width:2",[81,100,104],{"x":101,"y":102,"style":103,"fill":86,"textAnchor":87},"134","80","text-anchor:middle;font-size:11px;font-weight:bold;font-family:sans-serif","layer.source()",[81,106,109],{"x":101,"y":107,"style":108,"fill":79,"textAnchor":87},"106","text-anchor:middle;font-size:9.5px;font-family:monospace","\u002Fold\u002Fshare\u002Fbase.gpkg",[81,111,113],{"x":101,"y":112,"style":108,"fill":79,"textAnchor":87},"126","|layername=roads",[115,116],"line",{"x1":117,"y1":118,"x2":119,"y2":118,"stroke":79,"style":120},"244","102","286","stroke-width:1.8;marker-end:url(#uriRoundArrow)",[57,122],{"x":123,"y":92,"width":124,"height":94,"rx":95,"fill":125,"stroke":126,"style":98},"294","172","#eef7f4","#0f766e",[81,128,129],{"x":83,"y":102,"style":103,"fill":126,"textAnchor":87},"decodeUri(\"ogr\", …)",[81,131,132],{"x":83,"y":107,"style":108,"fill":79,"textAnchor":87},"path: \u002Fold\u002Fshare\u002F…",[81,134,135],{"x":83,"y":112,"style":108,"fill":79,"textAnchor":87},"layerName: roads",[115,137],{"x1":138,"y1":118,"x2":139,"y2":118,"stroke":79,"style":120},"466","508",[57,141],{"x":142,"y":92,"width":93,"height":94,"rx":95,"fill":143,"stroke":144,"style":98},"516","#fdf2e2","#b45309",[81,146,148],{"x":147,"y":102,"style":103,"fill":144,"textAnchor":87},"626","change one key",[81,150,151],{"x":147,"y":107,"style":108,"fill":79,"textAnchor":87},"path: \u002Fnew\u002Fgis\u002Fbase.gpkg",[81,153,135],{"x":147,"y":112,"style":108,"fill":79,"textAnchor":87},[115,155],{"x1":147,"y1":156,"x2":147,"y2":157,"stroke":79,"style":120},"148","186",[57,159],{"x":142,"y":160,"width":93,"height":102,"rx":95,"fill":125,"stroke":126,"style":98},"194",[81,162,163],{"x":147,"y":93,"style":103,"fill":126,"textAnchor":87},"encodeUri(\"ogr\", parts)",[81,165,168],{"x":147,"y":166,"style":167,"fill":79,"textAnchor":87},"246","text-anchor:middle;font-size:9.5px;font-family:sans-serif","quoting handled for you",[115,170],{"x1":142,"y1":171,"x2":172,"y2":171,"stroke":79,"style":120},"234","474",[57,174],{"x":117,"y":160,"width":175,"height":102,"rx":95,"fill":176,"stroke":177,"style":178},"222","#e8efe6","#15803d","stroke-width:2.5",[81,180,183],{"x":181,"y":93,"style":103,"fill":182,"textAnchor":87},"355","#166534","layer.setDataSource(…)",[81,185,186],{"x":181,"y":166,"style":167,"fill":79,"textAnchor":87},"same layer id, style, joins",[81,188,192],{"x":189,"y":190,"style":191,"fill":97,"textAnchor":87},"130","226","text-anchor:middle;font-size:10px;font-family:sans-serif","works for ogr, gdal,",[81,194,195],{"x":189,"y":117,"style":191,"fill":97,"textAnchor":87},"delimitedtext, wms …",[197,198,200],"h2",{"id":199},"prerequisites","Prerequisites",[202,203,204,220],"ul",{},[205,206,207,211,212,215,216,219],"li",{},[208,209,210],"strong",{},"QGIS 3.40 LTR"," or newer. ",[18,213,214],{},"decodeUri"," exists from 3.10 and ",[18,217,218],{},"encodeUri"," from 3.12, so older installs work for everything except the newest provider keys.",[205,221,222],{},"A project or script with layers loaded from at least one file and, optionally, one database.",[197,224,226],{"id":225},"read-a-layers-source-apart","Read a layer's source apart",[14,228,229,232],{},[18,230,231],{},"QgsProviderRegistry.decodeUri"," takes a provider key and a source string and returns a dictionary. The keys depend on the provider, so the first thing to do with an unfamiliar layer is print what comes back.",[234,235,240],"pre",{"className":236,"code":237,"language":238,"meta":239,"style":239},"language-python shiki shiki-themes github-dark","from qgis.core import QgsProject, QgsProviderRegistry\n\nregistry = QgsProviderRegistry.instance()\n\nfor layer in QgsProject.instance().mapLayers().values():\n    key = layer.providerType()\n    parts = registry.decodeUri(key, layer.source())\n    print(f\"{layer.name():\u003C24} {key:\u003C14} {parts}\")\n","python","",[18,241,242,260,267,279,284,299,310,321],{"__ignoreMap":239},[243,244,246,250,254,257],"span",{"class":115,"line":245},1,[243,247,249],{"class":248},"snl16","from",[243,251,253],{"class":252},"s95oV"," qgis.core ",[243,255,256],{"class":248},"import",[243,258,259],{"class":252}," QgsProject, QgsProviderRegistry\n",[243,261,263],{"class":115,"line":262},2,[243,264,266],{"emptyLinePlaceholder":265},true,"\n",[243,268,270,273,276],{"class":115,"line":269},3,[243,271,272],{"class":252},"registry ",[243,274,275],{"class":248},"=",[243,277,278],{"class":252}," QgsProviderRegistry.instance()\n",[243,280,282],{"class":115,"line":281},4,[243,283,266],{"emptyLinePlaceholder":265},[243,285,287,290,293,296],{"class":115,"line":286},5,[243,288,289],{"class":248},"for",[243,291,292],{"class":252}," layer ",[243,294,295],{"class":248},"in",[243,297,298],{"class":252}," QgsProject.instance().mapLayers().values():\n",[243,300,302,305,307],{"class":115,"line":301},6,[243,303,304],{"class":252},"    key ",[243,306,275],{"class":248},[243,308,309],{"class":252}," layer.providerType()\n",[243,311,313,316,318],{"class":115,"line":312},7,[243,314,315],{"class":252},"    parts ",[243,317,275],{"class":248},[243,319,320],{"class":252}," registry.decodeUri(key, layer.source())\n",[243,322,324,328,331,334,338,341,344,347,350,353,356,359,361,363,366,368,370],{"class":115,"line":323},8,[243,325,327],{"class":326},"sDLfK","    print",[243,329,330],{"class":252},"(",[243,332,333],{"class":248},"f",[243,335,337],{"class":336},"sU2Wk","\"",[243,339,340],{"class":326},"{",[243,342,343],{"class":252},"layer.name()",[243,345,346],{"class":248},":\u003C24",[243,348,349],{"class":326},"}",[243,351,352],{"class":326}," {",[243,354,355],{"class":252},"key",[243,357,358],{"class":248},":\u003C14",[243,360,349],{"class":326},[243,362,352],{"class":326},[243,364,365],{"class":252},"parts",[243,367,349],{"class":326},[243,369,337],{"class":336},[243,371,372],{"class":252},")\n",[14,374,375,378,379,382,383,386,387,390,391,394,395,394,398,394,401,404,405,407,408,410,411,394,414,417,418,421,422,424,425,427,428,430,431,433],{},[208,376,377],{},"Breakdown:"," ",[18,380,381],{},"providerType()"," returns the key the layer was created with — ",[18,384,385],{},"ogr"," for most vector files, ",[18,388,389],{},"gdal"," for rasters, ",[18,392,393],{},"postgres",", ",[18,396,397],{},"delimitedtext",[18,399,400],{},"wms",[18,402,403],{},"memory"," and so on — and that key is what selects the right parser. For ",[18,406,385],{}," the dictionary holds ",[18,409,76],{},", and where relevant ",[18,412,413],{},"layerName",[18,415,416],{},"layerId"," and ",[18,419,420],{},"subset","; for ",[18,423,389],{}," it holds ",[18,426,76],{}," and sometimes ",[18,429,413],{}," for multi-dataset containers; for ",[18,432,393],{}," it holds nothing useful, because database URIs have their own class, covered below. A provider with no URI parser returns an empty dictionary rather than raising, so an empty result is a signal to fall back to the provider-specific class.",[14,435,436],{},[42,437,440,443,446,448,451,455,461,465,472,477,481,485,490,493,496,499,502,505,508,511,515,518,521,524,527],{"viewBox":438,"role":45,"ariaLabel":439,"xmlns":47},"0 0 760 286","Which tool parses which source: file-based providers go through decodeUri and encodeUri, database providers through QgsDataSourceUri, and web providers through QgsDataSourceUri with setParam",[49,441,442],{},"Three families of source string",[53,444,445],{},"File providers such as ogr, gdal and delimitedtext use a path-based string handled by decodeUri and encodeUri. Database providers such as postgres, spatialite and oracle use key equals quoted value pairs handled by QgsDataSourceUri with connection setters and setDataSource. Web providers such as wms, wfs and arcgisfeatureserver use key value parameters handled by QgsDataSourceUri setParam and encodedUri.",[57,447],{"x":59,"y":59,"width":60,"height":119,"fill":62},[81,449,450],{"x":83,"y":84,"style":85,"fill":86,"textAnchor":87},"Use the parser that matches the provider family",[57,452],{"x":91,"y":453,"width":190,"height":454,"rx":95,"fill":125,"stroke":126,"style":178},"50","212",[81,456,460],{"x":457,"y":458,"style":459,"fill":126,"textAnchor":87},"137","76","text-anchor:middle;font-size:12px;font-weight:bold;font-family:sans-serif","files",[81,462,464],{"x":457,"y":463,"style":191,"fill":97,"textAnchor":87},"100","ogr · gdal · delimitedtext",[57,466],{"x":467,"y":468,"width":157,"height":469,"rx":470,"fill":96,"stroke":126,"style":471},"44","118","46","6","stroke-width:1.5",[81,473,476],{"x":457,"y":474,"style":475,"fill":79,"textAnchor":87},"146","text-anchor:middle;font-size:10px;font-family:monospace","decodeUri \u002F encodeUri",[81,478,480],{"x":457,"y":479,"style":191,"fill":79,"textAnchor":87},"196","path plus layerName,",[81,482,484],{"x":457,"y":483,"style":191,"fill":79,"textAnchor":87},"214","subset, layerId",[57,486],{"x":487,"y":453,"width":190,"height":454,"rx":95,"fill":488,"stroke":489,"style":178},"267","#eff3ff","#2563eb",[81,491,492],{"x":83,"y":458,"style":459,"fill":489,"textAnchor":87},"databases",[81,494,495],{"x":83,"y":463,"style":191,"fill":97,"textAnchor":87},"postgres · spatialite · mssql",[57,497],{"x":498,"y":468,"width":157,"height":469,"rx":470,"fill":96,"stroke":489,"style":471},"287",[81,500,501],{"x":83,"y":474,"style":475,"fill":79,"textAnchor":87},"QgsDataSourceUri",[81,503,504],{"x":83,"y":479,"style":191,"fill":79,"textAnchor":87},"host, db, schema, table,",[81,506,507],{"x":83,"y":483,"style":191,"fill":79,"textAnchor":87},"geometry column, key",[57,509],{"x":510,"y":453,"width":190,"height":454,"rx":95,"fill":143,"stroke":144,"style":178},"510",[81,512,514],{"x":513,"y":458,"style":459,"fill":144,"textAnchor":87},"623","web services",[81,516,517],{"x":513,"y":463,"style":191,"fill":97,"textAnchor":87},"wms · WFS · arcgisfeatureserver",[57,519],{"x":520,"y":468,"width":157,"height":469,"rx":470,"fill":96,"stroke":144,"style":471},"530",[81,522,523],{"x":513,"y":474,"style":475,"fill":79,"textAnchor":87},"setParam \u002F encodedUri",[81,525,526],{"x":513,"y":479,"style":191,"fill":79,"textAnchor":87},"url, layers, typename,",[81,528,529],{"x":513,"y":483,"style":191,"fill":79,"textAnchor":87},"crs, authcfg",[14,531,532,533,535],{},"A practical use is an inventory: which files does this project actually depend on? Decoding every layer and collecting the ",[18,534,76],{}," values gives a list you can check for existence, archive or copy alongside the project file.",[234,537,539],{"className":236,"code":538,"language":238,"meta":239,"style":239},"import os\n\ndependencies = {}\nfor layer in QgsProject.instance().mapLayers().values():\n    parts = registry.decodeUri(layer.providerType(), layer.source())\n    path = parts.get(\"path\")\n    if path:\n        dependencies.setdefault(path, []).append(layer.name())\n\nfor path, names in sorted(dependencies.items()):\n    flag = \"ok \" if os.path.exists(path) else \"MISSING\"\n    print(flag, path, \"->\", \", \".join(names))\n",[18,540,541,548,552,562,572,581,596,604,609,614,630,653],{"__ignoreMap":239},[243,542,543,545],{"class":115,"line":245},[243,544,256],{"class":248},[243,546,547],{"class":252}," os\n",[243,549,550],{"class":115,"line":262},[243,551,266],{"emptyLinePlaceholder":265},[243,553,554,557,559],{"class":115,"line":269},[243,555,556],{"class":252},"dependencies ",[243,558,275],{"class":248},[243,560,561],{"class":252}," {}\n",[243,563,564,566,568,570],{"class":115,"line":281},[243,565,289],{"class":248},[243,567,292],{"class":252},[243,569,295],{"class":248},[243,571,298],{"class":252},[243,573,574,576,578],{"class":115,"line":286},[243,575,315],{"class":252},[243,577,275],{"class":248},[243,579,580],{"class":252}," registry.decodeUri(layer.providerType(), layer.source())\n",[243,582,583,586,588,591,594],{"class":115,"line":301},[243,584,585],{"class":252},"    path ",[243,587,275],{"class":248},[243,589,590],{"class":252}," parts.get(",[243,592,593],{"class":336},"\"path\"",[243,595,372],{"class":252},[243,597,598,601],{"class":115,"line":312},[243,599,600],{"class":248},"    if",[243,602,603],{"class":252}," path:\n",[243,605,606],{"class":115,"line":323},[243,607,608],{"class":252},"        dependencies.setdefault(path, []).append(layer.name())\n",[243,610,612],{"class":115,"line":611},9,[243,613,266],{"emptyLinePlaceholder":265},[243,615,617,619,622,624,627],{"class":115,"line":616},10,[243,618,289],{"class":248},[243,620,621],{"class":252}," path, names ",[243,623,295],{"class":248},[243,625,626],{"class":326}," sorted",[243,628,629],{"class":252},"(dependencies.items()):\n",[243,631,633,636,638,641,644,647,650],{"class":115,"line":632},11,[243,634,635],{"class":252},"    flag ",[243,637,275],{"class":248},[243,639,640],{"class":336}," \"ok \"",[243,642,643],{"class":248}," if",[243,645,646],{"class":252}," os.path.exists(path) ",[243,648,649],{"class":248},"else",[243,651,652],{"class":336}," \"MISSING\"\n",[243,654,656,658,661,664,666,669],{"class":115,"line":655},12,[243,657,327],{"class":326},[243,659,660],{"class":252},"(flag, path, ",[243,662,663],{"class":336},"\"->\"",[243,665,394],{"class":252},[243,667,668],{"class":336},"\", \"",[243,670,671],{"class":252},".join(names))\n",[14,673,674,676,677,681],{},[208,675,377],{}," Grouping by path matters for GeoPackages, where five layers often share one file; the inventory then shows one dependency, not five. The existence check catches the most common reason a shared project opens with red warning triangles, and the same loop is the starting point for ",[34,678,680],{"href":679},"\u002Fspatial-data-processing-automation\u002Flayer-data-sources-and-formats\u002Ffix-broken-layer-paths-in-project-pyqgis\u002F","fixing broken layer paths"," automatically.",[197,683,685],{"id":684},"build-a-source-from-parts","Build a source from parts",[14,687,688,689,691,692,695],{},"Going the other way, ",[18,690,218],{}," assembles a source string. It is the safe way to add a ",[18,693,694],{},"layername"," to a container path or attach a subset filter, because the provider applies its own quoting rules.",[234,697,699],{"className":236,"code":698,"language":238,"meta":239,"style":239},"from qgis.core import QgsVectorLayer\n\nparts = {\n    \"path\": \"\u002Fdata\u002Fwork\u002Fsurvey 2026.gpkg\",\n    \"layerName\": \"trees\",\n    \"subset\": \"\\\"height_m\\\" > 12\",\n}\nuri = registry.encodeUri(\"ogr\", parts)\nprint(uri)\n\ntall_trees = QgsVectorLayer(uri, \"tall trees\", \"ogr\")\nprint(tall_trees.isValid(), tall_trees.featureCount())\n",[18,700,701,712,716,726,740,752,774,779,795,803,807,826],{"__ignoreMap":239},[243,702,703,705,707,709],{"class":115,"line":245},[243,704,249],{"class":248},[243,706,253],{"class":252},[243,708,256],{"class":248},[243,710,711],{"class":252}," QgsVectorLayer\n",[243,713,714],{"class":115,"line":262},[243,715,266],{"emptyLinePlaceholder":265},[243,717,718,721,723],{"class":115,"line":269},[243,719,720],{"class":252},"parts ",[243,722,275],{"class":248},[243,724,725],{"class":252}," {\n",[243,727,728,731,734,737],{"class":115,"line":281},[243,729,730],{"class":336},"    \"path\"",[243,732,733],{"class":252},": ",[243,735,736],{"class":336},"\"\u002Fdata\u002Fwork\u002Fsurvey 2026.gpkg\"",[243,738,739],{"class":252},",\n",[243,741,742,745,747,750],{"class":115,"line":286},[243,743,744],{"class":336},"    \"layerName\"",[243,746,733],{"class":252},[243,748,749],{"class":336},"\"trees\"",[243,751,739],{"class":252},[243,753,754,757,759,761,764,767,769,772],{"class":115,"line":301},[243,755,756],{"class":336},"    \"subset\"",[243,758,733],{"class":252},[243,760,337],{"class":336},[243,762,763],{"class":326},"\\\"",[243,765,766],{"class":336},"height_m",[243,768,763],{"class":326},[243,770,771],{"class":336}," > 12\"",[243,773,739],{"class":252},[243,775,776],{"class":115,"line":312},[243,777,778],{"class":252},"}\n",[243,780,781,784,786,789,792],{"class":115,"line":323},[243,782,783],{"class":252},"uri ",[243,785,275],{"class":248},[243,787,788],{"class":252}," registry.encodeUri(",[243,790,791],{"class":336},"\"ogr\"",[243,793,794],{"class":252},", parts)\n",[243,796,797,800],{"class":115,"line":611},[243,798,799],{"class":326},"print",[243,801,802],{"class":252},"(uri)\n",[243,804,805],{"class":115,"line":616},[243,806,266],{"emptyLinePlaceholder":265},[243,808,809,812,814,817,820,822,824],{"class":115,"line":632},[243,810,811],{"class":252},"tall_trees ",[243,813,275],{"class":248},[243,815,816],{"class":252}," QgsVectorLayer(uri, ",[243,818,819],{"class":336},"\"tall trees\"",[243,821,394],{"class":252},[243,823,791],{"class":336},[243,825,372],{"class":252},[243,827,828,830],{"class":115,"line":655},[243,829,799],{"class":326},[243,831,832],{"class":252},"(tall_trees.isValid(), tall_trees.featureCount())\n",[14,834,835,837,838,840,841,843,844,846,847,849,850,852],{},[208,836,377],{}," The space in the file name and the quotes in the subset would each need escaping by hand; ",[18,839,218],{}," produces a string the ",[18,842,385],{}," provider reads back correctly. The ",[18,845,420],{}," key becomes the layer's subset string, so the filter survives saving the project. For a ",[18,848,389],{}," raster inside a GeoPackage the same pattern works with ",[18,851,413],{}," naming the raster table.",[197,854,856],{"id":855},"database-connection-strings","Database connection strings",[14,858,859,860,862,863,866],{},"Database providers use ",[18,861,501],{},", which parses and builds the ",[18,864,865],{},"dbname='…' host=… table=\"schema\".\"table\" (geom)"," format and exposes each part through a getter and a setter.",[234,868,870],{"className":236,"code":869,"language":238,"meta":239,"style":239},"from qgis.core import QgsDataSourceUri\n\npg_layer = QgsProject.instance().mapLayersByName(\"parcels\")[0]\nuri = QgsDataSourceUri(pg_layer.source())\nprint(uri.host(), uri.database(), uri.schema(), uri.table(),\n      uri.geometryColumn(), uri.keyColumn(), uri.authConfigId())\n\nuri.setConnection(\"db-new.internal\", \"5432\", \"gis\", \"\", \"\",\n                  QgsDataSourceUri.SslPrefer, uri.authConfigId())\nuri.setDataSource(\"cadastre\", \"parcels_2026\", \"geom\", \"\", \"fid\")\nprint(uri.uri(False))\n",[18,871,872,883,887,908,917,924,929,933,962,967,996],{"__ignoreMap":239},[243,873,874,876,878,880],{"class":115,"line":245},[243,875,249],{"class":248},[243,877,253],{"class":252},[243,879,256],{"class":248},[243,881,882],{"class":252}," QgsDataSourceUri\n",[243,884,885],{"class":115,"line":262},[243,886,266],{"emptyLinePlaceholder":265},[243,888,889,892,894,897,900,903,905],{"class":115,"line":269},[243,890,891],{"class":252},"pg_layer ",[243,893,275],{"class":248},[243,895,896],{"class":252}," QgsProject.instance().mapLayersByName(",[243,898,899],{"class":336},"\"parcels\"",[243,901,902],{"class":252},")[",[243,904,59],{"class":326},[243,906,907],{"class":252},"]\n",[243,909,910,912,914],{"class":115,"line":281},[243,911,783],{"class":252},[243,913,275],{"class":248},[243,915,916],{"class":252}," QgsDataSourceUri(pg_layer.source())\n",[243,918,919,921],{"class":115,"line":286},[243,920,799],{"class":326},[243,922,923],{"class":252},"(uri.host(), uri.database(), uri.schema(), uri.table(),\n",[243,925,926],{"class":115,"line":301},[243,927,928],{"class":252},"      uri.geometryColumn(), uri.keyColumn(), uri.authConfigId())\n",[243,930,931],{"class":115,"line":312},[243,932,266],{"emptyLinePlaceholder":265},[243,934,935,938,941,943,946,948,951,953,956,958,960],{"class":115,"line":323},[243,936,937],{"class":252},"uri.setConnection(",[243,939,940],{"class":336},"\"db-new.internal\"",[243,942,394],{"class":252},[243,944,945],{"class":336},"\"5432\"",[243,947,394],{"class":252},[243,949,950],{"class":336},"\"gis\"",[243,952,394],{"class":252},[243,954,955],{"class":336},"\"\"",[243,957,394],{"class":252},[243,959,955],{"class":336},[243,961,739],{"class":252},[243,963,964],{"class":115,"line":611},[243,965,966],{"class":252},"                  QgsDataSourceUri.SslPrefer, uri.authConfigId())\n",[243,968,969,972,975,977,980,982,985,987,989,991,994],{"class":115,"line":616},[243,970,971],{"class":252},"uri.setDataSource(",[243,973,974],{"class":336},"\"cadastre\"",[243,976,394],{"class":252},[243,978,979],{"class":336},"\"parcels_2026\"",[243,981,394],{"class":252},[243,983,984],{"class":336},"\"geom\"",[243,986,394],{"class":252},[243,988,955],{"class":336},[243,990,394],{"class":252},[243,992,993],{"class":336},"\"fid\"",[243,995,372],{"class":252},[243,997,998,1000,1003,1006],{"class":115,"line":632},[243,999,799],{"class":326},[243,1001,1002],{"class":252},"(uri.uri(",[243,1004,1005],{"class":326},"False",[243,1007,1008],{"class":252},"))\n",[14,1010,1011,378,1013,1016,1017,1020,1021,1025,1026,1029,1030,1033,1034,1037],{},[208,1012,377],{},[18,1014,1015],{},"setConnection"," takes host, port, database, user, password, SSL mode and an authentication configuration id; passing empty user and password with an ",[18,1018,1019],{},"authcfg"," keeps credentials out of the string entirely, which is the pattern ",[34,1022,1024],{"href":1023},"\u002Fpyqgis-fundamentals-environment-setup\u002Fheadless-qgis-and-server-automation\u002Fstore-credentials-with-qgsauthmanager-pyqgis\u002F","QgsAuthManager"," is designed for. ",[18,1027,1028],{},"setDataSource"," takes schema, table, geometry column, an optional SQL filter and the key column. ",[18,1031,1032],{},"uri(False)"," returns the string without expanding the auth config, which is the form to store; ",[18,1035,1036],{},"uri(True)"," would inline the resolved credentials, which is the form never to print or save.",[197,1039,1041],{"id":1040},"web-service-parameters","Web service parameters",[14,1043,1044,1045,1048,1049,1051,1052,417,1055,1058,1059,1062],{},"WMS, WFS, ArcGIS REST and XYZ layers use a ",[18,1046,1047],{},"key=value&key=value"," form in which some values are themselves URLs. ",[18,1050,501],{}," handles that form too, through ",[18,1053,1054],{},"setParam",[18,1056,1057],{},"param",", and ",[18,1060,1061],{},"encodedUri()"," produces the escaped bytes the providers expect.",[234,1064,1066],{"className":236,"code":1065,"language":238,"meta":239,"style":239},"from qgis.core import QgsRasterLayer\n\nwms = QgsDataSourceUri()\nwms.setParam(\"url\", \"https:\u002F\u002Fmaps.example.org\u002Fwms?map=\u002Fsrv\u002Fbase.map\")\nwms.setParam(\"layers\", \"topo\")\nwms.setParam(\"styles\", \"\")\nwms.setParam(\"format\", \"image\u002Fpng\")\nwms.setParam(\"crs\", \"EPSG:3857\")\nwms.setAuthConfigId(\"wms0001\")\n\nsource = bytes(wms.encodedUri()).decode()\ntopo = QgsRasterLayer(source, \"topo\", \"wms\")\nprint(topo.isValid())\n\nexisting = QgsDataSourceUri()\nexisting.setEncodedUri(topo.source())\nprint(existing.param(\"url\"), existing.param(\"layers\"))\n",[18,1067,1068,1079,1083,1093,1108,1122,1135,1149,1163,1173,1177,1190,1209,1217,1222,1232,1238],{"__ignoreMap":239},[243,1069,1070,1072,1074,1076],{"class":115,"line":245},[243,1071,249],{"class":248},[243,1073,253],{"class":252},[243,1075,256],{"class":248},[243,1077,1078],{"class":252}," QgsRasterLayer\n",[243,1080,1081],{"class":115,"line":262},[243,1082,266],{"emptyLinePlaceholder":265},[243,1084,1085,1088,1090],{"class":115,"line":269},[243,1086,1087],{"class":252},"wms ",[243,1089,275],{"class":248},[243,1091,1092],{"class":252}," QgsDataSourceUri()\n",[243,1094,1095,1098,1101,1103,1106],{"class":115,"line":281},[243,1096,1097],{"class":252},"wms.setParam(",[243,1099,1100],{"class":336},"\"url\"",[243,1102,394],{"class":252},[243,1104,1105],{"class":336},"\"https:\u002F\u002Fmaps.example.org\u002Fwms?map=\u002Fsrv\u002Fbase.map\"",[243,1107,372],{"class":252},[243,1109,1110,1112,1115,1117,1120],{"class":115,"line":286},[243,1111,1097],{"class":252},[243,1113,1114],{"class":336},"\"layers\"",[243,1116,394],{"class":252},[243,1118,1119],{"class":336},"\"topo\"",[243,1121,372],{"class":252},[243,1123,1124,1126,1129,1131,1133],{"class":115,"line":301},[243,1125,1097],{"class":252},[243,1127,1128],{"class":336},"\"styles\"",[243,1130,394],{"class":252},[243,1132,955],{"class":336},[243,1134,372],{"class":252},[243,1136,1137,1139,1142,1144,1147],{"class":115,"line":312},[243,1138,1097],{"class":252},[243,1140,1141],{"class":336},"\"format\"",[243,1143,394],{"class":252},[243,1145,1146],{"class":336},"\"image\u002Fpng\"",[243,1148,372],{"class":252},[243,1150,1151,1153,1156,1158,1161],{"class":115,"line":323},[243,1152,1097],{"class":252},[243,1154,1155],{"class":336},"\"crs\"",[243,1157,394],{"class":252},[243,1159,1160],{"class":336},"\"EPSG:3857\"",[243,1162,372],{"class":252},[243,1164,1165,1168,1171],{"class":115,"line":611},[243,1166,1167],{"class":252},"wms.setAuthConfigId(",[243,1169,1170],{"class":336},"\"wms0001\"",[243,1172,372],{"class":252},[243,1174,1175],{"class":115,"line":616},[243,1176,266],{"emptyLinePlaceholder":265},[243,1178,1179,1182,1184,1187],{"class":115,"line":632},[243,1180,1181],{"class":252},"source ",[243,1183,275],{"class":248},[243,1185,1186],{"class":326}," bytes",[243,1188,1189],{"class":252},"(wms.encodedUri()).decode()\n",[243,1191,1192,1195,1197,1200,1202,1204,1207],{"class":115,"line":655},[243,1193,1194],{"class":252},"topo ",[243,1196,275],{"class":248},[243,1198,1199],{"class":252}," QgsRasterLayer(source, ",[243,1201,1119],{"class":336},[243,1203,394],{"class":252},[243,1205,1206],{"class":336},"\"wms\"",[243,1208,372],{"class":252},[243,1210,1212,1214],{"class":115,"line":1211},13,[243,1213,799],{"class":326},[243,1215,1216],{"class":252},"(topo.isValid())\n",[243,1218,1220],{"class":115,"line":1219},14,[243,1221,266],{"emptyLinePlaceholder":265},[243,1223,1225,1228,1230],{"class":115,"line":1224},15,[243,1226,1227],{"class":252},"existing ",[243,1229,275],{"class":248},[243,1231,1092],{"class":252},[243,1233,1235],{"class":115,"line":1234},16,[243,1236,1237],{"class":252},"existing.setEncodedUri(topo.source())\n",[243,1239,1241,1243,1246,1248,1251,1253],{"class":115,"line":1240},17,[243,1242,799],{"class":326},[243,1244,1245],{"class":252},"(existing.param(",[243,1247,1100],{"class":336},[243,1249,1250],{"class":252},"), existing.param(",[243,1252,1114],{"class":336},[243,1254,1008],{"class":252},[14,1256,1257,1259,1260,417,1263,1265,1266,1268,1269,1272,1273,1275,1276,1279,1280,1283,1284,1287,1288,1291,1292,417,1296,1300],{},[208,1258,377],{}," The service URL contains its own ",[18,1261,1262],{},"?",[18,1264,275],{},", which is exactly what breaks string concatenation; ",[18,1267,1061],{}," escapes it so the provider sees one ",[18,1270,1271],{},"url"," parameter rather than several. ",[18,1274,1054],{}," can be called repeatedly with the same key for parameters that legitimately repeat — a WMS request for several ",[18,1277,1278],{},"layers"," and matching ",[18,1281,1282],{},"styles"," — and ",[18,1285,1286],{},"params(key)"," returns them all. Reading an existing layer goes the other way through ",[18,1289,1290],{},"setEncodedUri",", after which each parameter is available by name, so a script can swap a server URL across every WMS layer in a project without touching the layer names or styles. The same pattern builds sources for ",[34,1293,1295],{"href":1294},"\u002Fspatial-data-processing-automation\u002Fweb-services-and-remote-data\u002Fload-wms-layer-pyqgis\u002F","WMS",[34,1297,1299],{"href":1298},"\u002Fspatial-data-processing-automation\u002Fweb-services-and-remote-data\u002Fload-wfs-layer-pyqgis\u002F","WFS"," layers.",[197,1302,1304],{"id":1303},"repoint-a-live-layer","Repoint a live layer",[14,1306,1307,1309],{},[18,1308,1028],{}," swaps what a layer reads while keeping the layer object — its id, its style, its joins, its place in the layer tree and every layout item that refers to it. That is what distinguishes it from removing a layer and adding a new one.",[14,1311,1312],{},[42,1313,1316,1319,1322,1325,1328,1332,1335,1340,1344,1348,1352,1356,1360,1364,1368,1371,1374,1377],{"viewBox":1314,"role":45,"ariaLabel":1315,"xmlns":47},"0 0 760 262","Comparison of setDataSource with remove and re-add: setDataSource keeps layer id, style, joins, relations and layout references; remove and re-add loses them all",[49,1317,1318],{},"What survives a source swap",[53,1320,1321],{},"Two columns. setDataSource keeps the layer id, the renderer and labels, joins and relations, layer tree position, and references from layouts and map themes. Removing the layer and adding a new one produces a new id, default style, no joins, the bottom of the layer tree, and broken layout and theme references.",[57,1323],{"x":59,"y":59,"width":60,"height":1324,"fill":62},"262",[81,1326,1327],{"x":83,"y":84,"style":85,"fill":86,"textAnchor":87},"Swap the source, keep the layer",[57,1329],{"x":91,"y":1330,"width":1331,"height":160,"rx":95,"fill":176,"stroke":177,"style":178},"48","344",[81,1333,1028],{"x":479,"y":1334,"style":459,"fill":182,"textAnchor":87},"74",[81,1336,1339],{"x":1337,"y":107,"style":1338,"fill":79},"52","font-size:10.5px;font-family:sans-serif","✓ same layer id",[81,1341,1343],{"x":1337,"y":1342,"style":1338,"fill":79},"132","✓ renderer, labels, opacity",[81,1345,1347],{"x":1337,"y":1346,"style":1338,"fill":79},"158","✓ joins and relations",[81,1349,1351],{"x":1337,"y":1350,"style":1338,"fill":79},"184","✓ position in the layer tree",[81,1353,1355],{"x":1337,"y":1354,"style":1338,"fill":79},"210","✓ layout maps and map themes",[57,1357],{"x":1358,"y":1330,"width":1331,"height":160,"rx":95,"fill":143,"stroke":1359,"style":178},"392","#b91c1c",[81,1361,1363],{"x":1362,"y":1334,"style":459,"fill":1359,"textAnchor":87},"564","remove + add new layer",[81,1365,1367],{"x":1366,"y":107,"style":1338,"fill":79},"420","✗ new id, old references dangle",[81,1369,1370],{"x":1366,"y":1342,"style":1338,"fill":79},"✗ default random symbol",[81,1372,1373],{"x":1366,"y":1346,"style":1338,"fill":79},"✗ joins and relations gone",[81,1375,1376],{"x":1366,"y":1350,"style":1338,"fill":79},"✗ lands at the top of the tree",[81,1378,1379],{"x":1366,"y":1354,"style":1338,"fill":79},"✗ themes forget it existed",[234,1381,1383],{"className":236,"code":1382,"language":238,"meta":239,"style":239},"from qgis.core import QgsDataProvider\n\nroads = QgsProject.instance().mapLayersByName(\"roads\")[0]\nparts = registry.decodeUri(\"ogr\", roads.source())\nparts[\"path\"] = \"\u002Fsrv\u002Fgis\u002Fbase\u002Fbase.gpkg\"\nnew_source = registry.encodeUri(\"ogr\", parts)\n\noptions = QgsDataProvider.ProviderOptions()\noptions.transformContext = QgsProject.instance().transformContext()\nroads.setDataSource(new_source, roads.name(), \"ogr\", options)\n\nprint(roads.isValid(), roads.source())\nroads.triggerRepaint()\n",[18,1384,1385,1396,1400,1418,1432,1447,1460,1464,1474,1484,1494,1498,1505],{"__ignoreMap":239},[243,1386,1387,1389,1391,1393],{"class":115,"line":245},[243,1388,249],{"class":248},[243,1390,253],{"class":252},[243,1392,256],{"class":248},[243,1394,1395],{"class":252}," QgsDataProvider\n",[243,1397,1398],{"class":115,"line":262},[243,1399,266],{"emptyLinePlaceholder":265},[243,1401,1402,1405,1407,1409,1412,1414,1416],{"class":115,"line":269},[243,1403,1404],{"class":252},"roads ",[243,1406,275],{"class":248},[243,1408,896],{"class":252},[243,1410,1411],{"class":336},"\"roads\"",[243,1413,902],{"class":252},[243,1415,59],{"class":326},[243,1417,907],{"class":252},[243,1419,1420,1422,1424,1427,1429],{"class":115,"line":281},[243,1421,720],{"class":252},[243,1423,275],{"class":248},[243,1425,1426],{"class":252}," registry.decodeUri(",[243,1428,791],{"class":336},[243,1430,1431],{"class":252},", roads.source())\n",[243,1433,1434,1437,1439,1442,1444],{"class":115,"line":286},[243,1435,1436],{"class":252},"parts[",[243,1438,593],{"class":336},[243,1440,1441],{"class":252},"] ",[243,1443,275],{"class":248},[243,1445,1446],{"class":336}," \"\u002Fsrv\u002Fgis\u002Fbase\u002Fbase.gpkg\"\n",[243,1448,1449,1452,1454,1456,1458],{"class":115,"line":301},[243,1450,1451],{"class":252},"new_source ",[243,1453,275],{"class":248},[243,1455,788],{"class":252},[243,1457,791],{"class":336},[243,1459,794],{"class":252},[243,1461,1462],{"class":115,"line":312},[243,1463,266],{"emptyLinePlaceholder":265},[243,1465,1466,1469,1471],{"class":115,"line":323},[243,1467,1468],{"class":252},"options ",[243,1470,275],{"class":248},[243,1472,1473],{"class":252}," QgsDataProvider.ProviderOptions()\n",[243,1475,1476,1479,1481],{"class":115,"line":611},[243,1477,1478],{"class":252},"options.transformContext ",[243,1480,275],{"class":248},[243,1482,1483],{"class":252}," QgsProject.instance().transformContext()\n",[243,1485,1486,1489,1491],{"class":115,"line":616},[243,1487,1488],{"class":252},"roads.setDataSource(new_source, roads.name(), ",[243,1490,791],{"class":336},[243,1492,1493],{"class":252},", options)\n",[243,1495,1496],{"class":115,"line":632},[243,1497,266],{"emptyLinePlaceholder":265},[243,1499,1500,1502],{"class":115,"line":655},[243,1501,799],{"class":326},[243,1503,1504],{"class":252},"(roads.isValid(), roads.source())\n",[243,1506,1507],{"class":115,"line":1211},[243,1508,1509],{"class":252},"roads.triggerRepaint()\n",[14,1511,1512,1514,1515,1518,1519,1521],{},[208,1513,377],{}," Passing the project's transform context in the provider options keeps any datum transformation choices the project already made. The renderer is kept as long as the new source has a compatible geometry type and the fields the renderer references; point a categorised polygon layer at a table without the category field and the style survives but classifies nothing. ",[18,1516,1517],{},"isValid()"," afterwards is the check that the new source actually opened — ",[18,1520,1028],{}," does not raise on failure.",[197,1523,1525],{"id":1524},"qgis-version-compatibility","QGIS version compatibility",[14,1527,1528,1530,1531,1533,1534,1536,1537,1540,1541,1544,1545,1548,1549,1552],{},[18,1529,214],{}," has been available since QGIS 3.10 and ",[18,1532,218],{}," since 3.12; both work identically on 3.40, 3.44 and the QGIS 4 series. ",[18,1535,1028],{}," gained its ",[18,1538,1539],{},"ProviderOptions"," argument in 3.6 and a ",[18,1542,1543],{},"loadDefaultStyleFlag"," overload later; the four-argument form shown here is the portable one. On QGIS 4, ",[18,1546,1547],{},"QgsDataSourceUri.SslPrefer"," is spelled ",[18,1550,1551],{},"QgsDataSourceUri.SslMode.SslPrefer",", which also works on recent 3.x releases.",[197,1554,1556],{"id":1555},"troubleshooting","Troubleshooting",[202,1558,1559,1574,1585,1591,1603],{},[205,1560,1561,1566,1567,1569,1570,1573],{},[208,1562,1563,1565],{},[18,1564,214],{}," returns an empty dictionary."," The provider has no URI parser; for databases use ",[18,1568,501],{},", for web services read the parameters with ",[18,1571,1572],{},"QgsDataSourceUri.param()",".",[205,1575,1576,1581,1582,1573],{},[208,1577,1578,1579,1573],{},"The layer is invalid after ",[18,1580,1028],{}," The provider key does not match the new source, or the file is not there. Check ",[18,1583,1584],{},"roads.error().summary()",[205,1586,1587,1590],{},[208,1588,1589],{},"Style is lost after repointing."," The new source has a different geometry type, so QGIS reset the renderer.",[205,1592,1593,1596,1597,1599,1600,1602],{},[208,1594,1595],{},"Credentials appear in a printed URI."," You called ",[18,1598,1036],{},"; print ",[18,1601,1032],{}," instead.",[205,1604,1605,1612],{},[208,1606,1607,1608,1611],{},"A path with ",[18,1609,1610],{},"|"," in the name is misread."," It was split by hand. Decode it with the registry.",[197,1614,1616],{"id":1615},"conclusion","Conclusion",[14,1618,1619,1620,1622],{},"Treat source strings as serialised data, not text. Decode them with the registry, change only the key you mean to change, encode them again, and apply the result with ",[18,1621,1028],{}," so that every style, join and layout reference stays attached to the layer.",[197,1624,1626],{"id":1625},"frequently-asked-questions","Frequently Asked Questions",[14,1628,1629,1632,1573],{},[208,1630,1631],{},"How do I find the file behind a layer in one line?",[18,1633,1634],{},"QgsProviderRegistry.instance().decodeUri(layer.providerType(), layer.source()).get(\"path\")",[14,1636,1637,1640,1641,417,1643,1645,1646,1648,1649,1651],{},[208,1638,1639],{},"Can I convert a shapefile layer to point at a GeoPackage table?","\nYes, provided the table has the same geometry type and the fields the style uses. Set ",[18,1642,76],{},[18,1644,413],{}," and call ",[18,1647,1028],{}," with the ",[18,1650,385],{}," key.",[14,1653,1654,1657,1660],{},[208,1655,1656],{},"Is there a list of every provider key?",[18,1658,1659],{},"QgsProviderRegistry.instance().providerList()"," returns the keys available in your installation.",[14,1662,1663,1670,1671,1673,1674,1677,1678,1681,1682,1684],{},[208,1664,1665,1666,1669],{},"Why does ",[18,1667,1668],{},"source()"," sometimes differ from what I passed in?","\nProviders normalise what they are given. The ",[18,1672,385],{}," provider may append ",[18,1675,1676],{},"|layerid=0"," to a single-layer file, and a project saved with relative paths stores ",[18,1679,1680],{},".\u002Fdata\u002Froads.gpkg"," but hands ",[18,1683,1668],{}," back as an absolute path resolved against the project location. Decoding both strings and comparing the dictionaries is more reliable than comparing the raw text.",[14,1686,1687,1690,1691,417,1693,1695,1696,1698,1699,1702],{},[208,1688,1689],{},"Can I tell whether two layers read the same table?","\nFor file layers, compare the decoded ",[18,1692,76],{},[18,1694,413],{},". For database layers, compare ",[18,1697,501],{}," host, database, schema and table — two layers can share a table with different subset filters, so include ",[18,1700,1701],{},"sql()"," in the comparison if that distinction matters to your script.",[197,1704,1706],{"id":1705},"related","Related",[202,1708,1709,1714,1719,1725,1731],{},[205,1710,1711,1713],{},[34,1712,37],{"href":36}," — the guide this recipe belongs to",[205,1715,1716],{},[34,1717,1718],{"href":679},"Fix Broken Layer Paths in a Project with PyQGIS",[205,1720,1721],{},[34,1722,1724],{"href":1723},"\u002Fspatial-data-processing-automation\u002Flayer-data-sources-and-formats\u002Flist-and-load-geopackage-sublayers-pyqgis\u002F","List and Load GeoPackage Sublayers in PyQGIS",[205,1726,1727],{},[34,1728,1730],{"href":1729},"\u002Fspatial-data-processing-automation\u002Fpostgis-and-database-workflows\u002Fconnect-to-postgis-database-pyqgis\u002F","Connect to a PostGIS Database in PyQGIS",[205,1732,1733],{},[34,1734,1736],{"href":1735},"\u002Fpyqgis-fundamentals-environment-setup\u002Fworking-with-qgis-projects\u002Fadd-and-remove-layers-from-project-pyqgis\u002F","Add and Remove Layers from a Project in PyQGIS",[1738,1739,1740],"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 .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":239,"searchDepth":262,"depth":262,"links":1742},[1743,1744,1745,1746,1747,1748,1749,1750,1751,1752,1753],{"id":199,"depth":262,"text":200},{"id":225,"depth":262,"text":226},{"id":684,"depth":262,"text":685},{"id":855,"depth":262,"text":856},{"id":1040,"depth":262,"text":1041},{"id":1303,"depth":262,"text":1304},{"id":1524,"depth":262,"text":1525},{"id":1555,"depth":262,"text":1556},{"id":1615,"depth":262,"text":1616},{"id":1625,"depth":262,"text":1626},{"id":1705,"depth":262,"text":1706},"Read a layer's source apart with QgsProviderRegistry.decodeUri, build new URIs with encodeUri and QgsDataSourceUri, and repoint a layer at different data with setDataSource — without string hacking.","md",{"slug":1757,"type":1758,"breadcrumb":1759,"datePublished":1760,"dateModified":1760},"decode-and-build-data-source-uris-pyqgis","article","Decode and Build Data Source URIs","2026-09-17","\u002Fspatial-data-processing-automation\u002Flayer-data-sources-and-formats\u002Fdecode-and-build-data-source-uris-pyqgis",{"title":5,"description":1754},"spatial-data-processing-automation\u002Flayer-data-sources-and-formats\u002Fdecode-and-build-data-source-uris-pyqgis\u002Findex","_D7s5QUtUasm5UmqA_wBpy1H2qmJSK5zzu8oNE_7IqY",1789632908428]