Run inference on time series of Sentinel-2 images#

  • Last Modified: 15-03-2022

  • Author: Gonzalo Mateo-García


This notebook shows how to query time series of images of Sentinel-2 over an area of interest (AoI) between two dates using the Google Earth Engine. Hence to run this notebook you a Google Earth Engine account.

We show how to export those images to the Google Drive or to a Google bucket and we run inference on them to produce flood segmentation maps that are vectorized and shown in an interactive map.

Note: If you run this notebook in Google Colab change the running environment to use a GPU.

from datetime import datetime, timedelta, timezone
import geopandas as gpd
import pandas as pd
import ee
import geemap.eefolium as geemap
import folium
from ml4floods.data import ee_download
from shapely.geometry import mapping, shape
import matplotlib.pyplot as plt
import os

Step 1: Config AoI and dates to search for S2 images#

You can test this notebook on a different AoI and dates, just change those variables in the cell bellow for this.

date_event = datetime.strptime("2021-02-12","%Y-%m-%d").replace(tzinfo=timezone.utc)

date_start_search = datetime.strptime("2021-01-15","%Y-%m-%d").replace(tzinfo=timezone.utc)
date_end_search = date_start_search + timedelta(days=45)

area_of_interest_geojson = {'type': 'Polygon',
 'coordinates': (((19.483318354000062, 41.84407200000004),
   (19.351701478000052, 41.84053242300007),
   (19.298659824000026, 41.871157520000054),
   (19.236388306000038, 41.89588351100008),
   (19.22956438700004, 42.086957306000045),
   (19.327827977000027, 42.09102668200006),
   (19.778082109000025, 42.10312055000003),
   (19.777652446000047, 41.97309238100007),
   (19.777572772000042, 41.94912981900006),
   (19.582705341000064, 41.94398333100003),
   (19.581417139000052, 41.94394820700006),
   (19.54282145700006, 41.90168177700008),
   (19.483318354000062, 41.84407200000004)),)}

area_of_interest = shape(area_of_interest_geojson)

def generate_polygon(bbox):
    """
    Generates a list of coordinates: [[x1,y1],[x2,y2],[x3,y3],[x4,y4],[x1,y1]]
    """
    return [[bbox[0],bbox[1]],
             [bbox[2],bbox[1]],
             [bbox[2],bbox[3]],
             [bbox[0],bbox[3]],
             [bbox[0],bbox[1]]]

Step 2: Plot cloud coverage of available S2 image over AoI and search dates#

Next cell obtains the time series of S2 images over the provided AoI. This is a ee.ImageCollection object (img_col variable). Afterwards it obtains for each image the time of acquisition (system:time_start), the number of valid pixels (valids), and the average cloud probability (cloud_probability) in a pandas DataFrame (img_col_info_local). With this dataframe we plot the average cloud probability.

The cloud probability is obtained from the s2cloudless model which is available in the Google Earth Engine as an independent collection.

ee.Initialize()

bounds_pol = generate_polygon(area_of_interest.bounds)
pol_2_clip = ee.Geometry.Polygon(bounds_pol)

pol = ee.Geometry(area_of_interest_geojson)

# Grab the S2 images and the Permanent water image
img_col = ee_download.get_s2_collection(date_start_search, 
                                        date_end_search, pol)

permanent_water_img = ee_download.permanent_water_image(date_event.year, pol_2_clip)

# Get info of the S2 images (convert to table)
img_col_info = ee_download.img_collection_to_feature_collection(img_col, 
                                                                ["system:time_start", "valids", "cloud_probability"])

img_col_info_local = gpd.GeoDataFrame.from_features(img_col_info.getInfo())
img_col_info_local["valids"]*=100
img_col_info_local["system:time_start"] = img_col_info_local["system:time_start"].apply(lambda x: datetime.utcfromtimestamp(x/1000))

n_images_col = img_col_info_local.shape[0]
print(f"Found {n_images_col} S2 images between {date_event.isoformat()} and {date_end_search.isoformat()}")

