Skip to content

Commit 7c871c4

Browse files
authored
Merge pull request #65 from OPERA-Cal-Val/dev
v0.0.15
2 parents 42099e0 + 0804172 commit 7c871c4

7 files changed

Lines changed: 72 additions & 36 deletions

File tree

.github/workflows/distribute.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ jobs:
1111
outputs:
1212
SDIST_VERSION: ${{ steps.build.outputs.version }}
1313
steps:
14-
- uses: actions/checkout@v4
14+
- uses: actions/checkout@v5
1515

16-
- uses: actions/setup-python@v5
16+
- uses: actions/setup-python@v6
1717
with:
1818
python-version: 3.11
1919

.github/workflows/static-analysis.yml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,6 @@ name: Static Analysis (Flake8/Ruff)
33
on: push
44

55
jobs:
6-
call-flake8-workflow:
7-
uses: ASFHyP3/actions/.github/workflows/reusable-flake8.yml@v0.20.0
8-
with:
9-
local_package_names: tile_mate
10-
116
call-secrets-analysis-workflow:
127
uses: ASFHyP3/actions/.github/workflows/reusable-secrets-analysis.yml@v0.20.0
138

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [PEP 440](https://www.python.org/dev/peps/pep-0440/)
77
and uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
88

9+
## [0.0.15] - 2025-07-06
10+
11+
### Added
12+
- Antimeridian/dateline crossing using `dem-stitcher`.
13+
14+
### Changed
15+
- Enforces `xmin <= xmax` and `ymin <= xmax` where `extent = (xmin, ymin, xmax, ymax)` is the input for `get_raster_from_tiles`.
16+
917
## [0.0.14] - 2025-07-06
1018

1119
### Removed

README.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,12 @@ More information about these datasets can be found below
9797

9898
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.
9999

100-
# Dateline support
100+
# Dateline/antimeridian support
101101

102-
None curently.
102+
We support a single dateline crossing (crossing +/- 180 longitude) within `get_raster_from_tiles` using in-memory translation of tiles (same as [dem-stitcher](https://github.com/ACCESS-Cloud-Based-InSAR/dem-stitcher)'s functionality).
103+
We "wrap" tiles across this dateline crossing.
104+
We assume that the supplied bounds/extent overlap the standard lat/lon CRS grid i.e. longitudes between -/+ 180 longitude and are within -/+ 90 latitude, where a buffer around the dateline (longitude axis or $x$-axis).
105+
Wrapping tiles around the North and South poles (i.e. at -/+ 90 latitude) is *not* supported (a different CRS is what's required) and an exception will be raised.
103106

104107
# Contributing
105108

@@ -110,7 +113,7 @@ We welcome contributions to this open-source package. To do so:
110113
3. Make your modifications in your own fork
111114
4. Make a pull-request (PR) in this repo with the code in your fork and tag the repo owner or a relevant contributor.
112115

113-
We use `flake8` and associated linting packages to ensure some basic code quality (see the `environment.yml`). These will be checked for each commit in a PR. Try to write tests wherever possible.
116+
We use `ruff` and associated linting packages to ensure some basic code quality (see the `environment.yml`). These will be checked for each commit in a PR. Try to write tests wherever possible.
114117

115118
# Support
116119

notebooks/Basic_Demo.ipynb

Lines changed: 23 additions & 20 deletions
Large diffs are not rendered by default.

src/tile_mate/stitcher.py

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
from functools import lru_cache
22
from pathlib import Path
33
from typing import Optional
4+
import warnings
45

56
import geopandas as gpd
67
import rasterio
78
from dem_stitcher.geojson_io import read_geojson_gzip
89
from dem_stitcher.merge import merge_tile_datasets_within_extent
10+
from dem_stitcher.dateline import get_dateline_crossing
11+
from dem_stitcher.stitcher import _translate_one_tile_across_dateline
912
from rasterio.errors import RasterioIOError
1013
from rasterio.env import Env
1114
from shapely.geometry import box
15+
import pandas as pd
1216

1317
from .exceptions import NoTileCoverage, TilesetNotSupported
1418
from .tile_model import TILE_SCHEMA
@@ -128,6 +132,7 @@ def update_glad_landcover_url(url: str, year: int = year) -> str:
128132

129133
if df_tiles.empty:
130134
raise NoTileCoverage(f'{tile_key} has no global tiles with the parameters provided')
135+
131136
return df_tiles
132137

133138

@@ -152,9 +157,22 @@ def update_hansen_landsat_mosaic_url(url: str, year: int):
152157

153158

154159
def get_urls_from_tile_df(extent: list[float], df_tiles: gpd.GeoDataFrame) -> list[str]:
160+
df_tiles_all = df_tiles.copy()
161+
crossing = get_dateline_crossing(extent)
162+
if crossing:
163+
warnings.warn(
164+
'Getting tiles across dateline on the opposite hemisphere; '
165+
f'The source tiles will be {-2 * crossing} deg along the '
166+
'longitudinal axis from the extent requested',
167+
category=UserWarning,
168+
)
169+
df_tiles_all_translated = df_tiles_all.copy()
170+
x_translation = 2 * crossing
171+
df_tiles_all_translated.geometry = df_tiles_all.geometry.translate(xoff=x_translation)
172+
df_tiles_all = pd.concat([df_tiles_all, df_tiles_all_translated], axis=0).reset_index(drop=True)
155173
bbox = box(*extent)
156-
ind_inter = df_tiles.geometry.intersects(bbox)
157-
df_subset = df_tiles[ind_inter].reset_index(drop=True)
174+
ind_inter = df_tiles_all.geometry.intersects(bbox)
175+
df_subset = df_tiles_all[ind_inter].reset_index(drop=True)
158176
urls = df_subset.url.tolist()
159177
if not urls:
160178
raise NoTileCoverage('There are no tiles over the requested area')
@@ -218,13 +236,21 @@ def get_raster_from_tiles(
218236

219237
urls_subset = get_urls_from_tile_df(extent, df_tiles)
220238

239+
datasets = [rasterio.open(url) for url in urls_subset]
240+
crossing = get_dateline_crossing(extent)
241+
if crossing:
242+
zipped_data = list(map(lambda ds: _translate_one_tile_across_dateline(ds, crossing), datasets))
243+
memory_files, datasets = zip(*zipped_data)
244+
221245
if tile_shortname == 'umd_ocean_mask':
222246
env = Env(GS_NO_SIGN_REQUEST='YES')
223247
else:
224248
env = Env() # default environment
225249

226250
with env:
227-
X_merged, p_merged = merge_tile_datasets_within_extent(urls_subset, extent)
251+
X_merged, p_merged = merge_tile_datasets_within_extent(datasets, extent)
252+
if crossing:
253+
list(map(lambda mf: mf.close(), memory_files))
228254

229255
# Are stored in the profile for provenance
230256
p_merged.update(**tile_metadata)

tests/test_stitch_api.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,17 @@ def test_glad_change():
2828
assert len(X.shape) == 3
2929

3030

31-
def test_umd_ocean_mask():
32-
bounds = [-120.45, 34.85, -120.15, 35.15]
31+
# Antimeridian bounds too!
32+
@pytest.mark.parametrize('bounds', [[-181, 51.25, -179, 51.75], [-120.45, 34.85, -120.15, 35.15]])
33+
def test_umd_ocean_mask(bounds: list[float]):
3334
X, _ = get_raster_from_tiles(bounds, tile_shortname='umd_ocean_mask')
3435
assert len(X.shape) == 3
3536

3637

3738
@pytest.mark.parametrize('year', GLAD_LANDCOVER_YEARS)
3839
def test_glad_landcover_datasets(year):
3940
# Note only getting 1 tile - these are large datasets!
40-
bounds = [-120.45, 34.85, -121.15, 34.95]
41+
bounds = [-121.15, 34.85, -120.45, 34.95]
4142
X, _ = get_raster_from_tiles(bounds, tile_shortname='glad_landcover', year=year)
4243
assert len(X.shape) == 3
4344

0 commit comments

Comments
 (0)