[{"data":1,"prerenderedAt":1751},["ShallowReactive",2],{"doc:\u002Fspatial-data-processing-automation\u002Fraster-analysis-workflows\u002Fexport-cloud-optimized-geotiff-pyqgis":3},{"id":4,"title":5,"body":6,"description":1740,"extension":1741,"meta":1742,"navigation":189,"path":1747,"seo":1748,"stem":1749,"__hash__":1750},"docs\u002Fspatial-data-processing-automation\u002Fraster-analysis-workflows\u002Fexport-cloud-optimized-geotiff-pyqgis\u002Findex.md","Export a Cloud Optimized GeoTIFF in PyQGIS",{"type":7,"value":8,"toc":1725},"minimark",[9,13,17,31,122,127,137,141,148,387,406,410,413,494,707,717,721,724,904,917,921,924,991,1010,1014,1020,1116,1121,1125,1128,1188,1395,1400,1404,1407,1576,1591,1595,1611,1615,1646,1650,1660,1664,1670,1676,1682,1688,1692,1721],[10,11,5],"h1",{"id":12},"export-a-cloud-optimized-geotiff-in-pyqgis",[14,15,16],"p",{},"A Cloud Optimized GeoTIFF (COG) is an ordinary GeoTIFF with its internal layout arranged for partial reading: data stored in tiles, overviews included, and the index of everything at the start of the file. A client that wants one small area at one zoom level reads the header, then fetches just the few tiles it needs with HTTP range requests. That makes a COG on object storage behave almost like a tile service with no server, and makes the same file fast and compact on a local disk.",[14,18,19,20,25,26,30],{},"This recipe belongs to ",[21,22,24],"a",{"href":23},"\u002Fspatial-data-processing-automation\u002Fraster-analysis-workflows\u002F","Raster Analysis Workflows",". It writes COGs with GDAL's COG driver through Processing, chooses compression and overview settings for different data types, validates the layout, and loads the result back from object storage. Reading existing COGs is covered in ",[21,27,29],{"href":28},"\u002Fspatial-data-processing-automation\u002Fweb-services-and-remote-data\u002Fread-cloud-optimized-geotiff-pyqgis\u002F","reading a Cloud Optimized GeoTIFF",".",[14,32,33],{},[34,35,40,44,48,55,64,73,78,85,91,95,99,103,107,113,117],"svg",{"viewBox":36,"role":37,"ariaLabel":38,"xmlns":39},"0 0 760 270","img","A COG's layout with header and index first, then overviews and full-resolution tiles, read selectively with HTTP range requests","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg",[41,42,43],"title",{},"Inside a COG",[45,46,47],"desc",{},"A COG begins with a header and index describing every tile at every resolution. Overview levels follow, from the smallest to larger ones, then the full-resolution tiles. A client reads the header in one request, then fetches only the tiles covering its view at the right level with HTTP range requests, instead of downloading the whole file.",[49,50],"rect",{"x":51,"y":51,"width":52,"height":53,"fill":54},"0","760","270","#f6f3ea",[56,57,63],"text",{"x":58,"y":59,"style":60,"fill":61,"textAnchor":62},"380","28","text-anchor:middle;font-size:14px;font-family:sans-serif;font-weight:bold","#17211d","middle","Index first, then tiles a client can pick from",[49,65],{"x":66,"y":67,"width":68,"height":66,"rx":69,"fill":70,"stroke":71,"style":72},"40","60","680","4","#eef7f4","#0f766e","stroke-width:2",[56,74,77],{"x":58,"y":75,"style":76,"fill":71,"textAnchor":62},"85","text-anchor:middle;font-size:11.0px;font-family:sans-serif","header + tile index (first request)",[49,79],{"x":66,"y":80,"width":81,"height":66,"rx":69,"fill":82,"stroke":83,"style":84},"112","160","#fdf2e2","#b45309","stroke-width:1.5",[56,86,90],{"x":87,"y":88,"style":89,"fill":83,"textAnchor":62},"120","137","text-anchor:middle;font-size:10.5px;font-family:sans-serif","overview 1\u002F16",[49,92],{"x":93,"y":80,"width":94,"height":66,"rx":69,"fill":82,"stroke":83,"style":84},"210","220",[56,96,98],{"x":97,"y":88,"style":89,"fill":83,"textAnchor":62},"320","overview 1\u002F4",[49,100],{"x":101,"y":80,"width":102,"height":66,"rx":69,"fill":82,"stroke":83,"style":84},"440","280",[56,104,106],{"x":105,"y":88,"style":89,"fill":83,"textAnchor":62},"580","overview 1\u002F2",[49,108],{"x":66,"y":109,"width":68,"height":110,"rx":69,"fill":111,"stroke":112,"style":84},"164","56","#eff3ff","#2563eb",[56,114,116],{"x":58,"y":115,"style":76,"fill":112,"textAnchor":62},"197","full-resolution tiles (512 × 512)",[56,118,121],{"x":58,"y":119,"style":89,"fill":120,"textAnchor":62},"250","#59645f","a zoomed-out view fetches a handful of overview tiles, not gigabytes",[123,124,126],"h2",{"id":125},"prerequisites","Prerequisites",[128,129,130,134],"ul",{},[131,132,133],"li",{},"QGIS 3.34 LTR or newer, or the QGIS 4 series. The COG driver is part of GDAL 3.1 and later, which every current QGIS bundles.",[131,135,136],{},"A raster to publish — a DEM, an orthophoto, a classification — ideally already in its final CRS.",[123,138,140],{"id":139},"write-a-cog-with-gdaltranslate","Write a COG with gdal:translate",[14,142,143,147],{},[144,145,146],"code",{},"gdal:translate"," with the COG output format writes a fully compliant file in one step: tiling, overviews and the header layout are handled by the driver.",[149,150,155],"pre",{"className":151,"code":152,"language":153,"meta":154,"style":154},"language-python shiki shiki-themes github-dark","import processing\nfrom qgis.core import QgsRasterLayer\n\nsrc = \"\u002Fdata\u002Fdem\u002Fdem_mosaic.tif\"\nout = processing.run(\"gdal:translate\", {\n    \"INPUT\": src,\n    \"TARGET_CRS\": None,\n    \"NODATA\": -9999,\n    \"COPY_SUBDATASETS\": False,\n    \"OPTIONS\": \"\",\n    \"EXTRA\": \"-of COG -co COMPRESS=DEFLATE -co PREDICTOR=FLOATING_POINT \"\n             \"-co BLOCKSIZE=512 -co OVERVIEW_RESAMPLING=AVERAGE -co BIGTIFF=IF_SAFER\",\n    \"DATA_TYPE\": 0,\n    \"OUTPUT\": \"\u002Fdata\u002Fpublish\u002Fdem_cog.tif\",\n})[\"OUTPUT\"]\n\ncog = QgsRasterLayer(out, \"DEM (COG)\")\nprint(cog.isValid(), cog.width(), \"x\", cog.height(), cog.dataProvider().dataType(1))\n","python","",[144,156,157,170,184,191,204,221,230,246,262,275,288,299,307,319,332,344,349,366],{"__ignoreMap":154},[158,159,162,166],"span",{"class":160,"line":161},"line",1,[158,163,165],{"class":164},"snl16","import",[158,167,169],{"class":168},"s95oV"," processing\n",[158,171,173,176,179,181],{"class":160,"line":172},2,[158,174,175],{"class":164},"from",[158,177,178],{"class":168}," qgis.core ",[158,180,165],{"class":164},[158,182,183],{"class":168}," QgsRasterLayer\n",[158,185,187],{"class":160,"line":186},3,[158,188,190],{"emptyLinePlaceholder":189},true,"\n",[158,192,194,197,200],{"class":160,"line":193},4,[158,195,196],{"class":168},"src ",[158,198,199],{"class":164},"=",[158,201,203],{"class":202},"sU2Wk"," \"\u002Fdata\u002Fdem\u002Fdem_mosaic.tif\"\n",[158,205,207,210,212,215,218],{"class":160,"line":206},5,[158,208,209],{"class":168},"out ",[158,211,199],{"class":164},[158,213,214],{"class":168}," processing.run(",[158,216,217],{"class":202},"\"gdal:translate\"",[158,219,220],{"class":168},", {\n",[158,222,224,227],{"class":160,"line":223},6,[158,225,226],{"class":202},"    \"INPUT\"",[158,228,229],{"class":168},": src,\n",[158,231,233,236,239,243],{"class":160,"line":232},7,[158,234,235],{"class":202},"    \"TARGET_CRS\"",[158,237,238],{"class":168},": ",[158,240,242],{"class":241},"sDLfK","None",[158,244,245],{"class":168},",\n",[158,247,249,252,254,257,260],{"class":160,"line":248},8,[158,250,251],{"class":202},"    \"NODATA\"",[158,253,238],{"class":168},[158,255,256],{"class":164},"-",[158,258,259],{"class":241},"9999",[158,261,245],{"class":168},[158,263,265,268,270,273],{"class":160,"line":264},9,[158,266,267],{"class":202},"    \"COPY_SUBDATASETS\"",[158,269,238],{"class":168},[158,271,272],{"class":241},"False",[158,274,245],{"class":168},[158,276,278,281,283,286],{"class":160,"line":277},10,[158,279,280],{"class":202},"    \"OPTIONS\"",[158,282,238],{"class":168},[158,284,285],{"class":202},"\"\"",[158,287,245],{"class":168},[158,289,291,294,296],{"class":160,"line":290},11,[158,292,293],{"class":202},"    \"EXTRA\"",[158,295,238],{"class":168},[158,297,298],{"class":202},"\"-of COG -co COMPRESS=DEFLATE -co PREDICTOR=FLOATING_POINT \"\n",[158,300,302,305],{"class":160,"line":301},12,[158,303,304],{"class":202},"             \"-co BLOCKSIZE=512 -co OVERVIEW_RESAMPLING=AVERAGE -co BIGTIFF=IF_SAFER\"",[158,306,245],{"class":168},[158,308,310,313,315,317],{"class":160,"line":309},13,[158,311,312],{"class":202},"    \"DATA_TYPE\"",[158,314,238],{"class":168},[158,316,51],{"class":241},[158,318,245],{"class":168},[158,320,322,325,327,330],{"class":160,"line":321},14,[158,323,324],{"class":202},"    \"OUTPUT\"",[158,326,238],{"class":168},[158,328,329],{"class":202},"\"\u002Fdata\u002Fpublish\u002Fdem_cog.tif\"",[158,331,245],{"class":168},[158,333,335,338,341],{"class":160,"line":334},15,[158,336,337],{"class":168},"})[",[158,339,340],{"class":202},"\"OUTPUT\"",[158,342,343],{"class":168},"]\n",[158,345,347],{"class":160,"line":346},16,[158,348,190],{"emptyLinePlaceholder":189},[158,350,352,355,357,360,363],{"class":160,"line":351},17,[158,353,354],{"class":168},"cog ",[158,356,199],{"class":164},[158,358,359],{"class":168}," QgsRasterLayer(out, ",[158,361,362],{"class":202},"\"DEM (COG)\"",[158,364,365],{"class":168},")\n",[158,367,369,372,375,378,381,384],{"class":160,"line":368},18,[158,370,371],{"class":241},"print",[158,373,374],{"class":168},"(cog.isValid(), cog.width(), ",[158,376,377],{"class":202},"\"x\"",[158,379,380],{"class":168},", cog.height(), cog.dataProvider().dataType(",[158,382,383],{"class":241},"1",[158,385,386],{"class":168},"))\n",[14,388,389,393,394,397,398,401,402,405],{},[390,391,392],"strong",{},"Breakdown:"," ",[144,395,396],{},"-of COG"," selects the COG driver; its creation options go after ",[144,399,400],{},"-co",". The driver builds overviews automatically, so there is no separate overview step. Deflate with the floating-point predictor suits elevation and other continuous float data, shrinking files considerably without loss. A 512-pixel block size is a good balance between request count and request size for both local and remote reading. Average resampling for overviews keeps zoomed-out views of continuous data smooth. ",[144,403,404],{},"BIGTIFF=IF_SAFER"," avoids failures for outputs approaching 4 GB.",[123,407,409],{"id":408},"choose-compression-by-data-type","Choose compression by data type",[14,411,412],{},"Compression decides both file size and fidelity. The right choice depends on what the raster holds.",[14,414,415],{},[34,416,419,422,425,428,431,437,443,448,452,456,460,463,466,468,470,473,476,479,483,486,489,492],{"viewBox":417,"role":37,"ariaLabel":418,"xmlns":39},"0 0 760 242","Compression choices by raster content: lossless predictors for float and categorical data, lossy JPEG or WEBP only for imagery that is only viewed",[41,420,421],{},"Compression by content",[45,423,424],{},"Continuous floating-point data such as elevation: DEFLATE or ZSTD with the floating-point predictor, lossless. Integer and categorical data such as land cover: DEFLATE or ZSTD with the horizontal predictor, lossless, with nearest or mode overview resampling. Photographic imagery: JPEG or WEBP at high quality, lossy but far smaller. Never use lossy compression for data that will be analysed.",[49,426],{"x":51,"y":51,"width":52,"height":427,"fill":54},"242",[56,429,430],{"x":58,"y":59,"style":60,"fill":61,"textAnchor":62},"Lossless for data, lossy only for pictures",[49,432],{"x":433,"y":110,"width":434,"height":435,"rx":436,"fill":70,"stroke":71,"style":72},"24","222","170","8",[56,438,442],{"x":439,"y":440,"style":441,"fill":71,"textAnchor":62},"135","92.78","text-anchor:middle;font-size:11.5px;font-family:sans-serif;font-weight:bold","float (DEM)",[56,444,447],{"x":439,"y":445,"style":89,"fill":446,"textAnchor":62},"118.78","#2f3b35","DEFLATE \u002F ZSTD",[56,449,451],{"x":439,"y":450,"style":89,"fill":446,"textAnchor":62},"144.78","PREDICTOR=",[56,453,455],{"x":439,"y":454,"style":89,"fill":446,"textAnchor":62},"170.78","FLOATING_POINT",[56,457,459],{"x":439,"y":458,"style":89,"fill":120,"textAnchor":62},"196.78","overviews: average",[49,461],{"x":462,"y":110,"width":434,"height":435,"rx":436,"fill":111,"stroke":112,"style":72},"269",[56,464,465],{"x":58,"y":440,"style":441,"fill":112,"textAnchor":62},"categorical",[56,467,447],{"x":58,"y":445,"style":89,"fill":446,"textAnchor":62},[56,469,451],{"x":58,"y":450,"style":89,"fill":446,"textAnchor":62},[56,471,472],{"x":58,"y":454,"style":89,"fill":446,"textAnchor":62},"STANDARD",[56,474,475],{"x":58,"y":458,"style":89,"fill":120,"textAnchor":62},"overviews: mode",[49,477],{"x":478,"y":110,"width":434,"height":435,"rx":436,"fill":82,"stroke":83,"style":72},"514",[56,480,482],{"x":481,"y":440,"style":441,"fill":83,"textAnchor":62},"625","RGB imagery",[56,484,485],{"x":481,"y":445,"style":89,"fill":446,"textAnchor":62},"JPEG \u002F WEBP",[56,487,488],{"x":481,"y":450,"style":89,"fill":446,"textAnchor":62},"QUALITY=85",[56,490,491],{"x":481,"y":454,"style":89,"fill":120,"textAnchor":62},"viewing only",[56,493,459],{"x":481,"y":458,"style":89,"fill":120,"textAnchor":62},[149,495,497],{"className":151,"code":496,"language":153,"meta":154,"style":154},"PROFILES = {\n    \"continuous\": \"-co COMPRESS=DEFLATE -co PREDICTOR=FLOATING_POINT -co OVERVIEW_RESAMPLING=AVERAGE\",\n    \"categorical\": \"-co COMPRESS=DEFLATE -co PREDICTOR=STANDARD -co OVERVIEW_RESAMPLING=MODE\",\n    \"imagery\": \"-co COMPRESS=JPEG -co QUALITY=85 -co OVERVIEW_RESAMPLING=AVERAGE\",\n}\n\ndef write_cog(src, dst, kind, nodata=None):\n    extra = f\"-of COG -co BLOCKSIZE=512 -co BIGTIFF=IF_SAFER {PROFILES[kind]}\"\n    return processing.run(\"gdal:translate\", {\n        \"INPUT\": src, \"NODATA\": nodata, \"EXTRA\": extra, \"DATA_TYPE\": 0,\n        \"OUTPUT\": dst})[\"OUTPUT\"]\n\nwrite_cog(\"\u002Fdata\u002Flandcover\u002Fclassified_2026.tif\", \"\u002Fdata\u002Fpublish\u002Flandcover_cog.tif\",\n          \"categorical\", nodata=0)\nwrite_cog(\"\u002Fdata\u002Fortho\u002Fortho_2026.tif\", \"\u002Fdata\u002Fpublish\u002Fortho_cog.tif\", \"imagery\")\n",[144,498,499,510,522,534,546,551,555,574,599,610,639,651,655,671,688],{"__ignoreMap":154},[158,500,501,504,507],{"class":160,"line":161},[158,502,503],{"class":241},"PROFILES",[158,505,506],{"class":164}," =",[158,508,509],{"class":168}," {\n",[158,511,512,515,517,520],{"class":160,"line":172},[158,513,514],{"class":202},"    \"continuous\"",[158,516,238],{"class":168},[158,518,519],{"class":202},"\"-co COMPRESS=DEFLATE -co PREDICTOR=FLOATING_POINT -co OVERVIEW_RESAMPLING=AVERAGE\"",[158,521,245],{"class":168},[158,523,524,527,529,532],{"class":160,"line":186},[158,525,526],{"class":202},"    \"categorical\"",[158,528,238],{"class":168},[158,530,531],{"class":202},"\"-co COMPRESS=DEFLATE -co PREDICTOR=STANDARD -co OVERVIEW_RESAMPLING=MODE\"",[158,533,245],{"class":168},[158,535,536,539,541,544],{"class":160,"line":193},[158,537,538],{"class":202},"    \"imagery\"",[158,540,238],{"class":168},[158,542,543],{"class":202},"\"-co COMPRESS=JPEG -co QUALITY=85 -co OVERVIEW_RESAMPLING=AVERAGE\"",[158,545,245],{"class":168},[158,547,548],{"class":160,"line":206},[158,549,550],{"class":168},"}\n",[158,552,553],{"class":160,"line":223},[158,554,190],{"emptyLinePlaceholder":189},[158,556,557,560,564,567,569,571],{"class":160,"line":232},[158,558,559],{"class":164},"def",[158,561,563],{"class":562},"svObZ"," write_cog",[158,565,566],{"class":168},"(src, dst, kind, nodata",[158,568,199],{"class":164},[158,570,242],{"class":241},[158,572,573],{"class":168},"):\n",[158,575,576,579,581,584,587,590,593,596],{"class":160,"line":248},[158,577,578],{"class":168},"    extra ",[158,580,199],{"class":164},[158,582,583],{"class":164}," f",[158,585,586],{"class":202},"\"-of COG -co BLOCKSIZE=512 -co BIGTIFF=IF_SAFER ",[158,588,589],{"class":241},"{PROFILES",[158,591,592],{"class":168},"[kind]",[158,594,595],{"class":241},"}",[158,597,598],{"class":202},"\"\n",[158,600,601,604,606,608],{"class":160,"line":264},[158,602,603],{"class":164},"    return",[158,605,214],{"class":168},[158,607,217],{"class":202},[158,609,220],{"class":168},[158,611,612,615,618,621,624,627,630,633,635,637],{"class":160,"line":277},[158,613,614],{"class":202},"        \"INPUT\"",[158,616,617],{"class":168},": src, ",[158,619,620],{"class":202},"\"NODATA\"",[158,622,623],{"class":168},": nodata, ",[158,625,626],{"class":202},"\"EXTRA\"",[158,628,629],{"class":168},": extra, ",[158,631,632],{"class":202},"\"DATA_TYPE\"",[158,634,238],{"class":168},[158,636,51],{"class":241},[158,638,245],{"class":168},[158,640,641,644,647,649],{"class":160,"line":290},[158,642,643],{"class":202},"        \"OUTPUT\"",[158,645,646],{"class":168},": dst})[",[158,648,340],{"class":202},[158,650,343],{"class":168},[158,652,653],{"class":160,"line":301},[158,654,190],{"emptyLinePlaceholder":189},[158,656,657,660,663,666,669],{"class":160,"line":309},[158,658,659],{"class":168},"write_cog(",[158,661,662],{"class":202},"\"\u002Fdata\u002Flandcover\u002Fclassified_2026.tif\"",[158,664,665],{"class":168},", ",[158,667,668],{"class":202},"\"\u002Fdata\u002Fpublish\u002Flandcover_cog.tif\"",[158,670,245],{"class":168},[158,672,673,676,678,682,684,686],{"class":160,"line":321},[158,674,675],{"class":202},"          \"categorical\"",[158,677,665],{"class":168},[158,679,681],{"class":680},"s9osk","nodata",[158,683,199],{"class":164},[158,685,51],{"class":241},[158,687,365],{"class":168},[158,689,690,692,695,697,700,702,705],{"class":160,"line":334},[158,691,659],{"class":168},[158,693,694],{"class":202},"\"\u002Fdata\u002Fortho\u002Fortho_2026.tif\"",[158,696,665],{"class":168},[158,698,699],{"class":202},"\"\u002Fdata\u002Fpublish\u002Fortho_cog.tif\"",[158,701,665],{"class":168},[158,703,704],{"class":202},"\"imagery\"",[158,706,365],{"class":168},[14,708,709,711,712,716],{},[390,710,392],{}," Named profiles keep the decision in one place and make it hard to compress a land-cover map with JPEG by accident. Mode resampling for categorical overviews keeps class codes valid at every zoom level, for the same reason as in ",[21,713,715],{"href":714},"\u002Fspatial-data-processing-automation\u002Fraster-analysis-workflows\u002Fresample-and-align-rasters-pyqgis\u002F","resampling and aligning rasters",". JPEG works on 8-bit RGB imagery only; for imagery with an alpha band or more bands, use WEBP or a lossless option. ZSTD compresses faster and often smaller than DEFLATE where readers support it — recent GDAL everywhere, but check web clients.",[123,718,720],{"id":719},"validate-the-layout","Validate the layout",[14,722,723],{},"A file can be a valid GeoTIFF without being a valid COG — for example after being edited in place, which moves data after the index. GDAL's validation checks the layout.",[149,725,727],{"className":151,"code":726,"language":153,"meta":154,"style":154},"from osgeo import gdal\n\ndef cog_report(path):\n    ds = gdal.Open(path)\n    md = ds.GetMetadata(\"IMAGE_STRUCTURE\")\n    band = ds.GetRasterBand(1)\n    return {\"layout\": md.get(\"LAYOUT\"), \"compression\": md.get(\"COMPRESSION\"),\n            \"block\": band.GetBlockSize(), \"overviews\": band.GetOverviewCount()}\n\ninfo = cog_report(out)\nprint(info)\nassert info[\"layout\"] == \"COG\", \"not a COG layout\"\nassert info[\"overviews\"] > 0, \"no overviews\"\n",[144,728,729,741,745,755,765,780,794,824,838,842,852,859,883],{"__ignoreMap":154},[158,730,731,733,736,738],{"class":160,"line":161},[158,732,175],{"class":164},[158,734,735],{"class":168}," osgeo ",[158,737,165],{"class":164},[158,739,740],{"class":168}," gdal\n",[158,742,743],{"class":160,"line":172},[158,744,190],{"emptyLinePlaceholder":189},[158,746,747,749,752],{"class":160,"line":186},[158,748,559],{"class":164},[158,750,751],{"class":562}," cog_report",[158,753,754],{"class":168},"(path):\n",[158,756,757,760,762],{"class":160,"line":193},[158,758,759],{"class":168},"    ds ",[158,761,199],{"class":164},[158,763,764],{"class":168}," gdal.Open(path)\n",[158,766,767,770,772,775,778],{"class":160,"line":206},[158,768,769],{"class":168},"    md ",[158,771,199],{"class":164},[158,773,774],{"class":168}," ds.GetMetadata(",[158,776,777],{"class":202},"\"IMAGE_STRUCTURE\"",[158,779,365],{"class":168},[158,781,782,785,787,790,792],{"class":160,"line":223},[158,783,784],{"class":168},"    band ",[158,786,199],{"class":164},[158,788,789],{"class":168}," ds.GetRasterBand(",[158,791,383],{"class":241},[158,793,365],{"class":168},[158,795,796,798,801,804,807,810,813,816,818,821],{"class":160,"line":232},[158,797,603],{"class":164},[158,799,800],{"class":168}," {",[158,802,803],{"class":202},"\"layout\"",[158,805,806],{"class":168},": md.get(",[158,808,809],{"class":202},"\"LAYOUT\"",[158,811,812],{"class":168},"), ",[158,814,815],{"class":202},"\"compression\"",[158,817,806],{"class":168},[158,819,820],{"class":202},"\"COMPRESSION\"",[158,822,823],{"class":168},"),\n",[158,825,826,829,832,835],{"class":160,"line":248},[158,827,828],{"class":202},"            \"block\"",[158,830,831],{"class":168},": band.GetBlockSize(), ",[158,833,834],{"class":202},"\"overviews\"",[158,836,837],{"class":168},": band.GetOverviewCount()}\n",[158,839,840],{"class":160,"line":264},[158,841,190],{"emptyLinePlaceholder":189},[158,843,844,847,849],{"class":160,"line":277},[158,845,846],{"class":168},"info ",[158,848,199],{"class":164},[158,850,851],{"class":168}," cog_report(out)\n",[158,853,854,856],{"class":160,"line":290},[158,855,371],{"class":241},[158,857,858],{"class":168},"(info)\n",[158,860,861,864,867,869,872,875,878,880],{"class":160,"line":301},[158,862,863],{"class":164},"assert",[158,865,866],{"class":168}," info[",[158,868,803],{"class":202},[158,870,871],{"class":168},"] ",[158,873,874],{"class":164},"==",[158,876,877],{"class":202}," \"COG\"",[158,879,665],{"class":168},[158,881,882],{"class":202},"\"not a COG layout\"\n",[158,884,885,887,889,891,893,896,899,901],{"class":160,"line":309},[158,886,863],{"class":164},[158,888,866],{"class":168},[158,890,834],{"class":202},[158,892,871],{"class":168},[158,894,895],{"class":164},">",[158,897,898],{"class":241}," 0",[158,900,665],{"class":168},[158,902,903],{"class":202},"\"no overviews\"\n",[14,905,906,908,909,912,913,916],{},[390,907,392],{}," GDAL reports ",[144,910,911],{},"LAYOUT=COG"," in the image structure metadata for files written by the COG driver with an intact layout. The block size should be the tiled size you asked for, not a strip such as (width, 1). The overview count shows that zoomed-out reads will be cheap. For a full structural check, GDAL ships a ",[144,914,915],{},"validate_cloud_optimized_geotiff.py"," script that can be run on the file; the metadata check above is enough for files you produce yourself.",[123,918,920],{"id":919},"serve-from-object-storage","Serve from object storage",[14,922,923],{},"A COG's advantages show when it is read remotely. Upload it to any object storage that supports HTTP range requests — S3, Azure Blob, Google Cloud Storage, a plain web server — and QGIS opens it by URL.",[149,925,927],{"className":151,"code":926,"language":153,"meta":154,"style":154},"from qgis.core import QgsProject\n\nurl = \"\u002Fvsicurl\u002Fhttps:\u002F\u002Fdata.example.org\u002Fpublish\u002Fdem_cog.tif\"\nremote = QgsRasterLayer(url, \"DEM (remote COG)\", \"gdal\")\nprint(remote.isValid(), remote.width(), \"x\", remote.height())\nQgsProject.instance().addMapLayer(remote)\n",[144,928,929,940,944,954,974,986],{"__ignoreMap":154},[158,930,931,933,935,937],{"class":160,"line":161},[158,932,175],{"class":164},[158,934,178],{"class":168},[158,936,165],{"class":164},[158,938,939],{"class":168}," QgsProject\n",[158,941,942],{"class":160,"line":172},[158,943,190],{"emptyLinePlaceholder":189},[158,945,946,949,951],{"class":160,"line":186},[158,947,948],{"class":168},"url ",[158,950,199],{"class":164},[158,952,953],{"class":202}," \"\u002Fvsicurl\u002Fhttps:\u002F\u002Fdata.example.org\u002Fpublish\u002Fdem_cog.tif\"\n",[158,955,956,959,961,964,967,969,972],{"class":160,"line":193},[158,957,958],{"class":168},"remote ",[158,960,199],{"class":164},[158,962,963],{"class":168}," QgsRasterLayer(url, ",[158,965,966],{"class":202},"\"DEM (remote COG)\"",[158,968,665],{"class":168},[158,970,971],{"class":202},"\"gdal\"",[158,973,365],{"class":168},[158,975,976,978,981,983],{"class":160,"line":206},[158,977,371],{"class":241},[158,979,980],{"class":168},"(remote.isValid(), remote.width(), ",[158,982,377],{"class":202},[158,984,985],{"class":168},", remote.height())\n",[158,987,988],{"class":160,"line":223},[158,989,990],{"class":168},"QgsProject.instance().addMapLayer(remote)\n",[14,992,993,995,996,999,1000,1003,1004,1007,1008,30],{},[390,994,392],{}," The ",[144,997,998],{},"\u002Fvsicurl\u002F"," prefix tells GDAL to read the file over HTTP with range requests. QGIS fetches the header once, then only the tiles needed for each view — a national DEM displays at country scale after downloading a few hundred kilobytes. For private buckets, GDAL's ",[144,1001,1002],{},"\u002Fvsis3\u002F"," and ",[144,1005,1006],{},"\u002Fvsiaz\u002F"," handlers read credentials from environment variables or configuration options. The details of authentication, caching and performance are in ",[21,1009,29],{"href":28},[123,1011,1013],{"id":1012},"keep-metadata-with-the-file","Keep metadata with the file",[14,1015,1016,1017,30],{},"A COG published for others should describe itself. GeoTIFF tags hold short metadata — a description, units, the source and processing date — which QGIS shows in Layer Properties and GDAL reports with ",[144,1018,1019],{},"gdalinfo",[149,1021,1023],{"className":151,"code":1022,"language":153,"meta":154,"style":154},"ds = gdal.Open(out, gdal.GA_Update)\nds.SetMetadata({\"TIFFTAG_IMAGEDESCRIPTION\": \"Digital elevation model, 1 m, metres above datum\",\n                \"SOURCE\": \"dem_mosaic.tif (2026 survey tiles)\",\n                \"PROCESSED\": \"2026-10-02\"})\nds.GetRasterBand(1).SetDescription(\"elevation_m\")\nds = None\nprint(cog_report(out)[\"layout\"])\n",[144,1024,1025,1040,1055,1067,1080,1095,1104],{"__ignoreMap":154},[158,1026,1027,1030,1032,1035,1038],{"class":160,"line":161},[158,1028,1029],{"class":168},"ds ",[158,1031,199],{"class":164},[158,1033,1034],{"class":168}," gdal.Open(out, gdal.",[158,1036,1037],{"class":241},"GA_Update",[158,1039,365],{"class":168},[158,1041,1042,1045,1048,1050,1053],{"class":160,"line":172},[158,1043,1044],{"class":168},"ds.SetMetadata({",[158,1046,1047],{"class":202},"\"TIFFTAG_IMAGEDESCRIPTION\"",[158,1049,238],{"class":168},[158,1051,1052],{"class":202},"\"Digital elevation model, 1 m, metres above datum\"",[158,1054,245],{"class":168},[158,1056,1057,1060,1062,1065],{"class":160,"line":186},[158,1058,1059],{"class":202},"                \"SOURCE\"",[158,1061,238],{"class":168},[158,1063,1064],{"class":202},"\"dem_mosaic.tif (2026 survey tiles)\"",[158,1066,245],{"class":168},[158,1068,1069,1072,1074,1077],{"class":160,"line":193},[158,1070,1071],{"class":202},"                \"PROCESSED\"",[158,1073,238],{"class":168},[158,1075,1076],{"class":202},"\"2026-10-02\"",[158,1078,1079],{"class":168},"})\n",[158,1081,1082,1085,1087,1090,1093],{"class":160,"line":206},[158,1083,1084],{"class":168},"ds.GetRasterBand(",[158,1086,383],{"class":241},[158,1088,1089],{"class":168},").SetDescription(",[158,1091,1092],{"class":202},"\"elevation_m\"",[158,1094,365],{"class":168},[158,1096,1097,1099,1101],{"class":160,"line":223},[158,1098,1029],{"class":168},[158,1100,199],{"class":164},[158,1102,1103],{"class":241}," None\n",[158,1105,1106,1108,1111,1113],{"class":160,"line":232},[158,1107,371],{"class":241},[158,1109,1110],{"class":168},"(cog_report(out)[",[158,1112,803],{"class":202},[158,1114,1115],{"class":168},"])\n",[14,1117,1118,1120],{},[390,1119,392],{}," Writing small metadata items into an existing file is one of the few in-place edits that GDAL can usually make without disturbing the COG layout, because the tags fit in the header's reserved space; checking the layout again afterwards confirms it. For anything larger — a full metadata record — publish a sidecar file or a STAC item next to the COG instead. Setting the band description gives the band a readable name in QGIS's band selector.",[123,1122,1124],{"id":1123},"measure-what-the-conversion-gained","Measure what the conversion gained",[14,1126,1127],{},"Compression and layout choices are easiest to judge with numbers from your own data: file size, and how much has to be read to show a zoomed-out view.",[14,1129,1130],{},[34,1131,1134,1137,1140,1143,1146,1153,1161,1165,1169,1173,1176,1179,1183],{"viewBox":1132,"role":37,"ariaLabel":1133,"xmlns":39},"0 0 760 240","Bars comparing file size and zoomed-out read cost for an uncompressed GeoTIFF and a compressed COG",[41,1135,1136],{},"Size and read cost before and after",[45,1138,1139],{},"For a sample elevation mosaic, an uncompressed striped GeoTIFF is the largest file and a zoomed-out view must read most of it. A COG with DEFLATE and the floating-point predictor is a fraction of the size, and a zoomed-out view reads only a small overview. Exact ratios depend on the data, so measure your own.",[49,1141],{"x":51,"y":51,"width":52,"height":1142,"fill":54},"240",[56,1144,1145],{"x":58,"y":59,"style":60,"fill":61,"textAnchor":62},"Smaller file, far less to read",[56,1147,1152],{"x":1148,"y":1149,"style":1150,"fill":446,"textAnchor":1151},"180","82","text-anchor:end;font-size:10.5px;font-family:sans-serif","end","plain GeoTIFF size",[49,1154],{"x":1155,"y":1156,"width":1157,"height":1158,"rx":1159,"fill":1160,"stroke":1160,"style":84},"190","68","500","20","3","#b91c1c",[56,1162,1164],{"x":1148,"y":1163,"style":1150,"fill":446,"textAnchor":1151},"114","COG size",[49,1166],{"x":1155,"y":1167,"width":1155,"height":1158,"rx":1159,"fill":1168,"stroke":1168,"style":84},"100","#15803d",[56,1170,1172],{"x":1148,"y":1171,"style":1150,"fill":446,"textAnchor":1151},"158","zoomed-out read: plain",[49,1174],{"x":1155,"y":1175,"width":101,"height":1158,"rx":1159,"fill":1160,"stroke":1160,"style":84},"144",[56,1177,1178],{"x":1148,"y":1155,"style":1150,"fill":446,"textAnchor":1151},"zoomed-out read: COG",[49,1180],{"x":1155,"y":1181,"width":1182,"height":1158,"rx":1159,"fill":1168,"stroke":1168,"style":84},"176","16",[56,1184,1187],{"x":58,"y":1185,"style":1186,"fill":120,"textAnchor":62},"226","text-anchor:middle;font-size:10.0px;font-family:sans-serif","illustrative proportions — measure with your own rasters",[149,1189,1191],{"className":151,"code":1190,"language":153,"meta":154,"style":154},"import os\n\ndef mb(path):\n    return os.path.getsize(path) \u002F 1e6\n\nbefore, after = mb(src), mb(out)\nprint(f\"source {before:,.0f} MB → COG {after:,.0f} MB ({after \u002F before:.0%})\")\n\nds = gdal.Open(out)\nband = ds.GetRasterBand(1)\nfor i in range(band.GetOverviewCount()):\n    ov = band.GetOverview(i)\n    print(f\"overview {i}: {ov.XSize} × {ov.YSize}\")\n",[144,1192,1193,1200,1204,1213,1226,1230,1240,1299,1303,1312,1325,1342,1352],{"__ignoreMap":154},[158,1194,1195,1197],{"class":160,"line":161},[158,1196,165],{"class":164},[158,1198,1199],{"class":168}," os\n",[158,1201,1202],{"class":160,"line":172},[158,1203,190],{"emptyLinePlaceholder":189},[158,1205,1206,1208,1211],{"class":160,"line":186},[158,1207,559],{"class":164},[158,1209,1210],{"class":562}," mb",[158,1212,754],{"class":168},[158,1214,1215,1217,1220,1223],{"class":160,"line":193},[158,1216,603],{"class":164},[158,1218,1219],{"class":168}," os.path.getsize(path) ",[158,1221,1222],{"class":164},"\u002F",[158,1224,1225],{"class":241}," 1e6\n",[158,1227,1228],{"class":160,"line":206},[158,1229,190],{"emptyLinePlaceholder":189},[158,1231,1232,1235,1237],{"class":160,"line":223},[158,1233,1234],{"class":168},"before, after ",[158,1236,199],{"class":164},[158,1238,1239],{"class":168}," mb(src), mb(out)\n",[158,1241,1242,1244,1247,1250,1253,1256,1259,1262,1264,1267,1269,1272,1274,1276,1279,1281,1284,1286,1289,1292,1294,1297],{"class":160,"line":232},[158,1243,371],{"class":241},[158,1245,1246],{"class":168},"(",[158,1248,1249],{"class":164},"f",[158,1251,1252],{"class":202},"\"source ",[158,1254,1255],{"class":241},"{",[158,1257,1258],{"class":168},"before",[158,1260,1261],{"class":164},":,.0f",[158,1263,595],{"class":241},[158,1265,1266],{"class":202}," MB → COG ",[158,1268,1255],{"class":241},[158,1270,1271],{"class":168},"after",[158,1273,1261],{"class":164},[158,1275,595],{"class":241},[158,1277,1278],{"class":202}," MB (",[158,1280,1255],{"class":241},[158,1282,1283],{"class":168},"after ",[158,1285,1222],{"class":164},[158,1287,1288],{"class":168}," before",[158,1290,1291],{"class":164},":.0%",[158,1293,595],{"class":241},[158,1295,1296],{"class":202},")\"",[158,1298,365],{"class":168},[158,1300,1301],{"class":160,"line":248},[158,1302,190],{"emptyLinePlaceholder":189},[158,1304,1305,1307,1309],{"class":160,"line":264},[158,1306,1029],{"class":168},[158,1308,199],{"class":164},[158,1310,1311],{"class":168}," gdal.Open(out)\n",[158,1313,1314,1317,1319,1321,1323],{"class":160,"line":277},[158,1315,1316],{"class":168},"band ",[158,1318,199],{"class":164},[158,1320,789],{"class":168},[158,1322,383],{"class":241},[158,1324,365],{"class":168},[158,1326,1327,1330,1333,1336,1339],{"class":160,"line":290},[158,1328,1329],{"class":164},"for",[158,1331,1332],{"class":168}," i ",[158,1334,1335],{"class":164},"in",[158,1337,1338],{"class":241}," range",[158,1340,1341],{"class":168},"(band.GetOverviewCount()):\n",[158,1343,1344,1347,1349],{"class":160,"line":301},[158,1345,1346],{"class":168},"    ov ",[158,1348,199],{"class":164},[158,1350,1351],{"class":168}," band.GetOverview(i)\n",[158,1353,1354,1357,1359,1361,1364,1366,1369,1371,1373,1375,1378,1380,1383,1385,1388,1390,1393],{"class":160,"line":309},[158,1355,1356],{"class":241},"    print",[158,1358,1246],{"class":168},[158,1360,1249],{"class":164},[158,1362,1363],{"class":202},"\"overview ",[158,1365,1255],{"class":241},[158,1367,1368],{"class":168},"i",[158,1370,595],{"class":241},[158,1372,238],{"class":202},[158,1374,1255],{"class":241},[158,1376,1377],{"class":168},"ov.XSize",[158,1379,595],{"class":241},[158,1381,1382],{"class":202}," × ",[158,1384,1255],{"class":241},[158,1386,1387],{"class":168},"ov.YSize",[158,1389,595],{"class":241},[158,1391,1392],{"class":202},"\"",[158,1394,365],{"class":168},[14,1396,1397,1399],{},[390,1398,392],{}," The size ratio shows what compression achieved; for elevation data with the floating-point predictor, a result well under half the original is common, while already-compressed or noisy imagery shrinks less. Listing overview sizes shows how small the zoomed-out reads are — the smallest overview is typically a few hundred pixels across, which is all a client fetches to draw the whole extent. If the source was already a tiled, compressed GeoTIFF with overviews, gains are smaller, and the main benefit of the COG layout is efficient remote reading.",[123,1401,1403],{"id":1402},"convert-a-folder-in-one-run","Convert a folder in one run",[14,1405,1406],{},"Publishing a dataset usually means converting many files with one consistent profile. A loop with a skip for files already converted makes the job restartable.",[149,1408,1410],{"className":151,"code":1409,"language":153,"meta":154,"style":154},"from pathlib import Path\n\nsrc_dir, dst_dir = Path(\"\u002Fdata\u002Fortho\u002Fsheets\"), Path(\"\u002Fdata\u002Fpublish\u002Fortho\")\ndst_dir.mkdir(parents=True, exist_ok=True)\nfor tif in sorted(src_dir.glob(\"*.tif\")):\n    dst = dst_dir \u002F tif.name\n    if dst.exists() and cog_report(str(dst))[\"layout\"] == \"COG\":\n        continue\n    write_cog(str(tif), str(dst), \"imagery\")\n    print(\"written\", dst.name)\n",[144,1411,1412,1424,1428,1449,1473,1494,1509,1540,1545,1564],{"__ignoreMap":154},[158,1413,1414,1416,1419,1421],{"class":160,"line":161},[158,1415,175],{"class":164},[158,1417,1418],{"class":168}," pathlib ",[158,1420,165],{"class":164},[158,1422,1423],{"class":168}," Path\n",[158,1425,1426],{"class":160,"line":172},[158,1427,190],{"emptyLinePlaceholder":189},[158,1429,1430,1433,1435,1438,1441,1444,1447],{"class":160,"line":186},[158,1431,1432],{"class":168},"src_dir, dst_dir ",[158,1434,199],{"class":164},[158,1436,1437],{"class":168}," Path(",[158,1439,1440],{"class":202},"\"\u002Fdata\u002Fortho\u002Fsheets\"",[158,1442,1443],{"class":168},"), Path(",[158,1445,1446],{"class":202},"\"\u002Fdata\u002Fpublish\u002Fortho\"",[158,1448,365],{"class":168},[158,1450,1451,1454,1457,1459,1462,1464,1467,1469,1471],{"class":160,"line":193},[158,1452,1453],{"class":168},"dst_dir.mkdir(",[158,1455,1456],{"class":680},"parents",[158,1458,199],{"class":164},[158,1460,1461],{"class":241},"True",[158,1463,665],{"class":168},[158,1465,1466],{"class":680},"exist_ok",[158,1468,199],{"class":164},[158,1470,1461],{"class":241},[158,1472,365],{"class":168},[158,1474,1475,1477,1480,1482,1485,1488,1491],{"class":160,"line":206},[158,1476,1329],{"class":164},[158,1478,1479],{"class":168}," tif ",[158,1481,1335],{"class":164},[158,1483,1484],{"class":241}," sorted",[158,1486,1487],{"class":168},"(src_dir.glob(",[158,1489,1490],{"class":202},"\"*.tif\"",[158,1492,1493],{"class":168},")):\n",[158,1495,1496,1499,1501,1504,1506],{"class":160,"line":223},[158,1497,1498],{"class":168},"    dst ",[158,1500,199],{"class":164},[158,1502,1503],{"class":168}," dst_dir ",[158,1505,1222],{"class":164},[158,1507,1508],{"class":168}," tif.name\n",[158,1510,1511,1514,1517,1520,1523,1526,1529,1531,1533,1535,1537],{"class":160,"line":232},[158,1512,1513],{"class":164},"    if",[158,1515,1516],{"class":168}," dst.exists() ",[158,1518,1519],{"class":164},"and",[158,1521,1522],{"class":168}," cog_report(",[158,1524,1525],{"class":241},"str",[158,1527,1528],{"class":168},"(dst))[",[158,1530,803],{"class":202},[158,1532,871],{"class":168},[158,1534,874],{"class":164},[158,1536,877],{"class":202},[158,1538,1539],{"class":168},":\n",[158,1541,1542],{"class":160,"line":248},[158,1543,1544],{"class":164},"        continue\n",[158,1546,1547,1550,1552,1555,1557,1560,1562],{"class":160,"line":264},[158,1548,1549],{"class":168},"    write_cog(",[158,1551,1525],{"class":241},[158,1553,1554],{"class":168},"(tif), ",[158,1556,1525],{"class":241},[158,1558,1559],{"class":168},"(dst), ",[158,1561,704],{"class":202},[158,1563,365],{"class":168},[158,1565,1566,1568,1570,1573],{"class":160,"line":277},[158,1567,1356],{"class":241},[158,1569,1246],{"class":168},[158,1571,1572],{"class":202},"\"written\"",[158,1574,1575],{"class":168},", dst.name)\n",[14,1577,1578,1580,1581,1585,1586,1590],{},[390,1579,392],{}," Skipping outputs that already exist and validate as COGs means a crashed or interrupted run can simply be restarted. For very large folders, the ",[21,1582,1584],{"href":1583},"\u002Fspatial-data-processing-automation\u002Fbatch-processing-with-pyqgis\u002F","batch processing patterns"," for parallel runs and checkpoints apply directly. If the sheets form a mosaic, consider building a ",[21,1587,1589],{"href":1588},"\u002Fspatial-data-processing-automation\u002Fraster-analysis-workflows\u002Fbuild-virtual-raster-vrt-pyqgis\u002F","VRT"," and writing one COG from it, which is usually easier for clients than hundreds of separate files.",[123,1592,1594],{"id":1593},"qgis-version-compatibility","QGIS version compatibility",[14,1596,1597,1598,665,1601,1003,1604,1607,1608,1610],{},"The COG driver is available in the GDAL bundled with QGIS 3.34 LTR, 3.40 LTR and QGIS 4. Creation options such as ",[144,1599,1600],{},"PREDICTOR=FLOATING_POINT",[144,1602,1603],{},"OVERVIEW_RESAMPLING",[144,1605,1606],{},"ZSTD"," compression depend on GDAL 3.2 or newer, which all current releases include. ",[144,1609,998],{}," reading works on all platforms.",[123,1612,1614],{"id":1613},"troubleshooting","Troubleshooting",[128,1616,1617,1623,1629,1640],{},[131,1618,1619,1622],{},[390,1620,1621],{},"The file is much bigger than expected."," No compression options were passed, or a predictor was missing for float data.",[131,1624,1625,1628],{},[390,1626,1627],{},"Categorical overviews show odd values."," Overviews used average resampling; use mode.",[131,1630,1631,1637,1638,30],{},[390,1632,1633,1636],{},[144,1634,1635],{},"LAYOUT"," is not COG."," The file was written by a different driver or modified afterwards; rewrite it with ",[144,1639,396],{},[131,1641,1642,1645],{},[390,1643,1644],{},"Remote reads are slow."," The server does not support range requests, or the file has no overviews.",[123,1647,1649],{"id":1648},"conclusion","Conclusion",[14,1651,1652,1653,1656,1657,1659],{},"Write COGs with ",[144,1654,1655],{},"gdal:translate -of COG",", choose lossless predictors for analytical data and lossy compression only for viewed imagery, match overview resampling to the data type, validate the layout, serve from any range-request-capable storage and read with ",[144,1658,998],{},", and convert folders with a restartable loop.",[123,1661,1663],{"id":1662},"frequently-asked-questions","Frequently Asked Questions",[14,1665,1666,1669],{},[390,1667,1668],{},"Is a COG slower than a normal GeoTIFF locally?","\nNo. Tiling and overviews usually make it faster to display than a striped file without overviews.",[14,1671,1672,1675],{},[390,1673,1674],{},"Can I update a COG in place?","\nNot without breaking the layout. Rewrite it from the source.",[14,1677,1678,1681],{},[390,1679,1680],{},"Do web maps read COGs directly?","\nMany do — OpenLayers, deck.gl and TiTiler-based services among them.",[14,1683,1684,1687],{},[390,1685,1686],{},"What block size should I use?","\n512 is a good default; 256 suits small rasters and high-latency connections.",[123,1689,1691],{"id":1690},"related","Related",[128,1693,1694,1699,1704,1709,1715],{},[131,1695,1696,1698],{},[21,1697,24],{"href":23}," — the guide this recipe belongs to",[131,1700,1701],{},[21,1702,1703],{"href":28},"Read a Cloud Optimized GeoTIFF in PyQGIS",[131,1705,1706],{},[21,1707,1708],{"href":1588},"Build a Virtual Raster (VRT) in PyQGIS",[131,1710,1711],{},[21,1712,1714],{"href":1713},"\u002Fspatial-data-processing-automation\u002Fraster-analysis-workflows\u002Fmerge-raster-tiles-into-mosaic-pyqgis\u002F","Merge Raster Tiles into a Mosaic in PyQGIS",[131,1716,1717],{},[21,1718,1720],{"href":1719},"\u002Fpyqgis-cartography-visualization\u002Fmap-canvas-and-image-export\u002Fgenerate-xyz-tiles-and-mbtiles-pyqgis\u002F","Generate XYZ Tiles and MBTiles in PyQGIS",[1722,1723,1724],"style",{},"html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}",{"title":154,"searchDepth":172,"depth":172,"links":1726},[1727,1728,1729,1730,1731,1732,1733,1734,1735,1736,1737,1738,1739],{"id":125,"depth":172,"text":126},{"id":139,"depth":172,"text":140},{"id":408,"depth":172,"text":409},{"id":719,"depth":172,"text":720},{"id":919,"depth":172,"text":920},{"id":1012,"depth":172,"text":1013},{"id":1123,"depth":172,"text":1124},{"id":1402,"depth":172,"text":1403},{"id":1593,"depth":172,"text":1594},{"id":1613,"depth":172,"text":1614},{"id":1648,"depth":172,"text":1649},{"id":1662,"depth":172,"text":1663},{"id":1690,"depth":172,"text":1691},"Write rasters as Cloud Optimized GeoTIFFs with GDAL's COG driver from PyQGIS — compression and predictor choices, overviews and resampling, NoData and data types, validating the result, and serving it from object storage so QGIS and web clients read only what they need.","md",{"slug":1743,"type":1744,"breadcrumb":1745,"datePublished":1746,"dateModified":1746},"export-cloud-optimized-geotiff-pyqgis","article","Export a Cloud Optimized GeoTIFF","2026-10-02","\u002Fspatial-data-processing-automation\u002Fraster-analysis-workflows\u002Fexport-cloud-optimized-geotiff-pyqgis",{"title":5,"description":1740},"spatial-data-processing-automation\u002Fraster-analysis-workflows\u002Fexport-cloud-optimized-geotiff-pyqgis\u002Findex","Ep2vgeqCeVxEO_hkymOr7HWVa9yf4r7cNrc1psu1rQw",1790966264576]