Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v0.0.11 #40

Merged
merged 8 commits into from
Jun 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
15 changes: 10 additions & 5 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 @@ -81,18 +81,23 @@ Out[2]: 'pekel_water_occ_2021',
's1_coherence_2020',
'cop_100_lulc_discrete',
'radd_deforestation_alerts_2022',
'hand'
'hand',
'glad_landcover',
'glad_change'
```
More information about these datasets can be found below
+ Pekel water occurence: https://global-surface-water.appspot.com/download
+ 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: 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: https://data.globalforestwatch.org/datasets/gfw::deforestation-alerts-radd/about
+ 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',
)