diff --git a/CHANGELOG.md b/CHANGELOG.md index edc1cadc..25500dd1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Use `pyproject.toml` for project metadata ([#424](https://github.com/stac-utils/stactools/pull/424)) -- Use for antimeridian and pole fixes ([#426](https://github.com/stac-utils/stactools/pull/426)) +- Use for antimeridian and pole fixes ([#426](https://github.com/stac-utils/stactools/pull/426)). + Note that this _may_ change the output geometries when fixing some antimeridian-crossing polygons, e.g. if the input polygons are wound clockwise (instead of counter-clockwise), the output geometry will be unexpectedly large (). + +### Fixed + +- Bounding boxes after fixing geometries for antimeridian crossings ([#432](https://github.com/stac-utils/stactools/pull/432)) ### Deprecated diff --git a/pyproject.toml b/pyproject.toml index ef53d6b7..6f76ec5a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,7 +27,7 @@ requires-python = ">=3.8" dependencies = [ "Shapely>=1.8.5.post1", "aiohttp>=3.8.3", - "antimeridian>=0.2.4", + "antimeridian>=0.2.6", "click>=8.1.3", "fsspec>=2021.7", "lxml>=4.9.2", diff --git a/src/stactools/core/utils/antimeridian.py b/src/stactools/core/utils/antimeridian.py index bcecb9fd..82a71104 100644 --- a/src/stactools/core/utils/antimeridian.py +++ b/src/stactools/core/utils/antimeridian.py @@ -59,13 +59,13 @@ def fix_item(item: Item, strategy: Strategy) -> Item: else: normalized_geometry = normalize(geometry) if normalized_geometry: - bbox = normalized_geometry.bounds + bbox = list(normalized_geometry.bounds) item.geometry = shapely.geometry.mapping(normalized_geometry) item.bbox = bbox elif strategy == Strategy.SPLIT: - fixed = shapely.geometry.shape(antimeridian.fix_shape(geometry)) - item.bbox = fixed.bounds - item.geometry = shapely.geometry.mapping(fixed) + fixed = antimeridian.fix_shape(geometry) + item.bbox = antimeridian.bbox(fixed) + item.geometry = fixed else: raise NotImplementedError(f"Unknown strategy: {strategy}") return item @@ -75,7 +75,7 @@ def split(polygon: Polygon) -> Optional[MultiPolygon]: """Splits a single WGS84 polygon into a multipolygon across the antimeridian. - .. deprecated:: v0.4.7 + .. deprecated:: v0.4.8 Use the `antimeridian `_ package instead. @@ -105,7 +105,7 @@ def split_multipolygon(multi_polygon: MultiPolygon) -> Optional[MultiPolygon]: """Splits multiple WGS84 polygons into a multipolygon across the antimeridian. - .. deprecated:: v0.4.7 + .. deprecated:: v0.4.8 Use the `antimeridian `_ package instead. @@ -152,7 +152,7 @@ def normalize(polygon: Polygon) -> Optional[Polygon]: Todo: Fix this - .. deprecated:: v0.4.7 + .. deprecated:: v0.4.8 "Normalization" does not conform to the GeoJSON specification, and its use is discouraged. @@ -205,7 +205,7 @@ def normalize_multipolygon(multi_polygon: MultiPolygon) -> Optional[MultiPolygon Todo: Fix this - .. deprecated:: v0.4.7 + .. deprecated:: v0.4.8 "Normalization" does not conform to the GeoJSON specification, and its use is discouraged. @@ -247,7 +247,7 @@ def enclose_poles(polygon: Polygon) -> Polygon: the geometry up to the north (or down to the south) pole. This is useful for (e.g.) polar-orbiting satellites who have swaths that go over the poles. - .. deprecated:: v0.4.7 + .. deprecated:: v0.4.8 Use the `antimeridian `_ package instead. diff --git a/tests/core/utils/test_antimeridian.py b/tests/core/utils/test_antimeridian.py index 05cb30db..fe31cf07 100644 --- a/tests/core/utils/test_antimeridian.py +++ b/tests/core/utils/test_antimeridian.py @@ -108,7 +108,116 @@ def test_item_fix_antimeridian_split() -> None: expected.geoms, ): assert actual.equals(expected) - assert fix.bbox == (-180.0, 40.0, 180.0, 50.0) + assert fix.bbox == [170.0, 40.0, -170.0, 50.0] + + # https://github.com/stac-utils/stactools/issues/431 + item.geometry = { + "type": "MultiPolygon", + "coordinates": [ + [ + [ + [180.0, 71.05915991175688], + [179.9010750820252, 71.06541838862726], + [179.33609176796372, 71.09933099986762], + [178.94212273834066, 71.12173675373532], + [178.8438129745677, 70.9444458447684], + [178.752152818998, 70.76646311814699], + [178.65805412647518, 70.58865453189631], + [178.56519747139944, 70.41081414405018], + [178.47354940789182, 70.23294268101853], + [178.3830776723032, 70.0550408551604], + [178.29375108586135, 69.87710927076108], + [178.20553958576738, 69.69914858383564], + [178.11841414688843, 69.52115944285417], + [177.94089921353645, 69.16547224310027], + [177.84841186280488, 68.98789251198602], + [177.6811017579393, 68.63180788289334], + [177.59856429364814, 68.45374564350413], + [177.5170097254968, 68.27565414719149], + [177.35557827100007, 67.91945640451053], + [177.27786157853095, 67.74121946580148], + [177.2008812187222, 67.5629645683486], + [177.04801561874308, 67.20647273987551], + [176.999842641033, 67.08652173541694], + [177.33004216473563, 67.06599963683973], + [177.80402381607067, 67.0350911283458], + [178.2797509869322, 67.00256994873874], + [178.75469524409255, 66.96859439018418], + [179.22826647862672, 66.9332058638613], + [179.69748316523564, 66.89664359586376], + [180.0, 66.87210508518304], + [180.0, 71.05915991175688], + ] + ], + [ + [ + [-180.0, 71.05915991175688], + [-180.0, 66.87210508518304], + [-179.8356900497663, 66.85877716089777], + [-179.3711345161936, 66.81960857957779], + [-178.90872065703624, 66.77913696435182], + [-178.44665752494657, 66.73720767705268], + [-177.98865515860075, 66.69416388262516], + [-177.52780114205189, 66.6493554890298], + [-177.07275963647132, 66.6036210941224], + [-176.60787845770125, 66.55536115330877], + [-176.1601824748328, 66.50739485114256], + [-175.70001915807038, 66.4565657047866], + [-175.2469931583778, 66.40499213010705], + [-174.7964900612594, 66.35218457280217], + [-174.34347993447946, 66.29754039236374], + [-173.93806864778026, 66.24724531349037], + [-173.84461372118676, 66.36429279141416], + [-173.71659543892704, 66.53996608675018], + [-173.56903550480715, 66.7133044234658], + [-173.42527581962764, 66.88723670581788], + [-173.13377948252224, 67.23496576919374], + [-172.9861602177385, 67.40878187666635], + [-172.83662520799953, 67.58248608985471], + [-172.68512898875264, 67.75607579888592], + [-172.53162554562746, 67.92954828162647], + [-172.3760685337871, 68.10290084143905], + [-172.2184100683418, 68.27613086321372], + [-172.05860098199193, 68.44923546830157], + [-171.89659058995878, 68.6222117159503], + [-171.73232652538707, 68.79505671303144], + [-171.5657549702618, 68.96776730324396], + [-171.39682036750511, 69.14034030642564], + [-171.2254655001628, 69.31277236817284], + [-171.0516311905371, 69.48506019171748], + [-170.87525658150514, 69.65720018398522], + [-170.6962788340423, 69.82918868566634], + [-170.51463300157428, 70.00102201028209], + [-170.33062092998924, 70.17235288521945], + [-170.8013714414895, 70.22969914468295], + [-171.32505679115005, 70.2915919758267], + [-171.85186730974112, 70.35195515326475], + [-172.3817527878377, 70.41077446610994], + [-172.9146602469648, 70.46803605371709], + [-173.45053416371871, 70.52372627412625], + [-173.98931653005684, 70.5778316447626], + [-174.53094682771086, 70.6303388535604], + [-175.07536190589843, 70.68123489897708], + [-175.62249600000058, 70.73050710549359], + [-176.17228091814727, 70.77814294613066], + [-176.72464582082006, 70.82413036939441], + [-177.2795175859301, 70.86845740126327], + [-177.83682058594974, 70.91111247989673], + [-178.39647668879172, 70.95208453426882], + [-178.96676234549457, 70.99192936610521], + [-179.53562414647374, 71.02978121124382], + [-180.0, 71.05915991175688], + ] + ], + ], + } + fix = antimeridian.fix_item(item, antimeridian.Strategy.SPLIT) + assert fix.bbox == [ + 176.999842641033, + 66.24724531349037, + -170.33062092998924, + 71.12173675373532, + ] def test_item_fix_antimeridian_normalize() -> None: diff --git a/tests/core/utils/test_raster_footprint.py b/tests/core/utils/test_raster_footprint.py index aa7bd84a..367d5586 100644 --- a/tests/core/utils/test_raster_footprint.py +++ b/tests/core/utils/test_raster_footprint.py @@ -1,3 +1,4 @@ +import pytest from pystac import Item from rasterio.crs import CRS from shapely.geometry import shape @@ -24,12 +25,13 @@ def test_non_existent_asset() -> None: ) ) - assert not ( - update_geometry_from_asset_footprint( - item, - asset_names=["B01_doesnt_exist"], + with pytest.warns(DeprecationWarning): + assert not ( + update_geometry_from_asset_footprint( + item, + asset_names=["B01_doesnt_exist"], + ) ) - ) def test_modis() -> None: @@ -41,9 +43,10 @@ def test_modis() -> None: ) ) - update_geometry_from_asset_footprint( - item, asset_names=["B01"], densification_factor=10 - ) + with pytest.warns(DeprecationWarning): + update_geometry_from_asset_footprint( + item, asset_names=["B01"], densification_factor=10 + ) geometry = { "type": "Polygon", @@ -136,9 +139,10 @@ def test_sentinel2_sliver() -> None: ) ) - update_geometry_from_asset_footprint( - item, asset_names=["R60m_B01"], simplify_tolerance=0.005, no_data=0 - ) + with pytest.warns(DeprecationWarning): + update_geometry_from_asset_footprint( + item, asset_names=["R60m_B01"], simplify_tolerance=0.005, no_data=0 + ) geometry = { "type": "Polygon", @@ -167,10 +171,11 @@ def test_sentinel2_full() -> None: ) ) - update_geometry_from_asset_footprint( - item, - asset_names=["R60m_B01"], - ) + with pytest.warns(DeprecationWarning): + update_geometry_from_asset_footprint( + item, + asset_names=["R60m_B01"], + ) geometry = { "type": "Polygon", @@ -198,9 +203,10 @@ def test_landsat8() -> None: ) ) - update_geometry_from_asset_footprint( - item, asset_names=["B2"], simplify_tolerance=0.005 - ) + with pytest.warns(DeprecationWarning): + update_geometry_from_asset_footprint( + item, asset_names=["B2"], simplify_tolerance=0.005 + ) geometry = { "type": "Polygon", @@ -220,10 +226,11 @@ def test_landsat8() -> None: def test_nan_as_nodata() -> None: - polygon = data_footprint( - test_data.get_path("data-files/raster_footprint/LC08_LST_crop.tif"), # noqa - simplify_tolerance=0.01, - ) + with pytest.warns(DeprecationWarning): + polygon = data_footprint( + test_data.get_path("data-files/raster_footprint/LC08_LST_crop.tif"), # noqa + simplify_tolerance=0.01, + ) geometry = { "type": "Polygon", "coordinates": ( @@ -243,14 +250,15 @@ def test_nan_as_nodata() -> None: def test_data_footprint_precision() -> None: use_fsspec() - polygon = data_footprint( - test_data.get_path( - "data-files/raster_footprint/S2A_OPER_MSI_L2A_TL_ATOS_20220620T162319_A036527_T32TLS_N04.00_R60m_B01.jp2" # noqa - ), - precision=1, - simplify_tolerance=0.01, - no_data=0, - ) + with pytest.warns(DeprecationWarning): + polygon = data_footprint( + test_data.get_path( + "data-files/raster_footprint/S2A_OPER_MSI_L2A_TL_ATOS_20220620T162319_A036527_T32TLS_N04.00_R60m_B01.jp2" # noqa + ), + precision=1, + simplify_tolerance=0.01, + no_data=0, + ) geometry = { "type": "Polygon", "coordinates": ( @@ -375,7 +383,8 @@ def test_multiband_footprint() -> None: "data-files/raster_footprint/" "AST_L1T_00310012006175412_20150516104359-SWIR-cropped.tif" ) - footprint = data_footprint(path, no_data=0, bands=[], simplify_tolerance=0.005) + with pytest.warns(DeprecationWarning): + footprint = data_footprint(path, no_data=0, bands=[], simplify_tolerance=0.005) assert footprint geometry = {