[{"data":1,"prerenderedAt":2022},["ShallowReactive",2],{"doc:\u002Fspatial-data-processing-automation\u002Fvector-data-manipulation\u002Fautomating-shapefile-to-geojson-conversion":3},{"id":4,"title":5,"body":6,"description":2010,"extension":2011,"meta":2012,"navigation":372,"path":2018,"seo":2019,"stem":2020,"__hash__":2021},"docs\u002Fspatial-data-processing-automation\u002Fvector-data-manipulation\u002Fautomating-shapefile-to-geojson-conversion\u002Findex.md","Automating Shapefile to GeoJSON Conversion in QGIS",{"type":7,"value":8,"toc":1996},"minimark",[9,13,28,35,222,227,269,283,287,303,694,736,740,752,969,987,991,1002,1324,1352,1356,1359,1462,1468,1472,1475,1547,1555,1559,1562,1634,1638,1645,1729,1736,1740,1862,1866,1889,1893,1909,1926,1938,1954,1964,1968,1992],[10,11,5],"h1",{"id":12},"automating-shapefile-to-geojson-conversion-in-qgis",[14,15,16,17,21,22,27],"p",{},"Web maps, JavaScript libraries, and REST APIs almost all speak GeoJSON, while most desktop and agency data still ships as ESRI shapefiles. Converting one to the other by hand in the Save Layer As dialog is fine for a single file, but it does not scale to the folders of shapefiles that real delivery pipelines produce. PyQGIS closes that gap: the ",[18,19,20],"code",{},"QgsVectorFileWriter"," API writes GeoJSON headlessly from the Python Console, a standalone script, or a plugin, so the same conversion runs identically on your laptop and on a server. This is a routine ",[23,24,26],"a",{"href":25},"\u002Fspatial-data-processing-automation\u002Fvector-data-manipulation\u002F","Vector Data Manipulation"," task and it pairs naturally with format work once other layers have been merged or clipped.",[14,29,30,31,34],{},"This page shows how to convert a single shapefile with ",[18,32,33],{},"writeAsVectorFormatV3()",", reproject to WGS84 correctly on the way out (GeoJSON's RFC 7946 mandates it), and then loop over an entire directory. It also covers the encoding, geometry-validity, and environment traps that cause silent failures, plus reliable fallbacks when the QGIS Python environment is unavailable.",[36,37,42,46,50,57,66,76,81,85,91,95,99,105,109,113,117,120,123,126,129,133,137,140,143,146,151,156,159,162,169,173,177,181,186,188,194,198,202,206],"svg",{"viewBox":38,"role":39,"ariaLabel":40,"xmlns":41},"0 0 760 300","img","Data-flow pipeline converting a shapefile to GeoJSON in PyQGIS, from source sidecar files through load, reproject, and write to a WGS84 GeoJSON output","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg",[43,44,45],"title",{},"Shapefile to GeoJSON conversion pipeline in PyQGIS",[47,48,49],"desc",{},"A source shapefile with its sidecar files is loaded as a QgsVectorLayer through the ogr provider, reprojected to EPSG:4326 with a QgsCoordinateTransform, and written as RFC7946 UTF-8 GeoJSON by QgsVectorFileWriter, producing a web-ready WGS84 output. A guardrail branch validates geometry before writing and spot-checks encoding and coordinates after.",[51,52],"rect",{"x":53,"y":53,"width":54,"height":55,"fill":56},"0","760","300","#f6f3ea",[58,59,65],"text",{"x":60,"y":61,"style":62,"fill":63,"textAnchor":64},"380","30","text-anchor:middle;font-size:13px;font-weight:bold;font-family:sans-serif","#17211d","middle","Shapefile to GeoJSON: load, reproject, write",[51,67],{"x":68,"y":69,"width":70,"height":71,"rx":72,"fill":73,"stroke":74,"style":75},"14","68","128","118","10","#fffdf7","#6b7280","stroke-width:2.5",[58,77,80],{"x":78,"y":79,"style":62,"fill":63,"textAnchor":64},"78","94","Source",[58,82,84],{"x":78,"y":83,"style":62,"fill":63,"textAnchor":64},"111","shapefile",[58,86,90],{"x":78,"y":87,"style":88,"fill":89,"textAnchor":64},"138","text-anchor:middle;font-size:12px;font-family:sans-serif","#2f3b35",".shp .shx .dbf",[58,92,94],{"x":78,"y":93,"style":88,"fill":89,"textAnchor":64},"158",".prj .cpg",[51,96],{"x":97,"y":69,"width":70,"height":71,"rx":72,"fill":73,"stroke":98,"style":75},"165","#2563eb",[58,100,104],{"x":101,"y":102,"style":103,"fill":63,"textAnchor":64},"229","100","text-anchor:middle;font-size:12.5px;font-weight:bold;font-family:sans-serif","QgsVectorLayer",[58,106,108],{"x":101,"y":107,"style":88,"fill":89,"textAnchor":64},"130","provider: ogr",[58,110,112],{"x":101,"y":111,"style":88,"fill":89,"textAnchor":64},"150","isValid()",[51,114],{"x":115,"y":69,"width":70,"height":71,"rx":72,"fill":73,"stroke":116,"style":75},"316","#b45309",[58,118,119],{"x":60,"y":79,"style":103,"fill":63,"textAnchor":64},"QgsCoordinate",[58,121,122],{"x":60,"y":83,"style":103,"fill":63,"textAnchor":64},"Transform",[58,124,125],{"x":60,"y":87,"style":88,"fill":89,"textAnchor":64},"reproject",[58,127,128],{"x":60,"y":93,"style":88,"fill":89,"textAnchor":64},"to EPSG:4326",[51,130],{"x":131,"y":69,"width":70,"height":71,"rx":72,"fill":73,"stroke":132,"style":75},"467","#0f766e",[58,134,136],{"x":135,"y":79,"style":103,"fill":63,"textAnchor":64},"531","QgsVectorFile",[58,138,139],{"x":135,"y":83,"style":103,"fill":63,"textAnchor":64},"Writer",[58,141,142],{"x":135,"y":87,"style":88,"fill":89,"textAnchor":64},"V3 · RFC7946",[58,144,145],{"x":135,"y":93,"style":88,"fill":89,"textAnchor":64},"UTF-8 encoding",[51,147],{"x":148,"y":69,"width":70,"height":71,"rx":72,"fill":149,"stroke":150,"style":75},"618","#26322d","#15803d",[58,152,155],{"x":153,"y":102,"style":62,"fill":154,"textAnchor":64},"682","#d9f99d","GeoJSON",[58,157,158],{"x":153,"y":107,"style":88,"fill":154,"textAnchor":64},".geojson output",[58,160,161],{"x":153,"y":111,"style":88,"fill":154,"textAnchor":64},"WGS84, web-ready",[163,164],"line",{"x1":165,"y1":166,"x2":167,"y2":166,"stroke":89,"style":168},"144","127","163","stroke-width:2.5;marker-end:url(#shp2geo)",[163,170],{"x1":171,"y1":166,"x2":172,"y2":166,"stroke":89,"style":168},"295","314",[163,174],{"x1":175,"y1":166,"x2":176,"y2":166,"stroke":89,"style":168},"446","465",[163,178],{"x1":179,"y1":166,"x2":180,"y2":166,"stroke":89,"style":168},"597","616",[163,182],{"x1":101,"y1":183,"x2":101,"y2":184,"stroke":74,"style":185},"186","212","stroke-width:2;stroke-dasharray:4 3;marker-end:url(#shp2geo)",[163,187],{"x1":135,"y1":183,"x2":135,"y2":184,"stroke":74,"style":185},[51,189],{"x":97,"y":190,"width":191,"height":192,"rx":72,"fill":73,"stroke":74,"style":193},"214","430","66","stroke-width:2;stroke-dasharray:5 3",[58,195,197],{"x":60,"y":196,"style":103,"fill":63,"textAnchor":64},"238","Guardrails around the write",[58,199,201],{"x":60,"y":200,"style":88,"fill":89,"textAnchor":64},"258","Before: isGeosValid() · fixgeometries repair",[58,203,205],{"x":60,"y":204,"style":88,"fill":89,"textAnchor":64},"274","After: matching .cpg codepage · spot-check output coords",[207,208,209],"defs",{},[210,211,218],"marker",{"id":212,"markerWidth":213,"markerHeight":213,"refX":214,"refY":215,"orient":216,"markerUnits":217},"shp2geo","9","7","3","auto","strokeWidth",[219,220],"path",{"d":221,"fill":89},"M0,0 L7,3 L0,6 Z",[223,224,226],"h2",{"id":225},"prerequisites","Prerequisites",[228,229,230,234,259,262],"ul",{},[231,232,233],"li",{},"QGIS 3.34 LTR (bundled Python 3.12) with GDAL\u002FOGR available — GDAL ships with every standard QGIS install and provides the GeoJSON driver.",[231,235,236,237,240,241,240,244,247,248,251,252,255,256,258],{},"A source shapefile with its sidecar files present (",[18,238,239],{},".shp",", ",[18,242,243],{},".shx",[18,245,246],{},".dbf",", and ideally ",[18,249,250],{},".prj"," and ",[18,253,254],{},".cpg","). A missing ",[18,257,250],{}," means QGIS cannot know the source CRS.",[231,260,261],{},"Read access to the source and write access to the output folder.",[231,263,264,265,268],{},"The QGIS Python Console (",[18,266,267],{},"Plugins > Python Console",") for interactive runs, or a configured standalone environment for headless use.",[14,270,271,272,251,275,278,279,282],{},"If you run outside QGIS, the interpreter must be QGIS's bundled Python with ",[18,273,274],{},"QGIS_PREFIX_PATH",[18,276,277],{},"PYTHONPATH"," set and ",[18,280,281],{},"QgsApplication.initQgis()"," called first; otherwise the vector drivers will not load.",[223,284,286],{"id":285},"convert-a-single-shapefile","Convert a Single Shapefile",[14,288,289,290,292,293,295,296,299,300,302],{},"The core recipe loads the ",[18,291,239],{}," as a ",[18,294,104],{},", configures a ",[18,297,298],{},"SaveVectorOptions"," object for the GeoJSON driver, and writes it with ",[18,301,33],{},". On QGIS 3.16+ this is the current, non-deprecated writer:",[304,305,310],"pre",{"className":306,"code":307,"language":308,"meta":309,"style":309},"language-python shiki shiki-themes github-dark","from pathlib import Path\nfrom qgis.core import (\n    QgsVectorLayer,\n    QgsVectorFileWriter,\n    QgsProject,\n)\n\n\ndef convert_shp_to_geojson(input_path: str, output_path: str) -> None:\n    layer = QgsVectorLayer(input_path, Path(input_path).stem, \"ogr\")\n    if not layer.isValid():\n        raise RuntimeError(f\"Failed to load shapefile: {input_path}\")\n\n    opts = QgsVectorFileWriter.SaveVectorOptions()\n    opts.driverName = \"GeoJSON\"\n    opts.fileEncoding = \"UTF-8\"\n    # RFC7446-compliant output: right-hand-rule winding + trimmed coordinates\n    opts.layerOptions = [\"RFC7946=YES\"]\n\n    err_code, err_msg, _, _ = QgsVectorFileWriter.writeAsVectorFormatV3(\n        layer,\n        output_path,\n        QgsProject.instance().transformContext(),\n        opts,\n    )\n    if err_code != QgsVectorFileWriter.NoError:\n        raise RuntimeError(f\"Export error ({err_code}): {err_msg}\")\n\n    print(f\"Success: {Path(input_path).name} -> {Path(output_path).name}\")\n\n\nconvert_shp_to_geojson(\"\u002Fdata\u002Fparcels.shp\", \"\u002Fdata\u002Fout\u002Fparcels.geojson\")\n","python","",[18,311,312,330,343,349,355,361,367,374,379,410,428,440,472,477,488,499,510,517,534,539,550,556,562,568,574,580,594,629,634,668,673,678],{"__ignoreMap":309},[313,314,316,320,324,327],"span",{"class":163,"line":315},1,[313,317,319],{"class":318},"snl16","from",[313,321,323],{"class":322},"s95oV"," pathlib ",[313,325,326],{"class":318},"import",[313,328,329],{"class":322}," Path\n",[313,331,333,335,338,340],{"class":163,"line":332},2,[313,334,319],{"class":318},[313,336,337],{"class":322}," qgis.core ",[313,339,326],{"class":318},[313,341,342],{"class":322}," (\n",[313,344,346],{"class":163,"line":345},3,[313,347,348],{"class":322},"    QgsVectorLayer,\n",[313,350,352],{"class":163,"line":351},4,[313,353,354],{"class":322},"    QgsVectorFileWriter,\n",[313,356,358],{"class":163,"line":357},5,[313,359,360],{"class":322},"    QgsProject,\n",[313,362,364],{"class":163,"line":363},6,[313,365,366],{"class":322},")\n",[313,368,370],{"class":163,"line":369},7,[313,371,373],{"emptyLinePlaceholder":372},true,"\n",[313,375,377],{"class":163,"line":376},8,[313,378,373],{"emptyLinePlaceholder":372},[313,380,382,385,389,392,396,399,401,404,407],{"class":163,"line":381},9,[313,383,384],{"class":318},"def",[313,386,388],{"class":387},"svObZ"," convert_shp_to_geojson",[313,390,391],{"class":322},"(input_path: ",[313,393,395],{"class":394},"sDLfK","str",[313,397,398],{"class":322},", output_path: ",[313,400,395],{"class":394},[313,402,403],{"class":322},") -> ",[313,405,406],{"class":394},"None",[313,408,409],{"class":322},":\n",[313,411,413,416,419,422,426],{"class":163,"line":412},10,[313,414,415],{"class":322},"    layer ",[313,417,418],{"class":318},"=",[313,420,421],{"class":322}," QgsVectorLayer(input_path, Path(input_path).stem, ",[313,423,425],{"class":424},"sU2Wk","\"ogr\"",[313,427,366],{"class":322},[313,429,431,434,437],{"class":163,"line":430},11,[313,432,433],{"class":318},"    if",[313,435,436],{"class":318}," not",[313,438,439],{"class":322}," layer.isValid():\n",[313,441,443,446,449,452,455,458,461,464,467,470],{"class":163,"line":442},12,[313,444,445],{"class":318},"        raise",[313,447,448],{"class":394}," RuntimeError",[313,450,451],{"class":322},"(",[313,453,454],{"class":318},"f",[313,456,457],{"class":424},"\"Failed to load shapefile: ",[313,459,460],{"class":394},"{",[313,462,463],{"class":322},"input_path",[313,465,466],{"class":394},"}",[313,468,469],{"class":424},"\"",[313,471,366],{"class":322},[313,473,475],{"class":163,"line":474},13,[313,476,373],{"emptyLinePlaceholder":372},[313,478,480,483,485],{"class":163,"line":479},14,[313,481,482],{"class":322},"    opts ",[313,484,418],{"class":318},[313,486,487],{"class":322}," QgsVectorFileWriter.SaveVectorOptions()\n",[313,489,491,494,496],{"class":163,"line":490},15,[313,492,493],{"class":322},"    opts.driverName ",[313,495,418],{"class":318},[313,497,498],{"class":424}," \"GeoJSON\"\n",[313,500,502,505,507],{"class":163,"line":501},16,[313,503,504],{"class":322},"    opts.fileEncoding ",[313,506,418],{"class":318},[313,508,509],{"class":424}," \"UTF-8\"\n",[313,511,513],{"class":163,"line":512},17,[313,514,516],{"class":515},"sjoCn","    # RFC7446-compliant output: right-hand-rule winding + trimmed coordinates\n",[313,518,520,523,525,528,531],{"class":163,"line":519},18,[313,521,522],{"class":322},"    opts.layerOptions ",[313,524,418],{"class":318},[313,526,527],{"class":322}," [",[313,529,530],{"class":424},"\"RFC7946=YES\"",[313,532,533],{"class":322},"]\n",[313,535,537],{"class":163,"line":536},19,[313,538,373],{"emptyLinePlaceholder":372},[313,540,542,545,547],{"class":163,"line":541},20,[313,543,544],{"class":322},"    err_code, err_msg, _, _ ",[313,546,418],{"class":318},[313,548,549],{"class":322}," QgsVectorFileWriter.writeAsVectorFormatV3(\n",[313,551,553],{"class":163,"line":552},21,[313,554,555],{"class":322},"        layer,\n",[313,557,559],{"class":163,"line":558},22,[313,560,561],{"class":322},"        output_path,\n",[313,563,565],{"class":163,"line":564},23,[313,566,567],{"class":322},"        QgsProject.instance().transformContext(),\n",[313,569,571],{"class":163,"line":570},24,[313,572,573],{"class":322},"        opts,\n",[313,575,577],{"class":163,"line":576},25,[313,578,579],{"class":322},"    )\n",[313,581,583,585,588,591],{"class":163,"line":582},26,[313,584,433],{"class":318},[313,586,587],{"class":322}," err_code ",[313,589,590],{"class":318},"!=",[313,592,593],{"class":322}," QgsVectorFileWriter.NoError:\n",[313,595,597,599,601,603,605,608,610,613,615,618,620,623,625,627],{"class":163,"line":596},27,[313,598,445],{"class":318},[313,600,448],{"class":394},[313,602,451],{"class":322},[313,604,454],{"class":318},[313,606,607],{"class":424},"\"Export error (",[313,609,460],{"class":394},[313,611,612],{"class":322},"err_code",[313,614,466],{"class":394},[313,616,617],{"class":424},"): ",[313,619,460],{"class":394},[313,621,622],{"class":322},"err_msg",[313,624,466],{"class":394},[313,626,469],{"class":424},[313,628,366],{"class":322},[313,630,632],{"class":163,"line":631},28,[313,633,373],{"emptyLinePlaceholder":372},[313,635,637,640,642,644,647,649,652,654,657,659,662,664,666],{"class":163,"line":636},29,[313,638,639],{"class":394},"    print",[313,641,451],{"class":322},[313,643,454],{"class":318},[313,645,646],{"class":424},"\"Success: ",[313,648,460],{"class":394},[313,650,651],{"class":322},"Path(input_path).name",[313,653,466],{"class":394},[313,655,656],{"class":424}," -> ",[313,658,460],{"class":394},[313,660,661],{"class":322},"Path(output_path).name",[313,663,466],{"class":394},[313,665,469],{"class":424},[313,667,366],{"class":322},[313,669,671],{"class":163,"line":670},30,[313,672,373],{"emptyLinePlaceholder":372},[313,674,676],{"class":163,"line":675},31,[313,677,373],{"emptyLinePlaceholder":372},[313,679,681,684,687,689,692],{"class":163,"line":680},32,[313,682,683],{"class":322},"convert_shp_to_geojson(",[313,685,686],{"class":424},"\"\u002Fdata\u002Fparcels.shp\"",[313,688,240],{"class":322},[313,690,691],{"class":424},"\"\u002Fdata\u002Fout\u002Fparcels.geojson\"",[313,693,366],{"class":322},[14,695,696,700,701,704,705,707,708,711,712,715,716,719,720,723,724,727,728,731,732,735],{},[697,698,699],"strong",{},"Breakdown:"," ",[18,702,703],{},"QgsVectorLayer(..., \"ogr\")"," opens the shapefile through the OGR provider; the validity check stops a bad path or missing sidecar from producing a confusing driver error later. ",[18,706,298],{}," is the single place to configure the export — ",[18,709,710],{},"driverName"," selects GeoJSON and ",[18,713,714],{},"fileEncoding"," forces UTF-8 so accented attribute values survive. The ",[18,717,718],{},"RFC7946=YES"," layer option tells the GDAL GeoJSON driver to emit spec-compliant output. ",[18,721,722],{},"writeAsVectorFormatV3"," returns a four-tuple ",[18,725,726],{},"(error_code, error_message, new_filename, new_layer_name)"," — always unpack all four and compare ",[18,729,730],{},"error_code"," against ",[18,733,734],{},"QgsVectorFileWriter.NoError"," rather than assuming success.",[223,737,739],{"id":738},"reproject-to-wgs84-during-export","Reproject to WGS84 During Export",[14,741,742,743,746,747,751],{},"RFC 7946 requires GeoJSON coordinates in WGS84 (EPSG:4326). If your shapefile is in a projected CRS — a UTM zone, a national grid, Web Mercator — writing it unchanged produces coordinates in metres that web maps will place in the ocean off West Africa. Attach a ",[18,744,745],{},"QgsCoordinateTransform"," to the save options so the writer reprojects each feature on the way out. Getting the source and destination CRS right is the same discipline covered in ",[23,748,750],{"href":749},"\u002Fspatial-data-processing-automation\u002Fcoordinate-reference-systems\u002F","Coordinate Reference Systems",":",[304,753,755],{"className":306,"code":754,"language":308,"meta":309,"style":309},"from qgis.core import (\n    QgsVectorLayer,\n    QgsVectorFileWriter,\n    QgsCoordinateReferenceSystem,\n    QgsCoordinateTransform,\n    QgsProject,\n)\n\nlayer = QgsVectorLayer(\"\u002Fdata\u002Fparcels_utm.shp\", \"parcels\", \"ogr\")\ndest_crs = QgsCoordinateReferenceSystem(\"EPSG:4326\")\ncontext = QgsProject.instance().transformContext()\n\nopts = QgsVectorFileWriter.SaveVectorOptions()\nopts.driverName = \"GeoJSON\"\nopts.fileEncoding = \"UTF-8\"\nopts.layerOptions = [\"RFC7946=YES\"]\nif layer.crs().authid() != \"EPSG:4326\":\n    opts.ct = QgsCoordinateTransform(layer.crs(), dest_crs, context)\n\nerr_code, err_msg, _, _ = QgsVectorFileWriter.writeAsVectorFormatV3(\n    layer, \"\u002Fdata\u002Fout\u002Fparcels.geojson\", context, opts\n)\nprint(\"OK\" if err_code == QgsVectorFileWriter.NoError else err_msg)\n",[18,756,757,767,771,775,780,785,789,793,797,821,836,846,850,859,868,877,890,905,915,919,928,938,942],{"__ignoreMap":309},[313,758,759,761,763,765],{"class":163,"line":315},[313,760,319],{"class":318},[313,762,337],{"class":322},[313,764,326],{"class":318},[313,766,342],{"class":322},[313,768,769],{"class":163,"line":332},[313,770,348],{"class":322},[313,772,773],{"class":163,"line":345},[313,774,354],{"class":322},[313,776,777],{"class":163,"line":351},[313,778,779],{"class":322},"    QgsCoordinateReferenceSystem,\n",[313,781,782],{"class":163,"line":357},[313,783,784],{"class":322},"    QgsCoordinateTransform,\n",[313,786,787],{"class":163,"line":363},[313,788,360],{"class":322},[313,790,791],{"class":163,"line":369},[313,792,366],{"class":322},[313,794,795],{"class":163,"line":376},[313,796,373],{"emptyLinePlaceholder":372},[313,798,799,802,804,807,810,812,815,817,819],{"class":163,"line":381},[313,800,801],{"class":322},"layer ",[313,803,418],{"class":318},[313,805,806],{"class":322}," QgsVectorLayer(",[313,808,809],{"class":424},"\"\u002Fdata\u002Fparcels_utm.shp\"",[313,811,240],{"class":322},[313,813,814],{"class":424},"\"parcels\"",[313,816,240],{"class":322},[313,818,425],{"class":424},[313,820,366],{"class":322},[313,822,823,826,828,831,834],{"class":163,"line":412},[313,824,825],{"class":322},"dest_crs ",[313,827,418],{"class":318},[313,829,830],{"class":322}," QgsCoordinateReferenceSystem(",[313,832,833],{"class":424},"\"EPSG:4326\"",[313,835,366],{"class":322},[313,837,838,841,843],{"class":163,"line":430},[313,839,840],{"class":322},"context ",[313,842,418],{"class":318},[313,844,845],{"class":322}," QgsProject.instance().transformContext()\n",[313,847,848],{"class":163,"line":442},[313,849,373],{"emptyLinePlaceholder":372},[313,851,852,855,857],{"class":163,"line":474},[313,853,854],{"class":322},"opts ",[313,856,418],{"class":318},[313,858,487],{"class":322},[313,860,861,864,866],{"class":163,"line":479},[313,862,863],{"class":322},"opts.driverName ",[313,865,418],{"class":318},[313,867,498],{"class":424},[313,869,870,873,875],{"class":163,"line":490},[313,871,872],{"class":322},"opts.fileEncoding ",[313,874,418],{"class":318},[313,876,509],{"class":424},[313,878,879,882,884,886,888],{"class":163,"line":501},[313,880,881],{"class":322},"opts.layerOptions ",[313,883,418],{"class":318},[313,885,527],{"class":322},[313,887,530],{"class":424},[313,889,533],{"class":322},[313,891,892,895,898,900,903],{"class":163,"line":512},[313,893,894],{"class":318},"if",[313,896,897],{"class":322}," layer.crs().authid() ",[313,899,590],{"class":318},[313,901,902],{"class":424}," \"EPSG:4326\"",[313,904,409],{"class":322},[313,906,907,910,912],{"class":163,"line":519},[313,908,909],{"class":322},"    opts.ct ",[313,911,418],{"class":318},[313,913,914],{"class":322}," QgsCoordinateTransform(layer.crs(), dest_crs, context)\n",[313,916,917],{"class":163,"line":536},[313,918,373],{"emptyLinePlaceholder":372},[313,920,921,924,926],{"class":163,"line":541},[313,922,923],{"class":322},"err_code, err_msg, _, _ ",[313,925,418],{"class":318},[313,927,549],{"class":322},[313,929,930,933,935],{"class":163,"line":552},[313,931,932],{"class":322},"    layer, ",[313,934,691],{"class":424},[313,936,937],{"class":322},", context, opts\n",[313,939,940],{"class":163,"line":558},[313,941,366],{"class":322},[313,943,944,947,949,952,955,957,960,963,966],{"class":163,"line":564},[313,945,946],{"class":394},"print",[313,948,451],{"class":322},[313,950,951],{"class":424},"\"OK\"",[313,953,954],{"class":318}," if",[313,956,587],{"class":322},[313,958,959],{"class":318},"==",[313,961,962],{"class":322}," QgsVectorFileWriter.NoError ",[313,964,965],{"class":318},"else",[313,967,968],{"class":322}," err_msg)\n",[14,970,971,700,973,976,977,980,981,983,984,986],{},[697,972,699],{},[18,974,975],{},"opts.ct"," is the coordinate transform the writer applies per feature. Building it only when the source is not already EPSG:4326 avoids a needless identity transform. The transform pulls its datum-shift parameters from the project's ",[18,978,979],{},"transformContext",", which is why the same context object is passed both to the options (implicitly, via the transform) and to ",[18,982,722],{},". Always spot-check the first few output coordinates: values that still look like hundreds of thousands mean the source ",[18,985,250],{}," was missing and QGIS never knew the true source CRS.",[223,988,990],{"id":989},"convert-an-entire-folder","Convert an Entire Folder",[14,992,993,994,997,998,751],{},"The scalable payoff is batching. Glob the source directory with ",[18,995,996],{},"pathlib",", derive each output name from the input stem, and reuse the single-file function so one failure does not abort the run. This is the same looping pattern used across ",[23,999,1001],{"href":1000},"\u002Fspatial-data-processing-automation\u002Fbatch-processing-with-pyqgis\u002F","batch processing with PyQGIS",[304,1003,1005],{"className":306,"code":1004,"language":308,"meta":309,"style":309},"from pathlib import Path\n\nsrc_dir = Path(\"\u002Fdata\u002Fshapefiles\")\nout_dir = Path(\"\u002Fdata\u002Fgeojson\")\nout_dir.mkdir(parents=True, exist_ok=True)\n\nshapefiles = sorted(src_dir.glob(\"*.shp\"))\nif not shapefiles:\n    raise FileNotFoundError(f\"No shapefiles in {src_dir}\")\n\nfailures = []\nfor shp in shapefiles:\n    out_path = out_dir \u002F f\"{shp.stem}.geojson\"\n    try:\n        convert_shp_to_geojson(str(shp), str(out_path))\n    except RuntimeError as exc:\n        failures.append((shp.name, str(exc)))\n\nprint(f\"Converted {len(shapefiles) - len(failures)} of {len(shapefiles)} files\")\nfor name, err in failures:\n    print(f\"  FAILED {name}: {err}\")\n",[18,1006,1007,1017,1021,1036,1050,1075,1079,1098,1107,1133,1137,1147,1160,1188,1195,1210,1223,1233,1237,1280,1292],{"__ignoreMap":309},[313,1008,1009,1011,1013,1015],{"class":163,"line":315},[313,1010,319],{"class":318},[313,1012,323],{"class":322},[313,1014,326],{"class":318},[313,1016,329],{"class":322},[313,1018,1019],{"class":163,"line":332},[313,1020,373],{"emptyLinePlaceholder":372},[313,1022,1023,1026,1028,1031,1034],{"class":163,"line":345},[313,1024,1025],{"class":322},"src_dir ",[313,1027,418],{"class":318},[313,1029,1030],{"class":322}," Path(",[313,1032,1033],{"class":424},"\"\u002Fdata\u002Fshapefiles\"",[313,1035,366],{"class":322},[313,1037,1038,1041,1043,1045,1048],{"class":163,"line":351},[313,1039,1040],{"class":322},"out_dir ",[313,1042,418],{"class":318},[313,1044,1030],{"class":322},[313,1046,1047],{"class":424},"\"\u002Fdata\u002Fgeojson\"",[313,1049,366],{"class":322},[313,1051,1052,1055,1059,1061,1064,1066,1069,1071,1073],{"class":163,"line":357},[313,1053,1054],{"class":322},"out_dir.mkdir(",[313,1056,1058],{"class":1057},"s9osk","parents",[313,1060,418],{"class":318},[313,1062,1063],{"class":394},"True",[313,1065,240],{"class":322},[313,1067,1068],{"class":1057},"exist_ok",[313,1070,418],{"class":318},[313,1072,1063],{"class":394},[313,1074,366],{"class":322},[313,1076,1077],{"class":163,"line":363},[313,1078,373],{"emptyLinePlaceholder":372},[313,1080,1081,1084,1086,1089,1092,1095],{"class":163,"line":369},[313,1082,1083],{"class":322},"shapefiles ",[313,1085,418],{"class":318},[313,1087,1088],{"class":394}," sorted",[313,1090,1091],{"class":322},"(src_dir.glob(",[313,1093,1094],{"class":424},"\"*.shp\"",[313,1096,1097],{"class":322},"))\n",[313,1099,1100,1102,1104],{"class":163,"line":376},[313,1101,894],{"class":318},[313,1103,436],{"class":318},[313,1105,1106],{"class":322}," shapefiles:\n",[313,1108,1109,1112,1115,1117,1119,1122,1124,1127,1129,1131],{"class":163,"line":381},[313,1110,1111],{"class":318},"    raise",[313,1113,1114],{"class":394}," FileNotFoundError",[313,1116,451],{"class":322},[313,1118,454],{"class":318},[313,1120,1121],{"class":424},"\"No shapefiles in ",[313,1123,460],{"class":394},[313,1125,1126],{"class":322},"src_dir",[313,1128,466],{"class":394},[313,1130,469],{"class":424},[313,1132,366],{"class":322},[313,1134,1135],{"class":163,"line":412},[313,1136,373],{"emptyLinePlaceholder":372},[313,1138,1139,1142,1144],{"class":163,"line":430},[313,1140,1141],{"class":322},"failures ",[313,1143,418],{"class":318},[313,1145,1146],{"class":322}," []\n",[313,1148,1149,1152,1155,1158],{"class":163,"line":442},[313,1150,1151],{"class":318},"for",[313,1153,1154],{"class":322}," shp ",[313,1156,1157],{"class":318},"in",[313,1159,1106],{"class":322},[313,1161,1162,1165,1167,1170,1173,1176,1178,1180,1183,1185],{"class":163,"line":474},[313,1163,1164],{"class":322},"    out_path ",[313,1166,418],{"class":318},[313,1168,1169],{"class":322}," out_dir ",[313,1171,1172],{"class":318},"\u002F",[313,1174,1175],{"class":318}," f",[313,1177,469],{"class":424},[313,1179,460],{"class":394},[313,1181,1182],{"class":322},"shp.stem",[313,1184,466],{"class":394},[313,1186,1187],{"class":424},".geojson\"\n",[313,1189,1190,1193],{"class":163,"line":479},[313,1191,1192],{"class":318},"    try",[313,1194,409],{"class":322},[313,1196,1197,1200,1202,1205,1207],{"class":163,"line":490},[313,1198,1199],{"class":322},"        convert_shp_to_geojson(",[313,1201,395],{"class":394},[313,1203,1204],{"class":322},"(shp), ",[313,1206,395],{"class":394},[313,1208,1209],{"class":322},"(out_path))\n",[313,1211,1212,1215,1217,1220],{"class":163,"line":501},[313,1213,1214],{"class":318},"    except",[313,1216,448],{"class":394},[313,1218,1219],{"class":318}," as",[313,1221,1222],{"class":322}," exc:\n",[313,1224,1225,1228,1230],{"class":163,"line":512},[313,1226,1227],{"class":322},"        failures.append((shp.name, ",[313,1229,395],{"class":394},[313,1231,1232],{"class":322},"(exc)))\n",[313,1234,1235],{"class":163,"line":519},[313,1236,373],{"emptyLinePlaceholder":372},[313,1238,1239,1241,1243,1245,1248,1251,1254,1257,1260,1263,1265,1268,1270,1273,1275,1278],{"class":163,"line":536},[313,1240,946],{"class":394},[313,1242,451],{"class":322},[313,1244,454],{"class":318},[313,1246,1247],{"class":424},"\"Converted ",[313,1249,1250],{"class":394},"{len",[313,1252,1253],{"class":322},"(shapefiles) ",[313,1255,1256],{"class":318},"-",[313,1258,1259],{"class":394}," len",[313,1261,1262],{"class":322},"(failures)",[313,1264,466],{"class":394},[313,1266,1267],{"class":424}," of ",[313,1269,1250],{"class":394},[313,1271,1272],{"class":322},"(shapefiles)",[313,1274,466],{"class":394},[313,1276,1277],{"class":424}," files\"",[313,1279,366],{"class":322},[313,1281,1282,1284,1287,1289],{"class":163,"line":541},[313,1283,1151],{"class":318},[313,1285,1286],{"class":322}," name, err ",[313,1288,1157],{"class":318},[313,1290,1291],{"class":322}," failures:\n",[313,1293,1294,1296,1298,1300,1303,1305,1308,1310,1313,1315,1318,1320,1322],{"class":163,"line":552},[313,1295,639],{"class":394},[313,1297,451],{"class":322},[313,1299,454],{"class":318},[313,1301,1302],{"class":424},"\"  FAILED ",[313,1304,460],{"class":394},[313,1306,1307],{"class":322},"name",[313,1309,466],{"class":394},[313,1311,1312],{"class":424},": ",[313,1314,460],{"class":394},[313,1316,1317],{"class":322},"err",[313,1319,466],{"class":394},[313,1321,469],{"class":424},[313,1323,366],{"class":322},[14,1325,1326,700,1328,1331,1332,1335,1336,1339,1340,1343,1344,1347,1348,1351],{},[697,1327,699],{},[18,1329,1330],{},"sorted(src_dir.glob(\"*.shp\"))"," gathers inputs deterministically; swap in ",[18,1333,1334],{},"rglob"," to recurse into subfolders. Each output path is built from the input stem so ",[18,1337,1338],{},"parcels.shp"," becomes ",[18,1341,1342],{},"parcels.geojson",". Wrapping each conversion in ",[18,1345,1346],{},"try\u002Fexcept"," collects failures into a report instead of stopping at the first bad file — essential when one corrupt shapefile sits in a batch of hundreds. The early ",[18,1349,1350],{},"FileNotFoundError"," prevents a silent no-op when the glob pattern or path is wrong.",[223,1353,1355],{"id":1354},"what-the-format-change-costs-you","What the format change costs you",[14,1357,1358],{},"Shapefile and GeoJSON disagree about several things, and the conversion resolves each disagreement silently. Knowing which resolutions happen prevents the surprises that surface downstream.",[14,1360,1361],{},[36,1362,1365,1368,1371,1374,1379,1385,1392,1396,1399,1453],{"viewBox":1363,"role":39,"ariaLabel":1364,"xmlns":41},"0 0 760 250","A comparison of shapefile and GeoJSON across field name length, character encoding, CRS storage, geometry type strictness and file count",[43,1366,1367],{},"What changes when a shapefile becomes GeoJSON",[47,1369,1370],{},"Five rows compare the two formats. Shapefile truncates field names to ten characters while GeoJSON has no limit. Shapefile encoding depends on a companion file whereas GeoJSON is always UTF-8. Shapefile stores the CRS in a projection file whereas GeoJSON assumes WGS84. Shapefile fixes one geometry type per file whereas GeoJSON allows mixed types. Shapefile is several files whereas GeoJSON is one.",[51,1372],{"x":53,"y":53,"width":54,"height":1373,"fill":56},"250",[58,1375,1378],{"x":60,"y":1376,"style":1377,"fill":63,"textAnchor":64},"26","text-anchor:middle;font-size:14px;font-weight:bold;font-family:sans-serif","Five differences the conversion resolves for you",[163,1380],{"x1":1381,"y1":192,"x2":1382,"y2":192,"stroke":1383,"style":1384},"232","744","#59645f","stroke-width:1.5",[58,1386,1391],{"x":1387,"y":1388,"style":1389,"fill":63,"textAnchor":1390},"220","58","text-anchor:end;font-size:11.5px;font-weight:bold;font-family:sans-serif","end","aspect",[58,1393,84],{"x":1394,"y":1388,"style":1395,"fill":116,"textAnchor":64},"366","text-anchor:middle;font-size:11.5px;font-weight:bold;font-family:sans-serif",[58,1397,155],{"x":1398,"y":1388,"style":1395,"fill":150,"textAnchor":64},"608",[1400,1401,1403,1407,1411,1414,1417,1420,1423,1427,1430,1433,1437,1440,1443,1447,1450],"g",{"style":1402},"font-size:11.5px;font-family:sans-serif",[58,1404,1406],{"x":1387,"y":79,"style":1405,"fill":89,"textAnchor":1390},"text-anchor:end","field name length",[58,1408,1410],{"x":1394,"y":79,"style":1409,"fill":116,"textAnchor":64},"text-anchor:middle","10 characters",[58,1412,1413],{"x":1398,"y":79,"style":1409,"fill":150,"textAnchor":64},"unrestricted",[58,1415,1416],{"x":1387,"y":70,"style":1405,"fill":89,"textAnchor":1390},"encoding",[58,1418,1419],{"x":1394,"y":70,"style":1409,"fill":116,"textAnchor":64},"a .cpg file, if present",[58,1421,1422],{"x":1398,"y":70,"style":1409,"fill":150,"textAnchor":64},"always UTF-8",[58,1424,1426],{"x":1387,"y":1425,"style":1405,"fill":89,"textAnchor":1390},"162","CRS",[58,1428,1429],{"x":1394,"y":1425,"style":1409,"fill":116,"textAnchor":64},"a .prj file",[58,1431,1432],{"x":1398,"y":1425,"style":1409,"fill":150,"textAnchor":64},"assumed WGS84",[58,1434,1436],{"x":1387,"y":1435,"style":1405,"fill":89,"textAnchor":1390},"196","geometry types",[58,1438,1439],{"x":1394,"y":1435,"style":1409,"fill":116,"textAnchor":64},"one per file",[58,1441,1442],{"x":1398,"y":1435,"style":1409,"fill":150,"textAnchor":64},"mixed allowed",[58,1444,1446],{"x":1387,"y":1445,"style":1405,"fill":89,"textAnchor":1390},"230","files on disk",[58,1448,1449],{"x":1394,"y":1445,"style":1409,"fill":116,"textAnchor":64},"4 or more",[58,1451,1452],{"x":1398,"y":1445,"style":1409,"fill":150,"textAnchor":64},"exactly 1",[51,1454],{"x":1455,"y":1456,"width":1457,"height":1458,"rx":1459,"fill":150,"fillOpacity":1460,"stroke":150,"style":1461},"488","76","240","168","8",0.05,"stroke-width:2;stroke-dasharray:5 4",[14,1463,1464,1465,1467],{},"The CRS row is the one that bites. GeoJSON as published assumes WGS84, and every web consumer will read it that way regardless of what your ",[18,1466,250],{}," said — which is exactly why the reprojection step above is not optional dressing but the part that makes the output correct.",[223,1469,1471],{"id":1470},"keep-the-output-small","Keep the output small",[14,1473,1474],{},"A GeoJSON of survey-precision coordinates is often ten times larger than it needs to be for a web map, and the two levers that matter are coordinate precision and vertex count.",[14,1476,1477],{},[36,1478,1481,1484,1487,1490,1493,1497,1507,1512,1515,1519,1523,1527,1531,1536,1540,1543],{"viewBox":1479,"role":39,"ariaLabel":1480,"xmlns":41},"0 0 760 236","File size falling as coordinate precision is reduced from fifteen to six decimal places and again after simplification, with the ground accuracy each precision represents",[43,1482,1483],{},"Two levers on GeoJSON file size",[47,1485,1486],{},"Three bars show file size. Full precision with fifteen decimal places is the largest. Reducing to six decimal places, which is about eleven centimetres on the ground, roughly halves it. Adding simplification at a tolerance invisible at the target scale halves it again. Each bar is annotated with the resulting ground accuracy.",[51,1488],{"x":53,"y":53,"width":54,"height":1489,"fill":56},"236",[58,1491,1492],{"x":60,"y":1376,"style":1377,"fill":63,"textAnchor":64},"Six decimals is 11 cm — more than any web map can show",[58,1494,1496],{"x":184,"y":1495,"style":1389,"fill":63,"textAnchor":1390},"72","full precision",[51,1498],{"x":1499,"y":1500,"width":1501,"height":1502,"rx":1503,"fill":1504,"fillOpacity":1505,"stroke":1504,"style":1506},"224","56","500","28","4","#b91c1c",0.3,"stroke-width:1.8",[58,1508,1511],{"x":196,"y":1509,"style":1510,"fill":89},"75","font-size:11px;font-family:sans-serif","15 decimals · accurate to a nanometre",[58,1513,1514],{"x":184,"y":70,"style":1389,"fill":63,"textAnchor":1390},"COORDINATE_PRECISION=6",[51,1516],{"x":1499,"y":1517,"width":1518,"height":1502,"rx":1503,"fill":116,"fillOpacity":1505,"stroke":116,"style":1506},"112","264",[58,1520,1522],{"x":1501,"y":1521,"style":1510,"fill":89},"131","≈ 11 cm on the ground",[58,1524,1526],{"x":184,"y":1525,"style":1389,"fill":63,"textAnchor":1390},"184","+ simplify",[51,1528],{"x":1499,"y":1458,"width":1529,"height":1502,"rx":1503,"fill":150,"fillOpacity":1530,"stroke":150,"style":1506},"132",0.4,[58,1532,1535],{"x":1533,"y":1534,"style":1510,"fill":89},"368","187","invisible at the target scale",[163,1537],{"x1":1499,"y1":184,"x2":1538,"y2":184,"stroke":1383,"style":1539},"724","stroke-width:1.4",[58,1541,1542],{"x":1499,"y":1445,"style":1510,"fill":1383},"smaller",[58,1544,1546],{"x":1538,"y":1445,"style":1545,"fill":1383,"textAnchor":1390},"text-anchor:end;font-size:11px;font-family:sans-serif","larger",[14,1548,1549,1550,1554],{},"Both levers are lossy in a way that matters only if the GeoJSON is the archive copy. Keep the GeoPackage as the source of truth and treat the GeoJSON as a derived, disposable publication artefact — then trimming it aggressively costs nothing. ",[23,1551,1553],{"href":1552},"\u002Fspatial-data-processing-automation\u002Fgeometry-operations-and-predicates\u002Fsimplify-geometry-pyqgis\u002F","Simplify Geometry in PyQGIS"," covers choosing the tolerance from the target scale.",[223,1556,1558],{"id":1557},"fallback-methods","Fallback Methods",[14,1560,1561],{},"When the QGIS Python environment is unavailable or throws driver conflicts, two proven alternatives produce identical output:",[1563,1564,1565,1613],"ol",{},[231,1566,1567,1570,1571,1574,1575],{},[697,1568,1569],{},"GDAL\u002FOGR CLI."," Bypass QGIS entirely with ",[18,1572,1573],{},"ogr2ogr",". It is the fastest option on a headless server and carries no Python startup cost:\n",[304,1576,1580],{"className":1577,"code":1578,"language":1579,"meta":309,"style":309},"language-bash shiki shiki-themes github-dark","ogr2ogr -f GeoJSON -t_srs EPSG:4326 -lco RFC7946=YES -overwrite output.geojson input.shp\n","bash",[18,1581,1582],{"__ignoreMap":309},[313,1583,1584,1586,1589,1592,1595,1598,1601,1604,1607,1610],{"class":163,"line":315},[313,1585,1573],{"class":387},[313,1587,1588],{"class":394}," -f",[313,1590,1591],{"class":424}," GeoJSON",[313,1593,1594],{"class":394}," -t_srs",[313,1596,1597],{"class":424}," EPSG:4326",[313,1599,1600],{"class":394}," -lco",[313,1602,1603],{"class":424}," RFC7946=YES",[313,1605,1606],{"class":394}," -overwrite",[313,1608,1609],{"class":424}," output.geojson",[313,1611,1612],{"class":424}," input.shp\n",[231,1614,1615,1618,1619,1622,1623,1626,1627,1630,1631,1633],{},[697,1616,1617],{},"Processing Toolbox batch (no code)."," Open the Processing Toolbox (",[18,1620,1621],{},"Ctrl+Alt+T","), search ",[697,1624,1625],{},"Convert format",", right-click the tool and choose ",[697,1628,1629],{},"Execute as Batch Process",". Add every shapefile, set the output format to ",[18,1632,155],{},", and run. It is the friendliest route for non-programmers but offers no programmatic error handling.",[223,1635,1637],{"id":1636},"qgis-version-compatibility","QGIS Version Compatibility",[14,1639,1640,1641,1644],{},"The code targets ",[697,1642,1643],{},"QGIS 3.34 LTR"," (Python 3.12). The writer API differs across releases:",[1646,1647,1648,1664],"table",{},[1649,1650,1651],"thead",{},[1652,1653,1654,1658,1661],"tr",{},[1655,1656,1657],"th",{},"QGIS version",[1655,1659,1660],{},"Python",[1655,1662,1663],{},"Notes",[1665,1666,1667,1688,1704,1715],"tbody",{},[1652,1668,1669,1673,1676],{},[1670,1671,1672],"td",{},"3.10–3.14",[1670,1674,1675],{},"3.7–3.8",[1670,1677,1678,1680,1681,1684,1685,1687],{},[18,1679,722],{}," is unavailable — use ",[18,1682,1683],{},"writeAsVectorFormatV2"," with the same ",[18,1686,298],{},".",[1652,1689,1690,1693,1696],{},[1670,1691,1692],{},"3.28 LTR",[1670,1694,1695],{},"3.9",[1670,1697,1698,1700,1701,1703],{},[18,1699,722],{}," available; ",[18,1702,975],{}," reprojection identical.",[1652,1705,1706,1709,1712],{},[1670,1707,1708],{},"3.34 LTR",[1670,1710,1711],{},"3.12",[1670,1713,1714],{},"Baseline for this page.",[1652,1716,1717,1720,1722],{},[1670,1718,1719],{},"3.40 \u002F 3.44",[1670,1721,1711],{},[1670,1723,1724,1726,1727,1687],{},[18,1725,722],{}," unchanged; GDAL's GeoJSON driver still honours ",[18,1728,718],{},[14,1730,1731,1732,1735],{},"The bare ",[18,1733,1734],{},"QgsVectorFileWriter.writeAsVectorFormat"," (no version suffix) is deprecated and will emit warnings — prefer the V3 method on any 3.16+ install.",[223,1737,1739],{"id":1738},"troubleshooting","Troubleshooting",[228,1741,1742,1754,1770,1834,1852],{},[231,1743,1744,1749,1750,1753],{},[697,1745,1746,1687],{},[18,1747,1748],{},"Driver not found"," GDAL is not exposing the GeoJSON driver. Confirm GDAL is bundled with your QGIS install; on Windows reinstall via the OSGeo4W installer, on Linux install ",[18,1751,1752],{},"python3-gdal"," alongside QGIS.",[231,1755,1756,1759,1760,1763,1764,1766,1767,1769],{},[697,1757,1758],{},"Field names truncated or characters garbled."," Shapefiles cap field names at 10 characters and use legacy codepages, so corruption originates on the read, not the write. Keep ",[18,1761,1762],{},"opts.fileEncoding = \"UTF-8\""," and ensure a matching ",[18,1765,254],{}," file sits beside the ",[18,1768,239],{},". If it persists, convert to GeoPackage first, then export that to GeoJSON.",[231,1771,1772,1775,1776,1829,1830,1833],{},[697,1773,1774],{},"Export succeeds but features are missing."," Invalid geometries — self-intersections, unclosed rings — are dropped silently. Repair them before writing:\n",[304,1777,1779],{"className":306,"code":1778,"language":308,"meta":309,"style":309},"import processing\nfixed = processing.run(\"native:fixgeometries\", {\n    \"INPUT\": layer, \"OUTPUT\": \"TEMPORARY_OUTPUT\"\n})[\"OUTPUT\"]\n",[18,1780,1781,1788,1804,1820],{"__ignoreMap":309},[313,1782,1783,1785],{"class":163,"line":315},[313,1784,326],{"class":318},[313,1786,1787],{"class":322}," processing\n",[313,1789,1790,1793,1795,1798,1801],{"class":163,"line":332},[313,1791,1792],{"class":322},"fixed ",[313,1794,418],{"class":318},[313,1796,1797],{"class":322}," processing.run(",[313,1799,1800],{"class":424},"\"native:fixgeometries\"",[313,1802,1803],{"class":322},", {\n",[313,1805,1806,1809,1812,1815,1817],{"class":163,"line":345},[313,1807,1808],{"class":424},"    \"INPUT\"",[313,1810,1811],{"class":322},": layer, ",[313,1813,1814],{"class":424},"\"OUTPUT\"",[313,1816,1312],{"class":322},[313,1818,1819],{"class":424},"\"TEMPORARY_OUTPUT\"\n",[313,1821,1822,1825,1827],{"class":163,"line":351},[313,1823,1824],{"class":322},"})[",[313,1826,1814],{"class":424},[313,1828,533],{"class":322},"\nComparing ",[18,1831,1832],{},"featureCount()"," before and after export catches silent drops quickly.",[231,1835,1836,1839,1840,1842,1843,1846,1847,1849,1850,1687],{},[697,1837,1838],{},"Coordinates land in the wrong place."," The source CRS was wrong or unknown (missing ",[18,1841,250],{},"), or you skipped the reprojection step. Set ",[18,1844,1845],{},"layer.setCrs(...)"," explicitly if the ",[18,1848,250],{}," is absent, then attach ",[18,1851,975],{},[231,1853,1854,1857,1858,1861],{},[697,1855,1856],{},"Out-of-memory on large exports."," GeoJSON is read entirely into RAM by most consumers, so files above ~50–100 MB strain browsers. Split by attribute, or stream with ",[18,1859,1860],{},"ogr2ogr -gt 10000"," to batch features.",[223,1863,1865],{"id":1864},"conclusion","Conclusion",[14,1867,1868,1869,1871,1872,1874,1875,1877,1878,1880,1881,1883,1884,1888],{},"Automating shapefile-to-GeoJSON conversion in PyQGIS comes down to three reliable moves: load and validate the layer, configure ",[18,1870,298],{}," with the GeoJSON driver, UTF-8 encoding, and ",[18,1873,718],{},", and write it with ",[18,1876,33],{}," while attaching a ",[18,1879,745],{}," whenever the source is not already WGS84. Wrap that in a ",[18,1882,996],{}," loop with per-file error collection and the same recipe scales from one file to an entire delivery folder. Handled this way inside broader ",[23,1885,1887],{"href":1886},"\u002Fspatial-data-processing-automation\u002F","Spatial Data Processing & Automation"," pipelines, the conversion becomes a reproducible, version-controlled step rather than a manual chore — and validating output through a lightweight GeoJSON linter before deployment keeps web-map consumers happy.",[223,1890,1892],{"id":1891},"frequently-asked-questions","Frequently Asked Questions",[14,1894,1895,1898,1899,1901,1902,1904,1905,1908],{},[697,1896,1897],{},"Does my GeoJSON output need to be in WGS84 (EPSG:4326)?","\nYes. RFC 7946 mandates WGS84 and most web-mapping libraries assume it. If the source shapefile is in a projected CRS, attach a ",[18,1900,745],{}," to ",[18,1903,975],{}," so PyQGIS reprojects during export, or use ",[18,1906,1907],{},"ogr2ogr -t_srs EPSG:4326",". Skip it and coordinates stay in projected units that web maps will misplace.",[14,1910,1911,1914,1915,1917,1918,1920,1921,731,1923,1925],{},[697,1912,1913],{},"Which writer method should I use on QGIS 3.34 LTR?","\nUse ",[18,1916,722],{},", available from QGIS 3.16 and the current standard on 3.34 LTR. It returns a four-tuple ",[18,1919,726],{}," — unpack all four and check ",[18,1922,730],{},[18,1924,734],{},". The V2 method is only needed on releases between 3.10 and 3.14.",[14,1927,1928,1931,1932,1934,1935,1937],{},[697,1929,1930],{},"Why are my field names truncated or my characters garbled after conversion?","\nShapefiles cap field names at 10 characters and use legacy codepages, so the issue surfaces on the read, not the write. Keep ",[18,1933,1762],{}," and make sure a matching ",[18,1936,254],{}," file sits beside the shapefile. If corruption persists, convert the shapefile to GeoPackage first, then export that to GeoJSON.",[14,1939,1940,1943,1944,1947,1948,1950,1951,1953],{},[697,1941,1942],{},"How do I convert an entire folder of shapefiles in one run?","\nGlob the directory with ",[18,1945,1946],{},"pathlib.Path.glob(\"*.shp\")",", derive each output path from the input stem, and call the conversion function in a loop wrapped in ",[18,1949,1346],{}," so one bad file does not abort the batch. For headless servers you can loop ",[18,1952,1573],{}," instead, or use the Processing Toolbox \"Convert format\" tool's Execute as Batch Process option for a no-code approach.",[14,1955,1956,1959,1960,1963],{},[697,1957,1958],{},"My export runs but the GeoJSON is missing features — what happened?","\nInvalid geometries such as self-intersections or unclosed rings are dropped silently during export. Run ",[18,1961,1962],{},"native:fixgeometries"," on the layer before writing so the geometries are repaired first, and compare feature counts before and after to confirm nothing was lost.",[223,1965,1967],{"id":1966},"related","Related",[228,1969,1970,1975,1981,1987],{},[231,1971,1972],{},[23,1973,1974],{"href":25},"Vector Data Manipulation in PyQGIS",[231,1976,1977],{},[23,1978,1980],{"href":1979},"\u002Fspatial-data-processing-automation\u002Fvector-data-manipulation\u002Fmerge-multiple-shapefiles-pyqgis\u002F","Merge Multiple Shapefiles in PyQGIS",[231,1982,1983],{},[23,1984,1986],{"href":1985},"\u002Fspatial-data-processing-automation\u002Fvector-data-manipulation\u002Fclip-vector-layer-pyqgis\u002F","Clip a Vector Layer in PyQGIS",[231,1988,1989],{},[23,1990,1991],{"href":749},"Coordinate Reference Systems in PyQGIS",[1993,1994,1995],"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 .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}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 .sjoCn, html code.shiki .sjoCn{--shiki-default:#9AA79F}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 .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}",{"title":309,"searchDepth":332,"depth":332,"links":1997},[1998,1999,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009],{"id":225,"depth":332,"text":226},{"id":285,"depth":332,"text":286},{"id":738,"depth":332,"text":739},{"id":989,"depth":332,"text":990},{"id":1354,"depth":332,"text":1355},{"id":1470,"depth":332,"text":1471},{"id":1557,"depth":332,"text":1558},{"id":1636,"depth":332,"text":1637},{"id":1738,"depth":332,"text":1739},{"id":1864,"depth":332,"text":1865},{"id":1891,"depth":332,"text":1892},{"id":1966,"depth":332,"text":1967},"Convert shapefiles to GeoJSON in QGIS with PyQGIS. Automate bulk format conversion, reproject to WGS84, handle encoding, and batch a whole folder.","md",{"slug":2013,"type":2014,"breadcrumb":2015,"datePublished":2016,"dateModified":2017},"automating-shapefile-to-geojson-conversion","article","Shapefile to GeoJSON","2025-05-20","2026-08-27","\u002Fspatial-data-processing-automation\u002Fvector-data-manipulation\u002Fautomating-shapefile-to-geojson-conversion",{"title":5,"description":2010},"spatial-data-processing-automation\u002Fvector-data-manipulation\u002Fautomating-shapefile-to-geojson-conversion\u002Findex","tkOe22sVYT3KMUfY_LqvR7TuLRAddwrCNIDgYK_4txQ",1787823364716]