plt.figure(figsize=(15,5))
plt.plot(img_col_info_local["system:time_start"], img_col_info_local["cloud_probability"],marker="x")
plt.ylim(0,101)
plt.xticks(rotation=30)
plt.ylabel("mean cloud coverage % over AoI")
plt.grid(axis="x")
Found 18 S2 images between 2021-02-12T00:00:00+00:00 and 2021-03-01T00:00:00+00:00
../../_images/HOWTO_inference_on_image_time_series_6_1.png

DataFrame with date of acquisition, averaged cloud probability and percentage of valid pixels.

img_col_info_local
geometry cloud_probability system:time_start valids
0 POLYGON ((-180.00000 -90.00000, 180.00000 -90.... 13.254181 2021-01-16 09:38:59.983 100
1 POLYGON ((-180.00000 -90.00000, 180.00000 -90.... 7.172355 2021-01-19 09:48:56.039 100
2 POLYGON ((-180.00000 -90.00000, 180.00000 -90.... 91.966141 2021-01-21 09:39:00.993 100
3 POLYGON ((-180.00000 -90.00000, 180.00000 -90.... 96.029202 2021-01-24 09:48:56.823 100
4 POLYGON ((-180.00000 -90.00000, 180.00000 -90.... 99.941818 2021-01-26 09:39:00.106 100
5 POLYGON ((-180.00000 -90.00000, 180.00000 -90.... 99.897624 2021-01-29 09:48:55.986 100
6 POLYGON ((-180.00000 -90.00000, 180.00000 -90.... 100.000000 2021-01-31 09:39:00.341 100
7 POLYGON ((-180.00000 -90.00000, 180.00000 -90.... 9.503629 2021-02-03 09:48:55.943 100
8 POLYGON ((-180.00000 -90.00000, 180.00000 -90.... 22.013119 2021-02-05 09:38:59.531 100
9 POLYGON ((-180.00000 -90.00000, 180.00000 -90.... 99.999951 2021-02-08 09:48:55.218 100
10 POLYGON ((-180.00000 -90.00000, 180.00000 -90.... 99.999130 2021-02-10 09:38:59.013 100
11 POLYGON ((-180.00000 -90.00000, 180.00000 -90.... 100.000000 2021-02-13 09:48:54.985 100
12 POLYGON ((-180.00000 -90.00000, 180.00000 -90.... 15.191459 2021-02-15 09:38:58.363 100
13 POLYGON ((-180.00000 -90.00000, 180.00000 -90.... 8.315460 2021-02-18 09:48:53.845 100
14 POLYGON ((-180.00000 -90.00000, 180.00000 -90.... 94.017820 2021-02-20 09:38:59.706 100
15 POLYGON ((-180.00000 -90.00000, 180.00000 -90.... 4.401171 2021-02-23 09:48:55.862 100
16 POLYGON ((-180.00000 -90.00000, 180.00000 -90.... 7.978288 2021-02-25 09:38:58.740 100
17 POLYGON ((-180.00000 -90.00000, 180.00000 -90.... 4.595103 2021-02-28 09:48:55.012 100

Step 3: Display S2 images over the AoI#

In the next cell we loop over the available S2 images and show those with low cloud coverage using the geemap package. If you see this notebook in the tutorial web page you will not seen the images; however if you run this cell with access to the GEE you’ll be able to see the Sentinel-2 images in the interactive map!

Map = geemap.Map()

imgs_list = img_col.toList(n_images_col, 0)
for i in range(n_images_col):
    if img_col_info_local.iloc[i]["cloud_probability"] > 75:
        continue
    img_show = ee.Image(imgs_list.get(i))
    
    Map.addLayer(img_show.clip(pol), 
                 {"min":0, "max":3000, 
                  "bands":["B11","B8","B4"]},
                 f"({i}/{n_images_col}) S2 SWIR/NIR/RED {img_col_info_local['system:time_start'][i].strftime('%Y-%m-%d')}", 
                 True)


visualization = {
  "bands": ['waterClass'],
  "min": 0.0,
  "max": 3.0,
  "palette": ['cccccc', 'ffffff', '99d9ea', '0000ff']
}

Map.addLayer(permanent_water_img, visualization, name="JRC Permanent water")

Map.addLayer(pol, {"color": 'FF000000'}, "AoI")

Map.centerObject(pol, zoom=11)
folium.LayerControl(collapsed=False).add_to(Map)
Map
Make this Notebook Trusted to load map: File -> Trust Notebook

Step 4: Download images to run inference#

In order to run inference we need to download the images that we’ve shown before. For this we could either export them to a Google Cloud Storage bucket if we have a Google Cloud account or to the Googel Drive if you’re running this in Colab.

Change export_to_gcp=False to export to Google Drive.

export_to_gcp = False

if export_to_gcp:
    # You need to create your own bucket and download the application credentials see: https://cloud.google.com/docs/authentication/getting-started. 
    os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = "/path/to/creds.json"
    os.environ["GS_USER_PROJECT"] = "gcp-project-name"
    path_to_export = "preingest/S2/"
    bucket_name = "bucket-name"
else:
    from google.colab import drive
    drive.mount('/content/drive')
    bucket_name = None
    path_to_export = "/content/drive/My Drive/"
from shapely.geometry import shape

# Export the data in UTM CRS
aoi_shapely = shape(area_of_interest_geojson)
lon, lat = list(aoi_shapely.centroid.coords)[0]
crs = ee_download.convert_wgs_to_utm(lon=lon, lat=lat)


export_task_fun_img = ee_download.export_task_image(
    bucket=bucket_name,crs=crs
)

bands_export = ee_download.BANDS_S2_NAMES["COPERNICUS/S2"] + ["probability"]

tasks = []
for i in range(n_images_col):
    if img_col_info_local.iloc[i]["cloud_probability"] > 75:
        continue
        
    img_export = ee.Image(imgs_list.get(i))
    img_export = img_export.select(bands_export).toFloat().clip(pol)
    
    date = img_col_info_local['system:time_start'][i].strftime('%Y%m%d')
    
    filename = f"{path_to_export}albania_ts_{date}"
    desc=f"albania_ts_{date}"
    task = ee_download.mayberun(
                        filename,
                        desc,
                        lambda: img_export,
                        export_task_fun_img,
                        overwrite=False,
                        dry_run=False,
                        bucket_name=bucket_name,
                        verbose=2,
                    )
    if task is not None:
        tasks.append(task)

ee_download.wait_tasks(tasks)
	Downloading file preingest/S2/albania_ts_20210116
	Downloading file preingest/S2/albania_ts_20210119
	Downloading file preingest/S2/albania_ts_20210203
	Downloading file preingest/S2/albania_ts_20210205
	Downloading file preingest/S2/albania_ts_20210215
	Downloading file preingest/S2/albania_ts_20210218
	Downloading file preingest/S2/albania_ts_20210223
	Downloading file preingest/S2/albania_ts_20210225
	Downloading file preingest/S2/albania_ts_20210228
9 tasks running
9 tasks running
9 tasks running
9 tasks running
9 tasks running
9 tasks running
9 tasks running
9 tasks running
8 tasks running
6 tasks running
5 tasks running
5 tasks running
5 tasks running
5 tasks running
5 tasks running
5 tasks running
4 tasks running
4 tasks running
3 tasks running
2 tasks running
2 tasks running
2 tasks running
2 tasks running
2 tasks running
2 tasks running
1 tasks running
1 tasks running
1 tasks running
Tasks failed: 0

Step 5: Check downloaded files#

Here we will list the downloaded files and plot them with one of the custom functions of the package for plotting Sentinel-2 images. This function uses matplotlib under the hood for plotting

if export_to_gcp:
    import fsspec
    fs = fsspec.filesystem("gs", requester_pays=True)
    exported_files = [f"gs://{f}" for f in fs.glob(f"gs://{bucket_name}/{path_to_export}albania_ts_*.tif")]
else:
    from glob import glob
    exported_files = glob(f"{path_to_export}albania_ts_*.tif")

exported_files
['gs://ml4floods/preingest/S2/albania_ts_20210116.tif',
 'gs://ml4floods/preingest/S2/albania_ts_20210119.tif',
 'gs://ml4floods/preingest/S2/albania_ts_20210203.tif',
 'gs://ml4floods/preingest/S2/albania_ts_20210205.tif',
 'gs://ml4floods/preingest/S2/albania_ts_20210215.tif',
 'gs://ml4floods/preingest/S2/albania_ts_20210218.tif',
 'gs://ml4floods/preingest/S2/albania_ts_20210223.tif',
 'gs://ml4floods/preingest/S2/albania_ts_20210225.tif',
 'gs://ml4floods/preingest/S2/albania_ts_20210228.tif']
from ml4floods.visualization import plot_utils

plot_utils.plot_swirnirred_image(exported_files[0], size_read=600)
../../_images/HOWTO_inference_on_image_time_series_16_0.png
<AxesSubplot:>

Step 6: Run inference on the image time series#

Now, we’re going to iteratively load all the images that we’ve downloaded and run inference to compute flood extent estimations on them. See the Run inference notebook for a detailed tutorial on this.

First we need to download the model cofig and the model weights. If you’re running this tutorial in Google Colab you need to ‘add a shortcut to your Google Drive’ from the public Google Drive folder:

add_shortcut_drive.png

Then, mount that directory with the following code:

try:
    from google.colab import drive
    drive.mount('/content/drive')
    assert os.path.exists('/content/drive/My Drive/Public WorldFloods Dataset'), "Add a shortcut to the publice Google Drive folder: https://drive.google.com/drive/u/0/folders/1dqFYWetX614r49kuVE3CbZwVO6qHvRVH"
    google_colab = True
    path_to_dataset_folder = '/content/drive/My Drive/Public WorldFloods Dataset'
    dataset_folder = os.path.join(path_to_dataset_folder, "worldfloods_v1_0_sample")
    experiment_name = "WFV1_unet"
    folder_name_model_weights = os.path.join(path_to_dataset_folder, experiment_name)
except ImportError as e:
    print(e)
    print("Setting google colab to false, it will need to install the gdown package!")
    google_colab = False
No module named 'google.colab'
Setting google colab to false, it will need to install the gdown package!

Alternatively,you can do this programatically using the the gdown package. For other alternatives see download WorldFloods.

import os

if not google_colab:
    experiment_name = "WFV1_unet"
    path_to_dataset_folder = 'worldfloods_v1_0_sample'
    folder_name_model_weights = os.path.join(path_to_dataset_folder, experiment_name)
    
    if not os.path.exists(folder_name_model_weights):
        import gdown
        gdown.download_folder(id="1Oup-qVD1U-re3lIQkw7TOKJsdu90blsk", quiet=False, use_cookies=False,
                              output=folder_name_model_weights)    

# gdown.download(id="1mDQUzVL45_GTiILSDNeb-UN8xNROsKyC", output="WFV1_unet/model.pt", quiet=False)

Second we will load the model and the inference_function that tiles the Sentinel-2 image to run inference over those tiles.

from ml4floods.models.config_setup import get_default_config
from ml4floods.models.model_setup import get_model
from ml4floods.models.model_setup import get_model_inference_function

# Downlink the model weights and config from GDrive!
config_fp = os.path.join(folder_name_model_weights,"config.json")
config = get_default_config(config_fp)

# The max_tile_size param controls the max size of patches that are fed to the NN. If you're in a memory constrained environment set this value to 128
config["model_params"]["max_tile_size"] = 128

config["model_params"]['model_folder'] = path_to_dataset_folder
config["model_params"]['test'] = True
model = get_model(config.model_params, experiment_name)
model.to("cuda")

inference_function = get_model_inference_function(model, config,apply_normalization=True)
Loaded Config for experiment:  WFV1_unet
{   'data_params': {   'batch_size': 32,
                       'bucket_id': 'ml4floods',
                       'channel_configuration': 'all',
                       'filter_windows': False,
                       'input_folder': 'S2',
                       'loader_type': 'local',
                       'num_workers': 8,
                       'path_to_splits': '/worldfloods/public',
                       'target_folder': 'gt',
                       'test_transformation': {   'normalize': True,
                                                  'num_classes': 3,
                                                  'totensor': True},
                       'train_test_split_file': 'worldfloods/public/train_test_split.json',
                       'train_transformation': {   'normalize': True,
                                                   'num_classes': 3,
                                                   'totensor': True},
                       'window_size': [256, 256]},
    'deploy': False,
    'experiment_name': 'WFV1_unet',
    'gpus': '0',
    'model_params': {   'hyperparameters': {   'channel_configuration': 'all',
                                               'label_names': [   'land',
                                                                  'water',
                                                                  'cloud'],
                                               'lr': 0.0001,
                                               'lr_decay': 0.5,
                                               'lr_patience': 2,
                                               'max_epochs': 25,
                                               'max_tile_size': 256,
                                               'model_type': 'unet',
                                               'num_channels': 13,
                                               'num_classes': 3,
                                               'val_every': 1,
                                               'weight_per_class': [   1.93445299,
                                                                       36.60054169,
                                                                       2.19400729]},
                        'model_folder': 'gs://ml4cc_data_lake/0_DEV/2_Mart/2_MLModelMart',
                        'path_to_weights': 'checkpoints/',
                        'test': False,
                        'train': True,
                        'use_pretrained_weights': False},
    'resume_from_checkpoint': False,
    'seed': 12,
    'test': False,
    'train': False,
    'wandb_entity': 'ml4floods',
    'wandb_project': 'worldfloods'}
Loaded model weights: worldfloods_v1_0_sample/WFV1_unet/model.pt
Getting model inference function
import torch
from ml4floods.data.worldfloods import dataset
from ml4floods.models.model_setup import get_channel_configuration_bands
import matplotlib.pyplot as plt
from ml4floods.visualization import plot_utils
from tqdm import tqdm
import numpy as np
from ml4floods.models import postprocess
import geopandas as gpd

channels = get_channel_configuration_bands(config.model_params.hyperparameters.channel_configuration)

Here we loop over the images, making preditions and plotting both the Sentinel-2 image and the prediction.

%%time

folder_save = "ts_outputs"
os.makedirs(folder_save, exist_ok=True)

for filename in tqdm(exported_files):
    # Load input in memory
    torch_inputs, transform = dataset.load_input(filename, window=None, channels=channels)
    
    # Make predictions
    outputs = inference_function(torch_inputs.unsqueeze(0))[0] # (num_classes, h, w)
    prob_water_mask = outputs[1].cpu().numpy()
    binary_water_mask = prob_water_mask>.5
    prediction = torch.argmax(outputs, dim=0).long() # (h, w)
    mask_invalid = torch.all(torch_inputs == 0, dim=0)
    prediction+=1
    prediction[mask_invalid] = 0
    
    name_plot = os.path.splitext(os.path.basename(filename))[0]
    
    # Vectorize the output
    geoms_polygons = postprocess.get_water_polygons(binary_water_mask, transform=transform)
    vectorized_dataframe = gpd.GeoDataFrame({"geometry": geoms_polygons, "id": np.arange(len(geoms_polygons))})
    vectorized_dataframe.to_file(f"{folder_save}/{name_plot}.geojson", driver='GeoJSON')
    
    # Plot results
    fig, axs = plt.subplots(1,2, figsize=((16,4.5)))
    plot_utils.plot_swirnirred_image(torch_inputs, transform=transform, ax=axs[0])
    plot_utils.plot_gt_v1(prediction.unsqueeze(0),transform=transform, ax=axs[1])
    fig.suptitle(datetime.strptime(filename[-12:-4],"%Y%m%d").strftime("%Y-%m-%d"),fontsize=20)
    plt.tight_layout()
    fig.savefig(f"{folder_save}/{name_plot}.jpg")
    fig.show()
    
  0%|                                                                                                                                                                                 | 0/9 [00:00<?, ?it/s]/home/gonzalo/miniconda3/envs/ml4floods/lib/python3.9/site-packages/geopandas/io/file.py:362: FutureWarning: pandas.Int64Index is deprecated and will be removed from pandas in a future version. Use pandas.Index with the appropriate dtype instead.
  pd.Int64Index,
<timed exec>:31: UserWarning: Matplotlib is currently using module://matplotlib_inline.backend_inline, which is a non-GUI backend, so cannot show the figure.
 11%|██████████████████▋                                                                                                                                                     | 1/9 [02:00<16:07, 120.99s/it]/home/gonzalo/miniconda3/envs/ml4floods/lib/python3.9/site-packages/geopandas/io/file.py:362: FutureWarning: pandas.Int64Index is deprecated and will be removed from pandas in a future version. Use pandas.Index with the appropriate dtype instead.
  pd.Int64Index,
<timed exec>:31: UserWarning: Matplotlib is currently using module://matplotlib_inline.backend_inline, which is a non-GUI backend, so cannot show the figure.
 22%|█████████████████████████████████████▎                                                                                                                                  | 2/9 [04:03<14:12, 121.82s/it]/home/gonzalo/miniconda3/envs/ml4floods/lib/python3.9/site-packages/geopandas/io/file.py:362: FutureWarning: pandas.Int64Index is deprecated and will be removed from pandas in a future version. Use pandas.Index with the appropriate dtype instead.
  pd.Int64Index,
<timed exec>:31: UserWarning: Matplotlib is currently using module://matplotlib_inline.backend_inline, which is a non-GUI backend, so cannot show the figure.
 33%|████████████████████████████████████████████████████████                                                                                                                | 3/9 [05:51<11:34, 115.69s/it]/home/gonzalo/miniconda3/envs/ml4floods/lib/python3.9/site-packages/geopandas/io/file.py:362: FutureWarning: pandas.Int64Index is deprecated and will be removed from pandas in a future version. Use pandas.Index with the appropriate dtype instead.
  pd.Int64Index,
<timed exec>:31: UserWarning: Matplotlib is currently using module://matplotlib_inline.backend_inline, which is a non-GUI backend, so cannot show the figure.
 44%|██████████████████████████████████████████████████████████████████████████▋                                                                                             | 4/9 [07:48<09:39, 115.97s/it]/home/gonzalo/miniconda3/envs/ml4floods/lib/python3.9/site-packages/geopandas/io/file.py:362: FutureWarning: pandas.Int64Index is deprecated and will be removed from pandas in a future version. Use pandas.Index with the appropriate dtype instead.
  pd.Int64Index,
<timed exec>:31: UserWarning: Matplotlib is currently using module://matplotlib_inline.backend_inline, which is a non-GUI backend, so cannot show the figure.
 56%|█████████████████████████████████████████████████████████████████████████████████████████████▎                                                                          | 5/9 [09:41<07:40, 115.04s/it]/home/gonzalo/miniconda3/envs/ml4floods/lib/python3.9/site-packages/geopandas/io/file.py:362: FutureWarning: pandas.Int64Index is deprecated and will be removed from pandas in a future version. Use pandas.Index with the appropriate dtype instead.
  pd.Int64Index,
<timed exec>:31: UserWarning: Matplotlib is currently using module://matplotlib_inline.backend_inline, which is a non-GUI backend, so cannot show the figure.
 67%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████                                                        | 6/9 [11:39<05:47, 115.98s/it]/home/gonzalo/miniconda3/envs/ml4floods/lib/python3.9/site-packages/geopandas/io/file.py:362: FutureWarning: pandas.Int64Index is deprecated and will be removed from pandas in a future version. Use pandas.Index with the appropriate dtype instead.
  pd.Int64Index,
<timed exec>:31: UserWarning: Matplotlib is currently using module://matplotlib_inline.backend_inline, which is a non-GUI backend, so cannot show the figure.
 78%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▋                                     | 7/9 [13:40<03:55, 117.57s/it]/home/gonzalo/miniconda3/envs/ml4floods/lib/python3.9/site-packages/geopandas/io/file.py:362: FutureWarning: pandas.Int64Index is deprecated and will be removed from pandas in a future version. Use pandas.Index with the appropriate dtype instead.
  pd.Int64Index,
<timed exec>:31: UserWarning: Matplotlib is currently using module://matplotlib_inline.backend_inline, which is a non-GUI backend, so cannot show the figure.
 89%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▎                  | 8/9 [15:40<01:58, 118.54s/it]/home/gonzalo/miniconda3/envs/ml4floods/lib/python3.9/site-packages/geopandas/io/file.py:362: FutureWarning: pandas.Int64Index is deprecated and will be removed from pandas in a future version. Use pandas.Index with the appropriate dtype instead.
  pd.Int64Index,
<timed exec>:31: UserWarning: Matplotlib is currently using module://matplotlib_inline.backend_inline, which is a non-GUI backend, so cannot show the figure.
100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 9/9 [17:40<00:00, 117.79s/it]
CPU times: user 16min 4s, sys: 4min 34s, total: 20min 38s
Wall time: 17min 40s
../../_images/HOWTO_inference_on_image_time_series_25_2.png ../../_images/HOWTO_inference_on_image_time_series_25_3.png ../../_images/HOWTO_inference_on_image_time_series_25_4.png ../../_images/HOWTO_inference_on_image_time_series_25_5.png ../../_images/HOWTO_inference_on_image_time_series_25_6.png ../../_images/HOWTO_inference_on_image_time_series_25_7.png ../../_images/HOWTO_inference_on_image_time_series_25_8.png ../../_images/HOWTO_inference_on_image_time_series_25_9.png ../../_images/HOWTO_inference_on_image_time_series_25_10.png

Step 7: Show vectorized floodmaps and S2 images in an interactive map#

In the final step we show the vectorized floodmaps over an interactive map using the geemap package.

Map = geemap.Map()

imgs_list = img_col.toList(n_images_col, 0)
for i in range(n_images_col):
    if img_col_info_local.iloc[i]["cloud_probability"] > 75:
        continue
    img_show = ee.Image(imgs_list.get(i))
    
    date_iter = img_col_info_local['system:time_start'][i]
    
    Map.addLayer(img_show.clip(pol), 
                 {"min":0, "max":3000, 
                  "bands":["B11","B8","B4"]},
                 f"{date_iter.strftime('%Y-%m-%d')} S2 SWIR/NIR/RED", 
                 True)
    
    name_plot = f"albania_ts_{date_iter.strftime('%Y%m%d')}"
    floodmap = gpd.read_file(f"{folder_save}/{name_plot}.geojson")
    name = f"FloodMap {date_iter.strftime('%Y-%m-%d')}"
    floodmap_folium = folium.features.GeoJson(floodmap, name=name, show=False)
    Map.add_child(floodmap_folium, name=name)


# Show JRC permanent water layer
visualization = {
  "bands": ['waterClass'],
  "min": 0.0,
  "max": 3.0,
  "palette": ['cccccc', 'ffffff', '99d9ea', '0000ff']
}

Map.addLayer(permanent_water_img, visualization, name="JRC Permanent water")

Map.addLayer(pol, {"color": 'FF000000'}, "AoI")

Map.centerObject(pol, zoom=11)
folium.LayerControl(collapsed=False).add_to(Map)
Map
Make this Notebook Trusted to load map: File -> Trust Notebook