[{"data":1,"prerenderedAt":2181},["ShallowReactive",2],{"doc:\u002Fspatial-data-processing-automation\u002Fraster-analysis-workflows\u002Fread-raster-pixels-with-qgsrasterblock-pyqgis":3},{"id":4,"title":5,"body":6,"description":2170,"extension":2171,"meta":2172,"navigation":283,"path":2177,"seo":2178,"stem":2179,"__hash__":2180},"docs\u002Fspatial-data-processing-automation\u002Fraster-analysis-workflows\u002Fread-raster-pixels-with-qgsrasterblock-pyqgis\u002Findex.md","Read Raster Pixels with QgsRasterBlock in PyQGIS",{"type":7,"value":8,"toc":2157},"minimark",[9,13,22,31,210,215,236,240,243,653,667,788,792,799,969,1004,1008,1011,1251,1286,1388,1392,1395,1817,1831,1835,1846,1960,1980,1984,2014,2018,2060,2064,2077,2081,2087,2096,2110,2120,2124,2153],[10,11,5],"h1",{"id":12},"read-raster-pixels-with-qgsrasterblock-in-pyqgis",[14,15,16,17,21],"p",{},"Processing algorithms cover most raster work — statistics, reclassification, the raster calculator — and they should be your first choice. But sooner or later a job needs the numbers themselves: a custom index no algorithm computes, a check that every cell in a delivered DEM falls in a sane range, a histogram with bins nobody else uses, a value lookup inside a plugin that has to respond instantly. For that, PyQGIS reads cells through the layer's data provider as a ",[18,19,20],"code",{},"QgsRasterBlock",".",[14,23,24,25,30],{},"This recipe belongs to ",[26,27,29],"a",{"href":28},"\u002Fspatial-data-processing-automation\u002Fraster-analysis-workflows\u002F","Raster Analysis Workflows in PyQGIS",". It reads blocks for an extent, handles NoData properly, turns blocks into NumPy arrays, and walks a large raster in tiles so memory use stays flat.",[14,32,33],{},[34,35,40,44,48,55,72,81,90,96,101,104,108,112,115,119,123,131,134,138,147,152,157,162,168,171,178,181,185,188,190,192,194,196,201,205],"svg",{"viewBox":36,"role":37,"ariaLabel":38,"xmlns":39},"0 0 760 300","img","How a raster block is requested: a band number, a map extent and an output width and height go to the data provider, which resamples the source cells into a block of the requested size with a data type and NoData flags","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg",[41,42,43],"title",{},"A block is a resampled window, not a file read",[45,46,47],"desc",{},"Three inputs feed provider.block: the band number, an extent in the layer's CRS, and a width and height in cells. The provider reads the source cells that fall in the extent and returns a block of exactly the requested size. If width and height match the native resolution of the extent, the values are the original cells. If they are smaller, the provider resamples. The block records its data type and which cells are NoData.",[49,50],"rect",{"x":51,"y":51,"width":52,"height":53,"fill":54},"0","760","300","#f6f3ea",[56,57,58],"defs",{},[59,60,67],"marker",{"id":61,"viewBox":62,"refX":63,"refY":64,"markerWidth":65,"markerHeight":65,"orient":66},"rbReqArrow","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-weight:bold;font-family:sans-serif","#17211d","middle","You ask for an extent at a size — the provider fills it",[49,82],{"x":83,"y":84,"width":85,"height":86,"rx":63,"fill":87,"stroke":88,"style":89},"24","56","170","52","#fffdf7","#59645f","stroke-width:1.8",[73,91,95],{"x":92,"y":93,"style":94,"fill":78,"textAnchor":79},"109","80","text-anchor:middle;font-size:10.5px;font-weight:bold;font-family:sans-serif","band",[73,97,100],{"x":92,"y":98,"style":99,"fill":71,"textAnchor":79},"98","text-anchor:middle;font-size:10px;font-family:monospace","1",[49,102],{"x":83,"y":103,"width":85,"height":86,"rx":63,"fill":87,"stroke":88,"style":89},"124",[73,105,107],{"x":92,"y":106,"style":94,"fill":78,"textAnchor":79},"148","extent",[73,109,111],{"x":92,"y":110,"style":99,"fill":71,"textAnchor":79},"166","QgsRectangle",[49,113],{"x":83,"y":114,"width":85,"height":86,"rx":63,"fill":87,"stroke":88,"style":89},"192",[73,116,118],{"x":92,"y":117,"style":94,"fill":78,"textAnchor":79},"216","width × height",[73,120,122],{"x":92,"y":121,"style":99,"fill":71,"textAnchor":79},"234","cells in the block",[124,125],"line",{"x1":126,"y1":127,"x2":128,"y2":129,"stroke":71,"style":130},"194","82","262","140","stroke-width:1.6;marker-end:url(#rbReqArrow)",[124,132],{"x1":126,"y1":133,"x2":128,"y2":133,"stroke":71,"style":130},"150",[124,135],{"x1":126,"y1":136,"x2":128,"y2":137,"stroke":71,"style":130},"218","160",[49,139],{"x":140,"y":141,"width":85,"height":142,"rx":143,"fill":144,"stroke":145,"style":146},"270","108","84","10","#eef7f4","#0f766e","stroke-width:2.5",[73,148,151],{"x":149,"y":129,"style":150,"fill":145,"textAnchor":79},"355","text-anchor:middle;font-size:11px;font-weight:bold;font-family:sans-serif","provider.block()",[73,153,156],{"x":149,"y":154,"style":155,"fill":71,"textAnchor":79},"164","text-anchor:middle;font-size:10px;font-family:sans-serif","reads + resamples",[124,158],{"x1":159,"y1":133,"x2":160,"y2":133,"stroke":71,"style":161},"440","486","stroke-width:1.8;marker-end:url(#rbReqArrow)",[49,163],{"x":164,"y":84,"width":165,"height":166,"rx":143,"fill":87,"stroke":167,"style":146},"494","242","188","#2563eb",[73,169,20],{"x":170,"y":93,"style":150,"fill":167,"textAnchor":79},"615",[49,172],{"x":173,"y":174,"width":175,"height":76,"fill":176,"stroke":167,"style":177},"530","96","36","#dbeafe","stroke-width:1",[49,179],{"x":180,"y":174,"width":175,"height":76,"fill":176,"stroke":167,"style":177},"566",[49,182],{"x":183,"y":174,"width":175,"height":76,"fill":184,"stroke":167,"style":177},"602","#e8e4d8",[49,186],{"x":187,"y":174,"width":175,"height":76,"fill":176,"stroke":167,"style":177},"638",[49,189],{"x":173,"y":103,"width":175,"height":76,"fill":176,"stroke":167,"style":177},[49,191],{"x":180,"y":103,"width":175,"height":76,"fill":176,"stroke":167,"style":177},[49,193],{"x":183,"y":103,"width":175,"height":76,"fill":176,"stroke":167,"style":177},[49,195],{"x":187,"y":103,"width":175,"height":76,"fill":184,"stroke":167,"style":177},[73,197,200],{"x":198,"y":199,"style":155,"fill":71,"textAnchor":79},"620","178","dataType(), width(), height()",[73,202,204],{"x":198,"y":203,"style":155,"fill":71,"textAnchor":79},"198","value(row, col), isNoData()",[73,206,209],{"x":198,"y":207,"style":208,"fill":88,"textAnchor":79},"222","text-anchor:middle;font-size:9.5px;font-family:sans-serif","grey cells: NoData",[211,212,214],"h2",{"id":213},"prerequisites","Prerequisites",[216,217,218,226,233],"ul",{},[219,220,221,225],"li",{},[222,223,224],"strong",{},"QGIS 3.40 LTR"," or newer, or the QGIS 4 series.",[219,227,228,229,232],{},"A raster layer loaded through the ",[18,230,231],{},"gdal"," provider. The same code works for WMS and WCS providers, but reading cells from a rendered web map rarely gives meaningful numbers.",[219,234,235],{},"NumPy, which ships with QGIS installers on every platform.",[211,237,239],{"id":238},"read-a-block-at-native-resolution","Read a block at native resolution",[14,241,242],{},"A block request names a band, an extent and a size in cells. To get the original values rather than a resampled version, compute the size from the extent and the raster's cell size, and align the extent to the cell grid.",[244,245,250],"pre",{"className":246,"code":247,"language":248,"meta":249,"style":249},"language-python shiki shiki-themes github-dark","import math\nfrom qgis.core import QgsProject, QgsRectangle\n\ndem = QgsProject.instance().mapLayersByName(\"dem_2m\")[0]\nprovider = dem.dataProvider()\n\ncell_x = dem.rasterUnitsPerPixelX()\ncell_y = dem.rasterUnitsPerPixelY()\nfull = dem.extent()\n\ndef aligned_window(xmin, ymin, xmax, ymax):\n    col0 = math.floor((xmin - full.xMinimum()) \u002F cell_x)\n    col1 = math.ceil((xmax - full.xMinimum()) \u002F cell_x)\n    row0 = math.floor((full.yMaximum() - ymax) \u002F cell_y)\n    row1 = math.ceil((full.yMaximum() - ymin) \u002F cell_y)\n    rect = QgsRectangle(\n        full.xMinimum() + col0 * cell_x, full.yMaximum() - row1 * cell_y,\n        full.xMinimum() + col1 * cell_x, full.yMaximum() - row0 * cell_y,\n    )\n    return rect, col1 - col0, row1 - row0\n\nwindow, width, height = aligned_window(429500, 433000, 430000, 433400)\nblock = provider.block(1, window, width, height)\n\nprint(block.width(), block.height(), block.dataType())\nprint(\"top-left cell:\", block.value(0, 0), \"nodata?\", block.isNoData(0, 0))\n","python","",[18,251,252,264,278,285,310,321,326,337,348,359,364,377,400,419,440,460,471,499,522,528,547,552,585,601,606,615],{"__ignoreMap":249},[253,254,256,260],"span",{"class":124,"line":255},1,[253,257,259],{"class":258},"snl16","import",[253,261,263],{"class":262},"s95oV"," math\n",[253,265,267,270,273,275],{"class":124,"line":266},2,[253,268,269],{"class":258},"from",[253,271,272],{"class":262}," qgis.core ",[253,274,259],{"class":258},[253,276,277],{"class":262}," QgsProject, QgsRectangle\n",[253,279,281],{"class":124,"line":280},3,[253,282,284],{"emptyLinePlaceholder":283},true,"\n",[253,286,288,291,294,297,301,304,307],{"class":124,"line":287},4,[253,289,290],{"class":262},"dem ",[253,292,293],{"class":258},"=",[253,295,296],{"class":262}," QgsProject.instance().mapLayersByName(",[253,298,300],{"class":299},"sU2Wk","\"dem_2m\"",[253,302,303],{"class":262},")[",[253,305,51],{"class":306},"sDLfK",[253,308,309],{"class":262},"]\n",[253,311,313,316,318],{"class":124,"line":312},5,[253,314,315],{"class":262},"provider ",[253,317,293],{"class":258},[253,319,320],{"class":262}," dem.dataProvider()\n",[253,322,324],{"class":124,"line":323},6,[253,325,284],{"emptyLinePlaceholder":283},[253,327,329,332,334],{"class":124,"line":328},7,[253,330,331],{"class":262},"cell_x ",[253,333,293],{"class":258},[253,335,336],{"class":262}," dem.rasterUnitsPerPixelX()\n",[253,338,340,343,345],{"class":124,"line":339},8,[253,341,342],{"class":262},"cell_y ",[253,344,293],{"class":258},[253,346,347],{"class":262}," dem.rasterUnitsPerPixelY()\n",[253,349,351,354,356],{"class":124,"line":350},9,[253,352,353],{"class":262},"full ",[253,355,293],{"class":258},[253,357,358],{"class":262}," dem.extent()\n",[253,360,362],{"class":124,"line":361},10,[253,363,284],{"emptyLinePlaceholder":283},[253,365,367,370,374],{"class":124,"line":366},11,[253,368,369],{"class":258},"def",[253,371,373],{"class":372},"svObZ"," aligned_window",[253,375,376],{"class":262},"(xmin, ymin, xmax, ymax):\n",[253,378,380,383,385,388,391,394,397],{"class":124,"line":379},12,[253,381,382],{"class":262},"    col0 ",[253,384,293],{"class":258},[253,386,387],{"class":262}," math.floor((xmin ",[253,389,390],{"class":258},"-",[253,392,393],{"class":262}," full.xMinimum()) ",[253,395,396],{"class":258},"\u002F",[253,398,399],{"class":262}," cell_x)\n",[253,401,403,406,408,411,413,415,417],{"class":124,"line":402},13,[253,404,405],{"class":262},"    col1 ",[253,407,293],{"class":258},[253,409,410],{"class":262}," math.ceil((xmax ",[253,412,390],{"class":258},[253,414,393],{"class":262},[253,416,396],{"class":258},[253,418,399],{"class":262},[253,420,422,425,427,430,432,435,437],{"class":124,"line":421},14,[253,423,424],{"class":262},"    row0 ",[253,426,293],{"class":258},[253,428,429],{"class":262}," math.floor((full.yMaximum() ",[253,431,390],{"class":258},[253,433,434],{"class":262}," ymax) ",[253,436,396],{"class":258},[253,438,439],{"class":262}," cell_y)\n",[253,441,443,446,448,451,453,456,458],{"class":124,"line":442},15,[253,444,445],{"class":262},"    row1 ",[253,447,293],{"class":258},[253,449,450],{"class":262}," math.ceil((full.yMaximum() ",[253,452,390],{"class":258},[253,454,455],{"class":262}," ymin) ",[253,457,396],{"class":258},[253,459,439],{"class":262},[253,461,463,466,468],{"class":124,"line":462},16,[253,464,465],{"class":262},"    rect ",[253,467,293],{"class":258},[253,469,470],{"class":262}," QgsRectangle(\n",[253,472,474,477,480,483,486,489,491,494,496],{"class":124,"line":473},17,[253,475,476],{"class":262},"        full.xMinimum() ",[253,478,479],{"class":258},"+",[253,481,482],{"class":262}," col0 ",[253,484,485],{"class":258},"*",[253,487,488],{"class":262}," cell_x, full.yMaximum() ",[253,490,390],{"class":258},[253,492,493],{"class":262}," row1 ",[253,495,485],{"class":258},[253,497,498],{"class":262}," cell_y,\n",[253,500,502,504,506,509,511,513,515,518,520],{"class":124,"line":501},18,[253,503,476],{"class":262},[253,505,479],{"class":258},[253,507,508],{"class":262}," col1 ",[253,510,485],{"class":258},[253,512,488],{"class":262},[253,514,390],{"class":258},[253,516,517],{"class":262}," row0 ",[253,519,485],{"class":258},[253,521,498],{"class":262},[253,523,525],{"class":124,"line":524},19,[253,526,527],{"class":262},"    )\n",[253,529,531,534,537,539,542,544],{"class":124,"line":530},20,[253,532,533],{"class":258},"    return",[253,535,536],{"class":262}," rect, col1 ",[253,538,390],{"class":258},[253,540,541],{"class":262}," col0, row1 ",[253,543,390],{"class":258},[253,545,546],{"class":262}," row0\n",[253,548,550],{"class":124,"line":549},21,[253,551,284],{"emptyLinePlaceholder":283},[253,553,555,558,560,563,566,569,572,574,577,579,582],{"class":124,"line":554},22,[253,556,557],{"class":262},"window, width, height ",[253,559,293],{"class":258},[253,561,562],{"class":262}," aligned_window(",[253,564,565],{"class":306},"429500",[253,567,568],{"class":262},", ",[253,570,571],{"class":306},"433000",[253,573,568],{"class":262},[253,575,576],{"class":306},"430000",[253,578,568],{"class":262},[253,580,581],{"class":306},"433400",[253,583,584],{"class":262},")\n",[253,586,588,591,593,596,598],{"class":124,"line":587},23,[253,589,590],{"class":262},"block ",[253,592,293],{"class":258},[253,594,595],{"class":262}," provider.block(",[253,597,100],{"class":306},[253,599,600],{"class":262},", window, width, height)\n",[253,602,604],{"class":124,"line":603},24,[253,605,284],{"emptyLinePlaceholder":283},[253,607,609,612],{"class":124,"line":608},25,[253,610,611],{"class":306},"print",[253,613,614],{"class":262},"(block.width(), block.height(), block.dataType())\n",[253,616,618,620,623,626,629,631,633,635,638,641,644,646,648,650],{"class":124,"line":617},26,[253,619,611],{"class":306},[253,621,622],{"class":262},"(",[253,624,625],{"class":299},"\"top-left cell:\"",[253,627,628],{"class":262},", block.value(",[253,630,51],{"class":306},[253,632,568],{"class":262},[253,634,51],{"class":306},[253,636,637],{"class":262},"), ",[253,639,640],{"class":299},"\"nodata?\"",[253,642,643],{"class":262},", block.isNoData(",[253,645,51],{"class":306},[253,647,568],{"class":262},[253,649,51],{"class":306},[253,651,652],{"class":262},"))\n",[14,654,655,658,659,662,663,666],{},[222,656,657],{},"Breakdown:"," Rows count down from the top of the raster and columns across from the left, which is why the row numbers are computed from ",[18,660,661],{},"yMaximum",". Snapping the window outward to whole cells means every value in the block is a source cell, not an interpolated mix of neighbours; ask for a 250 × 200 block over an extent that is really 251 × 200 cells and the provider silently resamples. ",[18,664,665],{},"value(row, col)"," returns a float regardless of the underlying type, which is convenient for single lookups and slow for millions of them — the NumPy route below is the one to use for whole blocks.",[14,668,669],{},[34,670,673,676,679,682,686,691,697,732,738,742,746,751,781,785],{"viewBox":671,"role":37,"ariaLabel":672,"xmlns":39},"0 0 760 256","Aligned versus unaligned windows: a window snapped to the cell grid returns original cell values, a window that cuts through cells or is requested at the wrong size returns resampled values",[41,674,675],{},"Snap the window to the grid",[45,677,678],{},"Left: a requested window drawn in orange cuts through cells of the raster grid; the provider returns values resampled from partly covered cells. Right: the same area snapped outward to whole cell boundaries, with width and height equal to the cell counts, returns exactly the source values.",[49,680],{"x":51,"y":51,"width":52,"height":681,"fill":54},"256",[73,683,685],{"x":75,"y":684,"style":77,"fill":78,"textAnchor":79},"26","Half a cell off is a different answer",[49,687],{"x":83,"y":688,"width":689,"height":114,"rx":143,"fill":87,"stroke":690,"style":146},"44","344","#b91c1c",[73,692,696],{"x":693,"y":694,"style":695,"fill":690,"textAnchor":79},"196","68","text-anchor:middle;font-size:11.5px;font-weight:bold;font-family:sans-serif","unaligned: resampled",[698,699,701,705,708,711,713,716,719,722,724,727,730],"g",{"stroke":700,"style":177},"#c9c2ac",[124,702],{"x1":703,"y1":127,"x2":703,"y2":704},"76","202",[124,706],{"x1":707,"y1":127,"x2":707,"y2":704},"116",[124,709],{"x1":710,"y1":127,"x2":710,"y2":704},"156",[124,712],{"x1":693,"y1":127,"x2":693,"y2":704},[124,714],{"x1":715,"y1":127,"x2":715,"y2":704},"236",[124,717],{"x1":718,"y1":127,"x2":718,"y2":704},"276",[124,720],{"x1":721,"y1":127,"x2":721,"y2":704},"316",[124,723],{"x1":703,"y1":127,"x2":721,"y2":127},[124,725],{"x1":703,"y1":726,"x2":721,"y2":726},"122",[124,728],{"x1":703,"y1":729,"x2":721,"y2":729},"162",[124,731],{"x1":703,"y1":704,"x2":721,"y2":704},[49,733],{"x":98,"y":734,"width":133,"height":142,"fill":735,"stroke":736,"style":737},"102","none","#b45309","stroke-width:2.5;stroke-dasharray:6 3",[73,739,741],{"x":693,"y":740,"style":155,"fill":71,"textAnchor":79},"224","edges cut through cells",[49,743],{"x":744,"y":688,"width":689,"height":114,"rx":143,"fill":87,"stroke":745,"style":146},"392","#15803d",[73,747,750],{"x":748,"y":694,"style":695,"fill":749,"textAnchor":79},"564","#166534","aligned: source cells",[698,752,753,756,759,762,764,767,770,773,775,777,779],{"stroke":700,"style":177},[124,754],{"x1":755,"y1":127,"x2":755,"y2":704},"444",[124,757],{"x1":758,"y1":127,"x2":758,"y2":704},"484",[124,760],{"x1":761,"y1":127,"x2":761,"y2":704},"524",[124,763],{"x1":748,"y1":127,"x2":748,"y2":704},[124,765],{"x1":766,"y1":127,"x2":766,"y2":704},"604",[124,768],{"x1":769,"y1":127,"x2":769,"y2":704},"644",[124,771],{"x1":772,"y1":127,"x2":772,"y2":704},"684",[124,774],{"x1":755,"y1":127,"x2":772,"y2":127},[124,776],{"x1":755,"y1":726,"x2":772,"y2":726},[124,778],{"x1":755,"y1":729,"x2":772,"y2":729},[124,780],{"x1":755,"y1":704,"x2":772,"y2":704},[49,782],{"x":755,"y":127,"width":783,"height":784,"fill":735,"stroke":745,"style":146},"200","120",[73,786,787],{"x":748,"y":740,"style":155,"fill":71,"textAnchor":79},"5 × 3 cells, requested as 5 × 3",[211,789,791],{"id":790},"nodata-and-data-types","NoData and data types",[14,793,794,795,798],{},"Every raster band can declare a NoData value, and the block tracks which cells hold it. Ignoring that is the most common source of wrong statistics: an elevation mean dragged down by thousands of ",[18,796,797],{},"-9999"," cells looks plausible and is meaningless.",[244,800,802],{"className":246,"code":801,"language":248,"meta":249,"style":249},"band = 1\nprint(\"source NoData:\", provider.sourceHasNoDataValue(band),\n      provider.sourceNoDataValue(band))\nprint(\"data type:\", provider.dataType(band))\n\nvalid = [block.value(r, c)\n         for r in range(block.height())\n         for c in range(block.width())\n         if not block.isNoData(r, c)]\nprint(f\"{len(valid)} valid of {block.width() * block.height()} cells\")\nprint(\"min\", min(valid), \"max\", max(valid))\n",[18,803,804,814,826,831,843,847,857,874,888,899,941],{"__ignoreMap":249},[253,805,806,809,811],{"class":124,"line":255},[253,807,808],{"class":262},"band ",[253,810,293],{"class":258},[253,812,813],{"class":306}," 1\n",[253,815,816,818,820,823],{"class":124,"line":266},[253,817,611],{"class":306},[253,819,622],{"class":262},[253,821,822],{"class":299},"\"source NoData:\"",[253,824,825],{"class":262},", provider.sourceHasNoDataValue(band),\n",[253,827,828],{"class":124,"line":280},[253,829,830],{"class":262},"      provider.sourceNoDataValue(band))\n",[253,832,833,835,837,840],{"class":124,"line":287},[253,834,611],{"class":306},[253,836,622],{"class":262},[253,838,839],{"class":299},"\"data type:\"",[253,841,842],{"class":262},", provider.dataType(band))\n",[253,844,845],{"class":124,"line":312},[253,846,284],{"emptyLinePlaceholder":283},[253,848,849,852,854],{"class":124,"line":323},[253,850,851],{"class":262},"valid ",[253,853,293],{"class":258},[253,855,856],{"class":262}," [block.value(r, c)\n",[253,858,859,862,865,868,871],{"class":124,"line":328},[253,860,861],{"class":258},"         for",[253,863,864],{"class":262}," r ",[253,866,867],{"class":258},"in",[253,869,870],{"class":306}," range",[253,872,873],{"class":262},"(block.height())\n",[253,875,876,878,881,883,885],{"class":124,"line":339},[253,877,861],{"class":258},[253,879,880],{"class":262}," c ",[253,882,867],{"class":258},[253,884,870],{"class":306},[253,886,887],{"class":262},"(block.width())\n",[253,889,890,893,896],{"class":124,"line":350},[253,891,892],{"class":258},"         if",[253,894,895],{"class":258}," not",[253,897,898],{"class":262}," block.isNoData(r, c)]\n",[253,900,901,903,905,908,911,914,917,920,923,926,929,931,934,936,939],{"class":124,"line":361},[253,902,611],{"class":306},[253,904,622],{"class":262},[253,906,907],{"class":258},"f",[253,909,910],{"class":299},"\"",[253,912,913],{"class":306},"{len",[253,915,916],{"class":262},"(valid)",[253,918,919],{"class":306},"}",[253,921,922],{"class":299}," valid of ",[253,924,925],{"class":306},"{",[253,927,928],{"class":262},"block.width() ",[253,930,485],{"class":258},[253,932,933],{"class":262}," block.height()",[253,935,919],{"class":306},[253,937,938],{"class":299}," cells\"",[253,940,584],{"class":262},[253,942,943,945,947,950,952,955,958,961,963,966],{"class":124,"line":366},[253,944,611],{"class":306},[253,946,622],{"class":262},[253,948,949],{"class":299},"\"min\"",[253,951,568],{"class":262},[253,953,954],{"class":306},"min",[253,956,957],{"class":262},"(valid), ",[253,959,960],{"class":299},"\"max\"",[253,962,568],{"class":262},[253,964,965],{"class":306},"max",[253,967,968],{"class":262},"(valid))\n",[14,970,971,973,974,977,978,981,982,985,986,988,989,992,993,996,997,1000,1001,21],{},[222,972,657],{}," ",[18,975,976],{},"sourceHasNoDataValue"," reports whether the file declares one; ",[18,979,980],{},"useSourceNoDataValue"," controls whether QGIS honours it, and user-defined NoData ranges set in the layer properties are applied on top. ",[18,983,984],{},"isNoData"," accounts for all of those, which is why it is more reliable than comparing values against ",[18,987,797],{}," yourself. The data type matters beyond correctness: an ",[18,990,991],{},"Int16"," DEM with values in decimetres, or a ",[18,994,995],{},"UInt16"," satellite band with a scale factor, needs converting before the numbers mean anything — check the band's scale and offset with ",[18,998,999],{},"provider.bandScale(band)"," and ",[18,1002,1003],{},"provider.bandOffset(band)",[211,1005,1007],{"id":1006},"convert-a-block-to-a-numpy-array","Convert a block to a NumPy array",[14,1009,1010],{},"For anything beyond a few lookups, convert the block's raw bytes to a NumPy array in one step and do the arithmetic there. The byte layout is the provider's native data type, row by row.",[244,1012,1014],{"className":246,"code":1013,"language":248,"meta":249,"style":249},"import numpy as np\nfrom qgis.core import Qgis\n\nNUMPY_TYPES = {\n    Qgis.DataType.Byte: np.uint8,\n    Qgis.DataType.Int16: np.int16,\n    Qgis.DataType.UInt16: np.uint16,\n    Qgis.DataType.Int32: np.int32,\n    Qgis.DataType.UInt32: np.uint32,\n    Qgis.DataType.Float32: np.float32,\n    Qgis.DataType.Float64: np.float64,\n}\n\ndef block_to_array(block):\n    dtype = NUMPY_TYPES[block.dataType()]\n    data = np.frombuffer(bytes(block.data()), dtype=dtype)\n    array = data.reshape(block.height(), block.width()).astype(np.float64)\n    if block.hasNoDataValue():\n        array[array == block.noDataValue()] = np.nan\n    return array\n\nheights = block_to_array(block)\nprint(\"mean\", np.nanmean(heights), \"p95\", np.nanpercentile(heights, 95))\nprint(\"cells above 120 m:\", int(np.sum(heights > 120)))\n",[18,1015,1016,1029,1040,1044,1055,1060,1065,1070,1075,1080,1085,1090,1095,1099,1109,1122,1147,1157,1165,1181,1188,1192,1202,1225],{"__ignoreMap":249},[253,1017,1018,1020,1023,1026],{"class":124,"line":255},[253,1019,259],{"class":258},[253,1021,1022],{"class":262}," numpy ",[253,1024,1025],{"class":258},"as",[253,1027,1028],{"class":262}," np\n",[253,1030,1031,1033,1035,1037],{"class":124,"line":266},[253,1032,269],{"class":258},[253,1034,272],{"class":262},[253,1036,259],{"class":258},[253,1038,1039],{"class":262}," Qgis\n",[253,1041,1042],{"class":124,"line":280},[253,1043,284],{"emptyLinePlaceholder":283},[253,1045,1046,1049,1052],{"class":124,"line":287},[253,1047,1048],{"class":306},"NUMPY_TYPES",[253,1050,1051],{"class":258}," =",[253,1053,1054],{"class":262}," {\n",[253,1056,1057],{"class":124,"line":312},[253,1058,1059],{"class":262},"    Qgis.DataType.Byte: np.uint8,\n",[253,1061,1062],{"class":124,"line":323},[253,1063,1064],{"class":262},"    Qgis.DataType.Int16: np.int16,\n",[253,1066,1067],{"class":124,"line":328},[253,1068,1069],{"class":262},"    Qgis.DataType.UInt16: np.uint16,\n",[253,1071,1072],{"class":124,"line":339},[253,1073,1074],{"class":262},"    Qgis.DataType.Int32: np.int32,\n",[253,1076,1077],{"class":124,"line":350},[253,1078,1079],{"class":262},"    Qgis.DataType.UInt32: np.uint32,\n",[253,1081,1082],{"class":124,"line":361},[253,1083,1084],{"class":262},"    Qgis.DataType.Float32: np.float32,\n",[253,1086,1087],{"class":124,"line":366},[253,1088,1089],{"class":262},"    Qgis.DataType.Float64: np.float64,\n",[253,1091,1092],{"class":124,"line":379},[253,1093,1094],{"class":262},"}\n",[253,1096,1097],{"class":124,"line":402},[253,1098,284],{"emptyLinePlaceholder":283},[253,1100,1101,1103,1106],{"class":124,"line":421},[253,1102,369],{"class":258},[253,1104,1105],{"class":372}," block_to_array",[253,1107,1108],{"class":262},"(block):\n",[253,1110,1111,1114,1116,1119],{"class":124,"line":442},[253,1112,1113],{"class":262},"    dtype ",[253,1115,293],{"class":258},[253,1117,1118],{"class":306}," NUMPY_TYPES",[253,1120,1121],{"class":262},"[block.dataType()]\n",[253,1123,1124,1127,1129,1132,1135,1138,1142,1144],{"class":124,"line":462},[253,1125,1126],{"class":262},"    data ",[253,1128,293],{"class":258},[253,1130,1131],{"class":262}," np.frombuffer(",[253,1133,1134],{"class":306},"bytes",[253,1136,1137],{"class":262},"(block.data()), ",[253,1139,1141],{"class":1140},"s9osk","dtype",[253,1143,293],{"class":258},[253,1145,1146],{"class":262},"dtype)\n",[253,1148,1149,1152,1154],{"class":124,"line":473},[253,1150,1151],{"class":262},"    array ",[253,1153,293],{"class":258},[253,1155,1156],{"class":262}," data.reshape(block.height(), block.width()).astype(np.float64)\n",[253,1158,1159,1162],{"class":124,"line":501},[253,1160,1161],{"class":258},"    if",[253,1163,1164],{"class":262}," block.hasNoDataValue():\n",[253,1166,1167,1170,1173,1176,1178],{"class":124,"line":524},[253,1168,1169],{"class":262},"        array[array ",[253,1171,1172],{"class":258},"==",[253,1174,1175],{"class":262}," block.noDataValue()] ",[253,1177,293],{"class":258},[253,1179,1180],{"class":262}," np.nan\n",[253,1182,1183,1185],{"class":124,"line":530},[253,1184,533],{"class":258},[253,1186,1187],{"class":262}," array\n",[253,1189,1190],{"class":124,"line":549},[253,1191,284],{"emptyLinePlaceholder":283},[253,1193,1194,1197,1199],{"class":124,"line":554},[253,1195,1196],{"class":262},"heights ",[253,1198,293],{"class":258},[253,1200,1201],{"class":262}," block_to_array(block)\n",[253,1203,1204,1206,1208,1211,1214,1217,1220,1223],{"class":124,"line":587},[253,1205,611],{"class":306},[253,1207,622],{"class":262},[253,1209,1210],{"class":299},"\"mean\"",[253,1212,1213],{"class":262},", np.nanmean(heights), ",[253,1215,1216],{"class":299},"\"p95\"",[253,1218,1219],{"class":262},", np.nanpercentile(heights, ",[253,1221,1222],{"class":306},"95",[253,1224,652],{"class":262},[253,1226,1227,1229,1231,1234,1236,1239,1242,1245,1248],{"class":124,"line":603},[253,1228,611],{"class":306},[253,1230,622],{"class":262},[253,1232,1233],{"class":299},"\"cells above 120 m:\"",[253,1235,568],{"class":262},[253,1237,1238],{"class":306},"int",[253,1240,1241],{"class":262},"(np.sum(heights ",[253,1243,1244],{"class":258},">",[253,1246,1247],{"class":306}," 120",[253,1249,1250],{"class":262},")))\n",[14,1252,1253,973,1255,1258,1259,1262,1263,1265,1266,1269,1270,1273,1274,1277,1278,1281,1282,1285],{},[222,1254,657],{},[18,1256,1257],{},"block.data()"," returns a ",[18,1260,1261],{},"QByteArray"," of the raw cell bytes; converting it to Python ",[18,1264,1134],{}," and handing it to ",[18,1267,1268],{},"np.frombuffer"," avoids a Python loop entirely. Casting to ",[18,1271,1272],{},"float64"," and replacing NoData with ",[18,1275,1276],{},"NaN"," lets the ",[18,1279,1280],{},"nan","-aware NumPy functions ignore missing cells without masks. On a 5,000 × 5,000 block this is thousands of times faster than calling ",[18,1283,1284],{},"value()"," per cell. When the source uses a NoData value that also occurs as real data — a DEM with genuine zero heights and NoData of zero — the problem is in the data, not the code, and no conversion can fix it.",[14,1287,1288],{},[34,1289,1292,1295,1298,1301,1304,1308,1313,1318,1323,1326,1330,1333,1336,1376,1378,1382,1385],{"viewBox":1290,"role":37,"ariaLabel":1291,"xmlns":39},"0 0 760 244","Memory use when reading a large raster: one block for the whole raster grows with its size and can exhaust memory, while tiled reads keep a constant small footprint and combine running results",[41,1293,1294],{},"Whole raster or tiles?",[45,1296,1297],{},"A 40,000 by 40,000 cell Float32 raster needs about 6 GB as one block, more once converted to float64. Reading it in 2,048 by 2,048 tiles needs about 32 MB at a time. Running results such as counts, sums and histograms are accumulated per tile, so the answer is identical while memory stays flat.",[49,1299],{"x":51,"y":51,"width":52,"height":1300,"fill":54},"244",[73,1302,1303],{"x":75,"y":684,"style":77,"fill":78,"textAnchor":79},"Same result, very different memory",[49,1305],{"x":83,"y":688,"width":689,"height":1306,"rx":143,"fill":1307,"stroke":690,"style":146},"180","#fdf2e2",[73,1309,1312],{"x":693,"y":1310,"style":1311,"fill":690,"textAnchor":79},"70","text-anchor:middle;font-size:12px;font-weight:bold;font-family:sans-serif","one block",[49,1314],{"x":1315,"y":1316,"width":784,"height":784,"fill":690,"style":1317},"60","86","opacity:0.85",[73,1319,1322],{"x":140,"y":1320,"style":1321,"fill":71,"textAnchor":79},"126","text-anchor:middle;font-size:10.5px;font-family:sans-serif","40,000² Float32",[73,1324,1325],{"x":140,"y":133,"style":1311,"fill":690,"textAnchor":79},"≈ 6 GB",[73,1327,1329],{"x":140,"y":1328,"style":155,"fill":71,"textAnchor":79},"176","×2 as float64",[49,1331],{"x":744,"y":688,"width":689,"height":1306,"rx":143,"fill":1332,"stroke":745,"style":146},"#e8efe6",[73,1334,1335],{"x":748,"y":1310,"style":1311,"fill":749,"textAnchor":79},"tiles",[698,1337,1340,1344,1347,1350,1353,1355,1357,1359,1361,1364,1366,1368,1370,1372,1374],{"fill":1338,"stroke":745,"style":1339},"#c9d6cf","stroke-width:0.8",[49,1341],{"x":1342,"y":1316,"width":1343,"height":1343},"428","30",[49,1345],{"x":1346,"y":1316,"width":1343,"height":1343},"458",[49,1348],{"x":1349,"y":1316,"width":1343,"height":1343},"488",[49,1351],{"x":1352,"y":1316,"width":1343,"height":1343},"518",[49,1354],{"x":1342,"y":707,"width":1343,"height":1343},[49,1356],{"x":1346,"y":707,"width":1343,"height":1343},[49,1358],{"x":1349,"y":707,"width":1343,"height":1343},[49,1360],{"x":1352,"y":707,"width":1343,"height":1343},[49,1362],{"x":1342,"y":1363,"width":1343,"height":1343},"146",[49,1365],{"x":1346,"y":1363,"width":1343,"height":1343},[49,1367],{"x":1349,"y":1363,"width":1343,"height":1343},[49,1369],{"x":1352,"y":1363,"width":1343,"height":1343},[49,1371],{"x":1342,"y":1328,"width":1343,"height":1343},[49,1373],{"x":1346,"y":1328,"width":1343,"height":1343},[49,1375],{"x":1349,"y":1328,"width":1343,"height":1343},[49,1377],{"x":1352,"y":1328,"width":1343,"height":1343,"fill":745},[73,1379,1381],{"x":1380,"y":1320,"style":1321,"fill":71,"textAnchor":79},"646","2,048² at a time",[73,1383,1384],{"x":1380,"y":133,"style":1311,"fill":749,"textAnchor":79},"≈ 32 MB",[73,1386,1387],{"x":1380,"y":1328,"style":155,"fill":71,"textAnchor":79},"accumulate per tile",[211,1389,1391],{"id":1390},"read-a-large-raster-tile-by-tile","Read a large raster tile by tile",[14,1393,1394],{},"A national DEM at 1 m does not fit in memory as one block. Walk it in fixed-size tiles aligned to the cell grid and accumulate whatever you need — counts, sums, a histogram — as you go.",[244,1396,1398],{"className":246,"code":1397,"language":248,"meta":249,"style":249},"def iter_tiles(layer, band=1, tile=2048):\n    prov = layer.dataProvider()\n    ext = layer.extent()\n    cols, rows = layer.width(), layer.height()\n    cx, cy = layer.rasterUnitsPerPixelX(), layer.rasterUnitsPerPixelY()\n    for row0 in range(0, rows, tile):\n        for col0 in range(0, cols, tile):\n            w = min(tile, cols - col0)\n            h = min(tile, rows - row0)\n            rect = QgsRectangle(\n                ext.xMinimum() + col0 * cx, ext.yMaximum() - (row0 + h) * cy,\n                ext.xMinimum() + (col0 + w) * cx, ext.yMaximum() - row0 * cy,\n            )\n            yield row0, col0, prov.block(band, rect, w, h)\n\nbins = np.arange(0, 1001, 10)\nhistogram = np.zeros(len(bins) - 1, dtype=np.int64)\ncount = total = 0\n\nfor row0, col0, blk in iter_tiles(dem):\n    arr = block_to_array(blk)\n    values = arr[~np.isnan(arr)]\n    histogram += np.histogram(values, bins=bins)[0]\n    count += values.size\n    total += values.sum()\n\nprint(\"valid cells:\", count, \"mean:\", total \u002F count)\nprint(\"most common 10 m band starts at\", bins[histogram.argmax()], \"m\")\n",[18,1399,1400,1425,1435,1445,1455,1465,1483,1501,1519,1536,1545,1574,1600,1605,1613,1617,1640,1670,1685,1689,1702,1712,1728,1751,1761,1771,1775,1799],{"__ignoreMap":249},[253,1401,1402,1404,1407,1410,1412,1414,1417,1419,1422],{"class":124,"line":255},[253,1403,369],{"class":258},[253,1405,1406],{"class":372}," iter_tiles",[253,1408,1409],{"class":262},"(layer, band",[253,1411,293],{"class":258},[253,1413,100],{"class":306},[253,1415,1416],{"class":262},", tile",[253,1418,293],{"class":258},[253,1420,1421],{"class":306},"2048",[253,1423,1424],{"class":262},"):\n",[253,1426,1427,1430,1432],{"class":124,"line":266},[253,1428,1429],{"class":262},"    prov ",[253,1431,293],{"class":258},[253,1433,1434],{"class":262}," layer.dataProvider()\n",[253,1436,1437,1440,1442],{"class":124,"line":280},[253,1438,1439],{"class":262},"    ext ",[253,1441,293],{"class":258},[253,1443,1444],{"class":262}," layer.extent()\n",[253,1446,1447,1450,1452],{"class":124,"line":287},[253,1448,1449],{"class":262},"    cols, rows ",[253,1451,293],{"class":258},[253,1453,1454],{"class":262}," layer.width(), layer.height()\n",[253,1456,1457,1460,1462],{"class":124,"line":312},[253,1458,1459],{"class":262},"    cx, cy ",[253,1461,293],{"class":258},[253,1463,1464],{"class":262}," layer.rasterUnitsPerPixelX(), layer.rasterUnitsPerPixelY()\n",[253,1466,1467,1470,1472,1474,1476,1478,1480],{"class":124,"line":323},[253,1468,1469],{"class":258},"    for",[253,1471,517],{"class":262},[253,1473,867],{"class":258},[253,1475,870],{"class":306},[253,1477,622],{"class":262},[253,1479,51],{"class":306},[253,1481,1482],{"class":262},", rows, tile):\n",[253,1484,1485,1488,1490,1492,1494,1496,1498],{"class":124,"line":328},[253,1486,1487],{"class":258},"        for",[253,1489,482],{"class":262},[253,1491,867],{"class":258},[253,1493,870],{"class":306},[253,1495,622],{"class":262},[253,1497,51],{"class":306},[253,1499,1500],{"class":262},", cols, tile):\n",[253,1502,1503,1506,1508,1511,1514,1516],{"class":124,"line":339},[253,1504,1505],{"class":262},"            w ",[253,1507,293],{"class":258},[253,1509,1510],{"class":306}," min",[253,1512,1513],{"class":262},"(tile, cols ",[253,1515,390],{"class":258},[253,1517,1518],{"class":262}," col0)\n",[253,1520,1521,1524,1526,1528,1531,1533],{"class":124,"line":350},[253,1522,1523],{"class":262},"            h ",[253,1525,293],{"class":258},[253,1527,1510],{"class":306},[253,1529,1530],{"class":262},"(tile, rows ",[253,1532,390],{"class":258},[253,1534,1535],{"class":262}," row0)\n",[253,1537,1538,1541,1543],{"class":124,"line":361},[253,1539,1540],{"class":262},"            rect ",[253,1542,293],{"class":258},[253,1544,470],{"class":262},[253,1546,1547,1550,1552,1554,1556,1559,1561,1564,1566,1569,1571],{"class":124,"line":366},[253,1548,1549],{"class":262},"                ext.xMinimum() ",[253,1551,479],{"class":258},[253,1553,482],{"class":262},[253,1555,485],{"class":258},[253,1557,1558],{"class":262}," cx, ext.yMaximum() ",[253,1560,390],{"class":258},[253,1562,1563],{"class":262}," (row0 ",[253,1565,479],{"class":258},[253,1567,1568],{"class":262}," h) ",[253,1570,485],{"class":258},[253,1572,1573],{"class":262}," cy,\n",[253,1575,1576,1578,1580,1583,1585,1588,1590,1592,1594,1596,1598],{"class":124,"line":379},[253,1577,1549],{"class":262},[253,1579,479],{"class":258},[253,1581,1582],{"class":262}," (col0 ",[253,1584,479],{"class":258},[253,1586,1587],{"class":262}," w) ",[253,1589,485],{"class":258},[253,1591,1558],{"class":262},[253,1593,390],{"class":258},[253,1595,517],{"class":262},[253,1597,485],{"class":258},[253,1599,1573],{"class":262},[253,1601,1602],{"class":124,"line":402},[253,1603,1604],{"class":262},"            )\n",[253,1606,1607,1610],{"class":124,"line":421},[253,1608,1609],{"class":258},"            yield",[253,1611,1612],{"class":262}," row0, col0, prov.block(band, rect, w, h)\n",[253,1614,1615],{"class":124,"line":442},[253,1616,284],{"emptyLinePlaceholder":283},[253,1618,1619,1622,1624,1627,1629,1631,1634,1636,1638],{"class":124,"line":462},[253,1620,1621],{"class":262},"bins ",[253,1623,293],{"class":258},[253,1625,1626],{"class":262}," np.arange(",[253,1628,51],{"class":306},[253,1630,568],{"class":262},[253,1632,1633],{"class":306},"1001",[253,1635,568],{"class":262},[253,1637,143],{"class":306},[253,1639,584],{"class":262},[253,1641,1642,1645,1647,1650,1653,1656,1658,1661,1663,1665,1667],{"class":124,"line":473},[253,1643,1644],{"class":262},"histogram ",[253,1646,293],{"class":258},[253,1648,1649],{"class":262}," np.zeros(",[253,1651,1652],{"class":306},"len",[253,1654,1655],{"class":262},"(bins) ",[253,1657,390],{"class":258},[253,1659,1660],{"class":306}," 1",[253,1662,568],{"class":262},[253,1664,1141],{"class":1140},[253,1666,293],{"class":258},[253,1668,1669],{"class":262},"np.int64)\n",[253,1671,1672,1675,1677,1680,1682],{"class":124,"line":501},[253,1673,1674],{"class":262},"count ",[253,1676,293],{"class":258},[253,1678,1679],{"class":262}," total ",[253,1681,293],{"class":258},[253,1683,1684],{"class":306}," 0\n",[253,1686,1687],{"class":124,"line":524},[253,1688,284],{"emptyLinePlaceholder":283},[253,1690,1691,1694,1697,1699],{"class":124,"line":530},[253,1692,1693],{"class":258},"for",[253,1695,1696],{"class":262}," row0, col0, blk ",[253,1698,867],{"class":258},[253,1700,1701],{"class":262}," iter_tiles(dem):\n",[253,1703,1704,1707,1709],{"class":124,"line":549},[253,1705,1706],{"class":262},"    arr ",[253,1708,293],{"class":258},[253,1710,1711],{"class":262}," block_to_array(blk)\n",[253,1713,1714,1717,1719,1722,1725],{"class":124,"line":554},[253,1715,1716],{"class":262},"    values ",[253,1718,293],{"class":258},[253,1720,1721],{"class":262}," arr[",[253,1723,1724],{"class":258},"~",[253,1726,1727],{"class":262},"np.isnan(arr)]\n",[253,1729,1730,1733,1736,1739,1742,1744,1747,1749],{"class":124,"line":587},[253,1731,1732],{"class":262},"    histogram ",[253,1734,1735],{"class":258},"+=",[253,1737,1738],{"class":262}," np.histogram(values, ",[253,1740,1741],{"class":1140},"bins",[253,1743,293],{"class":258},[253,1745,1746],{"class":262},"bins)[",[253,1748,51],{"class":306},[253,1750,309],{"class":262},[253,1752,1753,1756,1758],{"class":124,"line":603},[253,1754,1755],{"class":262},"    count ",[253,1757,1735],{"class":258},[253,1759,1760],{"class":262}," values.size\n",[253,1762,1763,1766,1768],{"class":124,"line":608},[253,1764,1765],{"class":262},"    total ",[253,1767,1735],{"class":258},[253,1769,1770],{"class":262}," values.sum()\n",[253,1772,1773],{"class":124,"line":617},[253,1774,284],{"emptyLinePlaceholder":283},[253,1776,1778,1780,1782,1785,1788,1791,1794,1796],{"class":124,"line":1777},27,[253,1779,611],{"class":306},[253,1781,622],{"class":262},[253,1783,1784],{"class":299},"\"valid cells:\"",[253,1786,1787],{"class":262},", count, ",[253,1789,1790],{"class":299},"\"mean:\"",[253,1792,1793],{"class":262},", total ",[253,1795,396],{"class":258},[253,1797,1798],{"class":262}," count)\n",[253,1800,1802,1804,1806,1809,1812,1815],{"class":124,"line":1801},28,[253,1803,611],{"class":306},[253,1805,622],{"class":262},[253,1807,1808],{"class":299},"\"most common 10 m band starts at\"",[253,1810,1811],{"class":262},", bins[histogram.argmax()], ",[253,1813,1814],{"class":299},"\"m\"",[253,1816,584],{"class":262},[14,1818,1819,1821,1822,1825,1826,1830],{},[222,1820,657],{}," Tiles are computed in cell coordinates and converted to map coordinates, so every tile is exactly aligned and the last row and column are shortened rather than resampled. A tile size that matches the file's internal block size — often 256, 512 or 1024 in a tiled GeoTIFF — lets GDAL read whole internal blocks, which is noticeably faster; ",[18,1823,1824],{},"gdalinfo"," reports it. Accumulating a histogram and running sums gives exact results in constant memory; percentiles need either the histogram approach or a sample, because an exact median of forty billion values needs them all. For per-zone results, ",[26,1827,1829],{"href":1828},"\u002Fspatial-data-processing-automation\u002Fraster-analysis-workflows\u002Fzonal-statistics-pyqgis\u002F","zonal statistics"," already does the tiling for you.",[211,1832,1834],{"id":1833},"look-up-single-cells-quickly","Look up single cells quickly",[14,1836,1837,1838,1841,1842,1845],{},"For a handful of points — a plugin that shows the height under the cursor, say — ",[18,1839,1840],{},"identify"," or ",[18,1843,1844],{},"sample"," avoid building blocks at all.",[244,1847,1849],{"className":246,"code":1848,"language":248,"meta":249,"style":249},"from qgis.core import QgsPointXY, QgsRaster\n\npoint = QgsPointXY(429812.4, 433120.7)\nvalue, ok = provider.sample(point, 1)\nprint(\"sample:\", value if ok else \"outside or NoData\")\n\nresult = provider.identify(point, QgsRaster.IdentifyFormatValue)\nif result.isValid():\n    print(\"all bands:\", result.results())\n",[18,1850,1851,1862,1866,1886,1900,1926,1930,1940,1947],{"__ignoreMap":249},[253,1852,1853,1855,1857,1859],{"class":124,"line":255},[253,1854,269],{"class":258},[253,1856,272],{"class":262},[253,1858,259],{"class":258},[253,1860,1861],{"class":262}," QgsPointXY, QgsRaster\n",[253,1863,1864],{"class":124,"line":266},[253,1865,284],{"emptyLinePlaceholder":283},[253,1867,1868,1871,1873,1876,1879,1881,1884],{"class":124,"line":280},[253,1869,1870],{"class":262},"point ",[253,1872,293],{"class":258},[253,1874,1875],{"class":262}," QgsPointXY(",[253,1877,1878],{"class":306},"429812.4",[253,1880,568],{"class":262},[253,1882,1883],{"class":306},"433120.7",[253,1885,584],{"class":262},[253,1887,1888,1891,1893,1896,1898],{"class":124,"line":287},[253,1889,1890],{"class":262},"value, ok ",[253,1892,293],{"class":258},[253,1894,1895],{"class":262}," provider.sample(point, ",[253,1897,100],{"class":306},[253,1899,584],{"class":262},[253,1901,1902,1904,1906,1909,1912,1915,1918,1921,1924],{"class":124,"line":312},[253,1903,611],{"class":306},[253,1905,622],{"class":262},[253,1907,1908],{"class":299},"\"sample:\"",[253,1910,1911],{"class":262},", value ",[253,1913,1914],{"class":258},"if",[253,1916,1917],{"class":262}," ok ",[253,1919,1920],{"class":258},"else",[253,1922,1923],{"class":299}," \"outside or NoData\"",[253,1925,584],{"class":262},[253,1927,1928],{"class":124,"line":323},[253,1929,284],{"emptyLinePlaceholder":283},[253,1931,1932,1935,1937],{"class":124,"line":328},[253,1933,1934],{"class":262},"result ",[253,1936,293],{"class":258},[253,1938,1939],{"class":262}," provider.identify(point, QgsRaster.IdentifyFormatValue)\n",[253,1941,1942,1944],{"class":124,"line":339},[253,1943,1914],{"class":258},[253,1945,1946],{"class":262}," result.isValid():\n",[253,1948,1949,1952,1954,1957],{"class":124,"line":350},[253,1950,1951],{"class":306},"    print",[253,1953,622],{"class":262},[253,1955,1956],{"class":299},"\"all bands:\"",[253,1958,1959],{"class":262},", result.results())\n",[14,1961,1962,973,1964,1966,1967,1969,1970,1974,1975,1979],{},[222,1963,657],{},[18,1965,1844],{}," returns a single band's value and a success flag, and is the fastest way to read one cell. ",[18,1968,1840],{}," returns every band at once as a dictionary keyed by band number, which suits multispectral imagery. Both expect the point in the layer's CRS; transform it first if it comes from the canvas, as in ",[26,1971,1973],{"href":1972},"\u002Fspatial-data-processing-automation\u002Fcoordinate-reference-systems\u002Ftransform-point-coordinates-pyqgis\u002F","transforming point coordinates",". For many points, ",[26,1976,1978],{"href":1977},"\u002Fspatial-data-processing-automation\u002Fraster-analysis-workflows\u002Fsample-raster-values-at-points-pyqgis\u002F","sampling raster values at points"," with the Processing algorithm is simpler than a loop.",[211,1981,1983],{"id":1982},"qgis-version-compatibility","QGIS version compatibility",[14,1985,1986,568,1988,1000,1991,1993,1994,1997,1998,2001,2002,2005,2006,2009,2010,2013],{},[18,1987,20],{},[18,1989,1990],{},"provider.block",[18,1992,1840],{}," are unchanged since QGIS 3.0; ",[18,1995,1996],{},"provider.sample"," arrived in 3.4. ",[18,1999,2000],{},"Qgis.DataType"," is the scoped spelling used from 3.30 and required on the QGIS 4 series — older code may use ",[18,2003,2004],{},"Qgis.Float32"," directly. ",[18,2007,2008],{},"QgsRaster.IdentifyFormatValue"," becomes ",[18,2011,2012],{},"Qgis.RasterIdentifyFormat.Value"," on recent releases. The NumPy conversion depends only on the byte layout, which has not changed.",[211,2015,2017],{"id":2016},"troubleshooting","Troubleshooting",[216,2019,2020,2026,2035,2046,2052],{},[219,2021,2022,2025],{},[222,2023,2024],{},"Values look smoothed."," The window was not aligned or the size did not match the cell count, so the provider resampled.",[219,2027,2028,2031,2032,21],{},[222,2029,2030],{},"The array has the wrong shape or garbage values."," The NumPy dtype does not match ",[18,2033,2034],{},"block.dataType()",[219,2036,2037,2040,2041,2043,2044,21],{},[222,2038,2039],{},"Statistics are far too low."," NoData cells were included; use ",[18,2042,984],{}," or convert to ",[18,2045,1276],{},[219,2047,2048,2051],{},[222,2049,2050],{},"Reading is slow on a network drive."," Tile size does not match the file's internal blocks, or the file is not tiled at all; convert to a Cloud-Optimized GeoTIFF.",[219,2053,2054,2059],{},[222,2055,2056,2058],{},[18,2057,1257],{}," is empty."," The request fell entirely outside the raster extent.",[211,2061,2063],{"id":2062},"conclusion","Conclusion",[14,2065,2066,2067,1841,2069,2071,2072,1841,2074,2076],{},"Request blocks for grid-aligned windows at native size, respect NoData through ",[18,2068,984],{},[18,2070,1276],{},", and convert whole blocks to NumPy rather than reading cells one at a time. Walk large rasters in aligned tiles and accumulate results so memory stays flat, and use ",[18,2073,1844],{},[18,2075,1840],{}," when you only need a few cells.",[211,2078,2080],{"id":2079},"frequently-asked-questions","Frequently Asked Questions",[14,2082,2083,2086],{},[222,2084,2085],{},"Can I read a downsampled overview for speed?","\nYes — request a smaller width and height for the same extent. The provider uses the file's overviews when they exist, which is how renderers read quickly.",[14,2088,2089,2092,2093,2095],{},[222,2090,2091],{},"Does this work for multiband imagery?","\nYes. Request one block per band, or use ",[18,2094,1840],{}," for all bands at a point.",[14,2097,2098,2101,2102,2105,2106,21],{},[222,2099,2100],{},"Can I write values back through the same block?","\nNot to a read-only layer. Create a writable provider with ",[18,2103,2104],{},"QgsRasterFileWriter",", as in ",[26,2107,2109],{"href":2108},"\u002Fspatial-data-processing-automation\u002Fraster-analysis-workflows\u002Fwrite-numpy-array-to-raster-pyqgis\u002F","writing a NumPy array to a raster",[14,2111,2112,2115,2116,2119],{},[222,2113,2114],{},"Should I use GDAL directly instead?","\nGDAL's ",[18,2117,2118],{},"ReadAsArray"," is equally fast for files. The provider route works for any raster QGIS can open, respects layer NoData settings, and needs no second library in a plugin.",[211,2121,2123],{"id":2122},"related","Related",[216,2125,2126,2131,2136,2142,2147],{},[219,2127,2128,2130],{},[26,2129,29],{"href":28}," — the guide this recipe belongs to",[219,2132,2133],{},[26,2134,2135],{"href":2108},"Write a NumPy Array to a Raster in PyQGIS",[219,2137,2138],{},[26,2139,2141],{"href":2140},"\u002Fspatial-data-processing-automation\u002Fraster-analysis-workflows\u002Fcalculate-raster-statistics-pyqgis\u002F","Calculate Raster Statistics in PyQGIS",[219,2143,2144],{},[26,2145,2146],{"href":1977},"Sample Raster Values at Points in PyQGIS",[219,2148,2149],{},[26,2150,2152],{"href":2151},"\u002Fspatial-data-processing-automation\u002Fweb-services-and-remote-data\u002Fread-cloud-optimized-geotiff-pyqgis\u002F","Read a Cloud-Optimized GeoTIFF in PyQGIS",[2154,2155,2156],"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 .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}",{"title":249,"searchDepth":266,"depth":266,"links":2158},[2159,2160,2161,2162,2163,2164,2165,2166,2167,2168,2169],{"id":213,"depth":266,"text":214},{"id":238,"depth":266,"text":239},{"id":790,"depth":266,"text":791},{"id":1006,"depth":266,"text":1007},{"id":1390,"depth":266,"text":1391},{"id":1833,"depth":266,"text":1834},{"id":1982,"depth":266,"text":1983},{"id":2016,"depth":266,"text":2017},{"id":2062,"depth":266,"text":2063},{"id":2079,"depth":266,"text":2080},{"id":2122,"depth":266,"text":2123},"Read cell values from a raster layer through the data provider — whole bands, windows and single cells with QgsRasterBlock, NoData handling, conversion to NumPy arrays, and reading large rasters tile by tile without running out of memory.","md",{"slug":2173,"type":2174,"breadcrumb":2175,"datePublished":2176,"dateModified":2176},"read-raster-pixels-with-qgsrasterblock-pyqgis","article","Read Raster Pixels with QgsRasterBlock","2026-09-17","\u002Fspatial-data-processing-automation\u002Fraster-analysis-workflows\u002Fread-raster-pixels-with-qgsrasterblock-pyqgis",{"title":5,"description":2170},"spatial-data-processing-automation\u002Fraster-analysis-workflows\u002Fread-raster-pixels-with-qgsrasterblock-pyqgis\u002Findex","4-oYRdoU24Ate-zloLk8wKi82RdOd24qCI321bLkJnw",1789632908551]