Created
April 3, 2025 19:00
-
-
Save xen0f0n/3d872d38f033d9231d302315b5ab80d3 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"cells" : [ { | |
"metadata" : { }, | |
"cell_type" : "markdown", | |
"source" : [ "# 🛰️ Stream Cloud-Optimised GeoTIFFs into QGIS\n", "\n", "This notebook demonstrates how to query the Microsoft Planetary Computer STAC API,<br>\n", "retrieve Sentinel-2 and Landsat imagery, access their COG assets,<br>\n", "and programmatically build a QGIS project that streams false-colour composites - all without persistent downloads.\n", "<br>\n", "<br>\n", "<br>\n", "**NOTE on environment setup:**<br>\n", "If you're using Linux, this is likely the most straightforward way to create a Python virtual environment<br>\n", " that can access QGIS and GDAL (osgeo) bindings already installed system-wide:<br>\n", "\n", "```/usr/bin/python3 -m venv --upgrade-deps --system-site-packages .venv```<br>\n", "```source .venv/bin/activate```<br>\n", "```pip install planetary-computer pystac-client rich```" ], | |
"id" : "e24be54f123f4d5a" | |
}, { | |
"metadata" : { | |
"collapsed" : true, | |
"ExecuteTime" : { | |
"end_time" : "2025-04-03T17:00:25.724377Z", | |
"start_time" : "2025-04-03T17:00:25.497573Z" | |
} | |
}, | |
"cell_type" : "code", | |
"source" : [ "import planetary_computer\n", "import pystac_client\n", "import rich.table" ], | |
"id" : "initial_id", | |
"outputs" : [ ], | |
"execution_count" : 1 | |
}, { | |
"metadata" : { }, | |
"cell_type" : "markdown", | |
"source" : "## Define the Area of Interest (AoI)", | |
"id" : "33c970db5188d480" | |
}, { | |
"metadata" : { | |
"ExecuteTime" : { | |
"end_time" : "2025-04-03T17:00:25.732896Z", | |
"start_time" : "2025-04-03T17:00:25.729733Z" | |
} | |
}, | |
"cell_type" : "code", | |
"source" : [ "area_of_interest = {\n", " \"type\": \"Polygon\",\n", " \"coordinates\": [[\n", " [31.862215436096932, 30.535598484802946],\n", " [31.862215436096932, 30.200273791875617],\n", " [32.500539123832795, 30.200273791875617],\n", " [32.500539123832795, 30.535598484802946],\n", " [31.862215436096932, 30.535598484802946]\n", " ]]\n", "}" ], | |
"id" : "f623d2674fa8a1f", | |
"outputs" : [ ], | |
"execution_count" : 2 | |
}, { | |
"metadata" : { }, | |
"cell_type" : "markdown", | |
"source" : "## Connect to the Planetary Computer STAC API", | |
"id" : "ed219d823657c3a2" | |
}, { | |
"metadata" : { | |
"ExecuteTime" : { | |
"end_time" : "2025-04-03T17:00:26.513719Z", | |
"start_time" : "2025-04-03T17:00:25.843149Z" | |
} | |
}, | |
"cell_type" : "code", | |
"source" : [ "# Use the Microsoft Planetary Computer STAC API\n", "catalog = pystac_client.Client.open(\n", " \"https://planetarycomputer.microsoft.com/api/stac/v1\",\n", " modifier=planetary_computer.sign_inplace,\n", ")" ], | |
"id" : "1f2972979a49e176", | |
"outputs" : [ ], | |
"execution_count" : 3 | |
}, { | |
"metadata" : { | |
"ExecuteTime" : { | |
"end_time" : "2025-04-03T17:00:32.369940Z", | |
"start_time" : "2025-04-03T17:00:26.529597Z" | |
} | |
}, | |
"cell_type" : "code", | |
"source" : [ "# List all available STAC Collections\n", "for collection in catalog.get_collections():\n", " print(collection.id)" ], | |
"id" : "ac4ba3042173fe40", | |
"outputs" : [ { | |
"name" : "stdout", | |
"output_type" : "stream", | |
"text" : [ "daymet-annual-pr\n", "daymet-daily-hi\n", "3dep-seamless\n", "3dep-lidar-dsm\n", "fia\n", "sentinel-1-rtc\n", "gridmet\n", "daymet-annual-na\n", "daymet-monthly-na\n", "daymet-annual-hi\n", "daymet-monthly-hi\n", "daymet-monthly-pr\n", "gnatsgo-tables\n", "hgb\n", "cop-dem-glo-30\n", "cop-dem-glo-90\n", "terraclimate\n", "nasa-nex-gddp-cmip6\n", "gpm-imerg-hhr\n", "gnatsgo-rasters\n", "3dep-lidar-hag\n", "io-lulc-annual-v02\n", "goes-cmi\n", "conus404\n", "3dep-lidar-intensity\n", "3dep-lidar-pointsourceid\n", "mtbs\n", "noaa-c-cap\n", "3dep-lidar-copc\n", "modis-64A1-061\n", "alos-fnf-mosaic\n", "3dep-lidar-returns\n", "mobi\n", "landsat-c2-l2\n", "era5-pds\n", "chloris-biomass\n", "kaza-hydroforecast\n", "planet-nicfi-analytic\n", "modis-17A2H-061\n", "modis-11A2-061\n", "daymet-daily-pr\n", "3dep-lidar-dtm-native\n", "3dep-lidar-classification\n", "3dep-lidar-dtm\n", "gap\n", "modis-17A2HGF-061\n", "planet-nicfi-visual\n", "gbif\n", "modis-17A3HGF-061\n", "modis-09A1-061\n", "alos-dem\n", "alos-palsar-mosaic\n", "deltares-water-availability\n", "modis-16A3GF-061\n", "modis-21A2-061\n", "us-census\n", "jrc-gsw\n", "deltares-floods\n", "modis-43A4-061\n", "modis-09Q1-061\n", "modis-14A1-061\n", "hrea\n", "modis-13Q1-061\n", "modis-14A2-061\n", "sentinel-2-l2a\n", "modis-15A2H-061\n", "modis-11A1-061\n", "modis-15A3H-061\n", "modis-13A1-061\n", "daymet-daily-na\n", "nrcan-landcover\n", "modis-10A2-061\n", "ecmwf-forecast\n", "noaa-mrms-qpe-24h-pass2\n", "sentinel-1-grd\n", "nasadem\n", "io-lulc\n", "landsat-c2-l1\n", "drcog-lulc\n", "chesapeake-lc-7\n", "chesapeake-lc-13\n", "chesapeake-lu\n", "noaa-mrms-qpe-1h-pass1\n", "noaa-mrms-qpe-1h-pass2\n", "noaa-nclimgrid-monthly\n", "usda-cdl\n", "eclipse\n", "esa-cci-lc\n", "esa-cci-lc-netcdf\n", "fws-nwi\n", "usgs-lcmap-conus-v13\n", "usgs-lcmap-hawaii-v10\n", "noaa-climate-normals-tabular\n", "noaa-climate-normals-netcdf\n", "goes-glm\n", "noaa-climate-normals-gridded\n", "aster-l1t\n", "cil-gdpcir-cc-by-sa\n", "naip\n", "io-lulc-9-class\n", "io-biodiversity\n", "noaa-cdr-sea-surface-temperature-whoi\n", "noaa-cdr-ocean-heat-content\n", "cil-gdpcir-cc0\n", "cil-gdpcir-cc-by\n", "noaa-cdr-sea-surface-temperature-whoi-netcdf\n", "noaa-cdr-sea-surface-temperature-optimum-interpolation\n", "modis-10A1-061\n", "sentinel-5p-l2-netcdf\n", "sentinel-3-olci-wfr-l2-netcdf\n", "noaa-cdr-ocean-heat-content-netcdf\n", "hls2-l30\n", "sentinel-3-synergy-aod-l2-netcdf\n", "sentinel-3-synergy-v10-l2-netcdf\n", "sentinel-3-olci-lfr-l2-netcdf\n", "sentinel-3-sral-lan-l2-netcdf\n", "sentinel-3-slstr-lst-l2-netcdf\n", "sentinel-3-slstr-wst-l2-netcdf\n", "sentinel-3-sral-wat-l2-netcdf\n", "ms-buildings\n", "hls2-s30\n", "sentinel-3-slstr-frp-l2-netcdf\n", "sentinel-3-synergy-syn-l2-netcdf\n", "sentinel-3-synergy-vgp-l2-netcdf\n", "sentinel-3-synergy-vg1-l2-netcdf\n", "esa-worldcover\n" ] | |
} ], | |
"execution_count" : 4 | |
}, { | |
"metadata" : { }, | |
"cell_type" : "markdown", | |
"source" : [ "## Search for Sentinel-2 and Landsat Imagery\n", "We filter results based on:\n", "- Spatial coverage: our AoI\n", "- Temporal range: a single day\n", "- Data collections: Sentinel-2 Level-2A and Landsat Collection 2 Level-2" ], | |
"id" : "73726ef5278ab0b9" | |
}, { | |
"metadata" : { | |
"ExecuteTime" : { | |
"end_time" : "2025-04-03T17:00:33.218938Z", | |
"start_time" : "2025-04-03T17:00:32.378960Z" | |
} | |
}, | |
"cell_type" : "code", | |
"source" : [ "# Search for Sentinel-2 and Landsat imagery\n", "search = catalog.search(\n", " intersects=area_of_interest,\n", " datetime=['2025-03-13T00:00:00Z', '2025-03-14T00:00:00Z'],\n", " collections=[\n", " 'sentinel-2-l2a',\n", " 'landsat-c2-l2'\n", " ],\n", ")\n", "\n", "items = search.item_collection()\n", "for item in items:\n", " print(item.id)" ], | |
"id" : "1685dce360deb243", | |
"outputs" : [ { | |
"name" : "stdout", | |
"output_type" : "stream", | |
"text" : [ "S2C_MSIL2A_20250313T082751_R021_T36RVU_20250313T142215\n", "S2C_MSIL2A_20250313T082751_R021_T36RUU_20250313T142215\n", "LC08_L2SP_176039_20250313_02_T1\n" ] | |
} ], | |
"execution_count" : 5 | |
}, { | |
"metadata" : { | |
"ExecuteTime" : { | |
"end_time" : "2025-04-03T17:00:33.236468Z", | |
"start_time" : "2025-04-03T17:00:33.231262Z" | |
} | |
}, | |
"cell_type" : "code", | |
"source" : [ "# Display cloud cover for each result\n", "for item in items:\n", " print(f'Cloud cover for item {item.id}: {item.properties[\"eo:cloud_cover\"]:.2f}%')" ], | |
"id" : "3884310f4c941dad", | |
"outputs" : [ { | |
"name" : "stdout", | |
"output_type" : "stream", | |
"text" : [ "Cloud cover for item S2C_MSIL2A_20250313T082751_R021_T36RVU_20250313T142215: 0.03%\n", "Cloud cover for item S2C_MSIL2A_20250313T082751_R021_T36RUU_20250313T142215: 0.79%\n", "Cloud cover for item LC08_L2SP_176039_20250313_02_T1: 0.24%\n" ] | |
} ], | |
"execution_count" : 6 | |
}, { | |
"metadata" : { }, | |
"cell_type" : "markdown", | |
"source" : "# View metadata and assets for a Sentinel-2 and a Landsat STAC Item", | |
"id" : "e4190e69e71386fb" | |
}, { | |
"metadata" : { | |
"ExecuteTime" : { | |
"end_time" : "2025-04-03T17:00:33.324956Z", | |
"start_time" : "2025-04-03T17:00:33.282175Z" | |
} | |
}, | |
"cell_type" : "code", | |
"source" : [ "# Sentinel-2 STAC Item\n", "item = items[1]\n", "\n", "table = rich.table.Table(\"Asset Key\", \"Description\")\n", "for asset_key, asset in item.assets.items():\n", " table.add_row(asset_key, asset.title)\n", "\n", "table" ], | |
"id" : "c20ff0fd39b0fd96", | |
"outputs" : [ { | |
"data" : { | |
"text/plain" : [ "┏━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n", "┃\u001B[1m \u001B[0m\u001B[1mAsset Key \u001B[0m\u001B[1m \u001B[0m┃\u001B[1m \u001B[0m\u001B[1mDescription \u001B[0m\u001B[1m \u001B[0m┃\n", "┡━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩\n", "│ AOT │ Aerosol optical thickness (AOT) │\n", "│ B01 │ Band 1 - Coastal aerosol - 60m │\n", "│ B02 │ Band 2 - Blue - 10m │\n", "│ B03 │ Band 3 - Green - 10m │\n", "│ B04 │ Band 4 - Red - 10m │\n", "│ B05 │ Band 5 - Vegetation red edge 1 - 20m │\n", "│ B06 │ Band 6 - Vegetation red edge 2 - 20m │\n", "│ B07 │ Band 7 - Vegetation red edge 3 - 20m │\n", "│ B08 │ Band 8 - NIR - 10m │\n", "│ B09 │ Band 9 - Water vapor - 60m │\n", "│ B11 │ Band 11 - SWIR (1.6) - 20m │\n", "│ B12 │ Band 12 - SWIR (2.2) - 20m │\n", "│ B8A │ Band 8A - Vegetation red edge 4 - 20m │\n", "│ SCL │ Scene classfication map (SCL) │\n", "│ WVP │ Water vapour (WVP) │\n", "│ visual │ True color image │\n", "│ safe-manifest │ SAFE manifest │\n", "│ granule-metadata │ Granule metadata │\n", "│ inspire-metadata │ INSPIRE metadata │\n", "│ product-metadata │ Product metadata │\n", "│ datastrip-metadata │ Datastrip metadata │\n", "│ tilejson │ TileJSON with default rendering │\n", "│ rendered_preview │ Rendered preview │\n", "└────────────────────┴───────────────────────────────────────┘\n" ], | |
"text/html" : [ "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\">┏━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n", "┃<span style=\"font-weight: bold\"> Asset Key </span>┃<span style=\"font-weight: bold\"> Description </span>┃\n", "┡━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩\n", "│ AOT │ Aerosol optical thickness (AOT) │\n", "│ B01 │ Band 1 - Coastal aerosol - 60m │\n", "│ B02 │ Band 2 - Blue - 10m │\n", "│ B03 │ Band 3 - Green - 10m │\n", "│ B04 │ Band 4 - Red - 10m │\n", "│ B05 │ Band 5 - Vegetation red edge 1 - 20m │\n", "│ B06 │ Band 6 - Vegetation red edge 2 - 20m │\n", "│ B07 │ Band 7 - Vegetation red edge 3 - 20m │\n", "│ B08 │ Band 8 - NIR - 10m │\n", "│ B09 │ Band 9 - Water vapor - 60m │\n", "│ B11 │ Band 11 - SWIR (1.6) - 20m │\n", "│ B12 │ Band 12 - SWIR (2.2) - 20m │\n", "│ B8A │ Band 8A - Vegetation red edge 4 - 20m │\n", "│ SCL │ Scene classfication map (SCL) │\n", "│ WVP │ Water vapour (WVP) │\n", "│ visual │ True color image │\n", "│ safe-manifest │ SAFE manifest │\n", "│ granule-metadata │ Granule metadata │\n", "│ inspire-metadata │ INSPIRE metadata │\n", "│ product-metadata │ Product metadata │\n", "│ datastrip-metadata │ Datastrip metadata │\n", "│ tilejson │ TileJSON with default rendering │\n", "│ rendered_preview │ Rendered preview │\n", "└────────────────────┴───────────────────────────────────────┘\n", "</pre>\n" ] | |
}, | |
"execution_count" : 7, | |
"metadata" : { }, | |
"output_type" : "execute_result" | |
} ], | |
"execution_count" : 7 | |
}, { | |
"metadata" : { | |
"ExecuteTime" : { | |
"end_time" : "2025-04-03T17:00:33.347378Z", | |
"start_time" : "2025-04-03T17:00:33.337583Z" | |
} | |
}, | |
"cell_type" : "code", | |
"source" : [ "# Landsat STAC Item\n", "item = items[2]\n", "\n", "table = rich.table.Table(\"Asset Key\", \"Description\")\n", "for asset_key, asset in item.assets.items():\n", " table.add_row(asset_key, asset.title)\n", "\n", "table" ], | |
"id" : "a4ba5772576fbe6f", | |
"outputs" : [ { | |
"data" : { | |
"text/plain" : [ "┏━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n", "┃\u001B[1m \u001B[0m\u001B[1mAsset Key \u001B[0m\u001B[1m \u001B[0m┃\u001B[1m \u001B[0m\u001B[1mDescription \u001B[0m\u001B[1m \u001B[0m┃\n", "┡━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩\n", "│ qa │ Surface Temperature Quality Assessment Band │\n", "│ ang │ Angle Coefficients File │\n", "│ red │ Red Band │\n", "│ blue │ Blue Band │\n", "│ drad │ Downwelled Radiance Band │\n", "│ emis │ Emissivity Band │\n", "│ emsd │ Emissivity Standard Deviation Band │\n", "│ trad │ Thermal Radiance Band │\n", "│ urad │ Upwelled Radiance Band │\n", "│ atran │ Atmospheric Transmittance Band │\n", "│ cdist │ Cloud Distance Band │\n", "│ green │ Green Band │\n", "│ nir08 │ Near Infrared Band 0.8 │\n", "│ lwir11 │ Surface Temperature Band │\n", "│ swir16 │ Short-wave Infrared Band 1.6 │\n", "│ swir22 │ Short-wave Infrared Band 2.2 │\n", "│ coastal │ Coastal/Aerosol Band │\n", "│ mtl.txt │ Product Metadata File (txt) │\n", "│ mtl.xml │ Product Metadata File (xml) │\n", "│ mtl.json │ Product Metadata File (json) │\n", "│ qa_pixel │ Pixel Quality Assessment Band │\n", "│ qa_radsat │ Radiometric Saturation and Terrain Occlusion Quality Assessment Band │\n", "│ qa_aerosol │ Aerosol Quality Assessment Band │\n", "│ tilejson │ TileJSON with default rendering │\n", "│ rendered_preview │ Rendered preview │\n", "└──────────────────┴──────────────────────────────────────────────────────────────────────┘\n" ], | |
"text/html" : [ "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\">┏━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n", "┃<span style=\"font-weight: bold\"> Asset Key </span>┃<span style=\"font-weight: bold\"> Description </span>┃\n", "┡━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩\n", "│ qa │ Surface Temperature Quality Assessment Band │\n", "│ ang │ Angle Coefficients File │\n", "│ red │ Red Band │\n", "│ blue │ Blue Band │\n", "│ drad │ Downwelled Radiance Band │\n", "│ emis │ Emissivity Band │\n", "│ emsd │ Emissivity Standard Deviation Band │\n", "│ trad │ Thermal Radiance Band │\n", "│ urad │ Upwelled Radiance Band │\n", "│ atran │ Atmospheric Transmittance Band │\n", "│ cdist │ Cloud Distance Band │\n", "│ green │ Green Band │\n", "│ nir08 │ Near Infrared Band 0.8 │\n", "│ lwir11 │ Surface Temperature Band │\n", "│ swir16 │ Short-wave Infrared Band 1.6 │\n", "│ swir22 │ Short-wave Infrared Band 2.2 │\n", "│ coastal │ Coastal/Aerosol Band │\n", "│ mtl.txt │ Product Metadata File (txt) │\n", "│ mtl.xml │ Product Metadata File (xml) │\n", "│ mtl.json │ Product Metadata File (json) │\n", "│ qa_pixel │ Pixel Quality Assessment Band │\n", "│ qa_radsat │ Radiometric Saturation and Terrain Occlusion Quality Assessment Band │\n", "│ qa_aerosol │ Aerosol Quality Assessment Band │\n", "│ tilejson │ TileJSON with default rendering │\n", "│ rendered_preview │ Rendered preview │\n", "└──────────────────┴──────────────────────────────────────────────────────────────────────┘\n", "</pre>\n" ] | |
}, | |
"execution_count" : 8, | |
"metadata" : { }, | |
"output_type" : "execute_result" | |
} ], | |
"execution_count" : 8 | |
}, { | |
"metadata" : { }, | |
"cell_type" : "markdown", | |
"source" : [ "## Organise asset (band) URIs\n", "\n", "We collect the URIs of the NIR, Red, and Green bands for each STAC Item. These will be used to build false-colour composites." ], | |
"id" : "3dbafb813534e93b" | |
}, { | |
"metadata" : { | |
"ExecuteTime" : { | |
"end_time" : "2025-04-03T17:00:33.403966Z", | |
"start_time" : "2025-04-03T17:00:33.394385Z" | |
} | |
}, | |
"cell_type" : "code", | |
"source" : [ "# Organise COG assets\n", "cog_info = {} # holds the relevant COG URIs and the platform name\n", "\n", "for item in items:\n", " id = item.id\n", "\n", " # quick hack - Planetary Computer's asset names for S2 and LS don't match\n", " if 'Sentinel' in item.properties['platform']:\n", " _uri_nir = item.assets['B08'].href\n", " _uri_red = item.assets['B04'].href\n", " _uri_green = item.assets['B03'].href\n", " else:\n", " _uri_nir = item.assets['nir08'].href\n", " _uri_red = item.assets['red'].href\n", " _uri_green = item.assets['green'].href\n", "\n", " _platform = item.properties['platform']\n", "\n", " cog_info[id] = {\n", " 'uri_nir08': _uri_nir,\n", " 'uri_red': _uri_red,\n", " 'uri_green': _uri_green,\n", " 'platform': _platform\n", " }" ], | |
"id" : "115402f66815350e", | |
"outputs" : [ ], | |
"execution_count" : 9 | |
}, { | |
"metadata" : { }, | |
"cell_type" : "markdown", | |
"source" : [ "# Programmatically build a QGIS project with streamed COGs\n", "We launch QGIS in headless mode and use GDAL to build a [.vrt (virtual GDAL dataset)](https://gdal.org/en/stable/drivers/raster/vrt.html) that combines the NIR, Red, and Green bands - streamed from their URIs using /vsicurl/ or /vsis3/. Each layer is added to a group based on its platform (e.g. Sentinel-2C, Landsat-8).\n", "\n", "#### About /vsicurl/ and /vsis3/\n", "GDAL supports a set of [Virtual File Systems (VSI)](https://gdal.org/en/stable/user/virtual_file_systems.html) that allow it to read files hosted remotely \"without prior download of the entire file\":\n", "- /vsicurl/ is used to stream files over HTTP or HTTPS.\n", "- /vsis3/ is used to stream files directly from Amazon S3 buckets.\n", "\n", "**NOTE 1:** To re-run the following block you'll have to restart the kernel. It might be an issue related to the QgsApplication initialisation. <br>\n", "**NOTE 2:** It's very important to set the CRS on the project level (```project.setCrs```)\n" ], | |
"id" : "4adf2107d53e85c8" | |
}, { | |
"metadata" : { | |
"ExecuteTime" : { | |
"end_time" : "2025-04-03T17:00:40.838523Z", | |
"start_time" : "2025-04-03T17:00:33.452934Z" | |
} | |
}, | |
"cell_type" : "code", | |
"source" : [ "from qgis.core import QgsApplication, QgsRasterLayer, QgsCoordinateReferenceSystem, QgsProject\n", "from osgeo import gdal\n", "gdal.UseExceptions()\n", "\n", "# Optional GDAL speed tweaks\n", "gdal.SetConfigOption(\"GDAL_DISABLE_READDIR_ON_OPEN\", \"YES\")\n", "gdal.SetConfigOption(\"CPL_VSIL_CURL_ALLOWED_EXTENSIONS\", \".tif\")\n", "\n", "# Initialise QGIS Application\n", "qgs = QgsApplication([], False)\n", "qgs.initQgis()\n", "\n", "# Create a new QGIS project\n", "project = QgsProject.instance()\n", "root = project.layerTreeRoot()\n", "project.setCrs(QgsCoordinateReferenceSystem(\"EPSG:4326\"))\n", "\n", "for id, item_info in cog_info.items():\n", "\n", " # Wrap COG URLs with /vsicurl/ or /vsis3/\n", " try:\n", " if 's3://' in item_info['uri_nir08']:\n", " nir_path = f\"/vsis3/{item_info['uri_nir08'].replace('s3://', '')}\"\n", " red_path = f\"/vsis3/{item_info['uri_red'].replace('s3://', '')}\"\n", " green_path = f\"/vsis3/{item_info['uri_green'].replace('s3://', '')}\"\n", " else:\n", " nir_path = f\"/vsicurl/{item_info['uri_nir08']}\"\n", " red_path = f\"/vsicurl/{item_info['uri_red']}\"\n", " green_path = f\"/vsicurl/{item_info['uri_green']}\"\n", "\n", " # Define VRT path\n", " vrt_path = f\"{id}_false_colour.vrt\"\n", "\n", " # Build virtual raster (false colour composite)\n", " gdal.BuildVRT(\n", " vrt_path,\n", " [nir_path, red_path, green_path],\n", " separate=True,\n", " outputSRS=\"EPSG:32636\"\n", " )\n", "\n", " # Create QGIS raster layer\n", " layer_name = f\"{id}_false_colour\"\n", " layer = QgsRasterLayer(vrt_path, layer_name)\n", "\n", " if layer.isValid():\n", " project.addMapLayer(layer, addToLegend=False)\n", "\n", " # Group layer based on its platform (Sentinel-2C, Landsat-8)\n", " platform = item_info['platform']\n", " group = root.findGroup(platform) or root.addGroup(platform)\n", " group.insertLayer(0, layer)\n", "\n", " print(f\"Added {layer_name} to group '{group.name()}'\")\n", " else:\n", " print(f\"Failed to load {layer_name}\")\n", " except Exception as e:\n", " print(e)\n", "\n", "# Save the QGIS project\n", "project_path = \"streaming_COGs.qgz\"\n", "project.write(project_path)\n", "print(f\"Project saved to: {project_path}\")\n", "\n", "# Exit QGIS\n", "qgs.exitQgis()" ], | |
"id" : "a70538b9a98d82e9", | |
"outputs" : [ { | |
"name" : "stdout", | |
"output_type" : "stream", | |
"text" : [ "Added S2C_MSIL2A_20250313T082751_R021_T36RVU_20250313T142215_false_colour to group 'Sentinel-2C'\n", "Added S2C_MSIL2A_20250313T082751_R021_T36RUU_20250313T142215_false_colour to group 'Sentinel-2C'\n", "Added LC08_L2SP_176039_20250313_02_T1_false_colour to group 'landsat-8'\n", "Project saved to: streaming_COGs.qgz\n" ] | |
} ], | |
"execution_count" : 10 | |
} ], | |
"metadata" : { | |
"kernelspec" : { | |
"display_name" : "Python 3", | |
"language" : "python", | |
"name" : "python3" | |
}, | |
"language_info" : { | |
"codemirror_mode" : { | |
"name" : "ipython", | |
"version" : 2 | |
}, | |
"file_extension" : ".py", | |
"mimetype" : "text/x-python", | |
"name" : "python", | |
"nbconvert_exporter" : "python", | |
"pygments_lexer" : "ipython2", | |
"version" : "2.7.6" | |
} | |
}, | |
"nbformat" : 4, | |
"nbformat_minor" : 5 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment