Skip to content

Reading overlapping tiles with georeader

This tutorial shows how to read overlapping parts of two raster objects of different spatial resolution and with different transforms.

For this tutorial we will use a GeoTIFF file derived from an AVIRIS-NG image that we downloaded from here and a Sentinel-2 file SAFE file that can be read directly from the public GCP bucket (or downloaded from Copernicus SciHub).

Install package with Google dependecies

This is needed to read image from S2 bucket

pip install georeader-spaceml fsspec gcsfs

Step 1: Create the reader object for the AVIRIS image

%%time

import os
from pathlib import Path
from georeader.rasterio_reader import RasterioReader

# Resolve the repo-level examples/ folder (works regardless of where the notebook runs from).
EXAMPLES_DIR = next(
    (p / "examples" for p in [Path.cwd(), *Path.cwd().parents] if (p / "examples").is_dir()),
    Path("examples"),
)

aviris_reader = RasterioReader(os.fspath(EXAMPLES_DIR / "ang20190928t185111-4_r6871_c424_rgb.tif"))
aviris_reader
CPU times: user 2.51 s, sys: 48.8 ms, total: 2.56 s
Wall time: 371 ms

 
         Paths: ['/home/gonzalo/git/georeader/examples/ang20190928t185111-4_r6871_c424_rgb.tif']
         Transform: | 5.99, 4.35, 592577.80|
| 4.35,-5.99, 3519916.50|
| 0.00, 0.00, 1.00|
         Shape: (3, 151, 151)
         Resolution: (7.400000000000001, 7.400000000000001)
         Bounds: (592577.7996484624, 3519012.5018222863, 594138.5864788886, 3520573.2886527125)
         CRS: EPSG:32613
         nodata: None
         fill_value_default: 0
        
%%time

aviris_reader_in_memory = aviris_reader.load()
aviris_reader_in_memory.values
CPU times: user 23.7 ms, sys: 0 ns, total: 23.7 ms
Wall time: 22 ms

array([[[253, 254, 237, ..., 187, 181, 134],
        [231, 221, 199, ..., 203, 197, 164],
        [193, 194, 194, ..., 212, 202, 193],
        ...,
        [ 90,  88,  88, ..., 183, 160, 170],
        [ 92,  95,  95, ..., 192, 172, 181],
        [ 88,  93,  89, ..., 194, 187, 210]],

       [[235, 230, 213, ..., 172, 165, 131],
        [216, 203, 181, ..., 185, 180, 153],
        [184, 178, 177, ..., 189, 182, 178],
        ...,
        [ 96,  96,  96, ..., 183, 162, 171],
        [ 99, 100, 100, ..., 191, 173, 181],
        [ 97,  98,  96, ..., 193, 185, 205]],

       [[190, 183, 163, ..., 148, 141, 116],
        [174, 165, 142, ..., 151, 152, 129],
        [151, 145, 143, ..., 148, 147, 148],
        ...,
        [102, 101, 100, ..., 166, 149, 155],
        [103, 104, 103, ..., 173, 158, 163],
        [102, 102, 100, ..., 175, 167, 180]]],
      shape=(3, 151, 151), dtype=uint8)

Step 2: Create the reader object for the Sentinel-2 image

%%time

import os
from georeader.readers import S2_SAFE_reader
# This is required to do advaced operations in the GCP bucket
# os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = "path/to/file.json"
# os.environ["GS_USER_PROJECT"] = "project-name"
# S2_SAFE_reader.DEFAULT_REQUESTER_PAYS=True

os.environ["GS_NO_SIGN_REQUEST"] = "YES"

# Read only RGB bands

s2path = "gs://gcp-public-data-sentinel-2/tiles/13/S/ER/S2B_MSIL1C_20190928T173109_N0208_R055_T13SER_20190928T205958.SAFE"
s2_reader = S2_SAFE_reader.s2loader(s2path, out_res=10, bands=["B04", "B03", "B02"])
s2_reader
CPU times: user 937 ms, sys: 93.3 ms, total: 1.03 s
Wall time: 1.18 s

 
         gs://gcp-public-data-sentinel-2/tiles/13/S/ER/S2B_MSIL1C_20190928T173109_N0208_R055_T13SER_20190928T205958.SAFE
         Transform: | 10.00, 0.00, 499980.00|
| 0.00,-10.00, 3600000.00|
| 0.00, 0.00, 1.00|
         Shape: (3, 10980, 10980)
         Resolution: (10.0, 10.0)
         Bounds: (499980.0, 3490200.0, 609780.0, 3600000.0)
         CRS: EPSG:32613
         bands: ['B04', 'B03', 'B02']
         fill_value_default: 0
        

Read the S2 image at the AVIRIS location using read_from_bounds

%%time

from georeader import read

s2_at_aviris_loc = read.read_from_bounds(s2_reader, aviris_reader_in_memory.bounds, crs_bounds=aviris_reader_in_memory.crs)
s2_at_aviris_loc
CPU times: user 5.27 ms, sys: 4.1 ms, total: 9.38 ms
Wall time: 8.03 ms

 
         gs://gcp-public-data-sentinel-2/tiles/13/S/ER/S2B_MSIL1C_20190928T173109_N0208_R055_T13SER_20190928T205958.SAFE
         Transform: | 10.00, 0.00, 592570.00|
| 0.00,-10.00, 3520580.00|
| 0.00, 0.00, 1.00|
         Shape: (3, 157, 157)
         Resolution: (10.0, 10.0)
         Bounds: (592570.0, 3519010.0, 594140.0, 3520580.0)
         CRS: EPSG:32613
         bands: ['B04', 'B03', 'B02']
         fill_value_default: 0
        
%%time
s2_at_aviris_loc_in_memory = s2_at_aviris_loc.load()
s2_at_aviris_loc_in_memory.values
CPU times: user 2.18 s, sys: 197 ms, total: 2.38 s
Wall time: 17.2 s

array([[[2387, 2649, 2654, ..., 2302, 2292, 2241],
        [2244, 2454, 2630, ..., 2412, 2376, 2339],
        [2540, 2432, 2373, ..., 2473, 2442, 2403],
        ...,
        [1347, 1312, 1325, ..., 1851, 2073, 2010],
        [1268, 1265, 1368, ..., 2194, 2193, 2098],
        [1261, 1293, 1392, ..., 2235, 2018, 1881]],

       [[1803, 1946, 1970, ..., 1802, 1801, 1754],
        [1739, 1879, 1971, ..., 1873, 1876, 1810],
        [1914, 1862, 1796, ..., 1920, 1888, 1867],
        ...,
        [1265, 1239, 1268, ..., 1611, 1750, 1705],
        [1194, 1210, 1283, ..., 1815, 1851, 1770],
        [1181, 1214, 1271, ..., 1886, 1671, 1626]],

       [[1622, 1689, 1683, ..., 1621, 1653, 1576],
        [1517, 1632, 1690, ..., 1677, 1696, 1624],
        [1665, 1637, 1614, ..., 1718, 1703, 1637],
        ...,
        [1294, 1302, 1299, ..., 1459, 1566, 1569],
        [1288, 1256, 1333, ..., 1617, 1599, 1591],
        [1273, 1266, 1350, ..., 1662, 1561, 1474]]],
      shape=(3, 157, 157), dtype=uint16)
import matplotlib.pyplot as plt
import numpy as np

fig, ax = plt.subplots(1,2,figsize=(12,6))
ax[0].imshow(aviris_reader_in_memory.values.transpose((1,2,0)))
ax[1].imshow(np.clip(s2_at_aviris_loc_in_memory.values/3_500,0,1).transpose((1,2,0)))
<matplotlib.image.AxesImage at 0x7f17d31b0290>
No description has been provided for this image

Convert to and from xarray.DataArray

from georeader import dataarray
dr = dataarray.toDataArray(s2_at_aviris_loc_in_memory)
dr
<xarray.DataArray (band: 3, y: 157, x: 157)> Size: 148kB
array([[[2387, 2649, 2654, ..., 2302, 2292, 2241],
        [2244, 2454, 2630, ..., 2412, 2376, 2339],
        [2540, 2432, 2373, ..., 2473, 2442, 2403],
        ...,
        [1347, 1312, 1325, ..., 1851, 2073, 2010],
        [1268, 1265, 1368, ..., 2194, 2193, 2098],
        [1261, 1293, 1392, ..., 2235, 2018, 1881]],

       [[1803, 1946, 1970, ..., 1802, 1801, 1754],
        [1739, 1879, 1971, ..., 1873, 1876, 1810],
        [1914, 1862, 1796, ..., 1920, 1888, 1867],
        ...,
        [1265, 1239, 1268, ..., 1611, 1750, 1705],
        [1194, 1210, 1283, ..., 1815, 1851, 1770],
        [1181, 1214, 1271, ..., 1886, 1671, 1626]],

       [[1622, 1689, 1683, ..., 1621, 1653, 1576],
        [1517, 1632, 1690, ..., 1677, 1696, 1624],
        [1665, 1637, 1614, ..., 1718, 1703, 1637],
        ...,
        [1294, 1302, 1299, ..., 1459, 1566, 1569],
        [1288, 1256, 1333, ..., 1617, 1599, 1591],
        [1273, 1266, 1350, ..., 1662, 1561, 1474]]],
      shape=(3, 157, 157), dtype=uint16)
