[{"data":1,"prerenderedAt":1949},["ShallowReactive",2],{"doc:\u002Fspatial-data-processing-automation\u002Fchaining-processing-algorithms\u002Frun-grass-and-saga-algorithms-pyqgis":3},{"id":4,"title":5,"body":6,"description":1938,"extension":1939,"meta":1940,"navigation":221,"path":1945,"seo":1946,"stem":1947,"__hash__":1948},"docs\u002Fspatial-data-processing-automation\u002Fchaining-processing-algorithms\u002Frun-grass-and-saga-algorithms-pyqgis\u002Findex.md","Run GRASS and SAGA Algorithms from PyQGIS",{"type":7,"value":8,"toc":1923},"minimark",[9,13,22,31,167,172,182,186,189,449,468,472,479,651,667,671,674,772,981,1015,1019,1022,1264,1278,1282,1289,1422,1438,1442,1445,1522,1780,1796,1800,1803,1807,1816,1820,1849,1853,1856,1860,1866,1872,1878,1884,1888,1919],[10,11,5],"h1",{"id":12},"run-grass-and-saga-algorithms-from-pyqgis",[14,15,16,17,21],"p",{},"QGIS's native algorithms cover a lot, but some of the most powerful spatial tools live in GRASS GIS and SAGA: hydrological modelling, viewsheds with curvature, cost-distance surfaces, terrain indices, image segmentation, network cleaning. Processing wraps both as providers, so their tools can be called from Python with ",[18,19,20],"code",{},"processing.run"," like any native algorithm — with a few differences in naming, parameters and behaviour that trip up first-time users.",[14,23,24,25,30],{},"This recipe belongs to ",[26,27,29],"a",{"href":28},"\u002Fspatial-data-processing-automation\u002Fchaining-processing-algorithms\u002F","Chaining Processing Algorithms",". It checks that the providers are available, finds algorithm ids robustly, passes GRASS parameters and flags, controls the computational region and resolution, handles outputs and errors, and compares GRASS, SAGA and native tools.",[14,32,33],{},[34,35,40,44,48,55,72,81,90,95,100,107,114,119,123,127,131,136,140,143,146,150,156,161,164],"svg",{"viewBox":36,"role":37,"ariaLabel":38,"xmlns":39},"0 0 760 226","img","A processing call to a GRASS or SAGA algorithm exporting inputs, running the external tool in a temporary workspace and importing outputs back","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg",[41,42,43],"title",{},"How external tools run through Processing",[45,46,47],"desc",{},"A processing.run call to a GRASS or SAGA algorithm goes through the provider, which exports inputs to the tool's own format, writes a temporary GRASS location or SAGA files, runs the external program, and imports the outputs back as GeoTIFFs or GeoPackages. The extra conversion explains why these algorithms start more slowly than native ones and why output formats are files rather than memory layers.",[49,50],"rect",{"x":51,"y":51,"width":52,"height":53,"fill":54},"0","760","226","#f6f3ea",[56,57,58],"defs",{},[59,60,67],"marker",{"id":61,"viewBox":62,"refX":63,"refY":64,"markerWidth":65,"markerHeight":65,"orient":66},"gsProvidersArrow","0 0 10 10","8","5","7","auto-start-reverse",[68,69],"path",{"d":70,"fill":71},"M0 0 L10 5 L0 10 z","#2f3b35",[73,74,80],"text",{"x":75,"y":76,"style":77,"fill":78,"textAnchor":79},"380","28","text-anchor:middle;font-size:14px;font-family:sans-serif;font-weight:bold","#17211d","middle","Export, run externally, import back",[49,82],{"x":83,"y":84,"width":85,"height":86,"rx":63,"fill":87,"stroke":88,"style":89},"24","90","160","100","#fffdf7","#59645f","stroke-width:2",[73,91,20],{"x":92,"y":93,"style":94,"fill":78,"textAnchor":79},"104","131.78","text-anchor:middle;font-size:11.5px;font-family:sans-serif;font-weight:bold",[73,96,99],{"x":92,"y":97,"style":98,"fill":88,"textAnchor":79},"155.78","text-anchor:middle;font-size:10.5px;font-family:sans-serif","grass: \u002F saga:",[101,102],"line",{"x1":103,"y1":104,"x2":105,"y2":104,"stroke":71,"style":106},"184","140","224","stroke-width:1.8;marker-end:url(#gsProvidersArrow)",[49,108],{"x":109,"y":110,"width":111,"height":104,"rx":63,"fill":112,"stroke":113,"style":89},"228","70","170","#eef7f4","#0f766e",[73,115,118],{"x":116,"y":117,"style":94,"fill":113,"textAnchor":79},"313","117.78","provider",[73,120,122],{"x":116,"y":121,"style":98,"fill":71,"textAnchor":79},"143.78","export inputs",[73,124,126],{"x":116,"y":125,"style":98,"fill":71,"textAnchor":79},"169.78","temp workspace",[101,128],{"x1":129,"y1":104,"x2":130,"y2":104,"stroke":71,"style":106},"398","438",[49,132],{"x":133,"y":110,"width":104,"height":104,"rx":63,"fill":134,"stroke":135,"style":89},"442","#fdf2e2","#b45309",[73,137,139],{"x":138,"y":117,"style":94,"fill":135,"textAnchor":79},"512","GRASS \u002F SAGA",[73,141,142],{"x":138,"y":121,"style":98,"fill":88,"textAnchor":79},"external",[73,144,145],{"x":138,"y":125,"style":98,"fill":88,"textAnchor":79},"process",[101,147],{"x1":148,"y1":104,"x2":149,"y2":104,"stroke":71,"style":106},"582","622",[49,151],{"x":152,"y":110,"width":153,"height":104,"rx":63,"fill":154,"stroke":155,"style":89},"626","110","#e8efe6","#15803d",[73,157,160],{"x":158,"y":117,"style":94,"fill":159,"textAnchor":79},"681","#166534","outputs",[73,162,163],{"x":158,"y":121,"style":98,"fill":88,"textAnchor":79},"files",[73,165,166],{"x":158,"y":125,"style":98,"fill":88,"textAnchor":79},"imported",[168,169,171],"h2",{"id":170},"prerequisites","Prerequisites",[173,174,175,179],"ul",{},[176,177,178],"li",{},"QGIS 3.34 LTR or newer, or the QGIS 4 series. GRASS is bundled with the Windows (OSGeo4W) and macOS installers; on Linux it is a separate package. SAGA support comes from the separate \"Processing Saga NextGen Provider\" plugin on current releases.",[176,180,181],{},"Inputs in a projected CRS for anything involving distances or areas.",[168,183,185],{"id":184},"check-the-providers-are-available","Check the providers are available",[14,187,188],{},"Scripts that depend on GRASS or SAGA should verify the providers before doing any work, because a missing provider fails with an unhelpful \"algorithm not found\".",[190,191,196],"pre",{"className":192,"code":193,"language":194,"meta":195,"style":195},"language-python shiki shiki-themes github-dark","from qgis.core import QgsApplication\n\nregistry = QgsApplication.processingRegistry()\nfor p in registry.providers():\n    if p.id().startswith((\"grass\", \"saga\", \"sagang\")):\n        print(f\"{p.id():\u003C8} {p.name():\u003C28} active={p.isActive()} algorithms={len(p.algorithms())}\")\n\ndef require_provider(prefix):\n    for p in registry.providers():\n        if p.id().startswith(prefix) and p.isActive() and p.algorithms():\n            return p\n    raise RuntimeError(f\"Processing provider '{prefix}*' is not installed or not active\")\n\ngrass = require_provider(\"grass\")\n","python","",[18,197,198,216,223,235,250,277,342,347,360,372,392,401,429,434],{"__ignoreMap":195},[199,200,202,206,210,213],"span",{"class":101,"line":201},1,[199,203,205],{"class":204},"snl16","from",[199,207,209],{"class":208},"s95oV"," qgis.core ",[199,211,212],{"class":204},"import",[199,214,215],{"class":208}," QgsApplication\n",[199,217,219],{"class":101,"line":218},2,[199,220,222],{"emptyLinePlaceholder":221},true,"\n",[199,224,226,229,232],{"class":101,"line":225},3,[199,227,228],{"class":208},"registry ",[199,230,231],{"class":204},"=",[199,233,234],{"class":208}," QgsApplication.processingRegistry()\n",[199,236,238,241,244,247],{"class":101,"line":237},4,[199,239,240],{"class":204},"for",[199,242,243],{"class":208}," p ",[199,245,246],{"class":204},"in",[199,248,249],{"class":208}," registry.providers():\n",[199,251,253,256,259,263,266,269,271,274],{"class":101,"line":252},5,[199,254,255],{"class":204},"    if",[199,257,258],{"class":208}," p.id().startswith((",[199,260,262],{"class":261},"sU2Wk","\"grass\"",[199,264,265],{"class":208},", ",[199,267,268],{"class":261},"\"saga\"",[199,270,265],{"class":208},[199,272,273],{"class":261},"\"sagang\"",[199,275,276],{"class":208},")):\n",[199,278,280,284,287,290,293,296,299,302,305,308,311,314,316,319,321,324,326,329,332,335,337,339],{"class":101,"line":279},6,[199,281,283],{"class":282},"sDLfK","        print",[199,285,286],{"class":208},"(",[199,288,289],{"class":204},"f",[199,291,292],{"class":261},"\"",[199,294,295],{"class":282},"{",[199,297,298],{"class":208},"p.id()",[199,300,301],{"class":204},":\u003C8",[199,303,304],{"class":282},"}",[199,306,307],{"class":282}," {",[199,309,310],{"class":208},"p.name()",[199,312,313],{"class":204},":\u003C28",[199,315,304],{"class":282},[199,317,318],{"class":261}," active=",[199,320,295],{"class":282},[199,322,323],{"class":208},"p.isActive()",[199,325,304],{"class":282},[199,327,328],{"class":261}," algorithms=",[199,330,331],{"class":282},"{len",[199,333,334],{"class":208},"(p.algorithms())",[199,336,304],{"class":282},[199,338,292],{"class":261},[199,340,341],{"class":208},")\n",[199,343,345],{"class":101,"line":344},7,[199,346,222],{"emptyLinePlaceholder":221},[199,348,350,353,357],{"class":101,"line":349},8,[199,351,352],{"class":204},"def",[199,354,356],{"class":355},"svObZ"," require_provider",[199,358,359],{"class":208},"(prefix):\n",[199,361,363,366,368,370],{"class":101,"line":362},9,[199,364,365],{"class":204},"    for",[199,367,243],{"class":208},[199,369,246],{"class":204},[199,371,249],{"class":208},[199,373,375,378,381,384,387,389],{"class":101,"line":374},10,[199,376,377],{"class":204},"        if",[199,379,380],{"class":208}," p.id().startswith(prefix) ",[199,382,383],{"class":204},"and",[199,385,386],{"class":208}," p.isActive() ",[199,388,383],{"class":204},[199,390,391],{"class":208}," p.algorithms():\n",[199,393,395,398],{"class":101,"line":394},11,[199,396,397],{"class":204},"            return",[199,399,400],{"class":208}," p\n",[199,402,404,407,410,412,414,417,419,422,424,427],{"class":101,"line":403},12,[199,405,406],{"class":204},"    raise",[199,408,409],{"class":282}," RuntimeError",[199,411,286],{"class":208},[199,413,289],{"class":204},[199,415,416],{"class":261},"\"Processing provider '",[199,418,295],{"class":282},[199,420,421],{"class":208},"prefix",[199,423,304],{"class":282},[199,425,426],{"class":261},"*' is not installed or not active\"",[199,428,341],{"class":208},[199,430,432],{"class":101,"line":431},13,[199,433,222],{"emptyLinePlaceholder":221},[199,435,437,440,442,445,447],{"class":101,"line":436},14,[199,438,439],{"class":208},"grass ",[199,441,231],{"class":204},[199,443,444],{"class":208}," require_provider(",[199,446,262],{"class":261},[199,448,341],{"class":208},[14,450,451,455,456,459,460,463,464,467],{},[452,453,454],"strong",{},"Breakdown:"," Listing providers shows their ids, whether they are active and how many algorithms they expose — a provider that is active but has zero algorithms usually means the external program was not found. The GRASS provider's id has been ",[18,457,458],{},"grass7"," on older releases and ",[18,461,462],{},"grass"," on newer ones; SAGA appears as ",[18,465,466],{},"sagang"," from the NextGen plugin. Failing early with a clear message is much friendlier than a missing-algorithm error halfway through a batch.",[168,469,471],{"id":470},"find-algorithm-ids-robustly","Find algorithm ids robustly",[14,473,474,475,478],{},"Because provider ids differ between versions, hard-coding ",[18,476,477],{},"grass7:r.slope.aspect"," breaks on a newer QGIS. Looking algorithms up by the tool name keeps scripts portable.",[190,480,482],{"className":192,"code":481,"language":194,"meta":195,"style":195},"def find_alg(tool, prefixes=(\"grass\", \"sagang\", \"saga\")):\n    for alg in registry.algorithms():\n        prov, _, name = alg.id().partition(\":\")\n        if name == tool and prov.startswith(prefixes):\n            return alg.id()\n    raise RuntimeError(f\"{tool} not found in providers {prefixes}\")\n\nR_SLOPE = find_alg(\"r.slope.aspect\")\nprint(R_SLOPE)\nimport processing\nprint(processing.algorithmHelp(R_SLOPE)[:800])\n",[18,483,484,510,522,537,555,562,595,599,615,626,633],{"__ignoreMap":195},[199,485,486,488,491,494,496,498,500,502,504,506,508],{"class":101,"line":201},[199,487,352],{"class":204},[199,489,490],{"class":355}," find_alg",[199,492,493],{"class":208},"(tool, prefixes",[199,495,231],{"class":204},[199,497,286],{"class":208},[199,499,262],{"class":261},[199,501,265],{"class":208},[199,503,273],{"class":261},[199,505,265],{"class":208},[199,507,268],{"class":261},[199,509,276],{"class":208},[199,511,512,514,517,519],{"class":101,"line":218},[199,513,365],{"class":204},[199,515,516],{"class":208}," alg ",[199,518,246],{"class":204},[199,520,521],{"class":208}," registry.algorithms():\n",[199,523,524,527,529,532,535],{"class":101,"line":225},[199,525,526],{"class":208},"        prov, _, name ",[199,528,231],{"class":204},[199,530,531],{"class":208}," alg.id().partition(",[199,533,534],{"class":261},"\":\"",[199,536,341],{"class":208},[199,538,539,541,544,547,550,552],{"class":101,"line":237},[199,540,377],{"class":204},[199,542,543],{"class":208}," name ",[199,545,546],{"class":204},"==",[199,548,549],{"class":208}," tool ",[199,551,383],{"class":204},[199,553,554],{"class":208}," prov.startswith(prefixes):\n",[199,556,557,559],{"class":101,"line":252},[199,558,397],{"class":204},[199,560,561],{"class":208}," alg.id()\n",[199,563,564,566,568,570,572,574,576,579,581,584,586,589,591,593],{"class":101,"line":279},[199,565,406],{"class":204},[199,567,409],{"class":282},[199,569,286],{"class":208},[199,571,289],{"class":204},[199,573,292],{"class":261},[199,575,295],{"class":282},[199,577,578],{"class":208},"tool",[199,580,304],{"class":282},[199,582,583],{"class":261}," not found in providers ",[199,585,295],{"class":282},[199,587,588],{"class":208},"prefixes",[199,590,304],{"class":282},[199,592,292],{"class":261},[199,594,341],{"class":208},[199,596,597],{"class":101,"line":344},[199,598,222],{"emptyLinePlaceholder":221},[199,600,601,604,607,610,613],{"class":101,"line":349},[199,602,603],{"class":282},"R_SLOPE",[199,605,606],{"class":204}," =",[199,608,609],{"class":208}," find_alg(",[199,611,612],{"class":261},"\"r.slope.aspect\"",[199,614,341],{"class":208},[199,616,617,620,622,624],{"class":101,"line":362},[199,618,619],{"class":282},"print",[199,621,286],{"class":208},[199,623,603],{"class":282},[199,625,341],{"class":208},[199,627,628,630],{"class":101,"line":374},[199,629,212],{"class":204},[199,631,632],{"class":208}," processing\n",[199,634,635,637,640,642,645,648],{"class":101,"line":394},[199,636,619],{"class":282},[199,638,639],{"class":208},"(processing.algorithmHelp(",[199,641,603],{"class":282},[199,643,644],{"class":208},")[:",[199,646,647],{"class":282},"800",[199,649,650],{"class":208},"])\n",[14,652,653,655,656,659,660,662,663,666],{},[452,654,454],{}," Matching the part after the colon finds ",[18,657,658],{},"grass:r.slope.aspect"," or ",[18,661,477],{}," alike; the same works for SAGA tool names. ",[18,664,665],{},"algorithmHelp"," prints every parameter with its name, type and default — essential, because GRASS and SAGA parameter names come from the tools themselves and differ from native conventions. Keep the lookup function in a small shared module so every script uses it.",[168,668,670],{"id":669},"pass-grass-parameters-and-flags","Pass GRASS parameters and flags",[14,672,673],{},"GRASS parameters use the tool's own option names in lower case, and flags are booleans with a leading dash. Output parameters take file paths.",[14,675,676],{},[34,677,680,683,686,689,692,696,702,707,711,716,721,725,728,731,734,737,740,743,746,748,752,758,763,767],{"viewBox":678,"role":37,"ariaLabel":679,"xmlns":39},"0 0 760 240","GRASS parameter conventions in Processing: option names, dashed boolean flags, file outputs, and region and cell size parameters",[41,681,682],{},"GRASS parameter conventions",[45,684,685],{},"GRASS options such as elevation and slope keep their GRASS names. Flags such as -a or -e become boolean parameters whose keys start with a dash. Outputs are file paths, usually GeoTIFF. Region and resolution parameters, GRASS_REGION_PARAMETER and GRASS_REGION_CELLSIZE_PARAMETER, control the computational extent and cell size.",[49,687],{"x":51,"y":51,"width":52,"height":688,"fill":54},"240",[73,690,691],{"x":75,"y":76,"style":77,"fill":78,"textAnchor":79},"Options, flags, outputs, region",[49,693],{"x":83,"y":694,"width":695,"height":85,"rx":63,"fill":112,"stroke":113,"style":89},"56","168",[73,697,701],{"x":698,"y":699,"style":700,"fill":113,"textAnchor":79},"108","97.6","text-anchor:middle;font-size:11.0px;font-family:sans-serif;font-weight:bold","options",[73,703,706],{"x":698,"y":704,"style":705,"fill":71,"textAnchor":79},"125.6","text-anchor:middle;font-size:9.5px;font-family:monospace","elevation",[73,708,710],{"x":698,"y":709,"style":705,"fill":71,"textAnchor":79},"153.6","format",[73,712,715],{"x":698,"y":713,"style":714,"fill":88,"textAnchor":79},"181.6","text-anchor:middle;font-size:10.0px;font-family:sans-serif","GRASS names",[49,717],{"x":718,"y":694,"width":695,"height":85,"rx":63,"fill":719,"stroke":720,"style":89},"206","#eff3ff","#2563eb",[73,722,724],{"x":723,"y":699,"style":700,"fill":720,"textAnchor":79},"290","flags",[73,726,727],{"x":723,"y":704,"style":705,"fill":71,"textAnchor":79},"\"-a\": True",[73,729,730],{"x":723,"y":709,"style":705,"fill":71,"textAnchor":79},"\"-e\": True",[73,732,733],{"x":723,"y":713,"style":714,"fill":88,"textAnchor":79},"booleans",[49,735],{"x":736,"y":694,"width":695,"height":85,"rx":63,"fill":134,"stroke":135,"style":89},"388",[73,738,160],{"x":739,"y":699,"style":700,"fill":135,"textAnchor":79},"472",[73,741,742],{"x":739,"y":704,"style":705,"fill":71,"textAnchor":79},"slope: path",[73,744,745],{"x":739,"y":709,"style":705,"fill":71,"textAnchor":79},"aspect: path",[73,747,163],{"x":739,"y":713,"style":714,"fill":88,"textAnchor":79},[49,749],{"x":750,"y":694,"width":751,"height":85,"rx":63,"fill":154,"stroke":155,"style":89},"570","166",[73,753,757],{"x":754,"y":755,"style":756,"fill":159,"textAnchor":79},"653","97.42","text-anchor:middle;font-size:10.5px;font-family:sans-serif;font-weight:bold","region",[73,759,762],{"x":754,"y":760,"style":761,"fill":71,"textAnchor":79},"125.42","text-anchor:middle;font-size:9.0px;font-family:monospace","GRASS_REGION_",[73,764,766],{"x":754,"y":765,"style":761,"fill":71,"textAnchor":79},"153.42","PARAMETER",[73,768,771],{"x":754,"y":769,"style":770,"fill":88,"textAnchor":79},"181.42","text-anchor:middle;font-size:9.5px;font-family:sans-serif","extent, cell size",[190,773,775],{"className":192,"code":774,"language":194,"meta":195,"style":195},"dem = \"\u002Fdata\u002Fterrain\u002Fdem_10m.tif\"\nres = processing.run(R_SLOPE, {\n    \"elevation\": dem,\n    \"format\": 0,                         # 0 = degrees, 1 = percent\n    \"precision\": 0,                      # FCELL\n    \"-a\": True,                          # do not align region to input\n    \"-e\": True,                          # compute values at edges\n    \"zscale\": 1.0,\n    \"min_slope\": 0.0,\n    \"slope\": \"\u002Fdata\u002Fterrain\u002Fslope_grass.tif\",\n    \"aspect\": \"\u002Fdata\u002Fterrain\u002Faspect_grass.tif\",\n    \"GRASS_REGION_PARAMETER\": None,\n    \"GRASS_REGION_CELLSIZE_PARAMETER\": 0,\n    \"GRASS_RASTER_FORMAT_OPT\": \"COMPRESS=DEFLATE\",\n})\nprint(res[\"slope\"], res[\"aspect\"])\n",[18,776,777,787,802,810,827,842,858,872,885,897,909,921,933,944,956,962],{"__ignoreMap":195},[199,778,779,782,784],{"class":101,"line":201},[199,780,781],{"class":208},"dem ",[199,783,231],{"class":204},[199,785,786],{"class":261}," \"\u002Fdata\u002Fterrain\u002Fdem_10m.tif\"\n",[199,788,789,792,794,797,799],{"class":101,"line":218},[199,790,791],{"class":208},"res ",[199,793,231],{"class":204},[199,795,796],{"class":208}," processing.run(",[199,798,603],{"class":282},[199,800,801],{"class":208},", {\n",[199,803,804,807],{"class":101,"line":225},[199,805,806],{"class":261},"    \"elevation\"",[199,808,809],{"class":208},": dem,\n",[199,811,812,815,818,820,823],{"class":101,"line":237},[199,813,814],{"class":261},"    \"format\"",[199,816,817],{"class":208},": ",[199,819,51],{"class":282},[199,821,822],{"class":208},",                         ",[199,824,826],{"class":825},"sjoCn","# 0 = degrees, 1 = percent\n",[199,828,829,832,834,836,839],{"class":101,"line":252},[199,830,831],{"class":261},"    \"precision\"",[199,833,817],{"class":208},[199,835,51],{"class":282},[199,837,838],{"class":208},",                      ",[199,840,841],{"class":825},"# FCELL\n",[199,843,844,847,849,852,855],{"class":101,"line":279},[199,845,846],{"class":261},"    \"-a\"",[199,848,817],{"class":208},[199,850,851],{"class":282},"True",[199,853,854],{"class":208},",                          ",[199,856,857],{"class":825},"# do not align region to input\n",[199,859,860,863,865,867,869],{"class":101,"line":344},[199,861,862],{"class":261},"    \"-e\"",[199,864,817],{"class":208},[199,866,851],{"class":282},[199,868,854],{"class":208},[199,870,871],{"class":825},"# compute values at edges\n",[199,873,874,877,879,882],{"class":101,"line":349},[199,875,876],{"class":261},"    \"zscale\"",[199,878,817],{"class":208},[199,880,881],{"class":282},"1.0",[199,883,884],{"class":208},",\n",[199,886,887,890,892,895],{"class":101,"line":362},[199,888,889],{"class":261},"    \"min_slope\"",[199,891,817],{"class":208},[199,893,894],{"class":282},"0.0",[199,896,884],{"class":208},[199,898,899,902,904,907],{"class":101,"line":374},[199,900,901],{"class":261},"    \"slope\"",[199,903,817],{"class":208},[199,905,906],{"class":261},"\"\u002Fdata\u002Fterrain\u002Fslope_grass.tif\"",[199,908,884],{"class":208},[199,910,911,914,916,919],{"class":101,"line":394},[199,912,913],{"class":261},"    \"aspect\"",[199,915,817],{"class":208},[199,917,918],{"class":261},"\"\u002Fdata\u002Fterrain\u002Faspect_grass.tif\"",[199,920,884],{"class":208},[199,922,923,926,928,931],{"class":101,"line":403},[199,924,925],{"class":261},"    \"GRASS_REGION_PARAMETER\"",[199,927,817],{"class":208},[199,929,930],{"class":282},"None",[199,932,884],{"class":208},[199,934,935,938,940,942],{"class":101,"line":431},[199,936,937],{"class":261},"    \"GRASS_REGION_CELLSIZE_PARAMETER\"",[199,939,817],{"class":208},[199,941,51],{"class":282},[199,943,884],{"class":208},[199,945,946,949,951,954],{"class":101,"line":436},[199,947,948],{"class":261},"    \"GRASS_RASTER_FORMAT_OPT\"",[199,950,817],{"class":208},[199,952,953],{"class":261},"\"COMPRESS=DEFLATE\"",[199,955,884],{"class":208},[199,957,959],{"class":101,"line":958},15,[199,960,961],{"class":208},"})\n",[199,963,965,967,970,973,976,979],{"class":101,"line":964},16,[199,966,619],{"class":282},[199,968,969],{"class":208},"(res[",[199,971,972],{"class":261},"\"slope\"",[199,974,975],{"class":208},"], res[",[199,977,978],{"class":261},"\"aspect\"",[199,980,650],{"class":208},[14,982,983,985,986,265,988,265,990,993,994,997,998,1001,1002,1005,1006,1009,1010,1014],{},[452,984,454],{}," Option names (",[18,987,706],{},[18,989,710],{},[18,991,992],{},"zscale",") are exactly those in the GRASS manual for ",[18,995,996],{},"r.slope.aspect",". Flags are keys with a dash; omitting a flag means false. Each output is a separate parameter with a file path, and unrequested outputs can be left out. ",[18,999,1000],{},"GRASS_REGION_PARAMETER"," set to None uses the input's extent; ",[18,1003,1004],{},"GRASS_REGION_CELLSIZE_PARAMETER"," 0 uses the input's resolution. ",[18,1007,1008],{},"GRASS_RASTER_FORMAT_OPT"," passes creation options to the exported GeoTIFF. The same output from QGIS's native algorithms is covered in ",[26,1011,1013],{"href":1012},"\u002Fspatial-data-processing-automation\u002Fterrain-and-interpolation-analysis\u002Fgenerate-slope-aspect-hillshade-pyqgis\u002F","generating slope, aspect and hillshade",", useful for comparison.",[168,1016,1018],{"id":1017},"control-region-and-resolution","Control region and resolution",[14,1020,1021],{},"GRASS computes on a region: an extent and a cell size. When several inputs have different extents or resolutions, setting the region explicitly decides the output grid and avoids surprises.",[190,1023,1025],{"className":192,"code":1024,"language":194,"meta":195,"style":195},"from qgis.core import QgsRasterLayer\n\nref = QgsRasterLayer(dem, \"dem\")\ne = ref.extent()\nregion = f\"{e.xMinimum()},{e.xMaximum()},{e.yMinimum()},{e.yMaximum()} [{ref.crs().authid()}]\"\n\nR_COST = find_alg(\"r.cost\")\ncost = processing.run(R_COST, {\n    \"input\": \"\u002Fdata\u002Fmodel\u002Ffriction_10m.tif\",\n    \"start_points\": \"\u002Fdata\u002Fmodel\u002Forigins.gpkg\",\n    \"max_cost\": 0, \"null_cost\": None, \"memory\": 1024,\n    \"-k\": True,                           # knight's move for smoother surfaces\n    \"output\": \"\u002Fdata\u002Fmodel\u002Fcumulative_cost.tif\",\n    \"GRASS_REGION_PARAMETER\": region,\n    \"GRASS_REGION_CELLSIZE_PARAMETER\": ref.rasterUnitsPerPixelX(),\n})[\"output\"]\n",[18,1026,1027,1038,1042,1057,1067,1127,1131,1145,1158,1170,1182,1212,1227,1239,1246,1253],{"__ignoreMap":195},[199,1028,1029,1031,1033,1035],{"class":101,"line":201},[199,1030,205],{"class":204},[199,1032,209],{"class":208},[199,1034,212],{"class":204},[199,1036,1037],{"class":208}," QgsRasterLayer\n",[199,1039,1040],{"class":101,"line":218},[199,1041,222],{"emptyLinePlaceholder":221},[199,1043,1044,1047,1049,1052,1055],{"class":101,"line":225},[199,1045,1046],{"class":208},"ref ",[199,1048,231],{"class":204},[199,1050,1051],{"class":208}," QgsRasterLayer(dem, ",[199,1053,1054],{"class":261},"\"dem\"",[199,1056,341],{"class":208},[199,1058,1059,1062,1064],{"class":101,"line":237},[199,1060,1061],{"class":208},"e ",[199,1063,231],{"class":204},[199,1065,1066],{"class":208}," ref.extent()\n",[199,1068,1069,1072,1074,1077,1079,1081,1084,1086,1089,1091,1094,1096,1098,1100,1103,1105,1107,1109,1112,1114,1117,1119,1122,1124],{"class":101,"line":252},[199,1070,1071],{"class":208},"region ",[199,1073,231],{"class":204},[199,1075,1076],{"class":204}," f",[199,1078,292],{"class":261},[199,1080,295],{"class":282},[199,1082,1083],{"class":208},"e.xMinimum()",[199,1085,304],{"class":282},[199,1087,1088],{"class":261},",",[199,1090,295],{"class":282},[199,1092,1093],{"class":208},"e.xMaximum()",[199,1095,304],{"class":282},[199,1097,1088],{"class":261},[199,1099,295],{"class":282},[199,1101,1102],{"class":208},"e.yMinimum()",[199,1104,304],{"class":282},[199,1106,1088],{"class":261},[199,1108,295],{"class":282},[199,1110,1111],{"class":208},"e.yMaximum()",[199,1113,304],{"class":282},[199,1115,1116],{"class":261}," [",[199,1118,295],{"class":282},[199,1120,1121],{"class":208},"ref.crs().authid()",[199,1123,304],{"class":282},[199,1125,1126],{"class":261},"]\"\n",[199,1128,1129],{"class":101,"line":279},[199,1130,222],{"emptyLinePlaceholder":221},[199,1132,1133,1136,1138,1140,1143],{"class":101,"line":344},[199,1134,1135],{"class":282},"R_COST",[199,1137,606],{"class":204},[199,1139,609],{"class":208},[199,1141,1142],{"class":261},"\"r.cost\"",[199,1144,341],{"class":208},[199,1146,1147,1150,1152,1154,1156],{"class":101,"line":349},[199,1148,1149],{"class":208},"cost ",[199,1151,231],{"class":204},[199,1153,796],{"class":208},[199,1155,1135],{"class":282},[199,1157,801],{"class":208},[199,1159,1160,1163,1165,1168],{"class":101,"line":362},[199,1161,1162],{"class":261},"    \"input\"",[199,1164,817],{"class":208},[199,1166,1167],{"class":261},"\"\u002Fdata\u002Fmodel\u002Ffriction_10m.tif\"",[199,1169,884],{"class":208},[199,1171,1172,1175,1177,1180],{"class":101,"line":374},[199,1173,1174],{"class":261},"    \"start_points\"",[199,1176,817],{"class":208},[199,1178,1179],{"class":261},"\"\u002Fdata\u002Fmodel\u002Forigins.gpkg\"",[199,1181,884],{"class":208},[199,1183,1184,1187,1189,1191,1193,1196,1198,1200,1202,1205,1207,1210],{"class":101,"line":394},[199,1185,1186],{"class":261},"    \"max_cost\"",[199,1188,817],{"class":208},[199,1190,51],{"class":282},[199,1192,265],{"class":208},[199,1194,1195],{"class":261},"\"null_cost\"",[199,1197,817],{"class":208},[199,1199,930],{"class":282},[199,1201,265],{"class":208},[199,1203,1204],{"class":261},"\"memory\"",[199,1206,817],{"class":208},[199,1208,1209],{"class":282},"1024",[199,1211,884],{"class":208},[199,1213,1214,1217,1219,1221,1224],{"class":101,"line":403},[199,1215,1216],{"class":261},"    \"-k\"",[199,1218,817],{"class":208},[199,1220,851],{"class":282},[199,1222,1223],{"class":208},",                           ",[199,1225,1226],{"class":825},"# knight's move for smoother surfaces\n",[199,1228,1229,1232,1234,1237],{"class":101,"line":431},[199,1230,1231],{"class":261},"    \"output\"",[199,1233,817],{"class":208},[199,1235,1236],{"class":261},"\"\u002Fdata\u002Fmodel\u002Fcumulative_cost.tif\"",[199,1238,884],{"class":208},[199,1240,1241,1243],{"class":101,"line":436},[199,1242,925],{"class":261},[199,1244,1245],{"class":208},": region,\n",[199,1247,1248,1250],{"class":101,"line":958},[199,1249,937],{"class":261},[199,1251,1252],{"class":208},": ref.rasterUnitsPerPixelX(),\n",[199,1254,1255,1258,1261],{"class":101,"line":964},[199,1256,1257],{"class":208},"})[",[199,1259,1260],{"class":261},"\"output\"",[199,1262,1263],{"class":208},"]\n",[14,1265,1266,1268,1269,1273,1274,1277],{},[452,1267,454],{}," Passing the reference raster's extent and pixel size as region and cell size makes the cost surface align with the DEM exactly, so later raster arithmetic needs no resampling — the alignment discipline from ",[26,1270,1272],{"href":1271},"\u002Fspatial-data-processing-automation\u002Fraster-analysis-workflows\u002Fresample-and-align-rasters-pyqgis\u002F","resampling and aligning rasters",". Vector inputs such as start points are converted to GRASS vectors automatically. The ",[18,1275,1276],{},"memory"," option, in megabytes, lets GRASS use more RAM for large rasters, which speeds up many modules considerably.",[168,1279,1281],{"id":1280},"run-saga-tools","Run SAGA tools",[14,1283,1284,1285,1288],{},"SAGA tools follow the same pattern with their own parameter names, which are upper-case in Processing. The NextGen provider exposes them under ",[18,1286,1287],{},"sagang:",".",[190,1290,1292],{"className":192,"code":1291,"language":194,"meta":195,"style":195},"TWI = find_alg(\"topographicwetnessindex\", prefixes=(\"sagang\", \"saga\"))\ntwi = processing.run(TWI, {\n    \"SLOPE\": \"\u002Fdata\u002Fterrain\u002Fslope_rad.tif\",\n    \"AREA\": \"\u002Fdata\u002Fhydro\u002Fcatchment_area.tif\",\n    \"TRANS\": None,\n    \"CONV\": 0,\n    \"METHOD\": 0,\n    \"TWI\": \"\u002Fdata\u002Fterrain\u002Ftwi.sdat\",\n})[\"TWI\"]\nprint(twi)\n",[18,1293,1294,1324,1337,1349,1361,1372,1383,1394,1406,1415],{"__ignoreMap":195},[199,1295,1296,1299,1301,1303,1306,1308,1311,1313,1315,1317,1319,1321],{"class":101,"line":201},[199,1297,1298],{"class":282},"TWI",[199,1300,606],{"class":204},[199,1302,609],{"class":208},[199,1304,1305],{"class":261},"\"topographicwetnessindex\"",[199,1307,265],{"class":208},[199,1309,588],{"class":1310},"s9osk",[199,1312,231],{"class":204},[199,1314,286],{"class":208},[199,1316,273],{"class":261},[199,1318,265],{"class":208},[199,1320,268],{"class":261},[199,1322,1323],{"class":208},"))\n",[199,1325,1326,1329,1331,1333,1335],{"class":101,"line":218},[199,1327,1328],{"class":208},"twi ",[199,1330,231],{"class":204},[199,1332,796],{"class":208},[199,1334,1298],{"class":282},[199,1336,801],{"class":208},[199,1338,1339,1342,1344,1347],{"class":101,"line":225},[199,1340,1341],{"class":261},"    \"SLOPE\"",[199,1343,817],{"class":208},[199,1345,1346],{"class":261},"\"\u002Fdata\u002Fterrain\u002Fslope_rad.tif\"",[199,1348,884],{"class":208},[199,1350,1351,1354,1356,1359],{"class":101,"line":237},[199,1352,1353],{"class":261},"    \"AREA\"",[199,1355,817],{"class":208},[199,1357,1358],{"class":261},"\"\u002Fdata\u002Fhydro\u002Fcatchment_area.tif\"",[199,1360,884],{"class":208},[199,1362,1363,1366,1368,1370],{"class":101,"line":252},[199,1364,1365],{"class":261},"    \"TRANS\"",[199,1367,817],{"class":208},[199,1369,930],{"class":282},[199,1371,884],{"class":208},[199,1373,1374,1377,1379,1381],{"class":101,"line":279},[199,1375,1376],{"class":261},"    \"CONV\"",[199,1378,817],{"class":208},[199,1380,51],{"class":282},[199,1382,884],{"class":208},[199,1384,1385,1388,1390,1392],{"class":101,"line":344},[199,1386,1387],{"class":261},"    \"METHOD\"",[199,1389,817],{"class":208},[199,1391,51],{"class":282},[199,1393,884],{"class":208},[199,1395,1396,1399,1401,1404],{"class":101,"line":349},[199,1397,1398],{"class":261},"    \"TWI\"",[199,1400,817],{"class":208},[199,1402,1403],{"class":261},"\"\u002Fdata\u002Fterrain\u002Ftwi.sdat\"",[199,1405,884],{"class":208},[199,1407,1408,1410,1413],{"class":101,"line":362},[199,1409,1257],{"class":208},[199,1411,1412],{"class":261},"\"TWI\"",[199,1414,1263],{"class":208},[199,1416,1417,1419],{"class":101,"line":374},[199,1418,619],{"class":282},[199,1420,1421],{"class":208},"(twi)\n",[14,1423,1424,1426,1427,1429,1430,1433,1434,1437],{},[452,1425,454],{}," SAGA tool names in Processing are lower-case versions of the SAGA tool titles without spaces; find them with the lookup helper and confirm parameters with ",[18,1428,665],{},". SAGA writes its native ",[18,1431,1432],{},".sdat"," format by default; QGIS reads it directly, and ",[18,1435,1436],{},"gdal:translate"," converts it to GeoTIFF when needed. The topographic wetness index here combines slope and upslope area, typically computed with SAGA's own catchment area tool. Exact parameter names vary between SAGA versions, which is another reason to read the help on the target machine.",[168,1439,1441],{"id":1440},"handle-outputs-and-errors","Handle outputs and errors",[14,1443,1444],{},"External tools fail differently from native algorithms: missing dependencies, unsupported data types, a region that excludes all inputs. Catching exceptions and reading the log gives the real reason.",[14,1446,1447],{},[34,1448,1451,1454,1457,1460,1467,1470,1476,1481,1485,1490,1494,1498,1501,1505,1509,1514,1518],{"viewBox":1449,"role":37,"ariaLabel":1450,"xmlns":39},"0 0 760 196","A failed external algorithm raising a generic exception while the tool's real message goes to the processing feedback and log",[41,1452,1453],{},"Where the real error message is",[45,1455,1456],{},"When a GRASS or SAGA algorithm fails, processing.run raises a generic exception. The external tool's own message is written to the Processing log and the feedback object. Capturing feedback in a script, or reading the Processing log panel interactively, shows the actual cause, such as a region with no data or an unsupported input type.",[49,1458],{"x":51,"y":51,"width":52,"height":1459,"fill":54},"196",[56,1461,1462],{},[59,1463,1465],{"id":1464,"viewBox":62,"refX":63,"refY":64,"markerWidth":65,"markerHeight":65,"orient":66},"gsErrorsArrow",[68,1466],{"d":70,"fill":71},[73,1468,1469],{"x":75,"y":76,"style":77,"fill":78,"textAnchor":79},"The exception says that; the log says why",[49,1471],{"x":83,"y":1472,"width":1473,"height":1474,"rx":63,"fill":134,"stroke":1475,"style":89},"60","222","120","#b91c1c",[73,1477,1480],{"x":1478,"y":1479,"style":94,"fill":1475,"textAnchor":79},"135","110.78","exception",[73,1482,1484],{"x":1478,"y":1483,"style":98,"fill":88,"textAnchor":79},"136.78","algorithm failed",[101,1486],{"x1":1487,"y1":1474,"x2":1488,"y2":1474,"stroke":71,"style":1489},"246","288","stroke-width:1.8;marker-end:url(#gsErrorsArrow)",[49,1491],{"x":1492,"y":1472,"width":1493,"height":1474,"rx":63,"fill":134,"stroke":135,"style":89},"292","200",[73,1495,1497],{"x":1496,"y":1479,"style":94,"fill":135,"textAnchor":79},"392","feedback log",[73,1499,1500],{"x":1496,"y":1483,"style":98,"fill":88,"textAnchor":79},"tool's own message",[101,1502],{"x1":1503,"y1":1474,"x2":1504,"y2":1474,"stroke":71,"style":1489},"492","534",[49,1506],{"x":1507,"y":1472,"width":1508,"height":1474,"rx":63,"fill":154,"stroke":155,"style":89},"538","198",[73,1510,1513],{"x":1511,"y":1512,"style":94,"fill":159,"textAnchor":79},"637","97.78","fix",[73,1515,1517],{"x":1511,"y":1516,"style":98,"fill":88,"textAnchor":79},"123.78","region, types,",[73,1519,1521],{"x":1511,"y":1520,"style":98,"fill":88,"textAnchor":79},"149.78","missing deps",[190,1523,1525],{"className":192,"code":1524,"language":194,"meta":195,"style":195},"from qgis.core import QgsProcessingFeedback\n\nclass CollectingFeedback(QgsProcessingFeedback):\n    def __init__(self):\n        super().__init__()\n        self.lines = []\n    def pushInfo(self, info):\n        self.lines.append(info)\n    def reportError(self, error, fatalError=False):\n        self.lines.append(\"ERROR: \" + error)\n    def pushConsoleInfo(self, info):\n        self.lines.append(info)\n\nfb = CollectingFeedback()\ntry:\n    processing.run(R_SLOPE, {\"elevation\": \"\u002Fdata\u002Fterrain\u002Fmissing.tif\",\n                             \"slope\": \"\u002Ftmp\u002Fs.tif\"}, feedback=fb)\nexcept Exception as err:\n    print(\"failed:\", err)\n    print(\"\\n\".join(fb.lines[-15:]))\n",[18,1526,1527,1538,1542,1558,1569,1583,1596,1606,1613,1630,1646,1655,1661,1665,1675,1683,1703,1725,1740,1754],{"__ignoreMap":195},[199,1528,1529,1531,1533,1535],{"class":101,"line":201},[199,1530,205],{"class":204},[199,1532,209],{"class":208},[199,1534,212],{"class":204},[199,1536,1537],{"class":208}," QgsProcessingFeedback\n",[199,1539,1540],{"class":101,"line":218},[199,1541,222],{"emptyLinePlaceholder":221},[199,1543,1544,1547,1550,1552,1555],{"class":101,"line":225},[199,1545,1546],{"class":204},"class",[199,1548,1549],{"class":355}," CollectingFeedback",[199,1551,286],{"class":208},[199,1553,1554],{"class":355},"QgsProcessingFeedback",[199,1556,1557],{"class":208},"):\n",[199,1559,1560,1563,1566],{"class":101,"line":237},[199,1561,1562],{"class":204},"    def",[199,1564,1565],{"class":282}," __init__",[199,1567,1568],{"class":208},"(self):\n",[199,1570,1571,1574,1577,1580],{"class":101,"line":252},[199,1572,1573],{"class":282},"        super",[199,1575,1576],{"class":208},"().",[199,1578,1579],{"class":282},"__init__",[199,1581,1582],{"class":208},"()\n",[199,1584,1585,1588,1591,1593],{"class":101,"line":279},[199,1586,1587],{"class":282},"        self",[199,1589,1590],{"class":208},".lines ",[199,1592,231],{"class":204},[199,1594,1595],{"class":208}," []\n",[199,1597,1598,1600,1603],{"class":101,"line":344},[199,1599,1562],{"class":204},[199,1601,1602],{"class":355}," pushInfo",[199,1604,1605],{"class":208},"(self, info):\n",[199,1607,1608,1610],{"class":101,"line":349},[199,1609,1587],{"class":282},[199,1611,1612],{"class":208},".lines.append(info)\n",[199,1614,1615,1617,1620,1623,1625,1628],{"class":101,"line":362},[199,1616,1562],{"class":204},[199,1618,1619],{"class":355}," reportError",[199,1621,1622],{"class":208},"(self, error, fatalError",[199,1624,231],{"class":204},[199,1626,1627],{"class":282},"False",[199,1629,1557],{"class":208},[199,1631,1632,1634,1637,1640,1643],{"class":101,"line":374},[199,1633,1587],{"class":282},[199,1635,1636],{"class":208},".lines.append(",[199,1638,1639],{"class":261},"\"ERROR: \"",[199,1641,1642],{"class":204}," +",[199,1644,1645],{"class":208}," error)\n",[199,1647,1648,1650,1653],{"class":101,"line":394},[199,1649,1562],{"class":204},[199,1651,1652],{"class":355}," pushConsoleInfo",[199,1654,1605],{"class":208},[199,1656,1657,1659],{"class":101,"line":403},[199,1658,1587],{"class":282},[199,1660,1612],{"class":208},[199,1662,1663],{"class":101,"line":431},[199,1664,222],{"emptyLinePlaceholder":221},[199,1666,1667,1670,1672],{"class":101,"line":436},[199,1668,1669],{"class":208},"fb ",[199,1671,231],{"class":204},[199,1673,1674],{"class":208}," CollectingFeedback()\n",[199,1676,1677,1680],{"class":101,"line":958},[199,1678,1679],{"class":204},"try",[199,1681,1682],{"class":208},":\n",[199,1684,1685,1688,1690,1693,1696,1698,1701],{"class":101,"line":964},[199,1686,1687],{"class":208},"    processing.run(",[199,1689,603],{"class":282},[199,1691,1692],{"class":208},", {",[199,1694,1695],{"class":261},"\"elevation\"",[199,1697,817],{"class":208},[199,1699,1700],{"class":261},"\"\u002Fdata\u002Fterrain\u002Fmissing.tif\"",[199,1702,884],{"class":208},[199,1704,1706,1709,1711,1714,1717,1720,1722],{"class":101,"line":1705},17,[199,1707,1708],{"class":261},"                             \"slope\"",[199,1710,817],{"class":208},[199,1712,1713],{"class":261},"\"\u002Ftmp\u002Fs.tif\"",[199,1715,1716],{"class":208},"}, ",[199,1718,1719],{"class":1310},"feedback",[199,1721,231],{"class":204},[199,1723,1724],{"class":208},"fb)\n",[199,1726,1728,1731,1734,1737],{"class":101,"line":1727},18,[199,1729,1730],{"class":204},"except",[199,1732,1733],{"class":282}," Exception",[199,1735,1736],{"class":204}," as",[199,1738,1739],{"class":208}," err:\n",[199,1741,1743,1746,1748,1751],{"class":101,"line":1742},19,[199,1744,1745],{"class":282},"    print",[199,1747,286],{"class":208},[199,1749,1750],{"class":261},"\"failed:\"",[199,1752,1753],{"class":208},", err)\n",[199,1755,1757,1759,1761,1763,1766,1768,1771,1774,1777],{"class":101,"line":1756},20,[199,1758,1745],{"class":282},[199,1760,286],{"class":208},[199,1762,292],{"class":261},[199,1764,1765],{"class":282},"\\n",[199,1767,292],{"class":261},[199,1769,1770],{"class":208},".join(fb.lines[",[199,1772,1773],{"class":204},"-",[199,1775,1776],{"class":282},"15",[199,1778,1779],{"class":208},":]))\n",[14,1781,1782,1784,1785,265,1788,1791,1792,1288],{},[452,1783,454],{}," A feedback subclass that records ",[18,1786,1787],{},"pushInfo",[18,1789,1790],{},"pushConsoleInfo"," and errors captures the external tool's console output, which is where GRASS and SAGA explain what went wrong. Printing the last lines after a failure turns \"algorithm failed\" into \"Raster map not found\" or \"region contains no data\". In unattended scripts, log these lines rather than printing them, following ",[26,1793,1795],{"href":1794},"\u002Fpyqgis-fundamentals-environment-setup\u002Fheadless-qgis-and-server-automation\u002Fhandle-errors-and-logging-in-unattended-scripts\u002F","handling errors and logging in unattended scripts",[168,1797,1799],{"id":1798},"native-grass-or-saga","Native, GRASS or SAGA?",[14,1801,1802],{},"Prefer native algorithms when one exists: they start instantly, accept memory layers, need no external installation and behave identically on every machine. Use GRASS for its depth in hydrology, cost surfaces, viewsheds and topology cleaning, and when its algorithms are the established reference in a field. Use SAGA for terrain analysis indices, some classification tools and its large set of specialised modules. Whichever you choose, record the provider and version with the results — the same tool name can produce slightly different numbers across versions — and keep a fallback in mind for machines where the provider is missing.",[168,1804,1806],{"id":1805},"qgis-version-compatibility","QGIS version compatibility",[14,1808,1809,1810,1812,1813,1815],{},"GRASS algorithms are available on QGIS 3.34 LTR, 3.40 LTR and QGIS 4 when GRASS 8 is installed; the provider id changed from ",[18,1811,458],{}," to ",[18,1814,462],{}," during the 3.x series, hence the lookup. SAGA support comes from the Saga NextGen provider plugin on current releases, which supports SAGA 7.3 and newer. Parameter names follow the external tools and can change with their versions.",[168,1817,1819],{"id":1818},"troubleshooting","Troubleshooting",[173,1821,1822,1828,1834,1840],{},[176,1823,1824,1827],{},[452,1825,1826],{},"\"Algorithm not found\"."," The provider is missing or the id prefix differs; use the lookup helper.",[176,1829,1830,1833],{},[452,1831,1832],{},"The provider shows zero algorithms."," The external program is not installed or not on the path.",[176,1835,1836,1839],{},[452,1837,1838],{},"Outputs are offset or resampled."," The region was not set; pass region and cell size explicitly.",[176,1841,1842,1845,1846,1848],{},[452,1843,1844],{},"Very slow on large rasters."," Increase GRASS's ",[18,1847,1276],{}," option and avoid unnecessary format conversions.",[168,1850,1852],{"id":1851},"conclusion","Conclusion",[14,1854,1855],{},"Check providers before running, find algorithm ids by tool name rather than hard-coded prefixes, pass GRASS options by their own names with dashed boolean flags and file outputs, set region and cell size to control the output grid, use upper-case parameters for SAGA tools, capture feedback to see the real error, and prefer native algorithms where they exist.",[168,1857,1859],{"id":1858},"frequently-asked-questions","Frequently Asked Questions",[14,1861,1862,1865],{},[452,1863,1864],{},"Can GRASS algorithms use memory layers as input?","\nYes — the provider exports them to temporary files first. Outputs are files.",[14,1867,1868,1871],{},[452,1869,1870],{},"Do I need a GRASS location or mapset?","\nNo. The provider creates a temporary one for each run.",[14,1873,1874,1877],{},[452,1875,1876],{},"Can I chain GRASS steps without re-exporting data?","\nNot through Processing; each call exports and imports. For long GRASS-only chains, a GRASS session script is faster.",[14,1879,1880,1883],{},[452,1881,1882],{},"Are WhiteboxTools available the same way?","\nYes, through the WhiteboxTools for QGIS plugin, with the same lookup approach.",[168,1885,1887],{"id":1886},"related","Related",[173,1889,1890,1895,1901,1907,1913],{},[176,1891,1892,1894],{},[26,1893,29],{"href":28}," — the guide this recipe belongs to",[176,1896,1897],{},[26,1898,1900],{"href":1899},"\u002Fspatial-data-processing-automation\u002Fterrain-and-interpolation-analysis\u002Fdelineate-watersheds-pyqgis\u002F","Delineate Watersheds in PyQGIS",[176,1902,1903],{},[26,1904,1906],{"href":1905},"\u002Fspatial-data-processing-automation\u002Fterrain-and-interpolation-analysis\u002Frun-viewshed-analysis-pyqgis\u002F","Run a Viewshed Analysis in PyQGIS",[176,1908,1909],{},[26,1910,1912],{"href":1911},"\u002Fspatial-data-processing-automation\u002Fchaining-processing-algorithms\u002Fhandle-processing-feedback-and-errors-pyqgis\u002F","Handle Processing Feedback and Errors in PyQGIS",[176,1914,1915],{},[26,1916,1918],{"href":1917},"\u002Fspatial-data-processing-automation\u002Fbatch-processing-with-pyqgis\u002Frun-processing-algorithm-from-script\u002F","Run a Processing Algorithm from a Script",[1920,1921,1922],"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 pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sjoCn, html code.shiki .sjoCn{--shiki-default:#9AA79F}html pre.shiki code .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}",{"title":195,"searchDepth":218,"depth":218,"links":1924},[1925,1926,1927,1928,1929,1930,1931,1932,1933,1934,1935,1936,1937],{"id":170,"depth":218,"text":171},{"id":184,"depth":218,"text":185},{"id":470,"depth":218,"text":471},{"id":669,"depth":218,"text":670},{"id":1017,"depth":218,"text":1018},{"id":1280,"depth":218,"text":1281},{"id":1440,"depth":218,"text":1441},{"id":1798,"depth":218,"text":1799},{"id":1805,"depth":218,"text":1806},{"id":1818,"depth":218,"text":1819},{"id":1851,"depth":218,"text":1852},{"id":1858,"depth":218,"text":1859},{"id":1886,"depth":218,"text":1887},"Call GRASS GIS and SAGA tools through QGIS Processing from Python — checking the providers are installed, finding algorithm ids that change between versions, passing GRASS-style parameters and flags, controlling region and resolution, handling outputs and errors, and choosing between providers.","md",{"slug":1941,"type":1942,"breadcrumb":1943,"datePublished":1944,"dateModified":1944},"run-grass-and-saga-algorithms-pyqgis","article","Run GRASS and SAGA Algorithms","2026-10-02","\u002Fspatial-data-processing-automation\u002Fchaining-processing-algorithms\u002Frun-grass-and-saga-algorithms-pyqgis",{"title":5,"description":1938},"spatial-data-processing-automation\u002Fchaining-processing-algorithms\u002Frun-grass-and-saga-algorithms-pyqgis\u002Findex","r8oRzRJOsGxwwzfk0vkouwkJIIk2xnQ5OFA5YZICiqs",1790966260983]