[{"data":1,"prerenderedAt":1708},["ShallowReactive",2],{"doc:\u002Fspatial-data-processing-automation\u002Fchaining-processing-algorithms\u002Fchain-buffer-and-clip-pyqgis":3},{"id":4,"title":5,"body":6,"description":1701,"extension":1702,"meta":1703,"navigation":120,"path":1704,"seo":1705,"stem":1706,"__hash__":1707},"docs\u002Fspatial-data-processing-automation\u002Fchaining-processing-algorithms\u002Fchain-buffer-and-clip-pyqgis\u002Findex.md","Chain Buffer and Clip in PyQGIS",{"type":7,"value":8,"toc":1688},"minimark",[9,13,32,35,40,68,80,84,87,324,354,358,368,582,610,614,628,719,761,765,768,1259,1289,1293,1300,1401,1436,1440,1453,1502,1515,1519,1535,1551,1559,1565,1584,1588,1603,1607,1620,1638,1651,1664,1668,1684],[10,11,5],"h1",{"id":12},"chain-buffer-and-clip-in-pyqgis",[14,15,16,17,21,22,25,26,31],"p",{},"Buffering a vector layer and then clipping the result to a study area is one of the most common two-step operations in GIS. Done by hand it is two toolbox dialogs, two intermediate files, and two manual checks. Done in PyQGIS it is a single script: run ",[18,19,20],"code",{},"native:buffer",", pass its output straight into ",[18,23,24],{},"native:clip"," using a temporary intermediate, then write only the final layer to disk. This page is a focused, runnable recipe for exactly that chain, and it builds on the general patterns in ",[27,28,30],"a",{"href":29},"\u002Fspatial-data-processing-automation\u002Fchaining-processing-algorithms\u002F","Chaining Processing Algorithms in PyQGIS",".",[14,33,34],{},"The goal is a script you can paste into the QGIS Python Console, point at your data, and trust: it reports progress, validates inputs and outputs, and never leaves a stray buffer file behind because the intermediate lives in memory.",[36,37,39],"h2",{"id":38},"prerequisites","Prerequisites",[41,42,43,51,54,61],"ul",{},[44,45,46,50],"li",{},[47,48,49],"strong",{},"QGIS 3.34 LTR"," (Python 3.12) recommended; the script also runs on 3.28 LTR and the 3.40\u002F3.44 line.",[44,52,53],{},"A source vector layer (points, lines, or polygons) and an overlay polygon layer defining the area to keep.",[44,55,56,57,60],{},"Both layers in the ",[47,58,59],{},"same CRS",", or a destination CRS set on the context. Buffer distances are in the layer's map units, so a projected CRS in metres is strongly preferred.",[44,62,63,64,67],{},"The ",[18,65,66],{},"processing"," module available — automatic in the QGIS Python Console.",[14,69,70,71,75,76,79],{},"If you have never invoked a single algorithm from code, read ",[27,72,74],{"href":73},"\u002Fspatial-data-processing-automation\u002Fbatch-processing-with-pyqgis\u002Frun-processing-algorithm-from-script\u002F","running a processing algorithm from a script"," first; this page assumes you can make one ",[18,77,78],{},"processing.run()"," call and chains two of them.",[36,81,83],{"id":82},"step-1-validate-inputs-before-running-anything","Step 1: Validate Inputs Before Running Anything",[14,85,86],{},"A chain that fails on step two after a slow step one wastes time. Validate both layers and warn on geographic CRS up front.",[88,89,94],"pre",{"className":90,"code":91,"language":92,"meta":93,"style":93},"language-python shiki shiki-themes github-dark","from qgis.core import QgsVectorLayer\n\n\ndef load_and_check(path: str, name: str) -> QgsVectorLayer:\n    layer = QgsVectorLayer(path, name, \"ogr\")\n    if not layer.isValid():\n        raise ValueError(f\"Could not load {name}: {path}\")\n    if layer.featureCount() == 0:\n        raise ValueError(f\"{name} has no features: {path}\")\n    if layer.crs().isGeographic():\n        print(f\"WARNING: {name} is in a geographic CRS; buffer distance is in \"\n              f\"degrees, not metres. Reproject to a projected CRS first.\")\n    return layer\n","python","",[18,95,96,115,122,127,152,171,183,225,242,274,282,304,315],{"__ignoreMap":93},[97,98,101,105,109,112],"span",{"class":99,"line":100},"line",1,[97,102,104],{"class":103},"snl16","from",[97,106,108],{"class":107},"s95oV"," qgis.core ",[97,110,111],{"class":103},"import",[97,113,114],{"class":107}," QgsVectorLayer\n",[97,116,118],{"class":99,"line":117},2,[97,119,121],{"emptyLinePlaceholder":120},true,"\n",[97,123,125],{"class":99,"line":124},3,[97,126,121],{"emptyLinePlaceholder":120},[97,128,130,133,137,140,144,147,149],{"class":99,"line":129},4,[97,131,132],{"class":103},"def",[97,134,136],{"class":135},"svObZ"," load_and_check",[97,138,139],{"class":107},"(path: ",[97,141,143],{"class":142},"sDLfK","str",[97,145,146],{"class":107},", name: ",[97,148,143],{"class":142},[97,150,151],{"class":107},") -> QgsVectorLayer:\n",[97,153,155,158,161,164,168],{"class":99,"line":154},5,[97,156,157],{"class":107},"    layer ",[97,159,160],{"class":103},"=",[97,162,163],{"class":107}," QgsVectorLayer(path, name, ",[97,165,167],{"class":166},"sU2Wk","\"ogr\"",[97,169,170],{"class":107},")\n",[97,172,174,177,180],{"class":99,"line":173},6,[97,175,176],{"class":103},"    if",[97,178,179],{"class":103}," not",[97,181,182],{"class":107}," layer.isValid():\n",[97,184,186,189,192,195,198,201,204,207,210,213,215,218,220,223],{"class":99,"line":185},7,[97,187,188],{"class":103},"        raise",[97,190,191],{"class":142}," ValueError",[97,193,194],{"class":107},"(",[97,196,197],{"class":103},"f",[97,199,200],{"class":166},"\"Could not load ",[97,202,203],{"class":142},"{",[97,205,206],{"class":107},"name",[97,208,209],{"class":142},"}",[97,211,212],{"class":166},": ",[97,214,203],{"class":142},[97,216,217],{"class":107},"path",[97,219,209],{"class":142},[97,221,222],{"class":166},"\"",[97,224,170],{"class":107},[97,226,228,230,233,236,239],{"class":99,"line":227},8,[97,229,176],{"class":103},[97,231,232],{"class":107}," layer.featureCount() ",[97,234,235],{"class":103},"==",[97,237,238],{"class":142}," 0",[97,240,241],{"class":107},":\n",[97,243,245,247,249,251,253,255,257,259,261,264,266,268,270,272],{"class":99,"line":244},9,[97,246,188],{"class":103},[97,248,191],{"class":142},[97,250,194],{"class":107},[97,252,197],{"class":103},[97,254,222],{"class":166},[97,256,203],{"class":142},[97,258,206],{"class":107},[97,260,209],{"class":142},[97,262,263],{"class":166}," has no features: ",[97,265,203],{"class":142},[97,267,217],{"class":107},[97,269,209],{"class":142},[97,271,222],{"class":166},[97,273,170],{"class":107},[97,275,277,279],{"class":99,"line":276},10,[97,278,176],{"class":103},[97,280,281],{"class":107}," layer.crs().isGeographic():\n",[97,283,285,288,290,292,295,297,299,301],{"class":99,"line":284},11,[97,286,287],{"class":142},"        print",[97,289,194],{"class":107},[97,291,197],{"class":103},[97,293,294],{"class":166},"\"WARNING: ",[97,296,203],{"class":142},[97,298,206],{"class":107},[97,300,209],{"class":142},[97,302,303],{"class":166}," is in a geographic CRS; buffer distance is in \"\n",[97,305,307,310,313],{"class":99,"line":306},12,[97,308,309],{"class":103},"              f",[97,311,312],{"class":166},"\"degrees, not metres. Reproject to a projected CRS first.\"",[97,314,170],{"class":107},[97,316,318,321],{"class":99,"line":317},13,[97,319,320],{"class":103},"    return",[97,322,323],{"class":107}," layer\n",[14,325,326,329,330,333,334,337,338,341,342,345,346,349,350,353],{},[47,327,328],{},"Breakdown:"," ",[18,331,332],{},"QgsVectorLayer(path, name, \"ogr\")"," opens the file through the OGR provider. ",[18,335,336],{},"isValid()"," catches bad paths and unsupported formats; an empty ",[18,339,340],{},"featureCount()"," catches a file that loads but contains nothing to process. The ",[18,343,344],{},"crs().isGeographic()"," check is the single most useful guard for buffering — a ",[18,347,348],{},"DISTANCE"," of ",[18,351,352],{},"250"," against EPSG:4326 buffers by 250 degrees, which silently produces nonsense rather than an error.",[36,355,357],{"id":356},"step-2-buffer-into-a-temporary-output","Step 2: Buffer into a Temporary Output",[14,359,360,361,363,364,367],{},"Run ",[18,362,20],{}," and request ",[18,365,366],{},"'TEMPORARY_OUTPUT'"," so the framework keeps the result without writing a file you would have to delete.",[88,369,371],{"className":90,"code":370,"language":92,"meta":93,"style":93},"import processing\nfrom qgis.core import QgsProcessingContext, QgsProcessingFeedback, QgsProject\n\n\ndef buffer_layer(source, distance_m, context, feedback):\n    feedback.pushInfo(f\"Buffering by {distance_m} m ...\")\n    result = processing.run(\"native:buffer\", {\n        \"INPUT\": source,\n        \"DISTANCE\": distance_m,\n        \"SEGMENTS\": 8,\n        \"END_CAP_STYLE\": 0,      # 0 = round\n        \"JOIN_STYLE\": 0,         # 0 = round\n        \"MITER_LIMIT\": 2,\n        \"DISSOLVE\": False,\n        \"OUTPUT\": \"TEMPORARY_OUTPUT\",\n    }, context=context, feedback=feedback)\n    return result[\"OUTPUT\"]\n",[18,372,373,380,391,395,399,409,431,447,455,463,476,493,507,519,532,545,568],{"__ignoreMap":93},[97,374,375,377],{"class":99,"line":100},[97,376,111],{"class":103},[97,378,379],{"class":107}," processing\n",[97,381,382,384,386,388],{"class":99,"line":117},[97,383,104],{"class":103},[97,385,108],{"class":107},[97,387,111],{"class":103},[97,389,390],{"class":107}," QgsProcessingContext, QgsProcessingFeedback, QgsProject\n",[97,392,393],{"class":99,"line":124},[97,394,121],{"emptyLinePlaceholder":120},[97,396,397],{"class":99,"line":129},[97,398,121],{"emptyLinePlaceholder":120},[97,400,401,403,406],{"class":99,"line":154},[97,402,132],{"class":103},[97,404,405],{"class":135}," buffer_layer",[97,407,408],{"class":107},"(source, distance_m, context, feedback):\n",[97,410,411,414,416,419,421,424,426,429],{"class":99,"line":173},[97,412,413],{"class":107},"    feedback.pushInfo(",[97,415,197],{"class":103},[97,417,418],{"class":166},"\"Buffering by ",[97,420,203],{"class":142},[97,422,423],{"class":107},"distance_m",[97,425,209],{"class":142},[97,427,428],{"class":166}," m ...\"",[97,430,170],{"class":107},[97,432,433,436,438,441,444],{"class":99,"line":185},[97,434,435],{"class":107},"    result ",[97,437,160],{"class":103},[97,439,440],{"class":107}," processing.run(",[97,442,443],{"class":166},"\"native:buffer\"",[97,445,446],{"class":107},", {\n",[97,448,449,452],{"class":99,"line":227},[97,450,451],{"class":166},"        \"INPUT\"",[97,453,454],{"class":107},": source,\n",[97,456,457,460],{"class":99,"line":244},[97,458,459],{"class":166},"        \"DISTANCE\"",[97,461,462],{"class":107},": distance_m,\n",[97,464,465,468,470,473],{"class":99,"line":276},[97,466,467],{"class":166},"        \"SEGMENTS\"",[97,469,212],{"class":107},[97,471,472],{"class":142},"8",[97,474,475],{"class":107},",\n",[97,477,478,481,483,486,489],{"class":99,"line":284},[97,479,480],{"class":166},"        \"END_CAP_STYLE\"",[97,482,212],{"class":107},[97,484,485],{"class":142},"0",[97,487,488],{"class":107},",      ",[97,490,492],{"class":491},"sAwPA","# 0 = round\n",[97,494,495,498,500,502,505],{"class":99,"line":306},[97,496,497],{"class":166},"        \"JOIN_STYLE\"",[97,499,212],{"class":107},[97,501,485],{"class":142},[97,503,504],{"class":107},",         ",[97,506,492],{"class":491},[97,508,509,512,514,517],{"class":99,"line":317},[97,510,511],{"class":166},"        \"MITER_LIMIT\"",[97,513,212],{"class":107},[97,515,516],{"class":142},"2",[97,518,475],{"class":107},[97,520,522,525,527,530],{"class":99,"line":521},14,[97,523,524],{"class":166},"        \"DISSOLVE\"",[97,526,212],{"class":107},[97,528,529],{"class":142},"False",[97,531,475],{"class":107},[97,533,535,538,540,543],{"class":99,"line":534},15,[97,536,537],{"class":166},"        \"OUTPUT\"",[97,539,212],{"class":107},[97,541,542],{"class":166},"\"TEMPORARY_OUTPUT\"",[97,544,475],{"class":107},[97,546,548,551,555,557,560,563,565],{"class":99,"line":547},16,[97,549,550],{"class":107},"    }, ",[97,552,554],{"class":553},"s9osk","context",[97,556,160],{"class":103},[97,558,559],{"class":107},"context, ",[97,561,562],{"class":553},"feedback",[97,564,160],{"class":103},[97,566,567],{"class":107},"feedback)\n",[97,569,571,573,576,579],{"class":99,"line":570},17,[97,572,320],{"class":103},[97,574,575],{"class":107}," result[",[97,577,578],{"class":166},"\"OUTPUT\"",[97,580,581],{"class":107},"]\n",[14,583,584,586,587,589,590,593,594,597,598,601,602,605,606,609],{},[47,585,328],{}," Every key here is a real ",[18,588,20],{}," parameter. ",[18,591,592],{},"SEGMENTS"," controls how many line segments approximate each quarter-circle (8 is a good default). ",[18,595,596],{},"DISSOLVE=False"," keeps one buffer polygon per input feature; set it ",[18,599,600],{},"True"," to merge overlaps. The return value ",[18,603,604],{},"result[\"OUTPUT\"]"," is a ",[18,607,608],{},"QgsVectorLayer"," held in memory — it is what step three consumes, never a path on disk.",[36,611,613],{"id":612},"step-3-clip-the-buffer-to-the-study-area","Step 3: Clip the Buffer to the Study Area",[14,615,616,617,619,620,623,624,627],{},"Feed the buffer's output straight into ",[18,618,24],{}," as its ",[18,621,622],{},"INPUT",", using the study-area polygon as the ",[18,625,626],{},"OVERLAY",". This is the join that makes it a chain.",[88,629,631],{"className":90,"code":630,"language":92,"meta":93,"style":93},"def clip_to_area(buffered_layer, overlay, out_path, context, feedback):\n    feedback.pushInfo(\"Clipping buffer to study area ...\")\n    result = processing.run(\"native:clip\", {\n        \"INPUT\": buffered_layer,     # the OUTPUT from native:buffer\n        \"OVERLAY\": overlay,\n        \"OUTPUT\": out_path,          # final result -> permanent file\n    }, context=context, feedback=feedback)\n    return result[\"OUTPUT\"]\n",[18,632,633,643,652,665,675,683,693,709],{"__ignoreMap":93},[97,634,635,637,640],{"class":99,"line":100},[97,636,132],{"class":103},[97,638,639],{"class":135}," clip_to_area",[97,641,642],{"class":107},"(buffered_layer, overlay, out_path, context, feedback):\n",[97,644,645,647,650],{"class":99,"line":117},[97,646,413],{"class":107},[97,648,649],{"class":166},"\"Clipping buffer to study area ...\"",[97,651,170],{"class":107},[97,653,654,656,658,660,663],{"class":99,"line":124},[97,655,435],{"class":107},[97,657,160],{"class":103},[97,659,440],{"class":107},[97,661,662],{"class":166},"\"native:clip\"",[97,664,446],{"class":107},[97,666,667,669,672],{"class":99,"line":129},[97,668,451],{"class":166},[97,670,671],{"class":107},": buffered_layer,     ",[97,673,674],{"class":491},"# the OUTPUT from native:buffer\n",[97,676,677,680],{"class":99,"line":154},[97,678,679],{"class":166},"        \"OVERLAY\"",[97,681,682],{"class":107},": overlay,\n",[97,684,685,687,690],{"class":99,"line":173},[97,686,537],{"class":166},[97,688,689],{"class":107},": out_path,          ",[97,691,692],{"class":491},"# final result -> permanent file\n",[97,694,695,697,699,701,703,705,707],{"class":99,"line":185},[97,696,550],{"class":107},[97,698,554],{"class":553},[97,700,160],{"class":103},[97,702,559],{"class":107},[97,704,562],{"class":553},[97,706,160],{"class":103},[97,708,567],{"class":107},[97,710,711,713,715,717],{"class":99,"line":227},[97,712,320],{"class":103},[97,714,575],{"class":107},[97,716,578],{"class":166},[97,718,581],{"class":107},[14,720,721,329,723,725,726,728,729,732,733,736,737,741,742,744,745,748,749,752,753,757,758,760],{},[47,722,328],{},[18,724,622],{}," is the in-memory buffer layer object returned by step two — no filename is involved between buffer and clip. ",[18,727,626],{}," is the polygon layer that defines what to keep. Because this is the final step, ",[18,730,731],{},"OUTPUT"," is a real path (for example ",[18,734,735],{},"\u002Fdata\u002Fbuffer_clipped.gpkg","), so the result is written to disk. If you would rather keep only features that ",[738,739,740],"em",{},"intersect"," the area without trimming their geometry, swap ",[18,743,24],{}," for ",[18,746,747],{},"native:extractbylocation"," with ",[18,750,751],{},"PREDICATE: [0]"," (intersects). For the geometry-trimming behaviour described in the dedicated ",[27,754,756],{"href":755},"\u002Fspatial-data-processing-automation\u002Fvector-data-manipulation\u002Fclip-vector-layer-pyqgis\u002F","clip a vector layer in PyQGIS"," guide, ",[18,759,24],{}," is correct.",[36,762,764],{"id":763},"step-4-assemble-and-run-the-full-chain","Step 4: Assemble and Run the Full Chain",[14,766,767],{},"The orchestrator wires the three steps together, shares one context and one feedback object, and validates the final output.",[88,769,771],{"className":90,"code":770,"language":92,"meta":93,"style":93},"import processing\nfrom qgis.core import (\n    QgsVectorLayer,\n    QgsProcessingContext,\n    QgsProcessingFeedback,\n    QgsProcessingException,\n    QgsProject,\n)\n\n\nclass ProgressFeedback(QgsProcessingFeedback):\n    def setProgress(self, progress):\n        print(f\"\\r  {progress:5.1f}%\", end=\"\", flush=True)\n\n    def pushInfo(self, info):\n        print(f\"\\n{info}\")\n\n\ndef buffer_then_clip(source_path, overlay_path, out_path, distance_m):\n    source = load_and_check(source_path, \"source\")\n    overlay = load_and_check(overlay_path, \"overlay\")\n\n    context = QgsProcessingContext()\n    context.setProject(QgsProject.instance())\n    feedback = ProgressFeedback()\n\n    try:\n        buffered = buffer_layer(source, distance_m, context, feedback)\n        final = clip_to_area(buffered, overlay, out_path, context, feedback)\n    except QgsProcessingException as exc:\n        feedback.pushInfo(f\"Pipeline failed: {exc}\")\n        raise\n\n    # Validate the written result\n    written = QgsVectorLayer(out_path, \"result\", \"ogr\")\n    if not written.isValid():\n        raise RuntimeError(f\"Final output is not a valid layer: {out_path}\")\n    feedback.pushInfo(f\"Done. {written.featureCount()} features written to {out_path}\")\n    return written\n\n\n# Run in the QGIS Python Console:\n# buffer_then_clip(\n#     \"\u002Fdata\u002Fwells.gpkg\",\n#     \"\u002Fdata\u002Fcatchment.gpkg\",\n#     \"\u002Fdata\u002Fwells_buffer_clipped.gpkg\",\n#     distance_m=500,\n# )\n",[18,772,773,779,790,795,800,805,810,815,819,823,827,843,854,903,907,917,939,943,948,959,975,991,996,1007,1013,1024,1029,1037,1048,1059,1074,1096,1102,1107,1113,1133,1143,1169,1199,1207,1212,1217,1223,1229,1235,1241,1247,1253],{"__ignoreMap":93},[97,774,775,777],{"class":99,"line":100},[97,776,111],{"class":103},[97,778,379],{"class":107},[97,780,781,783,785,787],{"class":99,"line":117},[97,782,104],{"class":103},[97,784,108],{"class":107},[97,786,111],{"class":103},[97,788,789],{"class":107}," (\n",[97,791,792],{"class":99,"line":124},[97,793,794],{"class":107},"    QgsVectorLayer,\n",[97,796,797],{"class":99,"line":129},[97,798,799],{"class":107},"    QgsProcessingContext,\n",[97,801,802],{"class":99,"line":154},[97,803,804],{"class":107},"    QgsProcessingFeedback,\n",[97,806,807],{"class":99,"line":173},[97,808,809],{"class":107},"    QgsProcessingException,\n",[97,811,812],{"class":99,"line":185},[97,813,814],{"class":107},"    QgsProject,\n",[97,816,817],{"class":99,"line":227},[97,818,170],{"class":107},[97,820,821],{"class":99,"line":244},[97,822,121],{"emptyLinePlaceholder":120},[97,824,825],{"class":99,"line":276},[97,826,121],{"emptyLinePlaceholder":120},[97,828,829,832,835,837,840],{"class":99,"line":284},[97,830,831],{"class":103},"class",[97,833,834],{"class":135}," ProgressFeedback",[97,836,194],{"class":107},[97,838,839],{"class":135},"QgsProcessingFeedback",[97,841,842],{"class":107},"):\n",[97,844,845,848,851],{"class":99,"line":306},[97,846,847],{"class":103},"    def",[97,849,850],{"class":135}," setProgress",[97,852,853],{"class":107},"(self, progress):\n",[97,855,856,858,860,862,864,867,870,873,876,878,881,884,887,889,892,894,897,899,901],{"class":99,"line":317},[97,857,287],{"class":142},[97,859,194],{"class":107},[97,861,197],{"class":103},[97,863,222],{"class":166},[97,865,866],{"class":142},"\\r",[97,868,869],{"class":142},"  {",[97,871,872],{"class":107},"progress",[97,874,875],{"class":103},":5.1f",[97,877,209],{"class":142},[97,879,880],{"class":166},"%\"",[97,882,883],{"class":107},", ",[97,885,886],{"class":553},"end",[97,888,160],{"class":103},[97,890,891],{"class":166},"\"\"",[97,893,883],{"class":107},[97,895,896],{"class":553},"flush",[97,898,160],{"class":103},[97,900,600],{"class":142},[97,902,170],{"class":107},[97,904,905],{"class":99,"line":521},[97,906,121],{"emptyLinePlaceholder":120},[97,908,909,911,914],{"class":99,"line":534},[97,910,847],{"class":103},[97,912,913],{"class":135}," pushInfo",[97,915,916],{"class":107},"(self, info):\n",[97,918,919,921,923,925,927,930,933,935,937],{"class":99,"line":547},[97,920,287],{"class":142},[97,922,194],{"class":107},[97,924,197],{"class":103},[97,926,222],{"class":166},[97,928,929],{"class":142},"\\n{",[97,931,932],{"class":107},"info",[97,934,209],{"class":142},[97,936,222],{"class":166},[97,938,170],{"class":107},[97,940,941],{"class":99,"line":570},[97,942,121],{"emptyLinePlaceholder":120},[97,944,946],{"class":99,"line":945},18,[97,947,121],{"emptyLinePlaceholder":120},[97,949,951,953,956],{"class":99,"line":950},19,[97,952,132],{"class":103},[97,954,955],{"class":135}," buffer_then_clip",[97,957,958],{"class":107},"(source_path, overlay_path, out_path, distance_m):\n",[97,960,962,965,967,970,973],{"class":99,"line":961},20,[97,963,964],{"class":107},"    source ",[97,966,160],{"class":103},[97,968,969],{"class":107}," load_and_check(source_path, ",[97,971,972],{"class":166},"\"source\"",[97,974,170],{"class":107},[97,976,978,981,983,986,989],{"class":99,"line":977},21,[97,979,980],{"class":107},"    overlay ",[97,982,160],{"class":103},[97,984,985],{"class":107}," load_and_check(overlay_path, ",[97,987,988],{"class":166},"\"overlay\"",[97,990,170],{"class":107},[97,992,994],{"class":99,"line":993},22,[97,995,121],{"emptyLinePlaceholder":120},[97,997,999,1002,1004],{"class":99,"line":998},23,[97,1000,1001],{"class":107},"    context ",[97,1003,160],{"class":103},[97,1005,1006],{"class":107}," QgsProcessingContext()\n",[97,1008,1010],{"class":99,"line":1009},24,[97,1011,1012],{"class":107},"    context.setProject(QgsProject.instance())\n",[97,1014,1016,1019,1021],{"class":99,"line":1015},25,[97,1017,1018],{"class":107},"    feedback ",[97,1020,160],{"class":103},[97,1022,1023],{"class":107}," ProgressFeedback()\n",[97,1025,1027],{"class":99,"line":1026},26,[97,1028,121],{"emptyLinePlaceholder":120},[97,1030,1032,1035],{"class":99,"line":1031},27,[97,1033,1034],{"class":103},"    try",[97,1036,241],{"class":107},[97,1038,1040,1043,1045],{"class":99,"line":1039},28,[97,1041,1042],{"class":107},"        buffered ",[97,1044,160],{"class":103},[97,1046,1047],{"class":107}," buffer_layer(source, distance_m, context, feedback)\n",[97,1049,1051,1054,1056],{"class":99,"line":1050},29,[97,1052,1053],{"class":107},"        final ",[97,1055,160],{"class":103},[97,1057,1058],{"class":107}," clip_to_area(buffered, overlay, out_path, context, feedback)\n",[97,1060,1062,1065,1068,1071],{"class":99,"line":1061},30,[97,1063,1064],{"class":103},"    except",[97,1066,1067],{"class":107}," QgsProcessingException ",[97,1069,1070],{"class":103},"as",[97,1072,1073],{"class":107}," exc:\n",[97,1075,1077,1080,1082,1085,1087,1090,1092,1094],{"class":99,"line":1076},31,[97,1078,1079],{"class":107},"        feedback.pushInfo(",[97,1081,197],{"class":103},[97,1083,1084],{"class":166},"\"Pipeline failed: ",[97,1086,203],{"class":142},[97,1088,1089],{"class":107},"exc",[97,1091,209],{"class":142},[97,1093,222],{"class":166},[97,1095,170],{"class":107},[97,1097,1099],{"class":99,"line":1098},32,[97,1100,1101],{"class":103},"        raise\n",[97,1103,1105],{"class":99,"line":1104},33,[97,1106,121],{"emptyLinePlaceholder":120},[97,1108,1110],{"class":99,"line":1109},34,[97,1111,1112],{"class":491},"    # Validate the written result\n",[97,1114,1116,1119,1121,1124,1127,1129,1131],{"class":99,"line":1115},35,[97,1117,1118],{"class":107},"    written ",[97,1120,160],{"class":103},[97,1122,1123],{"class":107}," QgsVectorLayer(out_path, ",[97,1125,1126],{"class":166},"\"result\"",[97,1128,883],{"class":107},[97,1130,167],{"class":166},[97,1132,170],{"class":107},[97,1134,1136,1138,1140],{"class":99,"line":1135},36,[97,1137,176],{"class":103},[97,1139,179],{"class":103},[97,1141,1142],{"class":107}," written.isValid():\n",[97,1144,1146,1148,1151,1153,1155,1158,1160,1163,1165,1167],{"class":99,"line":1145},37,[97,1147,188],{"class":103},[97,1149,1150],{"class":142}," RuntimeError",[97,1152,194],{"class":107},[97,1154,197],{"class":103},[97,1156,1157],{"class":166},"\"Final output is not a valid layer: ",[97,1159,203],{"class":142},[97,1161,1162],{"class":107},"out_path",[97,1164,209],{"class":142},[97,1166,222],{"class":166},[97,1168,170],{"class":107},[97,1170,1172,1174,1176,1179,1181,1184,1186,1189,1191,1193,1195,1197],{"class":99,"line":1171},38,[97,1173,413],{"class":107},[97,1175,197],{"class":103},[97,1177,1178],{"class":166},"\"Done. ",[97,1180,203],{"class":142},[97,1182,1183],{"class":107},"written.featureCount()",[97,1185,209],{"class":142},[97,1187,1188],{"class":166}," features written to ",[97,1190,203],{"class":142},[97,1192,1162],{"class":107},[97,1194,209],{"class":142},[97,1196,222],{"class":166},[97,1198,170],{"class":107},[97,1200,1202,1204],{"class":99,"line":1201},39,[97,1203,320],{"class":103},[97,1205,1206],{"class":107}," written\n",[97,1208,1210],{"class":99,"line":1209},40,[97,1211,121],{"emptyLinePlaceholder":120},[97,1213,1215],{"class":99,"line":1214},41,[97,1216,121],{"emptyLinePlaceholder":120},[97,1218,1220],{"class":99,"line":1219},42,[97,1221,1222],{"class":491},"# Run in the QGIS Python Console:\n",[97,1224,1226],{"class":99,"line":1225},43,[97,1227,1228],{"class":491},"# buffer_then_clip(\n",[97,1230,1232],{"class":99,"line":1231},44,[97,1233,1234],{"class":491},"#     \"\u002Fdata\u002Fwells.gpkg\",\n",[97,1236,1238],{"class":99,"line":1237},45,[97,1239,1240],{"class":491},"#     \"\u002Fdata\u002Fcatchment.gpkg\",\n",[97,1242,1244],{"class":99,"line":1243},46,[97,1245,1246],{"class":491},"#     \"\u002Fdata\u002Fwells_buffer_clipped.gpkg\",\n",[97,1248,1250],{"class":99,"line":1249},47,[97,1251,1252],{"class":491},"#     distance_m=500,\n",[97,1254,1256],{"class":99,"line":1255},48,[97,1257,1258],{"class":491},"# )\n",[14,1260,1261,1263,1264,1267,1268,1271,1272,1274,1275,1277,1278,1281,1282,1284,1285,31],{},[47,1262,328],{}," One ",[18,1265,1266],{},"QgsProcessingContext"," and one ",[18,1269,1270],{},"ProgressFeedback"," are created once and threaded through both algorithms, so progress and any destination-CRS policy are consistent across the chain. The buffer's in-memory ",[18,1273,731],{}," flows into the clip's ",[18,1276,622],{}," with no disk write between them. Wrapping both runs in a single ",[18,1279,1280],{},"try\u002Fexcept QgsProcessingException"," makes a failure in either step report clearly and stop. The final block re-opens the written file with a fresh ",[18,1283,608],{}," and confirms validity and feature count — proof the chain produced real data, not an empty shell. This mirrors the shared-context discipline detailed in ",[27,1286,1288],{"href":1287},"\u002Fspatial-data-processing-automation\u002Fbatch-processing-with-pyqgis\u002F","batch processing with PyQGIS",[36,1290,1292],{"id":1291},"optional-swap-clip-for-extract-by-location","Optional: Swap Clip for Extract by Location",[14,1294,1295,1297,1298,31],{},[18,1296,24],{}," trims geometry to the overlay boundary — a buffer that straddles the study-area edge comes out cut along that edge. Sometimes you instead want every whole feature whose buffer touches the area, with geometry untouched. That is ",[18,1299,747],{},[88,1301,1303],{"className":90,"code":1302,"language":92,"meta":93,"style":93},"def extract_intersecting(buffered_layer, reference, out_path, context, feedback):\n    feedback.pushInfo(\"Extracting buffers intersecting the area ...\")\n    result = processing.run(\"native:extractbylocation\", {\n        \"INPUT\": buffered_layer,\n        \"PREDICATE\": [0],          # 0 = intersects\n        \"INTERSECT\": reference,\n        \"OUTPUT\": out_path,\n    }, context=context, feedback=feedback)\n    return result[\"OUTPUT\"]\n",[18,1304,1305,1315,1324,1337,1344,1360,1368,1375,1391],{"__ignoreMap":93},[97,1306,1307,1309,1312],{"class":99,"line":100},[97,1308,132],{"class":103},[97,1310,1311],{"class":135}," extract_intersecting",[97,1313,1314],{"class":107},"(buffered_layer, reference, out_path, context, feedback):\n",[97,1316,1317,1319,1322],{"class":99,"line":117},[97,1318,413],{"class":107},[97,1320,1321],{"class":166},"\"Extracting buffers intersecting the area ...\"",[97,1323,170],{"class":107},[97,1325,1326,1328,1330,1332,1335],{"class":99,"line":124},[97,1327,435],{"class":107},[97,1329,160],{"class":103},[97,1331,440],{"class":107},[97,1333,1334],{"class":166},"\"native:extractbylocation\"",[97,1336,446],{"class":107},[97,1338,1339,1341],{"class":99,"line":129},[97,1340,451],{"class":166},[97,1342,1343],{"class":107},": buffered_layer,\n",[97,1345,1346,1349,1352,1354,1357],{"class":99,"line":154},[97,1347,1348],{"class":166},"        \"PREDICATE\"",[97,1350,1351],{"class":107},": [",[97,1353,485],{"class":142},[97,1355,1356],{"class":107},"],          ",[97,1358,1359],{"class":491},"# 0 = intersects\n",[97,1361,1362,1365],{"class":99,"line":173},[97,1363,1364],{"class":166},"        \"INTERSECT\"",[97,1366,1367],{"class":107},": reference,\n",[97,1369,1370,1372],{"class":99,"line":185},[97,1371,537],{"class":166},[97,1373,1374],{"class":107},": out_path,\n",[97,1376,1377,1379,1381,1383,1385,1387,1389],{"class":99,"line":227},[97,1378,550],{"class":107},[97,1380,554],{"class":553},[97,1382,160],{"class":103},[97,1384,559],{"class":107},[97,1386,562],{"class":553},[97,1388,160],{"class":103},[97,1390,567],{"class":107},[97,1392,1393,1395,1397,1399],{"class":99,"line":244},[97,1394,320],{"class":103},[97,1396,575],{"class":107},[97,1398,578],{"class":166},[97,1400,581],{"class":107},[14,1402,1403,329,1405,1408,1409,1411,1412,1415,1416,1418,1419,1422,1423,1426,1427,1429,1430,1432,1433,1435],{},[47,1404,328],{},[18,1406,1407],{},"PREDICATE"," is a list of integer codes (",[18,1410,485],{}," = intersects, ",[18,1413,1414],{},"1"," = contains, ",[18,1417,516],{}," = disjoint, and so on). With ",[18,1420,1421],{},"intersects",", any buffer polygon that overlaps the ",[18,1424,1425],{},"INTERSECT"," reference is kept in full — no trimming. ",[18,1428,622],{}," is still the buffer layer from step two, so the chain is identical up to the final operation; only the algorithm and the keep-vs-trim semantics differ. Choose ",[18,1431,24],{}," for cookie-cutter geometry and ",[18,1434,747],{}," for whole-feature selection.",[36,1437,1439],{"id":1438},"qgis-version-compatibility","QGIS Version Compatibility",[14,1441,1442,1443,1446,1447,1449,1450,1452],{},"The script targets ",[47,1444,1445],{},"QGIS 3.34 LTR (Python 3.12)"," as the baseline. Both ",[18,1448,20],{}," and ",[18,1451,24],{}," have been stable native algorithms since the early 3.x series, so the chain runs unchanged across recent releases.",[1454,1455,1456,1469],"table",{},[1457,1458,1459],"thead",{},[1460,1461,1462,1466],"tr",{},[1463,1464,1465],"th",{},"QGIS \u002F Python",[1463,1467,1468],{},"Notes",[1470,1471,1472,1484,1494],"tbody",{},[1460,1473,1474,1478],{},[1475,1476,1477],"td",{},"3.28 LTR \u002F Py 3.9",[1475,1479,1480,1481,1483],{},"Fully supported. ",[18,1482,20],{}," parameter keys identical.",[1460,1485,1486,1491],{},[1475,1487,1488],{},[47,1489,1490],{},"3.34 LTR \u002F Py 3.12 (baseline)",[1475,1492,1493],{},"Recommended. Round-cap\u002Fround-join defaults as shown.",[1460,1495,1496,1499],{},[1475,1497,1498],{},"3.40 \u002F 3.44 \u002F Py 3.12",[1475,1500,1501],{},"Same parameters; some additional optional keys exist. Use \"Copy as Python Command\" to confirm.",[14,1503,1504,883,1507,1510,1511,1514],{},[18,1505,1506],{},"END_CAP_STYLE",[18,1508,1509],{},"JOIN_STYLE",", and ",[18,1512,1513],{},"MITER_LIMIT"," are integers in every version (0 = round, 1 = flat\u002Fmiter, 2 = square\u002Fbevel depending on the parameter). When in doubt, configure the buffer dialog interactively and copy the exact parameter dictionary.",[36,1516,1518],{"id":1517},"troubleshooting","Troubleshooting",[14,1520,1521,1524,1525,349,1527,1530,1531,1534],{},[47,1522,1523],{},"Buffer produces enormous or empty geometries."," The source layer is in a geographic CRS (degrees). A ",[18,1526,348],{},[18,1528,1529],{},"500"," then means 500 degrees. Reproject to a projected, metre-based CRS (for example ",[18,1532,1533],{},"native:reprojectlayer"," to a local UTM zone) before buffering, or set the distance in degrees deliberately.",[14,1536,1537,1540,1541,1449,1544,1547,1548,31],{},[47,1538,1539],{},"Clip returns zero features."," The buffer and overlay do not overlap, or they are in different CRSs so they appear not to intersect. Confirm ",[18,1542,1543],{},"source.crs()",[18,1545,1546],{},"overlay.crs()"," match, and check extents overlap with ",[18,1549,1550],{},"source.extent().intersects(overlay.extent())",[14,1552,1553,1558],{},[47,1554,1555,31],{},[18,1556,1557],{},"QgsProcessingException: Unknown parameter"," A parameter key is misspelled or does not exist for that algorithm. Open the algorithm dialog, set it up, and use \"Advanced > Copy as Python Command\" to get the authoritative keys.",[14,1560,1561,1564],{},[47,1562,1563],{},"The output file is locked or cannot be overwritten."," A previous run's layer is still loaded in QGIS, or another program (a GIS viewer, cloud-sync) holds the file. Remove the layer from the project or write to a fresh path, then retry.",[14,1566,1567,1570,1571,1573,1574,1576,1577,1449,1580,1583],{},[47,1568,1569],{},"Nothing prints during a long buffer."," The default ",[18,1572,839],{}," is silent. Pass the ",[18,1575,1270],{}," subclass shown above so ",[18,1578,1579],{},"setProgress",[18,1581,1582],{},"pushInfo"," reach the console.",[36,1585,1587],{"id":1586},"conclusion","Conclusion",[14,1589,1590,1591,1593,1594,1596,1597,1599,1600,1602],{},"Chaining ",[18,1592,20],{}," into ",[18,1595,24],{}," is the canonical small pipeline: two algorithms, one in-memory intermediate, one written result. The pattern scales — drop a dissolve or reproject step in the middle, or loop the whole function over many inputs — because the mechanics never change: capture ",[18,1598,604],{},", hand it to the next step's ",[18,1601,622],{},", and write only at the end. Validate before you start and after you finish, share one context and feedback object, and you have a reliable, repeatable operation you can fold into any larger automation.",[36,1604,1606],{"id":1605},"frequently-asked-questions","Frequently Asked Questions",[14,1608,1609,1616,1617,1619],{},[47,1610,1611,1612,1615],{},"Why use ",[18,1613,1614],{},"TEMPORARY_OUTPUT"," instead of writing the buffer to a file?","\nThe buffer is an intermediate you immediately consume. ",[18,1618,1614],{}," keeps it managed in memory, so the chain is faster and leaves no file to clean up. Only the clip — the final step — writes to disk.",[14,1621,1622,1631,1632,1634,1635,1637],{},[47,1623,1624,1625,1627,1628,1630],{},"Should I use ",[18,1626,24],{}," or ",[18,1629,747],{},"?","\nUse ",[18,1633,24],{}," when you want features trimmed to the overlay boundary. Use ",[18,1636,747],{}," (predicate intersects) when you want whole features that touch the area, with their geometry unchanged.",[14,1639,1640,1643,1644,1646,1647,1650],{},[47,1641,1642],{},"How do I buffer by a field value instead of a fixed distance?","\nSet ",[18,1645,348],{}," to a ",[18,1648,1649],{},"QgsProperty.fromExpression('\"radius_m\"')"," to drive the distance from an attribute, or pass the field reference supported by the buffer parameter in your QGIS version. Confirm the exact form with \"Copy as Python Command\".",[14,1652,1653,1656,1657,1660,1661,1663],{},[47,1654,1655],{},"Can I run this on hundreds of files?","\nYes — wrap ",[18,1658,1659],{},"buffer_then_clip()"," in a loop over your inputs, reusing the validation and feedback pattern. See ",[27,1662,1288],{"href":1287}," for the iteration and error-isolation structure.",[36,1665,1667],{"id":1666},"related","Related",[41,1669,1670,1674,1679],{},[44,1671,1672],{},[27,1673,30],{"href":29},[44,1675,1676],{},[27,1677,1678],{"href":73},"Batch Processing with PyQGIS",[44,1680,1681],{},[27,1682,1683],{"href":755},"Clip a Vector Layer in PyQGIS",[1685,1686,1687],"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 .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 .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}",{"title":93,"searchDepth":117,"depth":117,"links":1689},[1690,1691,1692,1693,1694,1695,1696,1697,1698,1699,1700],{"id":38,"depth":117,"text":39},{"id":82,"depth":117,"text":83},{"id":356,"depth":117,"text":357},{"id":612,"depth":117,"text":613},{"id":763,"depth":117,"text":764},{"id":1291,"depth":117,"text":1292},{"id":1438,"depth":117,"text":1439},{"id":1517,"depth":117,"text":1518},{"id":1586,"depth":117,"text":1587},{"id":1605,"depth":117,"text":1606},{"id":1666,"depth":117,"text":1667},"Chain native:buffer into native:clip in one PyQGIS script using TEMPORARY_OUTPUT, with progress feedback, validation, and a written final result.","md",{},"\u002Fspatial-data-processing-automation\u002Fchaining-processing-algorithms\u002Fchain-buffer-and-clip-pyqgis",{"title":5,"description":1701},"spatial-data-processing-automation\u002Fchaining-processing-algorithms\u002Fchain-buffer-and-clip-pyqgis\u002Findex","hFXEU5iNUf9SQo_j0F05_V8ArPNYUTQVLOfz4p5Wus4",1781792483476]