Coordinates:
  * band     (band) int64 24B 0 1 2
  * y        (y) float64 1kB 3.521e+06 3.521e+06 ... 3.519e+06 3.519e+06
  * x        (x) float64 1kB 5.926e+05 5.926e+05 ... 5.941e+05 5.941e+05
Attributes:
    crs:                 EPSG:32613
    fill_value_default:  0

geotensorback = dataarray.fromDataArray(dr)
geotensorback
 
         Transform: | 10.00, 0.00, 592570.00|
| 0.00,-10.00, 3520580.00|
| 0.00, 0.00, 1.00|
         Shape: (3, 157, 157)
         Resolution: (10.0, 10.0)
         Bounds: (592570.0, 3519010.0, 594140.0, 3520580.0)
         CRS: EPSG:32613
         fill_value_default: 0
        

Read the S2 image at the AVIRIS location using read_from_center_coords

center_coords_aviris = aviris_reader_in_memory.transform * (aviris_reader_in_memory.shape[1] / 2, aviris_reader_in_memory.shape[0] / 2)
center_coords_aviris
(593036.3218595202, 3520235.9129436193)
%%time


s2_at_aviris_loc = read.read_from_center_coords(s2_reader, shape=(150,150),
                                                center_coords=center_coords_aviris, crs_center_coords=aviris_reader_in_memory.crs)
s2_at_aviris_loc
CPU times: user 5.96 ms, sys: 3.6 ms, total: 9.56 ms
Wall time: 7.19 ms

 
         gs://gcp-public-data-sentinel-2/tiles/13/S/ER/S2B_MSIL1C_20190928T173109_N0208_R055_T13SER_20190928T205958.SAFE
         Transform: | 10.00, 0.00, 592290.00|
| 0.00,-10.00, 3520990.00|
| 0.00, 0.00, 1.00|
         Shape: (3, 150, 150)
         Resolution: (10.0, 10.0)
         Bounds: (592290.0, 3519490.0, 593790.0, 3520990.0)
         CRS: EPSG:32613
         bands: ['B04', 'B03', 'B02']
         fill_value_default: 0
        
%%time
s2_at_aviris_loc_in_memory = s2_at_aviris_loc.load()
s2_at_aviris_loc_in_memory.values
CPU times: user 1.83 s, sys: 82.5 ms, total: 1.92 s
Wall time: 200 ms

array([[[2403, 2577, 2603, ..., 1957, 2000, 1975],
        [2360, 2327, 2266, ..., 2012, 1952, 1887],
        [2298, 2156, 2188, ..., 2026, 2011, 1925],
        ...,
        [2368, 2257, 2093, ..., 2197, 2050, 2044],
        [2316, 2276, 2117, ..., 2418, 2195, 1936],
        [2370, 2346, 2337, ..., 2401, 2182, 1805]],

       [[1962, 2124, 2189, ..., 1695, 1664, 1711],
        [1895, 1891, 1861, ..., 1690, 1675, 1602],
        [1864, 1769, 1811, ..., 1715, 1789, 1610],
        ...,
        [1842, 1765, 1661, ..., 1756, 1714, 1698],
        [1817, 1764, 1653, ..., 1955, 1785, 1633],
        [1880, 1804, 1762, ..., 1932, 1752, 1546]],

       [[1777, 1899, 1964, ..., 1584, 1567, 1603],
        [1688, 1676, 1719, ..., 1580, 1540, 1509],
        [1640, 1561, 1644, ..., 1649, 1626, 1526],
        ...,
        [1589, 1559, 1443, ..., 1647, 1579, 1553],
        [1613, 1565, 1441, ..., 1705, 1601, 1534],
        [1627, 1575, 1521, ..., 1764, 1610, 1461]]],
      shape=(3, 150, 150), dtype=uint16)
fig, ax = plt.subplots(1,2,figsize=(12,6))
ax[0].imshow(aviris_reader_in_memory.values.transpose((1,2,0)))
ax[1].imshow(np.clip(s2_at_aviris_loc_in_memory.values/3_500,0,1).transpose((1,2,0)))
<matplotlib.image.AxesImage at 0x7f17d30471a0>
No description has been provided for this image

Read the S2 image at the AVIRIS location using read_reproject_like

This will read the S2 image with the same resolution and transform as the AVIRIS image.

%%time

s2_at_aviris_loc = read.read_reproject_like(s2_reader, aviris_reader_in_memory)
s2_at_aviris_loc
CPU times: user 1.92 s, sys: 61.3 ms, total: 1.98 s
Wall time: 254 ms

 
         Transform: | 5.99, 4.35, 592577.80|
