Skip to content

Commit

Permalink
Merge pull request #39 from OPERA-Cal-Val/scott-branch
Browse files Browse the repository at this point in the history
Finalize Decay Params for S1 tiles
  • Loading branch information
cmarshak authored Jun 24, 2024
2 parents eec5d3b + 18502a7 commit c81752a
Show file tree
Hide file tree
Showing 5 changed files with 161 additions and 21 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [PEP 440](https://www.python.org/dev/peps/pep-0440/)
and uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.0.11]
* Added `rho`, `tau` and `rmse` variables for VV polarization from Sentinel-1 Global coherence. See: http://sentinel-1-global-coherence-earthbigdata.s3-website-us-west-2.amazonaws.com/

## [0.0.10]

### Added
Expand Down
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ We have notebooks to demonstrate common usage:

# Datasets Supported

The datasets supported are:
There are numerous tile sets. There are keyword arguments for many of the tiles. For example, for `hansen_annual_mosaic` the years 2013-2022 can be specified. The easiest way to see what is possible is to look at the [Basic Demo](notebooks/Basic_Demo.ipynb). The datasets supported are:

```
In [1]: from tile_mate.stitcher import DATASET_SHORTNAMES
Expand All @@ -90,12 +90,14 @@ More information about these datasets can be found below
+ ESA World Cover (10 m) for 2020 and 2021: https://aws.amazon.com/marketplace/pp/prodview-7oorylcamixxc
+ Hansen annual mosaic, treecover 2000, gain, and loss year: https://data.globalforestwatch.org/documents/941f17325a494ed78c4817f9bb20f33a/explore
+ S1 Coherence from December 2019 - Nov 2020: https://aws.amazon.com/marketplace/pp/prodview-iz6lnjbdlgcwa#resources
+ Include all temporal baselines and seasons for VV coherence
+ Include `rho`, `tau` and `rmse` seasonal decay modeling parameters
+ The copernicus 100 m LULC dataset from 2015 - 2019: https://land.copernicus.eu/global/content/annual-100m-global-land-cover-maps-available
+ Height above nearest drainage (distributed and generated by ASF) in 2021 derived from Copernicus Global DEM: https://github.com/asjohnston-asf/agu-2022-notebooks/blob/main/hand-notebook.ipynb (and their [tool](https://github.com/HydroSAR/HydroSAR/blob/develop/src/hydrosar/hand/prepare.py) to do the exact same thing)
+ RADD Deforestation alerts (ongoing): https://data.globalforestwatch.org/datasets/gfw::deforestation-alerts-radd/about
+ Glad landcover maps (2000, 2005, 2010, 2015, 2020) and change map (2020 changes when compared to 2000): https://storage.googleapis.com/earthenginepartners-hansen/GLCLU2000-2020/v2/download.html

See these [notebooks](notebooks/tile_creation) to see how these tiles are generated and organized.
See these [notebooks](notebooks/tile_creation) to see how these tiles are generated and organized. Feel free to open a issue ticket or PR if there are modifications or new tilesets you would like to see.

# Dateline support

Expand Down
98 changes: 92 additions & 6 deletions notebooks/Basic_Demo.ipynb

Large diffs are not rendered by default.

48 changes: 35 additions & 13 deletions src/tile_mate/stitcher.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from functools import lru_cache
from pathlib import Path
from typing import Optional

import geopandas as gpd
import rasterio
Expand Down Expand Up @@ -36,6 +37,7 @@
CURRENT_HANSEN_YEAR = 2022
SEASONS = ['fall', 'winter', 'spring', 'summer']
S1_TEMPORAL_BASELINE_DAYS = [6, 12, 18, 24, 36, 48]
S1_MODEL_VARIABLES = ['rho', 'tau', 'rmse']
GLAD_LANDCOVER_YEARS = [2000, 2005, 2010, 2015, 2020]


Expand All @@ -52,9 +54,10 @@ def get_all_tile_data(tile_key: str) -> gpd.GeoDataFrame:
@lru_cache
def get_tile_data(
tile_key: str,
year: int = None,
season: str = None,
temporal_baseline_days: int = None,
year: Optional[int] = None,
season: Optional[str] = None,
temporal_baseline_days: Optional[str] = None,
s1_decay_model_param: Optional[str] = None,
) -> gpd.GeoDataFrame:
# Because tile data is cached - we need to copy it.
df_tiles = get_all_tile_data(tile_key).copy()
Expand Down Expand Up @@ -102,16 +105,33 @@ def update_glad_landcover_url(url: str, year: int = year) -> str:
raise ValueError('Year is required for tile lookup')

if tile_key == 's1_coherence_2020':
if any([var is None for var in [temporal_baseline_days, season]]):
raise ValueError(f'{tile_key} requires season and temporal baseline ' 'to be specified')
if (season is None) and (s1_decay_model_param is None):
raise ValueError(f'{tile_key} requires *both* season or s1_decay_model_param to be specified')
# XOR of variables
if (temporal_baseline_days is None) == (s1_decay_model_param is None):
raise ValueError(f'{tile_key} requires either s1_decay_model_param or temporal baseline to be specified')
if season not in SEASONS:
raise ValueError(f'season keyword must be in {", ".join(SEASONS)}')
if temporal_baseline_days not in S1_TEMPORAL_BASELINE_DAYS:
if temporal_baseline_days not in [None, *S1_TEMPORAL_BASELINE_DAYS]:
tb_days_str = list(map(str, S1_TEMPORAL_BASELINE_DAYS))
raise ValueError(f'temporal_baseline_days must be in {", ".join(tb_days_str)}')
if s1_decay_model_param not in [None, *S1_MODEL_VARIABLES]:
raise ValueError(f's1_decay_model_param keyword must be in {", ".join(S1_MODEL_VARIABLES)}')
# we need season, but we can either do temporal baseline, or variable
ind_season = df_tiles.season == season
ind_tb = df_tiles.temporal_baseline_days == temporal_baseline_days
df_tiles = df_tiles[ind_tb & ind_season].reset_index(drop=True)
# temporal baseline and season
if temporal_baseline_days is not None:
ind_tb = df_tiles.temporal_baseline_days == temporal_baseline_days
total_ind = ind_tb & ind_season
df_tiles = df_tiles[total_ind].reset_index(drop=True)
# s1_decay_model_param
else:
# use 12 day temporal baseline to replace with s1_decay_model_param
ind_tb = df_tiles.temporal_baseline_days == 12
total_ind = ind_tb & ind_season
df_tiles = df_tiles[total_ind].reset_index(drop=True)
df_tiles.loc[:, 'url'] = df_tiles.loc[:, 'url'].str.replace('_COH12', f'_{s1_decay_model_param}')

if df_tiles.empty:
raise NoTileCoverage(f'{tile_key} has no global tiles with the parameters provided')
return df_tiles
Expand Down Expand Up @@ -177,11 +197,12 @@ def get_additional_tile_metadata(urls: list[str], max_tile_tries: int = 10) -> d

def get_raster_from_tiles(
extent: list[float],
tile_shortname: str = None,
df_tiles: gpd.GeoDataFrame = None,
year: int = None,
season: str = None,
temporal_baseline_days: int = None,
tile_shortname: Optional[str] = None,
df_tiles: Optional[gpd.GeoDataFrame] = None,
year: Optional[int] = None,
season: Optional[str] = None,
temporal_baseline_days: Optional[int] = None,
s1_decay_model_param: Optional[str] = None,
) -> tuple:
if (tile_shortname is None) and (df_tiles is None):
raise ValueError('Either "tile_shortname" or "df_tiles" must be provided')
Expand All @@ -195,6 +216,7 @@ def get_raster_from_tiles(
year=year,
temporal_baseline_days=temporal_baseline_days,
season=season,
s1_decay_model_param=s1_decay_model_param,
)

df_tiles = TILE_SCHEMA.validate(df_tiles)
Expand Down
27 changes: 27 additions & 0 deletions tests/test_stitch_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
DATASETS_WITH_YEAR,
GLAD_LANDCOVER_YEARS,
HANSEN_MOSAIC_YEARS,
S1_MODEL_VARIABLES,
S1_TEMPORAL_BASELINE_DAYS,
SEASONS,
)
Expand Down Expand Up @@ -69,6 +70,22 @@ def test_coherence_dataset(season, temporal_baseline_days):
tile_shortname='s1_coherence_2020',
season=season,
temporal_baseline_days=temporal_baseline_days,
s1_decay_model_param=None,
)
assert len(X.shape) == 3


@pytest.mark.parametrize('season', SEASONS)
@pytest.mark.parametrize('s1_var', S1_MODEL_VARIABLES)
def test_s1_model_tiles(season, s1_var):
# Note only getting 1 tile
bounds = [-120.45, 34.85, -120.15, 34.95]
X, _ = get_raster_from_tiles(
bounds,
tile_shortname='s1_coherence_2020',
season=season,
temporal_baseline_days=None,
s1_decay_model_param=s1_var,
)
assert len(X.shape) == 3

Expand Down Expand Up @@ -132,3 +149,13 @@ def test_s1_coherence_exceptions():
X, _ = get_raster_from_tiles(
bounds, tile_shortname='s1_coherence_2020', season='fall', temporal_baseline_days=5
)

# No temporal baseline when using model param
with pytest.raises(ValueError):
X, _ = get_raster_from_tiles(
bounds,
tile_shortname='s1_coherence_2020',
season='fall',
temporal_baseline_days=12,
s1_decay_model_param='rmse',
)

0 comments on commit c81752a

Please sign in to comment.