[{"data":1,"prerenderedAt":1552},["ShallowReactive",2],{"doc:\u002Fspatial-data-processing-automation\u002Fgeometry-operations-and-predicates\u002Fconvert-multipart-to-singlepart-pyqgis":3},{"id":4,"title":5,"body":6,"description":1541,"extension":1542,"meta":1543,"navigation":245,"path":1548,"seo":1549,"stem":1550,"__hash__":1551},"docs\u002Fspatial-data-processing-automation\u002Fgeometry-operations-and-predicates\u002Fconvert-multipart-to-singlepart-pyqgis\u002Findex.md","Convert Multipart to Singlepart Geometries in PyQGIS",{"type":7,"value":8,"toc":1527},"minimark",[9,13,17,26,183,188,205,209,212,377,408,411,414,468,473,572,584,588,591,840,869,882,886,889,954,973,1080,1084,1087,1238,1259,1263,1266,1283,1286,1290,1296,1383,1387,1443,1447,1456,1460,1466,1477,1483,1489,1493,1523],[10,11,5],"h1",{"id":12},"convert-multipart-to-singlepart-geometries-in-pyqgis",[14,15,16],"p",{},"A multipart feature is one row whose geometry is several disconnected shapes: an island group as one country, a dual carriageway as one road, three separate woods as one land parcel. It is a perfectly valid model and it breaks a surprising number of assumptions — that a feature has one centroid, that a length is a distance you could walk, that clicking a shape selects only what you clicked.",[14,18,19,20,25],{},"This recipe belongs to ",[21,22,24],"a",{"href":23},"\u002Fspatial-data-processing-automation\u002Fgeometry-operations-and-predicates\u002F","Geometry Operations & Predicates in PyQGIS",". It covers exploding features into parts, checking whether a layer is multipart at all, what happens to attributes when one row becomes seven, aggregating back, and the operations that quietly require singlepart input.",[14,27,28],{},[29,30,35,39,43,50,67,76,86,92,97,100,103,113,118,125,131,135,139,143,151,157,160,164,167,170,173,177],"svg",{"viewBox":31,"role":32,"ariaLabel":33,"xmlns":34},"0 0 760 316","img","One multipart feature with three separate polygons becoming three singlepart features, each inheriting a copy of the original attributes including its area value","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg",[36,37,38],"title",{},"One row becomes three, attributes and all",[40,41,42],"desc",{},"A single feature holds three disconnected polygons and one row of attributes including a stored area of forty hectares. Exploding it produces three features, each with one polygon and an identical copy of the attributes, so all three now claim an area of forty hectares until the field is recalculated.",[44,45],"rect",{"x":46,"y":46,"width":47,"height":48,"fill":49},"0","760","316","#f6f3ea",[51,52,53],"defs",{},[54,55,62],"marker",{"id":56,"viewBox":57,"refX":58,"refY":59,"markerWidth":60,"markerHeight":60,"orient":61},"mpArrow","0 0 10 10","8","5","7","auto-start-reverse",[63,64],"path",{"d":65,"fill":66},"M0 0 L10 5 L0 10 z","#2f3b35",[68,69,75],"text",{"x":70,"y":71,"style":72,"fill":73,"textAnchor":74},"380","28","text-anchor:middle;font-size:14px;font-weight:bold;font-family:sans-serif","#17211d","middle","Attributes are copied, not divided",[44,77],{"x":78,"y":79,"width":80,"height":81,"rx":82,"fill":83,"stroke":84,"style":85},"24","52","300","230","10","#fffdf7","#2563eb","stroke-width:2.5",[68,87,91],{"x":88,"y":89,"style":90,"fill":84,"textAnchor":74},"174","78","text-anchor:middle;font-size:11.5px;font-weight:bold;font-family:sans-serif","one multipart feature",[63,93],{"d":94,"fill":95,"stroke":84,"style":96},"M56 148 L104 112 L142 146 L110 184 Z","#dbeafe","stroke-width:2",[63,98],{"d":99,"fill":95,"stroke":84,"style":96},"M170 118 L214 104 L236 142 L192 158 Z",[63,101],{"d":102,"fill":95,"stroke":84,"style":96},"M156 190 L216 178 L240 218 L176 232 Z",[44,104],{"x":105,"y":106,"width":107,"height":108,"rx":109,"fill":110,"stroke":111,"style":112},"50","244","248","26","4","#efeadd","#59645f","stroke-width:1.4",[68,114,117],{"x":88,"y":115,"style":116,"fill":66,"textAnchor":74},"262","text-anchor:middle;font-size:10px;font-family:monospace","id=7 · name=Ash Wood · area_ha=40",[119,120],"line",{"x1":121,"y1":122,"x2":123,"y2":122,"stroke":66,"style":124},"330","164","368","stroke-width:2;marker-end:url(#mpArrow)",[68,126,130],{"x":127,"y":128,"style":129,"fill":111,"textAnchor":74},"349","152","text-anchor:middle;font-size:9.5px;font-family:sans-serif","explode",[44,132],{"x":70,"y":79,"width":133,"height":81,"rx":82,"fill":83,"stroke":134,"style":85},"356","#b45309",[68,136,138],{"x":137,"y":89,"style":90,"fill":134,"textAnchor":74},"558","three singlepart features",[63,140],{"d":141,"fill":142,"stroke":134,"style":96},"M410 130 L458 94 L496 128 L464 166 Z","#fdf2e2",[44,144],{"x":145,"y":146,"width":147,"height":148,"rx":149,"fill":110,"stroke":111,"style":150},"404","172","112","22","3","stroke-width:1.2",[68,152,156],{"x":153,"y":154,"style":155,"fill":66,"textAnchor":74},"460","188","text-anchor:middle;font-size:9px;font-family:monospace","area_ha=40",[63,158],{"d":159,"fill":142,"stroke":134,"style":96},"M540 106 L584 92 L606 130 L562 146 Z",[44,161],{"x":162,"y":163,"width":147,"height":148,"rx":149,"fill":110,"stroke":111,"style":150},"530","156",[68,165,156],{"x":166,"y":146,"style":155,"fill":66,"textAnchor":74},"586",[63,168],{"d":169,"fill":142,"stroke":134,"style":96},"M628 178 L688 166 L712 206 L648 220 Z",[44,171],{"x":172,"y":81,"width":147,"height":148,"rx":149,"fill":110,"stroke":111,"style":150},"616",[68,174,156],{"x":175,"y":176,"style":155,"fill":66,"textAnchor":74},"672","246",[68,178,182],{"x":137,"y":179,"style":180,"fill":181,"textAnchor":74},"272","text-anchor:middle;font-size:10.5px;font-family:sans-serif","#b91c1c","total area now reads 120 ha",[184,185,187],"h2",{"id":186},"prerequisites","Prerequisites",[189,190,191,199,202],"ul",{},[192,193,194,198],"li",{},[195,196,197],"strong",{},"QGIS 3.34 LTR"," (bundled Python 3.12) or newer.",[192,200,201],{},"A layer that may or may not be multipart. Shapefiles and many GeoPackages declare a multi geometry type regardless of whether any feature actually has more than one part.",[192,203,204],{},"A copy of the data, or a fresh output. Exploding changes the row count, and a stored id is no longer unique afterwards.",[184,206,208],{"id":207},"check-first","Check first",[14,210,211],{},"Declaring a layer multipart and containing multipart features are different things, and the check is worth making before running anything.",[213,214,219],"pre",{"className":215,"code":216,"language":217,"meta":218,"style":218},"language-python shiki shiki-themes github-dark","from qgis.core import QgsProject, QgsWkbTypes\n\nlayer = QgsProject.instance().mapLayersByName(\"woodland\")[0]\ndeclared = QgsWkbTypes.isMultiType(layer.wkbType())\nactual = sum(1 for f in layer.getFeatures() if f.geometry().isMultipart()\n             and len(f.geometry().asGeometryCollection()) > 1)\nprint(f\"declared multi: {declared}, features with >1 part: {actual}\")\n","python","",[220,221,222,240,247,272,283,318,339],"code",{"__ignoreMap":218},[223,224,226,230,234,237],"span",{"class":119,"line":225},1,[223,227,229],{"class":228},"snl16","from",[223,231,233],{"class":232},"s95oV"," qgis.core ",[223,235,236],{"class":228},"import",[223,238,239],{"class":232}," QgsProject, QgsWkbTypes\n",[223,241,243],{"class":119,"line":242},2,[223,244,246],{"emptyLinePlaceholder":245},true,"\n",[223,248,250,253,256,259,263,266,269],{"class":119,"line":249},3,[223,251,252],{"class":232},"layer ",[223,254,255],{"class":228},"=",[223,257,258],{"class":232}," QgsProject.instance().mapLayersByName(",[223,260,262],{"class":261},"sU2Wk","\"woodland\"",[223,264,265],{"class":232},")[",[223,267,46],{"class":268},"sDLfK",[223,270,271],{"class":232},"]\n",[223,273,275,278,280],{"class":119,"line":274},4,[223,276,277],{"class":232},"declared ",[223,279,255],{"class":228},[223,281,282],{"class":232}," QgsWkbTypes.isMultiType(layer.wkbType())\n",[223,284,286,289,291,294,297,300,303,306,309,312,315],{"class":119,"line":285},5,[223,287,288],{"class":232},"actual ",[223,290,255],{"class":228},[223,292,293],{"class":268}," sum",[223,295,296],{"class":232},"(",[223,298,299],{"class":268},"1",[223,301,302],{"class":228}," for",[223,304,305],{"class":232}," f ",[223,307,308],{"class":228},"in",[223,310,311],{"class":232}," layer.getFeatures() ",[223,313,314],{"class":228},"if",[223,316,317],{"class":232}," f.geometry().isMultipart()\n",[223,319,321,324,327,330,333,336],{"class":119,"line":320},6,[223,322,323],{"class":228},"             and",[223,325,326],{"class":268}," len",[223,328,329],{"class":232},"(f.geometry().asGeometryCollection()) ",[223,331,332],{"class":228},">",[223,334,335],{"class":268}," 1",[223,337,338],{"class":232},")\n",[223,340,342,345,347,350,353,356,359,362,365,367,370,372,375],{"class":119,"line":341},7,[223,343,344],{"class":268},"print",[223,346,296],{"class":232},[223,348,349],{"class":228},"f",[223,351,352],{"class":261},"\"declared multi: ",[223,354,355],{"class":268},"{",[223,357,358],{"class":232},"declared",[223,360,361],{"class":268},"}",[223,363,364],{"class":261},", features with >1 part: ",[223,366,355],{"class":268},[223,368,369],{"class":232},"actual",[223,371,361],{"class":268},[223,373,374],{"class":261},"\"",[223,376,338],{"class":232},[14,378,379,382,383,386,387,390,391,395,396,398,399,402,403,398,405,407],{},[195,380,381],{},"Breakdown:"," ",[220,384,385],{},"isMultiType()"," reads the layer's declared geometry type from the provider, which for a shapefile is almost always multi because the format has no singlepart polygon type. ",[220,388,389],{},"isMultipart()"," on a geometry says whether ",[392,393,394],"em",{},"that geometry"," is a multi type, which can still be true with exactly one part — hence counting the parts rather than trusting the flag. A layer where ",[220,397,358],{}," is ",[220,400,401],{},"True"," and ",[220,404,369],{},[220,406,46],{}," needs no explosion, and running one anyway costs a full rewrite for nothing.",[184,409,410],{"id":130},"Explode",[14,412,413],{},"The algorithm is a single call and does exactly what it says.",[213,415,417],{"className":215,"code":416,"language":217,"meta":218,"style":218},"import processing\n\nprocessing.run(\"native:multiparttosingleparts\", {\n    \"INPUT\": layer,\n    \"OUTPUT\": \"\u002Fdata\u002Foutput\u002Fwoodland_parts.gpkg\",\n})\n",[220,418,419,426,430,441,449,463],{"__ignoreMap":218},[223,420,421,423],{"class":119,"line":225},[223,422,236],{"class":228},[223,424,425],{"class":232}," processing\n",[223,427,428],{"class":119,"line":242},[223,429,246],{"emptyLinePlaceholder":245},[223,431,432,435,438],{"class":119,"line":249},[223,433,434],{"class":232},"processing.run(",[223,436,437],{"class":261},"\"native:multiparttosingleparts\"",[223,439,440],{"class":232},", {\n",[223,442,443,446],{"class":119,"line":274},[223,444,445],{"class":261},"    \"INPUT\"",[223,447,448],{"class":232},": layer,\n",[223,450,451,454,457,460],{"class":119,"line":285},[223,452,453],{"class":261},"    \"OUTPUT\"",[223,455,456],{"class":232},": ",[223,458,459],{"class":261},"\"\u002Fdata\u002Foutput\u002Fwoodland_parts.gpkg\"",[223,461,462],{"class":232},",\n",[223,464,465],{"class":119,"line":320},[223,466,467],{"class":232},"})\n",[14,469,470,472],{},[195,471,381],{}," Every part becomes its own feature with a full copy of the original attributes and a new feature id. Nothing is aggregated, averaged or divided — which is the source of the classic error where a stored area or population field is now repeated across every part and any sum over the layer is inflated. Recalculating derived fields immediately after exploding is not optional:",[213,474,476],{"className":215,"code":475,"language":217,"meta":218,"style":218},"processing.run(\"native:fieldcalculator\", {\n    \"INPUT\": \"\u002Fdata\u002Foutput\u002Fwoodland_parts.gpkg\",\n    \"FIELD_NAME\": \"area_ha\",\n    \"FIELD_TYPE\": 0,\n    \"FIELD_LENGTH\": 12,\n    \"FIELD_PRECISION\": 3,\n    \"FORMULA\": \"$area \u002F 10000\",\n    \"OUTPUT\": \"\u002Fdata\u002Foutput\u002Fwoodland_parts_fixed.gpkg\",\n})\n",[220,477,478,487,497,509,520,532,543,555,567],{"__ignoreMap":218},[223,479,480,482,485],{"class":119,"line":225},[223,481,434],{"class":232},[223,483,484],{"class":261},"\"native:fieldcalculator\"",[223,486,440],{"class":232},[223,488,489,491,493,495],{"class":119,"line":242},[223,490,445],{"class":261},[223,492,456],{"class":232},[223,494,459],{"class":261},[223,496,462],{"class":232},[223,498,499,502,504,507],{"class":119,"line":249},[223,500,501],{"class":261},"    \"FIELD_NAME\"",[223,503,456],{"class":232},[223,505,506],{"class":261},"\"area_ha\"",[223,508,462],{"class":232},[223,510,511,514,516,518],{"class":119,"line":274},[223,512,513],{"class":261},"    \"FIELD_TYPE\"",[223,515,456],{"class":232},[223,517,46],{"class":268},[223,519,462],{"class":232},[223,521,522,525,527,530],{"class":119,"line":285},[223,523,524],{"class":261},"    \"FIELD_LENGTH\"",[223,526,456],{"class":232},[223,528,529],{"class":268},"12",[223,531,462],{"class":232},[223,533,534,537,539,541],{"class":119,"line":320},[223,535,536],{"class":261},"    \"FIELD_PRECISION\"",[223,538,456],{"class":232},[223,540,149],{"class":268},[223,542,462],{"class":232},[223,544,545,548,550,553],{"class":119,"line":341},[223,546,547],{"class":261},"    \"FORMULA\"",[223,549,456],{"class":232},[223,551,552],{"class":261},"\"$area \u002F 10000\"",[223,554,462],{"class":232},[223,556,558,560,562,565],{"class":119,"line":557},8,[223,559,453],{"class":261},[223,561,456],{"class":232},[223,563,564],{"class":261},"\"\u002Fdata\u002Foutput\u002Fwoodland_parts_fixed.gpkg\"",[223,566,462],{"class":232},[223,568,570],{"class":119,"line":569},9,[223,571,467],{"class":232},[14,573,574,382,576,579,580,583],{},[195,575,381],{},[220,577,578],{},"$area"," is computed from the geometry in the layer's CRS units, so this only produces hectares if the layer is in a metric projected CRS — in EPSG:4326 it produces square degrees, which are meaningless. ",[220,581,582],{},"FIELD_TYPE: 0"," is a double. Recalculating rather than dividing by the part count is important because parts are rarely equal, and dividing a total by three assigns the same area to a five-hectare wood and a thirty-hectare one.",[184,585,587],{"id":586},"keeping-a-link-to-the-parent","Keeping a link to the parent",[14,589,590],{},"Nothing in the exploded output records which feature a part came from, and that link is almost always wanted later — to join a summary back, to report which parcel a stray fragment belongs to, or to aggregate correctly. Adding it before exploding costs one algorithm call.",[213,592,594],{"className":215,"code":593,"language":217,"meta":218,"style":218},"import processing\n\nwith_parent = processing.run(\"native:fieldcalculator\", {\n    \"INPUT\": layer,\n    \"FIELD_NAME\": \"parent_fid\",\n    \"FIELD_TYPE\": 1,\n    \"FIELD_LENGTH\": 10,\n    \"FORMULA\": \"$id\",\n    \"OUTPUT\": \"TEMPORARY_OUTPUT\",\n})[\"OUTPUT\"]\n\nexploded = processing.run(\"native:multiparttosingleparts\", {\n    \"INPUT\": with_parent,\n    \"OUTPUT\": \"TEMPORARY_OUTPUT\",\n})[\"OUTPUT\"]\n\nnumbered = processing.run(\"native:fieldcalculator\", {\n    \"INPUT\": exploded,\n    \"FIELD_NAME\": \"part_area_ha\",\n    \"FIELD_TYPE\": 0,\n    \"FIELD_LENGTH\": 12,\n    \"FIELD_PRECISION\": 3,\n    \"FORMULA\": \"$area \u002F 10000\",\n    \"OUTPUT\": \"\u002Fdata\u002Foutput\u002Fwoodland_parts.gpkg\",\n})[\"OUTPUT\"]\n",[220,595,596,602,606,620,626,637,647,657,668,679,690,695,709,717,728,737,742,756,764,776,787,798,809,820,831],{"__ignoreMap":218},[223,597,598,600],{"class":119,"line":225},[223,599,236],{"class":228},[223,601,425],{"class":232},[223,603,604],{"class":119,"line":242},[223,605,246],{"emptyLinePlaceholder":245},[223,607,608,611,613,616,618],{"class":119,"line":249},[223,609,610],{"class":232},"with_parent ",[223,612,255],{"class":228},[223,614,615],{"class":232}," processing.run(",[223,617,484],{"class":261},[223,619,440],{"class":232},[223,621,622,624],{"class":119,"line":274},[223,623,445],{"class":261},[223,625,448],{"class":232},[223,627,628,630,632,635],{"class":119,"line":285},[223,629,501],{"class":261},[223,631,456],{"class":232},[223,633,634],{"class":261},"\"parent_fid\"",[223,636,462],{"class":232},[223,638,639,641,643,645],{"class":119,"line":320},[223,640,513],{"class":261},[223,642,456],{"class":232},[223,644,299],{"class":268},[223,646,462],{"class":232},[223,648,649,651,653,655],{"class":119,"line":341},[223,650,524],{"class":261},[223,652,456],{"class":232},[223,654,82],{"class":268},[223,656,462],{"class":232},[223,658,659,661,663,666],{"class":119,"line":557},[223,660,547],{"class":261},[223,662,456],{"class":232},[223,664,665],{"class":261},"\"$id\"",[223,667,462],{"class":232},[223,669,670,672,674,677],{"class":119,"line":569},[223,671,453],{"class":261},[223,673,456],{"class":232},[223,675,676],{"class":261},"\"TEMPORARY_OUTPUT\"",[223,678,462],{"class":232},[223,680,682,685,688],{"class":119,"line":681},10,[223,683,684],{"class":232},"})[",[223,686,687],{"class":261},"\"OUTPUT\"",[223,689,271],{"class":232},[223,691,693],{"class":119,"line":692},11,[223,694,246],{"emptyLinePlaceholder":245},[223,696,698,701,703,705,707],{"class":119,"line":697},12,[223,699,700],{"class":232},"exploded ",[223,702,255],{"class":228},[223,704,615],{"class":232},[223,706,437],{"class":261},[223,708,440],{"class":232},[223,710,712,714],{"class":119,"line":711},13,[223,713,445],{"class":261},[223,715,716],{"class":232},": with_parent,\n",[223,718,720,722,724,726],{"class":119,"line":719},14,[223,721,453],{"class":261},[223,723,456],{"class":232},[223,725,676],{"class":261},[223,727,462],{"class":232},[223,729,731,733,735],{"class":119,"line":730},15,[223,732,684],{"class":232},[223,734,687],{"class":261},[223,736,271],{"class":232},[223,738,740],{"class":119,"line":739},16,[223,741,246],{"emptyLinePlaceholder":245},[223,743,745,748,750,752,754],{"class":119,"line":744},17,[223,746,747],{"class":232},"numbered ",[223,749,255],{"class":228},[223,751,615],{"class":232},[223,753,484],{"class":261},[223,755,440],{"class":232},[223,757,759,761],{"class":119,"line":758},18,[223,760,445],{"class":261},[223,762,763],{"class":232},": exploded,\n",[223,765,767,769,771,774],{"class":119,"line":766},19,[223,768,501],{"class":261},[223,770,456],{"class":232},[223,772,773],{"class":261},"\"part_area_ha\"",[223,775,462],{"class":232},[223,777,779,781,783,785],{"class":119,"line":778},20,[223,780,513],{"class":261},[223,782,456],{"class":232},[223,784,46],{"class":268},[223,786,462],{"class":232},[223,788,790,792,794,796],{"class":119,"line":789},21,[223,791,524],{"class":261},[223,793,456],{"class":232},[223,795,529],{"class":268},[223,797,462],{"class":232},[223,799,801,803,805,807],{"class":119,"line":800},22,[223,802,536],{"class":261},[223,804,456],{"class":232},[223,806,149],{"class":268},[223,808,462],{"class":232},[223,810,812,814,816,818],{"class":119,"line":811},23,[223,813,547],{"class":261},[223,815,456],{"class":232},[223,817,552],{"class":261},[223,819,462],{"class":232},[223,821,823,825,827,829],{"class":119,"line":822},24,[223,824,453],{"class":261},[223,826,456],{"class":232},[223,828,459],{"class":261},[223,830,462],{"class":232},[223,832,834,836,838],{"class":119,"line":833},25,[223,835,684],{"class":232},[223,837,687],{"class":261},[223,839,271],{"class":232},[14,841,842,382,844,847,848,851,852,855,856,860,861,864,865,868],{},[195,843,381],{},[220,845,846],{},"$id"," is the provider's feature id, which is stable for a file-based layer within a session but not guaranteed across a rewrite — so capturing it into a real field is exactly the point. ",[220,849,850],{},"FIELD_TYPE: 1"," is an integer. Chaining three algorithms through ",[220,853,854],{},"TEMPORARY_OUTPUT"," means only the final file is written, which is the pattern described in ",[21,857,859],{"href":858},"\u002Fspatial-data-processing-automation\u002Fchaining-processing-algorithms\u002F","chaining processing algorithms",". Naming the recomputed field ",[220,862,863],{},"part_area_ha"," rather than overwriting ",[220,866,867],{},"area_ha"," keeps both numbers visible, so anyone reading the table can see that the parent total and the part areas are different things.",[14,870,871,872,875,876,878,879,881],{},"With the parent id present, aggregating back is a dissolve or aggregate on ",[220,873,874],{},"parent_fid",", and a sanity check becomes possible: the sum of ",[220,877,863],{}," grouped by ",[220,880,874],{}," should match the parent's original area to within rounding.",[184,883,885],{"id":884},"going-the-other-way","Going the other way",[14,887,888],{},"Aggregating parts back into multipart features is a dissolve on a key field.",[213,890,892],{"className":215,"code":891,"language":217,"meta":218,"style":218},"processing.run(\"native:dissolve\", {\n    \"INPUT\": \"\u002Fdata\u002Foutput\u002Fwoodland_parts_fixed.gpkg\",\n    \"FIELD\": [\"parcel_ref\"],\n    \"SEPARATE_DISJOINT\": False,\n    \"OUTPUT\": \"\u002Fdata\u002Foutput\u002Fwoodland_merged.gpkg\",\n})\n",[220,893,894,903,913,927,939,950],{"__ignoreMap":218},[223,895,896,898,901],{"class":119,"line":225},[223,897,434],{"class":232},[223,899,900],{"class":261},"\"native:dissolve\"",[223,902,440],{"class":232},[223,904,905,907,909,911],{"class":119,"line":242},[223,906,445],{"class":261},[223,908,456],{"class":232},[223,910,564],{"class":261},[223,912,462],{"class":232},[223,914,915,918,921,924],{"class":119,"line":249},[223,916,917],{"class":261},"    \"FIELD\"",[223,919,920],{"class":232},": [",[223,922,923],{"class":261},"\"parcel_ref\"",[223,925,926],{"class":232},"],\n",[223,928,929,932,934,937],{"class":119,"line":274},[223,930,931],{"class":261},"    \"SEPARATE_DISJOINT\"",[223,933,456],{"class":232},[223,935,936],{"class":268},"False",[223,938,462],{"class":232},[223,940,941,943,945,948],{"class":119,"line":285},[223,942,453],{"class":261},[223,944,456],{"class":232},[223,946,947],{"class":261},"\"\u002Fdata\u002Foutput\u002Fwoodland_merged.gpkg\"",[223,949,462],{"class":232},[223,951,952],{"class":119,"line":320},[223,953,467],{"class":232},[14,955,956,382,958,961,962,965,966,968,969,972],{},[195,957,381],{},[220,959,960],{},"FIELD"," groups by one or more attributes and produces one feature per distinct combination, with disconnected parts held as a multipart geometry. ",[220,963,964],{},"SEPARATE_DISJOINT: False"," is what keeps them multipart; setting it ",[220,967,401],{}," splits disjoint results back into singleparts and undoes the point of the exercise. Attributes other than the grouping fields are taken from an arbitrary feature in each group, so numeric totals must be rebuilt afterwards — ",[220,970,971],{},"native:aggregate"," is the algorithm that lets you specify a summary function per field and is the better choice when the attributes matter as much as the geometry.",[14,974,975],{},[29,976,979,982,985,988,995,998,1005,1011,1015,1020,1024,1029,1032,1035,1038,1041,1045,1049,1054,1058,1060,1063,1068,1072,1076],{"viewBox":977,"role":32,"ariaLabel":978,"xmlns":34},"0 0 760 288","The round trip between multipart and singlepart, showing that exploding then dissolving returns the same geometry but not necessarily the same attributes",[36,980,981],{},"The round trip is lossless for geometry, not for attributes",[40,983,984],{},"Exploding a multipart feature and dissolving it back on a key field reproduces the original geometry exactly. The attributes do not survive unchanged, because dissolve takes non grouping fields from an arbitrary member of each group, so any value that was a total must be recomputed.",[44,986],{"x":46,"y":46,"width":47,"height":987,"fill":49},"288",[51,989,990],{},[54,991,993],{"id":992,"viewBox":57,"refX":58,"refY":59,"markerWidth":60,"markerHeight":60,"orient":61},"rtArrow",[63,994],{"d":65,"fill":66},[68,996,997],{"x":70,"y":71,"style":72,"fill":73,"textAnchor":74},"Geometry survives the round trip; totals do not",[44,999],{"x":1000,"y":1001,"width":1002,"height":1003,"rx":82,"fill":1004,"stroke":84,"style":85},"30","58","196","96","#eff3ff",[68,1006,1010],{"x":1007,"y":1008,"style":1009,"fill":84,"textAnchor":74},"128","86","text-anchor:middle;font-size:11px;font-weight:bold;font-family:sans-serif","multipart",[68,1012,1014],{"x":1007,"y":1013,"style":180,"fill":66,"textAnchor":74},"110","1 row, 3 parts",[68,1016,1019],{"x":1007,"y":1017,"style":1018,"fill":66,"textAnchor":74},"132","text-anchor:middle;font-size:10.5px;font-family:monospace","area_ha = 40",[119,1021],{"x1":81,"y1":1022,"x2":179,"y2":1022,"stroke":66,"style":1023},"106","stroke-width:2;marker-end:url(#rtArrow)",[68,1025,130],{"x":1026,"y":1027,"style":1028,"fill":111,"textAnchor":74},"251","94","text-anchor:middle;font-size:9px;font-family:sans-serif",[44,1030],{"x":1031,"y":1001,"width":1002,"height":1003,"rx":82,"fill":142,"stroke":134,"style":85},"282",[68,1033,1034],{"x":70,"y":1008,"style":1009,"fill":134,"textAnchor":74},"singlepart",[68,1036,1037],{"x":70,"y":1013,"style":180,"fill":66,"textAnchor":74},"3 rows, 1 part each",[68,1039,1040],{"x":70,"y":1017,"style":1018,"fill":181,"textAnchor":74},"area_ha = 40 ×3",[119,1042],{"x1":1043,"y1":1022,"x2":1044,"y2":1022,"stroke":66,"style":1023},"482","524",[68,1046,1048],{"x":1047,"y":1027,"style":1028,"fill":111,"textAnchor":74},"503","dissolve",[44,1050],{"x":1051,"y":1001,"width":1002,"height":1003,"rx":82,"fill":1052,"stroke":1053,"style":85},"534","#edf8e9","#15803d",[68,1055,1057],{"x":1056,"y":1008,"style":1009,"fill":1053,"textAnchor":74},"632","multipart again",[68,1059,1014],{"x":1056,"y":1013,"style":180,"fill":66,"textAnchor":74},[68,1061,1062],{"x":1056,"y":1017,"style":1018,"fill":66,"textAnchor":74},"geometry identical",[44,1064],{"x":1065,"y":1066,"width":153,"height":1067,"rx":58,"fill":83,"stroke":181,"style":96},"150","186","76",[68,1069,1071],{"x":70,"y":1070,"style":1009,"fill":181,"textAnchor":74},"212","recompute every derived field at each step",[68,1073,1075],{"x":70,"y":1074,"style":180,"fill":66,"textAnchor":74},"234","area, length, perimeter, counts —",[68,1077,1079],{"x":70,"y":1078,"style":180,"fill":66,"textAnchor":74},"252","none of them are updated for you",[184,1081,1083],{"id":1082},"exploding-in-memory-without-writing-a-file","Exploding in memory, without writing a file",[14,1085,1086],{},"Where the split is an intermediate step, materialising it in memory keeps the disk clean.",[213,1088,1090],{"className":215,"code":1089,"language":217,"meta":218,"style":218},"from qgis.core import QgsFeature, QgsVectorLayer, QgsProject\n\nparts = QgsVectorLayer(\n    f\"Polygon?crs={layer.crs().authid()}\", \"parts\", \"memory\"\n)\nparts.dataProvider().addAttributes(layer.fields())\nparts.updateFields()\n\nbuffer = []\nfor feature in layer.getFeatures():\n    for geometry in feature.geometry().asGeometryCollection():\n        part = QgsFeature(parts.fields())\n        part.setAttributes(feature.attributes())\n        part.setGeometry(geometry)\n        buffer.append(part)\n\nparts.dataProvider().addFeatures(buffer)\nparts.updateExtents()\n",[220,1091,1092,1103,1107,1117,1145,1149,1154,1159,1163,1173,1186,1199,1209,1214,1219,1224,1228,1233],{"__ignoreMap":218},[223,1093,1094,1096,1098,1100],{"class":119,"line":225},[223,1095,229],{"class":228},[223,1097,233],{"class":232},[223,1099,236],{"class":228},[223,1101,1102],{"class":232}," QgsFeature, QgsVectorLayer, QgsProject\n",[223,1104,1105],{"class":119,"line":242},[223,1106,246],{"emptyLinePlaceholder":245},[223,1108,1109,1112,1114],{"class":119,"line":249},[223,1110,1111],{"class":232},"parts ",[223,1113,255],{"class":228},[223,1115,1116],{"class":232}," QgsVectorLayer(\n",[223,1118,1119,1122,1125,1127,1130,1132,1134,1137,1140,1142],{"class":119,"line":274},[223,1120,1121],{"class":228},"    f",[223,1123,1124],{"class":261},"\"Polygon?crs=",[223,1126,355],{"class":268},[223,1128,1129],{"class":232},"layer.crs().authid()",[223,1131,361],{"class":268},[223,1133,374],{"class":261},[223,1135,1136],{"class":232},", ",[223,1138,1139],{"class":261},"\"parts\"",[223,1141,1136],{"class":232},[223,1143,1144],{"class":261},"\"memory\"\n",[223,1146,1147],{"class":119,"line":285},[223,1148,338],{"class":232},[223,1150,1151],{"class":119,"line":320},[223,1152,1153],{"class":232},"parts.dataProvider().addAttributes(layer.fields())\n",[223,1155,1156],{"class":119,"line":341},[223,1157,1158],{"class":232},"parts.updateFields()\n",[223,1160,1161],{"class":119,"line":557},[223,1162,246],{"emptyLinePlaceholder":245},[223,1164,1165,1168,1170],{"class":119,"line":569},[223,1166,1167],{"class":232},"buffer ",[223,1169,255],{"class":228},[223,1171,1172],{"class":232}," []\n",[223,1174,1175,1178,1181,1183],{"class":119,"line":681},[223,1176,1177],{"class":228},"for",[223,1179,1180],{"class":232}," feature ",[223,1182,308],{"class":228},[223,1184,1185],{"class":232}," layer.getFeatures():\n",[223,1187,1188,1191,1194,1196],{"class":119,"line":692},[223,1189,1190],{"class":228},"    for",[223,1192,1193],{"class":232}," geometry ",[223,1195,308],{"class":228},[223,1197,1198],{"class":232}," feature.geometry().asGeometryCollection():\n",[223,1200,1201,1204,1206],{"class":119,"line":697},[223,1202,1203],{"class":232},"        part ",[223,1205,255],{"class":228},[223,1207,1208],{"class":232}," QgsFeature(parts.fields())\n",[223,1210,1211],{"class":119,"line":711},[223,1212,1213],{"class":232},"        part.setAttributes(feature.attributes())\n",[223,1215,1216],{"class":119,"line":719},[223,1217,1218],{"class":232},"        part.setGeometry(geometry)\n",[223,1220,1221],{"class":119,"line":730},[223,1222,1223],{"class":232},"        buffer.append(part)\n",[223,1225,1226],{"class":119,"line":739},[223,1227,246],{"emptyLinePlaceholder":245},[223,1229,1230],{"class":119,"line":744},[223,1231,1232],{"class":232},"parts.dataProvider().addFeatures(buffer)\n",[223,1234,1235],{"class":119,"line":758},[223,1236,1237],{"class":232},"parts.updateExtents()\n",[14,1239,1240,382,1242,1245,1246,1249,1250,1253,1254,1258],{},[195,1241,381],{},[220,1243,1244],{},"asGeometryCollection()"," returns a list of single geometries and works on both multipart and singlepart input — a singlepart geometry yields a one-element list, so no branch is needed. Collecting into a list and calling ",[220,1247,1248],{},"addFeatures()"," once is far faster than adding one at a time, because each call is a provider transaction. ",[220,1251,1252],{},"updateExtents()"," at the end refreshes the layer's cached bounding box, without which the layer reports an empty extent and ",[21,1255,1257],{"href":1256},"\u002Fpyqgis-cartography-visualization\u002Fmap-canvas-and-image-export\u002Fzoom-to-layer-extent-pyqgis\u002F","zooming to it"," does nothing.",[184,1260,1262],{"id":1261},"what-actually-needs-singlepart-input","What actually needs singlepart input",[14,1264,1265],{},"Several common operations behave oddly or fail on multipart data, and knowing which saves a lot of confused debugging.",[14,1267,1268,1269,1273,1274,1277,1278,1282],{},"Centroid-based labelling and any ",[21,1270,1272],{"href":1271},"\u002Fpyqgis-cartography-visualization\u002Fsymbol-layers-and-advanced-symbology\u002Fgeometry-generator-symbol-layer-pyqgis\u002F","geometry generator"," using ",[220,1275,1276],{},"centroid()"," produce one point per feature, which for an island group lands in the sea. Length and perimeter sum across parts, so a \"longest road\" query returns the road with the most fragments. Many topology and network algorithms require singlepart lines and either error or silently use only the first part. And an ",[21,1279,1281],{"href":1280},"\u002Fspatial-data-processing-automation\u002Fterrain-and-interpolation-analysis\u002Fextract-elevation-profile-along-line-pyqgis\u002F","elevation profile"," along a multipart route jumps between disconnected segments.",[14,1284,1285],{},"The rule of thumb: explode before anything that reasons about a feature as a single connected shape, and aggregate afterwards if the deliverable needs the original rows back.",[184,1287,1289],{"id":1288},"qgis-version-compatibility","QGIS version compatibility",[14,1291,1292,1293,1295],{},"The examples target ",[195,1294,197],{}," (Python 3.12).",[1297,1298,1299,1315],"table",{},[1300,1301,1302],"thead",{},[1303,1304,1305,1309,1312],"tr",{},[1306,1307,1308],"th",{},"QGIS version",[1306,1310,1311],{},"Python",[1306,1313,1314],{},"Notes",[1316,1317,1318,1336,1349,1362,1373],"tbody",{},[1303,1319,1320,1324,1327],{},[1321,1322,1323],"td",{},"3.16 LTR",[1321,1325,1326],{},"3.7",[1321,1328,1329,402,1332,1335],{},[220,1330,1331],{},"native:multiparttosingleparts",[220,1333,1334],{},"native:dissolve"," present.",[1303,1337,1338,1341,1344],{},[1321,1339,1340],{},"3.22 LTR",[1321,1342,1343],{},"3.9",[1321,1345,1346,1348],{},[220,1347,971],{}," offers per-field summary functions when dissolving.",[1303,1350,1351,1354,1356],{},[1321,1352,1353],{},"3.28 LTR",[1321,1355,1343],{},[1321,1357,1358,1361],{},[220,1359,1360],{},"SEPARATE_DISJOINT"," added to the dissolve algorithm.",[1303,1363,1364,1367,1370],{},[1321,1365,1366],{},"3.34 LTR",[1321,1368,1369],{},"3.12",[1321,1371,1372],{},"Baseline for this page.",[1303,1374,1375,1378,1380],{},[1321,1376,1377],{},"3.40+",[1321,1379,1369],{},[1321,1381,1382],{},"Improved handling of mixed single and multi geometries in one layer.",[184,1384,1386],{"id":1385},"troubleshooting","Troubleshooting",[189,1388,1389,1395,1404,1417,1426,1434],{},[192,1390,1391,1394],{},[195,1392,1393],{},"The layer totals tripled."," Stored area or population fields were copied to every part. Recalculate them.",[192,1396,1397,1400,1401,1403],{},[195,1398,1399],{},"Explode produced the same number of features."," No feature had more than one part. Check with ",[220,1402,1244],{}," rather than the declared type.",[192,1405,1406,382,1409,398,1411,1413,1414,1416],{},[195,1407,1408],{},"Dissolve produced singleparts.",[220,1410,1360],{},[220,1412,401],{},". Set it ",[220,1415,936],{},".",[192,1418,1419,1422,1423,1425],{},[195,1420,1421],{},"Attributes are wrong after dissolving."," Non-grouping fields come from an arbitrary member. Use ",[220,1424,971],{}," with explicit functions.",[192,1427,1428,382,1431,1433],{},[195,1429,1430],{},"The memory layer has no extent.",[220,1432,1252],{}," was not called after adding features.",[192,1435,1436,1439,1440,1416],{},[195,1437,1438],{},"A centroid landed outside the shape."," That is correct for a multipart or concave geometry. Explode first, or use ",[220,1441,1442],{},"point_on_surface()",[184,1444,1446],{"id":1445},"conclusion","Conclusion",[14,1448,1449,1450,1452,1453,1455],{},"Check whether the layer really has multipart features before exploding, recalculate every derived field immediately afterwards, and use ",[220,1451,971],{}," rather than ",[220,1454,1334],{}," when the attributes matter. Explode before anything that treats a feature as one connected shape, and remember that the geometry round trip is lossless while the attribute round trip is not.",[184,1457,1459],{"id":1458},"frequently-asked-questions","Frequently Asked Questions",[14,1461,1462,1465],{},[195,1463,1464],{},"Can I explode only some features?","\nYes — filter first with a subset string or a selection, run the algorithm on the selection, and merge the result with the untouched remainder. There is no per-feature option in the algorithm itself.",[14,1467,1468,1471,1472,1476],{},[195,1469,1470],{},"Does exploding change the geometry at all?","\nNo. Each part is copied verbatim, so vertices, precision and validity are unchanged. Any ",[21,1473,1475],{"href":1474},"\u002Fspatial-data-processing-automation\u002Fgeometry-operations-and-predicates\u002Ffix-invalid-geometries-pyqgis\u002F","invalid geometry"," in the input is still invalid in the output.",[14,1478,1479,1482],{},[195,1480,1481],{},"How do I keep a link back to the original feature?","\nAdd a field holding the original id before exploding. Nothing in the output records the parentage otherwise, and the new feature ids are unrelated to the old ones.",[14,1484,1485,1488],{},[195,1486,1487],{},"Should I store data as singlepart or multipart?","\nSinglepart where each shape is independently meaningful, multipart where the parts genuinely are one thing — an archipelago that is one administrative unit. Storing multipart for convenience and exploding on every use is the arrangement that causes the most trouble.",[184,1490,1492],{"id":1491},"related","Related",[189,1494,1495,1500,1506,1511,1517],{},[192,1496,1497,1499],{},[21,1498,24],{"href":23}," — the guide this recipe belongs to",[192,1501,1502],{},[21,1503,1505],{"href":1504},"\u002Fspatial-data-processing-automation\u002Fvector-data-manipulation\u002Fdissolve-features-by-attribute-pyqgis\u002F","Dissolve Features by Attribute in PyQGIS",[192,1507,1508],{},[21,1509,1510],{"href":1474},"Fix Invalid Geometries in PyQGIS",[192,1512,1513],{},[21,1514,1516],{"href":1515},"\u002Fspatial-data-processing-automation\u002Fvector-data-manipulation\u002Fsplit-vector-layer-by-attribute-pyqgis\u002F","Split a Vector Layer by Attribute in PyQGIS",[192,1518,1519],{},[21,1520,1522],{"href":1521},"\u002Fspatial-data-processing-automation\u002Fvector-data-manipulation\u002Fpyqgis-script-to-calculate-polygon-areas\u002F","Calculate Polygon Areas with a PyQGIS Script",[1524,1525,1526],"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);}",{"title":218,"searchDepth":242,"depth":242,"links":1528},[1529,1530,1531,1532,1533,1534,1535,1536,1537,1538,1539,1540],{"id":186,"depth":242,"text":187},{"id":207,"depth":242,"text":208},{"id":130,"depth":242,"text":410},{"id":586,"depth":242,"text":587},{"id":884,"depth":242,"text":885},{"id":1082,"depth":242,"text":1083},{"id":1261,"depth":242,"text":1262},{"id":1288,"depth":242,"text":1289},{"id":1385,"depth":242,"text":1386},{"id":1445,"depth":242,"text":1446},{"id":1458,"depth":242,"text":1459},{"id":1491,"depth":242,"text":1492},"Explode multipart features into single parts with native:multiparttosingleparts, aggregate them back, detect which layers are multipart, and keep attributes and areas honest.","md",{"slug":1544,"type":1545,"breadcrumb":1546,"datePublished":1547,"dateModified":1547},"convert-multipart-to-singlepart-pyqgis","article","Multipart to Singlepart","2026-08-27","\u002Fspatial-data-processing-automation\u002Fgeometry-operations-and-predicates\u002Fconvert-multipart-to-singlepart-pyqgis",{"title":5,"description":1541},"spatial-data-processing-automation\u002Fgeometry-operations-and-predicates\u002Fconvert-multipart-to-singlepart-pyqgis\u002Findex","Ct9TXWwfAf0u8igNany5f9wa0F9KfdNIonu189_HAro",1787823363688]