[{"data":1,"prerenderedAt":1809},["ShallowReactive",2],{"doc:\u002Fspatial-data-processing-automation\u002Fattribute-tables-and-field-management\u002Fexport-attribute-table-to-csv-pyqgis":3},{"id":4,"title":5,"body":6,"description":1798,"extension":1799,"meta":1800,"navigation":228,"path":1805,"seo":1806,"stem":1807,"__hash__":1808},"docs\u002Fspatial-data-processing-automation\u002Fattribute-tables-and-field-management\u002Fexport-attribute-table-to-csv-pyqgis\u002Findex.md","Export an Attribute Table to CSV with PyQGIS",{"type":7,"value":8,"toc":1785},"minimark",[9,13,22,31,135,140,161,165,171,401,423,427,430,601,618,622,633,706,730,787,797,801,804,1058,1077,1085,1100,1109,1113,1116,1234,1494,1517,1521,1527,1596,1602,1606,1677,1681,1691,1695,1706,1723,1736,1742,1752,1756,1781],[10,11,5],"h1",{"id":12},"export-an-attribute-table-to-csv-with-pyqgis",[14,15,16,17,21],"p",{},"Handing data to someone who does not use QGIS almost always means a CSV. It is tempting to write one by hand — open a file, join the values with commas — and that works right up until a field contains a comma, a quote, an accented character or a line break. ",[18,19,20],"code",{},"QgsVectorFileWriter"," uses the GDAL CSV driver, which handles all four correctly and additionally gives you column selection, encoding control and optional geometry in one call.",[14,23,24,25,30],{},"This page is a focused recipe within ",[26,27,29],"a",{"href":28},"\u002Fspatial-data-processing-automation\u002Fattribute-tables-and-field-management\u002F","Attribute Tables and Field Management in PyQGIS",". It covers a basic export, choosing columns, encoding and separators, including geometry as WKT, and exporting only the rows that matter.",[14,32,33],{},[34,35,40,44,48,55,72,81,91,97,102,108,112,116,120,124,128,132],"svg",{"viewBox":36,"role":37,"ariaLabel":38,"xmlns":39},"0 0 760 256","img","A hand-written CSV breaking on a value containing a comma, compared with the same row correctly quoted by the GDAL CSV writer","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg",[41,42,43],"title",{},"Why hand-built CSV goes wrong",[45,46,47],"desc",{},"A source row contains an owner name with an embedded comma and a note containing a double quote. Joining the values with commas by hand produces a line with too many fields, which shifts every later column. The GDAL writer quotes the offending values and doubles the internal quote, producing a line that parses back to exactly three fields.",[49,50],"rect",{"x":51,"y":51,"width":52,"height":53,"fill":54},"0","760","256","#f6f3ea",[56,57,58],"defs",{},[59,60,67],"marker",{"id":61,"viewBox":62,"refX":63,"refY":64,"markerWidth":65,"markerHeight":65,"orient":66},"csvFixArrow","0 0 10 10","8","5","7","auto-start-reverse",[68,69],"path",{"d":70,"fill":71},"M0 0 L10 5 L0 10 z","#2f3b35",[73,74,80],"text",{"x":75,"y":76,"style":77,"fill":78,"textAnchor":79},"380","26","text-anchor:middle;font-size:14px;font-weight:bold;font-family:sans-serif","#17211d","middle","One comma inside a value shifts every column after it",[49,82],{"x":83,"y":84,"width":85,"height":86,"rx":87,"fill":88,"stroke":89,"style":90},"16","44","728","56","10","#fffdf7","#59645f","stroke-width:2",[73,92,96],{"x":93,"y":94,"style":95,"fill":78},"36","66","font-size:11.5px;font-weight:bold;font-family:sans-serif","source row",[73,98,101],{"x":93,"y":99,"style":100,"fill":71},"88","font-size:11.5px;font-family:monospace","1041   |   Ash, Brant & Co   |   note: he said \"fine\"",[49,103],{"x":83,"y":104,"width":85,"height":105,"rx":87,"fill":88,"stroke":106,"style":107},"112","60","#b91c1c","stroke-width:2.5",[73,109,111],{"x":93,"y":110,"style":95,"fill":106},"134","joined by hand",[73,113,115],{"x":93,"y":114,"style":100,"fill":71},"158","1041,Ash, Brant & Co,note: he said \"fine\"",[73,117,119],{"x":118,"y":114,"style":95,"fill":106},"520","→ parses as 4 fields",[49,121],{"x":83,"y":122,"width":85,"height":105,"rx":87,"fill":88,"stroke":123,"style":107},"184","#15803d",[73,125,127],{"x":93,"y":126,"style":95,"fill":123},"206","GDAL CSV writer",[73,129,131],{"x":93,"y":130,"style":100,"fill":71},"230","1041,\"Ash, Brant & Co\",\"note: he said \"\"fine\"\"\"",[73,133,134],{"x":118,"y":130,"style":95,"fill":123},"→ parses as 3 fields",[136,137,139],"h2",{"id":138},"prerequisites","Prerequisites",[141,142,143,151,158],"ul",{},[144,145,146,150],"li",{},[147,148,149],"strong",{},"QGIS 3.34 LTR"," (bundled Python 3.12) or newer.",[144,152,153,154,157],{},"A loaded vector layer, or a path you can open with ",[18,155,156],{},"QgsVectorLayer",".",[144,159,160],{},"Write permission to the output directory.",[136,162,164],{"id":163},"the-basic-export","The basic export",[14,166,167,170],{},[18,168,169],{},"writeAsVectorFormatV3()"," is the current entry point and takes its settings through an options object.",[172,173,178],"pre",{"className":174,"code":175,"language":176,"meta":177,"style":177},"language-python shiki shiki-themes github-dark","from qgis.core import (\n    QgsCoordinateTransformContext,\n    QgsProject,\n    QgsVectorFileWriter,\n)\n\nlayer = QgsProject.instance().mapLayersByName(\"parcels\")[0]\n\noptions = QgsVectorFileWriter.SaveVectorOptions()\noptions.driverName = \"CSV\"\noptions.fileEncoding = \"UTF-8\"\n\nerror, message, _, _ = QgsVectorFileWriter.writeAsVectorFormatV3(\n    layer,\n    \"\u002Fdata\u002Foutput\u002Fparcels.csv\",\n    QgsCoordinateTransformContext(),\n    options,\n)\nif error != QgsVectorFileWriter.NoError:\n    raise RuntimeError(f\"export failed: {message}\")\nprint(\"written\")\n","python","",[18,179,180,199,205,211,217,223,230,255,260,271,282,293,298,309,315,324,330,336,341,356,388],{"__ignoreMap":177},[181,182,185,189,193,196],"span",{"class":183,"line":184},"line",1,[181,186,188],{"class":187},"snl16","from",[181,190,192],{"class":191},"s95oV"," qgis.core ",[181,194,195],{"class":187},"import",[181,197,198],{"class":191}," (\n",[181,200,202],{"class":183,"line":201},2,[181,203,204],{"class":191},"    QgsCoordinateTransformContext,\n",[181,206,208],{"class":183,"line":207},3,[181,209,210],{"class":191},"    QgsProject,\n",[181,212,214],{"class":183,"line":213},4,[181,215,216],{"class":191},"    QgsVectorFileWriter,\n",[181,218,220],{"class":183,"line":219},5,[181,221,222],{"class":191},")\n",[181,224,226],{"class":183,"line":225},6,[181,227,229],{"emptyLinePlaceholder":228},true,"\n",[181,231,233,236,239,242,246,249,252],{"class":183,"line":232},7,[181,234,235],{"class":191},"layer ",[181,237,238],{"class":187},"=",[181,240,241],{"class":191}," QgsProject.instance().mapLayersByName(",[181,243,245],{"class":244},"sU2Wk","\"parcels\"",[181,247,248],{"class":191},")[",[181,250,51],{"class":251},"sDLfK",[181,253,254],{"class":191},"]\n",[181,256,258],{"class":183,"line":257},8,[181,259,229],{"emptyLinePlaceholder":228},[181,261,263,266,268],{"class":183,"line":262},9,[181,264,265],{"class":191},"options ",[181,267,238],{"class":187},[181,269,270],{"class":191}," QgsVectorFileWriter.SaveVectorOptions()\n",[181,272,274,277,279],{"class":183,"line":273},10,[181,275,276],{"class":191},"options.driverName ",[181,278,238],{"class":187},[181,280,281],{"class":244}," \"CSV\"\n",[181,283,285,288,290],{"class":183,"line":284},11,[181,286,287],{"class":191},"options.fileEncoding ",[181,289,238],{"class":187},[181,291,292],{"class":244}," \"UTF-8\"\n",[181,294,296],{"class":183,"line":295},12,[181,297,229],{"emptyLinePlaceholder":228},[181,299,301,304,306],{"class":183,"line":300},13,[181,302,303],{"class":191},"error, message, _, _ ",[181,305,238],{"class":187},[181,307,308],{"class":191}," QgsVectorFileWriter.writeAsVectorFormatV3(\n",[181,310,312],{"class":183,"line":311},14,[181,313,314],{"class":191},"    layer,\n",[181,316,318,321],{"class":183,"line":317},15,[181,319,320],{"class":244},"    \"\u002Fdata\u002Foutput\u002Fparcels.csv\"",[181,322,323],{"class":191},",\n",[181,325,327],{"class":183,"line":326},16,[181,328,329],{"class":191},"    QgsCoordinateTransformContext(),\n",[181,331,333],{"class":183,"line":332},17,[181,334,335],{"class":191},"    options,\n",[181,337,339],{"class":183,"line":338},18,[181,340,222],{"class":191},[181,342,344,347,350,353],{"class":183,"line":343},19,[181,345,346],{"class":187},"if",[181,348,349],{"class":191}," error ",[181,351,352],{"class":187},"!=",[181,354,355],{"class":191}," QgsVectorFileWriter.NoError:\n",[181,357,359,362,365,368,371,374,377,380,383,386],{"class":183,"line":358},20,[181,360,361],{"class":187},"    raise",[181,363,364],{"class":251}," RuntimeError",[181,366,367],{"class":191},"(",[181,369,370],{"class":187},"f",[181,372,373],{"class":244},"\"export failed: ",[181,375,376],{"class":251},"{",[181,378,379],{"class":191},"message",[181,381,382],{"class":251},"}",[181,384,385],{"class":244},"\"",[181,387,222],{"class":191},[181,389,391,394,396,399],{"class":183,"line":390},21,[181,392,393],{"class":251},"print",[181,395,367],{"class":191},[181,397,398],{"class":244},"\"written\"",[181,400,222],{"class":191},[14,402,403,406,407,410,411,414,415,418,419,422],{},[147,404,405],{},"Breakdown:"," ",[18,408,409],{},"driverName = \"CSV\""," selects the GDAL driver; the file extension alone is not enough. ",[18,412,413],{},"fileEncoding = \"UTF-8\""," should be set explicitly rather than relying on the system default, which on Windows is often a legacy code page that mangles accented characters. The function returns a four-tuple and ",[147,416,417],{},"does not raise on failure"," — checking ",[18,420,421],{},"error != NoError"," is the only way to notice a permission problem or a locked file.",[136,424,426],{"id":425},"choose-which-columns-to-export","Choose which columns to export",[14,428,429],{},"An export of every column is rarely what the recipient wants, and it leaks internal working fields.",[172,431,433],{"className":174,"code":432,"language":176,"meta":177,"style":177},"WANTED = [\"parcel_id\", \"owner\", \"zoning\", \"assessed_value\"]\n\noptions = QgsVectorFileWriter.SaveVectorOptions()\noptions.driverName = \"CSV\"\noptions.fileEncoding = \"UTF-8\"\noptions.attributes = [layer.fields().indexOf(n) for n in WANTED]\n\nmissing = [n for n, i in zip(WANTED, options.attributes) if i == -1]\nif missing:\n    raise KeyError(f\"missing field(s): {', '.join(missing)}\")\n",[18,434,435,467,471,479,487,495,519,523,566,573],{"__ignoreMap":177},[181,436,437,440,443,446,449,452,455,457,460,462,465],{"class":183,"line":184},[181,438,439],{"class":251},"WANTED",[181,441,442],{"class":187}," =",[181,444,445],{"class":191}," [",[181,447,448],{"class":244},"\"parcel_id\"",[181,450,451],{"class":191},", ",[181,453,454],{"class":244},"\"owner\"",[181,456,451],{"class":191},[181,458,459],{"class":244},"\"zoning\"",[181,461,451],{"class":191},[181,463,464],{"class":244},"\"assessed_value\"",[181,466,254],{"class":191},[181,468,469],{"class":183,"line":201},[181,470,229],{"emptyLinePlaceholder":228},[181,472,473,475,477],{"class":183,"line":207},[181,474,265],{"class":191},[181,476,238],{"class":187},[181,478,270],{"class":191},[181,480,481,483,485],{"class":183,"line":213},[181,482,276],{"class":191},[181,484,238],{"class":187},[181,486,281],{"class":244},[181,488,489,491,493],{"class":183,"line":219},[181,490,287],{"class":191},[181,492,238],{"class":187},[181,494,292],{"class":244},[181,496,497,500,502,505,508,511,514,517],{"class":183,"line":225},[181,498,499],{"class":191},"options.attributes ",[181,501,238],{"class":187},[181,503,504],{"class":191}," [layer.fields().indexOf(n) ",[181,506,507],{"class":187},"for",[181,509,510],{"class":191}," n ",[181,512,513],{"class":187},"in",[181,515,516],{"class":251}," WANTED",[181,518,254],{"class":191},[181,520,521],{"class":183,"line":232},[181,522,229],{"emptyLinePlaceholder":228},[181,524,525,528,530,533,535,538,540,543,545,547,550,552,555,558,561,564],{"class":183,"line":257},[181,526,527],{"class":191},"missing ",[181,529,238],{"class":187},[181,531,532],{"class":191}," [n ",[181,534,507],{"class":187},[181,536,537],{"class":191}," n, i ",[181,539,513],{"class":187},[181,541,542],{"class":251}," zip",[181,544,367],{"class":191},[181,546,439],{"class":251},[181,548,549],{"class":191},", options.attributes) ",[181,551,346],{"class":187},[181,553,554],{"class":191}," i ",[181,556,557],{"class":187},"==",[181,559,560],{"class":187}," -",[181,562,563],{"class":251},"1",[181,565,254],{"class":191},[181,567,568,570],{"class":183,"line":262},[181,569,346],{"class":187},[181,571,572],{"class":191}," missing:\n",[181,574,575,577,580,582,584,587,589,592,595,597,599],{"class":183,"line":273},[181,576,361],{"class":187},[181,578,579],{"class":251}," KeyError",[181,581,367],{"class":191},[181,583,370],{"class":187},[181,585,586],{"class":244},"\"missing field(s): ",[181,588,376],{"class":251},[181,590,591],{"class":244},"', '",[181,593,594],{"class":191},".join(missing)",[181,596,382],{"class":251},[181,598,385],{"class":244},[181,600,222],{"class":191},[14,602,603,406,605,608,609,613,614,617],{},[147,604,405],{},[18,606,607],{},"attributes"," takes field ",[610,611,612],"em",{},"indexes",", in the order you want the columns to appear — so the list doubles as a column ordering. Checking for ",[18,615,616],{},"-1"," before writing turns a silently missing column into an explicit failure; without it the export succeeds with fewer columns than expected, which is easy to miss in a file nobody opens until next week.",[136,619,621],{"id":620},"encoding-separators-and-geometry","Encoding, separators and geometry",[14,623,624,625,628,629,632],{},"The CSV driver's behaviour is tuned through ",[18,626,627],{},"layerOptions",", a list of ",[18,630,631],{},"KEY=VALUE"," strings passed straight to GDAL.",[172,634,636],{"className":174,"code":635,"language":176,"meta":177,"style":177},"options = QgsVectorFileWriter.SaveVectorOptions()\noptions.driverName = \"CSV\"\noptions.fileEncoding = \"UTF-8\"\noptions.layerOptions = [\n    \"GEOMETRY=AS_WKT\",          # add a WKT geometry column\n    \"SEPARATOR=SEMICOLON\",      # for locales where comma is the decimal mark\n    \"STRING_QUOTING=IF_AMBIGUOUS\",\n]\n",[18,637,638,646,654,662,672,684,695,702],{"__ignoreMap":177},[181,639,640,642,644],{"class":183,"line":184},[181,641,265],{"class":191},[181,643,238],{"class":187},[181,645,270],{"class":191},[181,647,648,650,652],{"class":183,"line":201},[181,649,276],{"class":191},[181,651,238],{"class":187},[181,653,281],{"class":244},[181,655,656,658,660],{"class":183,"line":207},[181,657,287],{"class":191},[181,659,238],{"class":187},[181,661,292],{"class":244},[181,663,664,667,669],{"class":183,"line":213},[181,665,666],{"class":191},"options.layerOptions ",[181,668,238],{"class":187},[181,670,671],{"class":191}," [\n",[181,673,674,677,680],{"class":183,"line":219},[181,675,676],{"class":244},"    \"GEOMETRY=AS_WKT\"",[181,678,679],{"class":191},",          ",[181,681,683],{"class":682},"sjoCn","# add a WKT geometry column\n",[181,685,686,689,692],{"class":183,"line":225},[181,687,688],{"class":244},"    \"SEPARATOR=SEMICOLON\"",[181,690,691],{"class":191},",      ",[181,693,694],{"class":682},"# for locales where comma is the decimal mark\n",[181,696,697,700],{"class":183,"line":232},[181,698,699],{"class":244},"    \"STRING_QUOTING=IF_AMBIGUOUS\"",[181,701,323],{"class":191},[181,703,704],{"class":183,"line":257},[181,705,254],{"class":191},[14,707,708,406,710,713,714,717,718,721,722,725,726,729],{},[147,709,405],{},[18,711,712],{},"GEOMETRY=AS_WKT"," prepends a ",[18,715,716],{},"WKT"," column holding each feature's geometry as text — omit it entirely for a pure attribute table, and use ",[18,719,720],{},"GEOMETRY=AS_XY"," for a point layer where separate X and Y columns are friendlier. ",[18,723,724],{},"SEPARATOR=SEMICOLON"," matters in locales where the comma is the decimal separator and Excel expects semicolons. ",[18,727,728],{},"STRING_QUOTING=IF_AMBIGUOUS"," quotes only the values that need it, which keeps the file readable while staying correct.",[14,731,732],{},[34,733,736,739,742,745,748,750,753,757,762,765,767,771,774,777,780,784],{"viewBox":734,"role":37,"ariaLabel":735,"xmlns":39},"0 0 760 240","Three geometry options for a CSV export: no geometry column, a WKT column, and separate X and Y columns for point layers",[41,737,738],{},"Three ways to handle geometry in a CSV",[45,740,741],{},"Three sample outputs. With no geometry option the file contains only attribute columns. With GEOMETRY set to AS_WKT a leading WKT column holds the full geometry as text, which is verbose but lossless. With GEOMETRY set to AS_XY two numeric columns hold the coordinates, which suits point layers and spreadsheet users.",[49,743],{"x":51,"y":51,"width":52,"height":744,"fill":54},"240",[73,746,747],{"x":75,"y":76,"style":77,"fill":78,"textAnchor":79},"Pick the geometry form your recipient can actually use",[49,749],{"x":83,"y":84,"width":85,"height":86,"rx":87,"fill":88,"stroke":89,"style":90},[73,751,752],{"x":93,"y":94,"style":95,"fill":78},"no GEOMETRY option",[73,754,756],{"x":93,"y":99,"style":755,"fill":71},"font-size:11px;font-family:monospace","parcel_id,owner,assessed_value",[73,758,761],{"x":759,"y":99,"style":760,"fill":89},"536","font-size:11px;font-family:sans-serif","attributes only",[49,763],{"x":83,"y":104,"width":85,"height":86,"rx":87,"fill":88,"stroke":764,"style":107},"#2563eb",[73,766,712],{"x":93,"y":110,"style":95,"fill":764},[73,768,770],{"x":93,"y":769,"style":755,"fill":71},"156","WKT,parcel_id,owner,assessed_value",[73,772,773],{"x":759,"y":769,"style":760,"fill":764},"lossless, verbose",[49,775],{"x":83,"y":776,"width":85,"height":86,"rx":87,"fill":88,"stroke":123,"style":107},"180",[73,778,720],{"x":93,"y":779,"style":95,"fill":123},"202",[73,781,783],{"x":93,"y":782,"style":755,"fill":71},"224","X,Y,parcel_id,owner,assessed_value",[73,785,786],{"x":759,"y":782,"style":760,"fill":123},"points only, spreadsheet-friendly",[14,788,789,790,792,793,157],{},"If the recipient will reopen the file in QGIS, ",[18,791,712],{}," round-trips faithfully — but remember WKT carries no CRS, so send the EPSG code alongside it or the coordinates are ambiguous. See ",[26,794,796],{"href":795},"\u002Fspatial-data-processing-automation\u002Fcoordinate-reference-systems\u002F","Coordinate Reference Systems in PyQGIS",[136,798,800],{"id":799},"export-only-the-rows-you-need","Export only the rows you need",[14,802,803],{},"Writing the whole table and filtering afterwards wastes time and hands over data the recipient did not ask for. Filter at the source.",[172,805,807],{"className":174,"code":806,"language":176,"meta":177,"style":177},"from qgis.core import (\n    QgsCoordinateTransformContext,\n    QgsFeatureRequest,\n    QgsProject,\n    QgsVectorFileWriter,\n)\n\nlayer = QgsProject.instance().mapLayersByName(\"parcels\")[0]\n\noptions = QgsVectorFileWriter.SaveVectorOptions()\noptions.driverName = \"CSV\"\noptions.fileEncoding = \"UTF-8\"\noptions.attributes = [layer.fields().indexOf(n) for n in (\"parcel_id\", \"assessed_value\")]\n\nrequest = QgsFeatureRequest()\nrequest.setFilterExpression('\"zoning\" = \\'commercial\\' AND \"assessed_value\" > 500000')\nrequest.setFlags(QgsFeatureRequest.NoGeometry)\noptions.filterExtent = layer.extent()\noptions.symbologyExport = QgsVectorFileWriter.NoSymbology\n\nwriter_layer = layer.materialize(request)\n\nerror, message, _, _ = QgsVectorFileWriter.writeAsVectorFormatV3(\n    writer_layer, \"\u002Fdata\u002Foutput\u002Fcommercial.csv\",\n    QgsCoordinateTransformContext(), options,\n)\nif error != QgsVectorFileWriter.NoError:\n    raise RuntimeError(message)\nprint(writer_layer.featureCount(), \"row(s) exported\")\n",[18,808,809,819,823,828,832,836,840,844,860,864,872,880,888,914,918,928,949,954,964,974,978,988,993,1002,1013,1019,1024,1035,1045],{"__ignoreMap":177},[181,810,811,813,815,817],{"class":183,"line":184},[181,812,188],{"class":187},[181,814,192],{"class":191},[181,816,195],{"class":187},[181,818,198],{"class":191},[181,820,821],{"class":183,"line":201},[181,822,204],{"class":191},[181,824,825],{"class":183,"line":207},[181,826,827],{"class":191},"    QgsFeatureRequest,\n",[181,829,830],{"class":183,"line":213},[181,831,210],{"class":191},[181,833,834],{"class":183,"line":219},[181,835,216],{"class":191},[181,837,838],{"class":183,"line":225},[181,839,222],{"class":191},[181,841,842],{"class":183,"line":232},[181,843,229],{"emptyLinePlaceholder":228},[181,845,846,848,850,852,854,856,858],{"class":183,"line":257},[181,847,235],{"class":191},[181,849,238],{"class":187},[181,851,241],{"class":191},[181,853,245],{"class":244},[181,855,248],{"class":191},[181,857,51],{"class":251},[181,859,254],{"class":191},[181,861,862],{"class":183,"line":262},[181,863,229],{"emptyLinePlaceholder":228},[181,865,866,868,870],{"class":183,"line":273},[181,867,265],{"class":191},[181,869,238],{"class":187},[181,871,270],{"class":191},[181,873,874,876,878],{"class":183,"line":284},[181,875,276],{"class":191},[181,877,238],{"class":187},[181,879,281],{"class":244},[181,881,882,884,886],{"class":183,"line":295},[181,883,287],{"class":191},[181,885,238],{"class":187},[181,887,292],{"class":244},[181,889,890,892,894,896,898,900,902,905,907,909,911],{"class":183,"line":300},[181,891,499],{"class":191},[181,893,238],{"class":187},[181,895,504],{"class":191},[181,897,507],{"class":187},[181,899,510],{"class":191},[181,901,513],{"class":187},[181,903,904],{"class":191}," (",[181,906,448],{"class":244},[181,908,451],{"class":191},[181,910,464],{"class":244},[181,912,913],{"class":191},")]\n",[181,915,916],{"class":183,"line":311},[181,917,229],{"emptyLinePlaceholder":228},[181,919,920,923,925],{"class":183,"line":317},[181,921,922],{"class":191},"request ",[181,924,238],{"class":187},[181,926,927],{"class":191}," QgsFeatureRequest()\n",[181,929,930,933,936,939,942,944,947],{"class":183,"line":326},[181,931,932],{"class":191},"request.setFilterExpression(",[181,934,935],{"class":244},"'\"zoning\" = ",[181,937,938],{"class":251},"\\'",[181,940,941],{"class":244},"commercial",[181,943,938],{"class":251},[181,945,946],{"class":244}," AND \"assessed_value\" > 500000'",[181,948,222],{"class":191},[181,950,951],{"class":183,"line":332},[181,952,953],{"class":191},"request.setFlags(QgsFeatureRequest.NoGeometry)\n",[181,955,956,959,961],{"class":183,"line":338},[181,957,958],{"class":191},"options.filterExtent ",[181,960,238],{"class":187},[181,962,963],{"class":191}," layer.extent()\n",[181,965,966,969,971],{"class":183,"line":343},[181,967,968],{"class":191},"options.symbologyExport ",[181,970,238],{"class":187},[181,972,973],{"class":191}," QgsVectorFileWriter.NoSymbology\n",[181,975,976],{"class":183,"line":358},[181,977,229],{"emptyLinePlaceholder":228},[181,979,980,983,985],{"class":183,"line":390},[181,981,982],{"class":191},"writer_layer ",[181,984,238],{"class":187},[181,986,987],{"class":191}," layer.materialize(request)\n",[181,989,991],{"class":183,"line":990},22,[181,992,229],{"emptyLinePlaceholder":228},[181,994,996,998,1000],{"class":183,"line":995},23,[181,997,303],{"class":191},[181,999,238],{"class":187},[181,1001,308],{"class":191},[181,1003,1005,1008,1011],{"class":183,"line":1004},24,[181,1006,1007],{"class":191},"    writer_layer, ",[181,1009,1010],{"class":244},"\"\u002Fdata\u002Foutput\u002Fcommercial.csv\"",[181,1012,323],{"class":191},[181,1014,1016],{"class":183,"line":1015},25,[181,1017,1018],{"class":191},"    QgsCoordinateTransformContext(), options,\n",[181,1020,1022],{"class":183,"line":1021},26,[181,1023,222],{"class":191},[181,1025,1027,1029,1031,1033],{"class":183,"line":1026},27,[181,1028,346],{"class":187},[181,1030,349],{"class":191},[181,1032,352],{"class":187},[181,1034,355],{"class":191},[181,1036,1038,1040,1042],{"class":183,"line":1037},28,[181,1039,361],{"class":187},[181,1041,364],{"class":251},[181,1043,1044],{"class":191},"(message)\n",[181,1046,1048,1050,1053,1056],{"class":183,"line":1047},29,[181,1049,393],{"class":251},[181,1051,1052],{"class":191},"(writer_layer.featureCount(), ",[181,1054,1055],{"class":244},"\"row(s) exported\"",[181,1057,222],{"class":191},[14,1059,1060,406,1062,1065,1066,1068,1069,1072,1073,1076],{},[147,1061,405],{},[18,1063,1064],{},"layer.materialize(request)"," produces an in-memory layer containing only the matching features, which the writer then exports in full — this is the cleanest way to combine a filter with an export, because ",[18,1067,169],{}," has no filter parameter of its own. ",[18,1070,1071],{},"NoGeometry"," on the request avoids decoding geometry that the CSV will not contain anyway. ",[18,1074,1075],{},"NoSymbology"," suppresses style export, which is meaningless for CSV and merely slows things down.",[14,1078,1079,1080,1084],{},"The alternative — exporting only the current selection — is a one-line change, and pairs naturally with ",[26,1081,1083],{"href":1082},"\u002Fpyqgis-fundamentals-environment-setup\u002Fworking-with-qgis-expressions\u002Fselect-features-by-expression-pyqgis\u002F","Select Features by Expression in PyQGIS",":",[172,1086,1088],{"className":174,"code":1087,"language":176,"meta":177,"style":177},"options.onlySelectedFeatures = True\n",[18,1089,1090],{"__ignoreMap":177},[181,1091,1092,1095,1097],{"class":183,"line":184},[181,1093,1094],{"class":191},"options.onlySelectedFeatures ",[181,1096,238],{"class":187},[181,1098,1099],{"class":251}," True\n",[14,1101,1102,1104,1105,1108],{},[147,1103,405],{}," With this flag the writer exports only what is currently selected on the layer, which is exactly what a plugin's \"Export selection\" button should do. It is ignored when nothing is selected, producing a full export rather than an empty file — worth guarding with a ",[18,1106,1107],{},"selectedFeatureCount()"," check if an empty selection should be an error.",[136,1110,1112],{"id":1111},"make-the-file-reopenable","Make the file reopenable",[14,1114,1115],{},"A CSV that QGIS or a colleague can load back without guesswork needs two companion files. Writing them alongside the export takes a few lines and removes an entire category of support questions.",[14,1117,1118,1212,1215,1218,1221,1224],{},[34,1119,1122,1125,1128,1131,1138,1141,1146,1152,1168,1172,1175,1179,1185,1188,1193,1197,1202,1206],{"viewBox":1120,"role":37,"ariaLabel":1121,"xmlns":39},"0 0 760 238","A CSV export accompanied by a csvt type-definition file and a prj projection file, together making the data reopenable without manual configuration",[41,1123,1124],{},"The three files that make a CSV self-describing",[45,1126,1127],{},"An export produces parcels.csv holding the data. Beside it, parcels.csvt declares each column's type so numbers do not load as text, and parcels.prj records the coordinate reference system so any WKT geometry column is unambiguous. Without the companions the CSV loads with every column as text and no CRS.",[49,1129],{"x":51,"y":51,"width":52,"height":1130,"fill":54},"238",[56,1132,1133],{},[59,1134,1136],{"id":1135,"viewBox":62,"refX":63,"refY":64,"markerWidth":65,"markerHeight":65,"orient":66},"csvCompArrow",[68,1137],{"d":70,"fill":123},[73,1139,1140],{"x":75,"y":76,"style":77,"fill":78,"textAnchor":79},"Three files, and the two small ones do the explaining",[49,1142],{"x":83,"y":1143,"width":1144,"height":1145,"rx":87,"fill":88,"stroke":764,"style":107},"52","216","150",[73,1147,1151],{"x":1148,"y":1149,"style":1150,"fill":764,"textAnchor":79},"124","78","text-anchor:middle;font-size:12px;font-weight:bold;font-family:sans-serif","parcels.csv",[1153,1154,1156,1161,1165],"g",{"style":1155},"font-size:10.5px;font-family:monospace",[73,1157,1160],{"x":1158,"y":1159,"fill":71},"34","106","parcel_id,owner,value",[73,1162,1164],{"x":1158,"y":1163,"fill":71},"128","1041,\"Ash & Co\",420000",[73,1166,1167],{"x":1158,"y":1145,"fill":71},"1042,\"Brant\",380000",[73,1169,1171],{"x":1148,"y":122,"style":1170,"fill":89,"textAnchor":79},"text-anchor:middle;font-size:11px;font-family:sans-serif","the data",[49,1173],{"x":1174,"y":1143,"width":1144,"height":1145,"rx":87,"fill":88,"stroke":123,"style":107},"280",[73,1176,1178],{"x":1177,"y":1149,"style":1150,"fill":123,"textAnchor":79},"388","parcels.csvt",[1153,1180,1181],{"style":1155},[73,1182,1184],{"x":1183,"y":104,"fill":71},"298","\"Integer\",\"String\",\"Real\"",[73,1186,1187],{"x":1177,"y":1145,"style":1170,"fill":71,"textAnchor":79},"without it every column",[73,1189,1192],{"x":1177,"y":1190,"style":1170,"fill":1191,"textAnchor":79},"168","#b45309","loads as text",[73,1194,1196],{"x":1177,"y":1195,"style":1170,"fill":89,"textAnchor":79},"190","the types",[49,1198],{"x":1199,"y":1143,"width":1200,"height":1145,"rx":87,"fill":88,"stroke":1201,"style":107},"544","200","#0f766e",[73,1203,1205],{"x":1204,"y":1149,"style":1150,"fill":1201,"textAnchor":79},"644","parcels.prj",[1153,1207,1208],{"style":1155},[73,1209,1211],{"x":1210,"y":104,"fill":71},"560","PROJCS",[181,1213,1214],{},"\"ETRS89 …\"",[73,1216,1217],{"x":1204,"y":1145,"style":1170,"fill":71,"text-anchor":79},"without it a WKT column",[73,1219,1220],{"x":1204,"y":1190,"style":1170,"fill":1191,"text-anchor":79},"has no CRS",[73,1222,1223],{"x":1204,"y":1195,"style":1170,"fill":89,"text-anchor":79},"the projection",[183,1225,1230],{"x1":1226,"y1":1227,"x2":1228,"y2":1227,"stroke":123,"style":1229},"232","127","276","stroke-width:2;marker-end:url(#csvCompArrow)",[183,1231],{"x1":1232,"y1":1227,"x2":1233,"y2":1227,"stroke":123,"style":1229},"496","540",[172,1235,1237],{"className":174,"code":1236,"language":176,"meta":177,"style":177},"from pathlib import Path\nfrom qgis.PyQt.QtCore import QVariant\n\nCSVT = {\n    QVariant.Int: \"Integer\",\n    QVariant.LongLong: \"Integer64\",\n    QVariant.Double: \"Real\",\n    QVariant.Date: \"Date\",\n    QVariant.DateTime: \"DateTime\",\n}\n\nout = Path(\"\u002Fdata\u002Foutput\u002Fparcels.csv\")\nexported = [layer.fields()[i] for i in options.attributes]\n\ntypes = ['\"WKT\"'] if \"GEOMETRY=AS_WKT\" in options.layerOptions else []\ntypes += [f'\"{CSVT.get(f.type(), \"String\")}\"' for f in exported]\nout.with_suffix(\".csvt\").write_text(\",\".join(types), encoding=\"utf-8\")\nout.with_suffix(\".prj\").write_text(layer.crs().toWkt(), encoding=\"utf-8\")\n",[18,1238,1239,1251,1263,1267,1277,1287,1297,1307,1317,1327,1332,1336,1351,1370,1374,1406,1448,1476],{"__ignoreMap":177},[181,1240,1241,1243,1246,1248],{"class":183,"line":184},[181,1242,188],{"class":187},[181,1244,1245],{"class":191}," pathlib ",[181,1247,195],{"class":187},[181,1249,1250],{"class":191}," Path\n",[181,1252,1253,1255,1258,1260],{"class":183,"line":201},[181,1254,188],{"class":187},[181,1256,1257],{"class":191}," qgis.PyQt.QtCore ",[181,1259,195],{"class":187},[181,1261,1262],{"class":191}," QVariant\n",[181,1264,1265],{"class":183,"line":207},[181,1266,229],{"emptyLinePlaceholder":228},[181,1268,1269,1272,1274],{"class":183,"line":213},[181,1270,1271],{"class":251},"CSVT",[181,1273,442],{"class":187},[181,1275,1276],{"class":191}," {\n",[181,1278,1279,1282,1285],{"class":183,"line":219},[181,1280,1281],{"class":191},"    QVariant.Int: ",[181,1283,1284],{"class":244},"\"Integer\"",[181,1286,323],{"class":191},[181,1288,1289,1292,1295],{"class":183,"line":225},[181,1290,1291],{"class":191},"    QVariant.LongLong: ",[181,1293,1294],{"class":244},"\"Integer64\"",[181,1296,323],{"class":191},[181,1298,1299,1302,1305],{"class":183,"line":232},[181,1300,1301],{"class":191},"    QVariant.Double: ",[181,1303,1304],{"class":244},"\"Real\"",[181,1306,323],{"class":191},[181,1308,1309,1312,1315],{"class":183,"line":257},[181,1310,1311],{"class":191},"    QVariant.Date: ",[181,1313,1314],{"class":244},"\"Date\"",[181,1316,323],{"class":191},[181,1318,1319,1322,1325],{"class":183,"line":262},[181,1320,1321],{"class":191},"    QVariant.DateTime: ",[181,1323,1324],{"class":244},"\"DateTime\"",[181,1326,323],{"class":191},[181,1328,1329],{"class":183,"line":273},[181,1330,1331],{"class":191},"}\n",[181,1333,1334],{"class":183,"line":284},[181,1335,229],{"emptyLinePlaceholder":228},[181,1337,1338,1341,1343,1346,1349],{"class":183,"line":295},[181,1339,1340],{"class":191},"out ",[181,1342,238],{"class":187},[181,1344,1345],{"class":191}," Path(",[181,1347,1348],{"class":244},"\"\u002Fdata\u002Foutput\u002Fparcels.csv\"",[181,1350,222],{"class":191},[181,1352,1353,1356,1358,1361,1363,1365,1367],{"class":183,"line":300},[181,1354,1355],{"class":191},"exported ",[181,1357,238],{"class":187},[181,1359,1360],{"class":191}," [layer.fields()[i] ",[181,1362,507],{"class":187},[181,1364,554],{"class":191},[181,1366,513],{"class":187},[181,1368,1369],{"class":191}," options.attributes]\n",[181,1371,1372],{"class":183,"line":311},[181,1373,229],{"emptyLinePlaceholder":228},[181,1375,1376,1379,1381,1383,1386,1389,1391,1394,1397,1400,1403],{"class":183,"line":317},[181,1377,1378],{"class":191},"types ",[181,1380,238],{"class":187},[181,1382,445],{"class":191},[181,1384,1385],{"class":244},"'\"WKT\"'",[181,1387,1388],{"class":191},"] ",[181,1390,346],{"class":187},[181,1392,1393],{"class":244}," \"GEOMETRY=AS_WKT\"",[181,1395,1396],{"class":187}," in",[181,1398,1399],{"class":191}," options.layerOptions ",[181,1401,1402],{"class":187},"else",[181,1404,1405],{"class":191}," []\n",[181,1407,1408,1410,1413,1415,1417,1420,1423,1426,1429,1432,1434,1437,1440,1443,1445],{"class":183,"line":326},[181,1409,1378],{"class":191},[181,1411,1412],{"class":187},"+=",[181,1414,445],{"class":191},[181,1416,370],{"class":187},[181,1418,1419],{"class":244},"'\"",[181,1421,1422],{"class":251},"{CSVT",[181,1424,1425],{"class":191},".get(f.type(), ",[181,1427,1428],{"class":244},"\"String\"",[181,1430,1431],{"class":191},")",[181,1433,382],{"class":251},[181,1435,1436],{"class":244},"\"'",[181,1438,1439],{"class":187}," for",[181,1441,1442],{"class":191}," f ",[181,1444,513],{"class":187},[181,1446,1447],{"class":191}," exported]\n",[181,1449,1450,1453,1456,1459,1462,1465,1469,1471,1474],{"class":183,"line":332},[181,1451,1452],{"class":191},"out.with_suffix(",[181,1454,1455],{"class":244},"\".csvt\"",[181,1457,1458],{"class":191},").write_text(",[181,1460,1461],{"class":244},"\",\"",[181,1463,1464],{"class":191},".join(types), ",[181,1466,1468],{"class":1467},"s9osk","encoding",[181,1470,238],{"class":187},[181,1472,1473],{"class":244},"\"utf-8\"",[181,1475,222],{"class":191},[181,1477,1478,1480,1483,1486,1488,1490,1492],{"class":183,"line":338},[181,1479,1452],{"class":191},[181,1481,1482],{"class":244},"\".prj\"",[181,1484,1485],{"class":191},").write_text(layer.crs().toWkt(), ",[181,1487,1468],{"class":1467},[181,1489,238],{"class":187},[181,1491,1473],{"class":244},[181,1493,222],{"class":191},[14,1495,1496,1498,1499,1502,1503,1506,1507,1509,1510,1513,1514,1516],{},[147,1497,405],{}," A ",[18,1500,1501],{},".csvt"," file is a single line of quoted type names, positionally matched to the CSV's columns — get the order wrong and every column is mistyped, which is why deriving it from the same ",[18,1504,1505],{},"options.attributes"," list used for the export is important. The ",[18,1508,716],{}," entry has to lead when a geometry column was requested, because the driver writes it first. The ",[18,1511,1512],{},".prj"," file carries the CRS as WKT, which is what makes a ",[18,1515,712],{}," column unambiguous. Both are read automatically by QGIS, GDAL and most other tools when the CSV is opened.",[136,1518,1520],{"id":1519},"qgis-version-compatibility","QGIS version compatibility",[14,1522,1523,1524,1526],{},"The examples target ",[147,1525,149],{}," (Python 3.12).",[1528,1529,1530,1546],"table",{},[1531,1532,1533],"thead",{},[1534,1535,1536,1540,1543],"tr",{},[1537,1538,1539],"th",{},"QGIS version",[1537,1541,1542],{},"Python",[1537,1544,1545],{},"Notes",[1547,1548,1549,1568,1579],"tbody",{},[1534,1550,1551,1555,1558],{},[1552,1553,1554],"td",{},"3.28 LTR",[1552,1556,1557],{},"3.9",[1552,1559,1560,1563,1564,1567],{},[18,1561,1562],{},"writeAsVectorFormatV3"," available; the older ",[18,1565,1566],{},"V2"," form is deprecated but still present.",[1534,1569,1570,1573,1576],{},[1552,1571,1572],{},"3.34 LTR",[1552,1574,1575],{},"3.12",[1552,1577,1578],{},"Baseline for this page.",[1534,1580,1581,1584,1586],{},[1552,1582,1583],{},"3.40 \u002F 3.44",[1552,1585,1575],{},[1552,1587,1588,1591,1592,1595],{},[18,1589,1590],{},"SaveVectorOptions"," gained ",[18,1593,1594],{},"layerMetadata","; CSV driver options unchanged.",[14,1597,1598,1599,1601],{},"The ",[18,1600,1590],{}," fields and GDAL CSV layer options have been stable across 3.x.",[136,1603,1605],{"id":1604},"troubleshooting","Troubleshooting",[141,1607,1608,1617,1630,1641,1647,1665],{},[144,1609,1610,1613,1614,1616],{},[147,1611,1612],{},"The function returned no error but the file is missing."," The return value was not checked. ",[18,1615,169],{}," returns an error code rather than raising.",[144,1618,1619,406,1622,1625,1626,1629],{},[147,1620,1621],{},"Accented characters are mangled.",[18,1623,1624],{},"fileEncoding"," was left at the default. Set it to ",[18,1627,1628],{},"\"UTF-8\""," explicitly, and tell the recipient — Excel needs a byte-order mark or an import step to read UTF-8 CSV correctly.",[144,1631,1632,1635,1636,1638,1639,157],{},[147,1633,1634],{},"Excel puts everything in one column."," The locale expects semicolons. Add ",[18,1637,724],{}," to ",[18,1640,627],{},[144,1642,1643,1646],{},[147,1644,1645],{},"Numbers appear as text in the spreadsheet."," The field is a string in the source layer, so the CSV is faithful. Fix the type at the layer level rather than in the export.",[144,1648,1649,1652,1653,1656,1657,1660,1661,1664],{},[147,1650,1651],{},"The geometry column is missing."," No ",[18,1654,1655],{},"GEOMETRY="," layer option was set. Add ",[18,1658,1659],{},"AS_WKT",", or ",[18,1662,1663],{},"AS_XY"," for points.",[144,1666,1667,1670,1671,1673,1674,1676],{},[147,1668,1669],{},"Fewer columns than expected."," One of the names in the ",[18,1672,607],{}," list resolved to ",[18,1675,616],{},". Validate before writing.",[136,1678,1680],{"id":1679},"conclusion","Conclusion",[14,1682,1683,1684,1686,1687,1690],{},"Let ",[18,1685,20],{}," handle the quoting, escaping and encoding — the failure modes it prevents are exactly the ones that survive testing and break on real data. Set the encoding explicitly, choose the columns deliberately, check the returned error code, and use ",[18,1688,1689],{},"materialize()"," when only a subset should leave the building.",[136,1692,1694],{"id":1693},"frequently-asked-questions","Frequently Asked Questions",[14,1696,1697,1700,1702,1703,157],{},[147,1698,1699],{},"Why is my exported CSV empty or missing?",[18,1701,169],{}," returns an error code instead of raising, so a permissions problem or a locked file passes silently. Always compare the first element of the returned tuple against ",[18,1704,1705],{},"QgsVectorFileWriter.NoError",[14,1707,1708,1711,1712,1638,1715,1718,1719,1722],{},[147,1709,1710],{},"How do I include the geometry in the CSV?","\nAdd ",[18,1713,1714],{},"\"GEOMETRY=AS_WKT\"",[18,1716,1717],{},"options.layerOptions"," for a WKT column, or ",[18,1720,1721],{},"\"GEOMETRY=AS_XY\""," for separate coordinate columns on a point layer. WKT carries no CRS, so send the EPSG code with the file.",[14,1724,1725,1728,1729,1732,1733,1735],{},[147,1726,1727],{},"Can I export only the selected features?","\nYes — set ",[18,1730,1731],{},"options.onlySelectedFeatures = True",". Note that with nothing selected it exports everything rather than producing an empty file, so guard it with a ",[18,1734,1107],{}," check.",[14,1737,1738,1741],{},[147,1739,1740],{},"Why does Excel show my accented characters incorrectly?","\nThe file is UTF-8 but Excel is reading it as a legacy code page. Import the CSV through Excel's text-import wizard specifying UTF-8, or export with an encoding your recipient's tooling expects.",[14,1743,1744,1747,1748,1751],{},[147,1745,1746],{},"Can I append to an existing CSV rather than replacing it?","\nSet ",[18,1749,1750],{},"options.actionOnExistingFile"," to the append value. It is worth confirming the field order matches, because the writer appends positionally and will happily produce a file whose later rows are misaligned with its header.",[136,1753,1755],{"id":1754},"related","Related",[141,1757,1758,1763,1769,1775],{},[144,1759,1760,1762],{},[26,1761,29],{"href":28}," — the guide this recipe belongs to",[144,1764,1765],{},[26,1766,1768],{"href":1767},"\u002Fspatial-data-processing-automation\u002Fattribute-tables-and-field-management\u002Fcount-features-by-attribute-pyqgis\u002F","Count and Summarise Features by Attribute in PyQGIS",[144,1770,1771],{},[26,1772,1774],{"href":1773},"\u002Fspatial-data-processing-automation\u002Fattribute-tables-and-field-management\u002Fjoin-attributes-by-field-value-pyqgis\u002F","Join Attributes by Field Value in PyQGIS",[144,1776,1777],{},[26,1778,1780],{"href":1779},"\u002Fspatial-data-processing-automation\u002Fvector-data-manipulation\u002Fautomating-shapefile-to-geojson-conversion\u002F","Automating Shapefile to GeoJSON Conversion",[1782,1783,1784],"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 .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 .sjoCn, html code.shiki .sjoCn{--shiki-default:#9AA79F}html pre.shiki code .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}",{"title":177,"searchDepth":201,"depth":201,"links":1786},[1787,1788,1789,1790,1791,1792,1793,1794,1795,1796,1797],{"id":138,"depth":201,"text":139},{"id":163,"depth":201,"text":164},{"id":425,"depth":201,"text":426},{"id":620,"depth":201,"text":621},{"id":799,"depth":201,"text":800},{"id":1111,"depth":201,"text":1112},{"id":1519,"depth":201,"text":1520},{"id":1604,"depth":201,"text":1605},{"id":1679,"depth":201,"text":1680},{"id":1693,"depth":201,"text":1694},{"id":1754,"depth":201,"text":1755},"Export a QGIS attribute table to CSV in PyQGIS with QgsVectorFileWriter. Pick columns, control encoding and separators, include geometry as WKT, and export a filtered subset.","md",{"slug":1801,"type":1802,"breadcrumb":1803,"datePublished":1804,"dateModified":1804},"export-attribute-table-to-csv-pyqgis","article","Export to CSV","2026-08-01","\u002Fspatial-data-processing-automation\u002Fattribute-tables-and-field-management\u002Fexport-attribute-table-to-csv-pyqgis",{"title":5,"description":1798},"spatial-data-processing-automation\u002Fattribute-tables-and-field-management\u002Fexport-attribute-table-to-csv-pyqgis\u002Findex","RBdzgAWmVN_XLzDf15auE8im3GItK9JzQDPlIl434oE",1785585990475]