| 4.35,-5.99, 3519916.50|
| 0.00, 0.00, 1.00|
         Shape: (3, 151, 151)
         Resolution: (7.400000000038687, 7.399999999944504)
         Bounds: (592577.7996484624, 3519012.5018222863, 594138.5864788886, 3520573.2886527125)
         CRS: EPSG:32613
         fill_value_default: 0
        
fig, ax = plt.subplots(1,2,figsize=(12,6))
ax[0].imshow(aviris_reader_in_memory.values.transpose((1,2,0)))
ax[1].imshow(np.clip(s2_at_aviris_loc.values/3_500,0,1).transpose((1,2,0)))
<matplotlib.image.AxesImage at 0x7f17d2317fb0>
No description has been provided for this image

Read the S2 image at the AVIRIS location using read_reproject

With this we will show how to reproject the S2 image to the AVIRIS grid but keeping the original spatial resolution of Sentinel-2 (10m).

%%time
import rasterio.windows
from math import ceil

shape_out = ceil(aviris_reader_in_memory.shape[1] * aviris_reader_in_memory.res[0] / s2_reader.res[0]), ceil(aviris_reader_in_memory.shape[2] * aviris_reader_in_memory.res[1] / s2_reader.res[1])

s2_at_aviris_loc = read.read_reproject(s2_reader, dst_crs=aviris_reader_in_memory.crs, 
                                       dst_transform=aviris_reader_in_memory.transform,
                                       resolution_dst_crs=s2_reader.res,
                                       window_out=rasterio.windows.Window(0,0, width=shape_out[-1], height=shape_out[-2]))
s2_at_aviris_loc
CPU times: user 1.92 s, sys: 45.6 ms, total: 1.97 s
Wall time: 233 ms

 
         Transform: | 8.09, 5.88, 592577.80|
| 5.88,-8.09, 3519916.50|
| 0.00, 0.00, 1.00|
         Shape: (3, 112, 112)
         Resolution: (9.999999999910532, 10.000000000141569)
         Bounds: (592577.7996484624, 3519010.398378094, 594142.2181647301, 3520574.816894365)
         CRS: EPSG:32613
         fill_value_default: 0
        
fig, ax = plt.subplots(1,2,figsize=(12,6))
ax[0].imshow(aviris_reader_in_memory.values.transpose((1,2,0)))
ax[1].imshow(np.clip(s2_at_aviris_loc.values/3_500,0,1).transpose((1,2,0)))
<matplotlib.image.AxesImage at 0x7f17d225a210>
No description has been provided for this image

Downscale AVIRIS-NG to the Sentinel-2 resolution (10m)

This AVIRIS-NG image is at 7.40m resolution and we'd like to convert it to 10m resolution. We will set the option anti_aliasing to True to avoid aliasing efects of reducing the sampling size.

avirs_at_10m = read.resize(aviris_reader_in_memory,resolution_dst=s2_reader.res,anti_aliasing=True)
avirs_at_10m
 
         Transform: | 8.09, 5.88, 592577.80|
| 5.88,-8.09, 3519916.50|
| 0.00, 0.00, 1.00|
         Shape: (3, 112, 112)
         Resolution: (9.999999999910532, 10.000000000141569)
         Bounds: (592577.7996484624, 3519010.398378094, 594142.2181647301, 3520574.816894365)
         CRS: EPSG:32613
         fill_value_default: 0
        
fig, ax = plt.subplots(1,3,figsize=(18,6))
ax[0].imshow(aviris_reader_in_memory.values.transpose((1,2,0)))
ax[1].imshow(avirs_at_10m.values.transpose((1,2,0)))
ax[2].imshow(np.clip(s2_at_aviris_loc.values/3_500,0,1).transpose((1,2,0)))
<matplotlib.image.AxesImage at 0x7f17d211f320>
No description has been provided for this image

Licence

The georeader package is published under a GNU Lesser GPL v3 licence

georeader tutorials and notebooks are released under a Creative Commons non-commercial licence.

If you find this work useful please cite:

@article{ruzicka_starcop_2023,
    title = {Semantic segmentation of methane plumes with hyperspectral machine learning models},
    volume = {13},
    issn = {2045-2322},
    url = {https://www.nature.com/articles/s41598-023-44918-6},
    doi = {10.1038/s41598-023-44918-6},
    number = {1},
    journal = {Scientific Reports},
    author = {Růžička, Vít and Mateo-Garcia, Gonzalo and Gómez-Chova, Luis and Vaughan, Anna, and Guanter, Luis and Markham, Andrew},
    month = nov,
    year = {2023},
    pages = {19999},
}