Skip to content

Commit e75fb60

Browse files
authored
fix(ci): repair integration test suite after v2 SearchResult refactor (#66)
* test(integration): migrate to SearchResult API and fix Phase 1 LAZ bbox The v2.0.0 refactor changed `abovepy.search()` to return a SearchResult workflow object instead of a bare GeoDataFrame, but the integration suite still accessed `.columns` and `.iloc` directly. Route those through SearchResult.tiles (the underlying GeoDataFrame). Also switch test_laz_products to pike_county_bbox. Phase 1 LiDAR was flown county-by-county 2010-2017 and Franklin County (Frankfort) was not in that batch, so the Frankfort fixture cannot return Phase 1 LAZ tiles. Pike County has coverage for all three phases. Fixes 10 of 11 failures in the weekly scheduled integration run on main. * fix(mosaic): raise actionable MosaicError when GDAL bindings missing _build_vrt imports `from osgeo import gdal`, which is not bundled with rasterio and not in any optional-dependency group. On systems without the GDAL Python bindings (including the CI integration runner) the import would fail with a bare ModuleNotFoundError. Wrap the import and raise MosaicError with install guidance instead. The `.tif` path through `_merge_tiles` does not need GDAL bindings, so the message points users there as a fallback.
1 parent 3bca018 commit e75fb60

2 files changed

Lines changed: 28 additions & 12 deletions

File tree

src/abovepy/_mosaic.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,17 @@ def _build_vrt(
103103
Path
104104
Path to the created VRT file.
105105
"""
106-
from osgeo import gdal
106+
try:
107+
from osgeo import gdal
108+
except ImportError as exc:
109+
from abovepy._exceptions import MosaicError
110+
111+
raise MosaicError(
112+
"VRT construction requires the GDAL Python bindings, which are not "
113+
"installed by default. Install with `conda install -c conda-forge gdal` "
114+
"(recommended) or `pip install gdal` (requires matching system GDAL headers). "
115+
"Alternatively, pass an output path ending in `.tif` to merge via rasterio."
116+
) from exc
107117

108118
gdal.UseExceptions()
109119

tests/test_integration.py

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ def test_search_dem_phase3_frankfort(self, frankfort_bbox):
3636
"""Search returns DEM tiles for the Frankfort area."""
3737
tiles = abovepy.search(bbox=frankfort_bbox, product="dem_phase3", max_items=10)
3838
assert len(tiles) > 0
39-
assert "tile_id" in tiles.columns
40-
assert "asset_url" in tiles.columns
39+
assert "tile_id" in tiles.tiles.columns
40+
assert "asset_url" in tiles.tiles.columns
4141

4242
def test_search_by_county(self):
4343
"""County-based search returns results."""
@@ -59,7 +59,7 @@ def test_asset_urls_are_accessible(self, frankfort_bbox):
5959
import httpx
6060

6161
tiles = abovepy.search(bbox=frankfort_bbox, product="dem_phase3", max_items=1)
62-
url = tiles.iloc[0]["asset_url"]
62+
url = tiles.tiles.iloc[0]["asset_url"]
6363
resp = httpx.head(url, follow_redirects=True, timeout=30)
6464
assert resp.status_code == 200
6565

@@ -90,7 +90,7 @@ def test_dem_products(self, frankfort_bbox, product):
9090
"""DEM products return tiles for Frankfort area."""
9191
tiles = abovepy.search(bbox=frankfort_bbox, product=product, max_items=3)
9292
assert len(tiles) > 0
93-
assert tiles.iloc[0]["product"] == product
93+
assert tiles.tiles.iloc[0]["product"] == product
9494

9595
@pytest.mark.parametrize(
9696
"product",
@@ -113,9 +113,11 @@ def test_ortho_products(self, frankfort_bbox, product):
113113
"laz_phase3",
114114
],
115115
)
116-
def test_laz_products(self, frankfort_bbox, product):
117-
"""LiDAR products return tiles for Frankfort area."""
118-
tiles = abovepy.search(bbox=frankfort_bbox, product=product, max_items=3)
116+
def test_laz_products(self, pike_county_bbox, product):
117+
"""LiDAR products return tiles. Uses Pike County because Phase 1 LAZ
118+
does not cover Franklin County (Frankfort); Phase 1 was flown
119+
county-by-county 2010-2017 and Franklin was not in that batch."""
120+
tiles = abovepy.search(bbox=pike_county_bbox, product=product, max_items=3)
119121
assert len(tiles) > 0
120122

121123

@@ -198,7 +200,7 @@ class TestLiveRead:
198200
def test_read_cog_windowed(self, frankfort_bbox):
199201
"""Read a real tile with a windowed bbox."""
200202
tiles = abovepy.search(bbox=frankfort_bbox, product="dem_phase3", max_items=1)
201-
url = tiles.iloc[0]["asset_url"]
203+
url = tiles.tiles.iloc[0]["asset_url"]
202204
data, profile = abovepy.read(url, bbox=frankfort_bbox)
203205
assert data.shape[0] >= 1
204206
assert profile["crs"] is not None
@@ -207,7 +209,7 @@ def test_read_cog_windowed(self, frankfort_bbox):
207209
def test_read_full_tile(self, frankfort_bbox):
208210
"""Read a full tile without bbox clipping."""
209211
tiles = abovepy.search(bbox=frankfort_bbox, product="dem_phase3", max_items=1)
210-
url = tiles.iloc[0]["asset_url"]
212+
url = tiles.tiles.iloc[0]["asset_url"]
211213
data, profile = abovepy.read(url)
212214
assert data.shape[1] > 0
213215
assert data.shape[2] > 0
@@ -216,7 +218,7 @@ def test_read_full_tile(self, frankfort_bbox):
216218
def test_read_returns_epsg3089(self, frankfort_bbox):
217219
"""Read tile CRS should be EPSG:3089."""
218220
tiles = abovepy.search(bbox=frankfort_bbox, product="dem_phase3", max_items=1)
219-
url = tiles.iloc[0]["asset_url"]
221+
url = tiles.tiles.iloc[0]["asset_url"]
220222
_, profile = abovepy.read(url)
221223
crs_str = str(profile["crs"])
222224
assert "3089" in crs_str
@@ -253,6 +255,10 @@ def test_download_skip_existing(self, frankfort_bbox):
253255
@pytest.mark.slow
254256
def test_mosaic_vrt(self, frankfort_bbox):
255257
"""Download 2 tiles, mosaic to VRT, verify it's readable."""
258+
pytest.importorskip(
259+
"osgeo",
260+
reason="VRT construction requires GDAL Python bindings (not a default runtime dep).",
261+
)
256262
tiles = abovepy.search(bbox=frankfort_bbox, product="dem_phase3", max_items=2)
257263
if len(tiles) < 2:
258264
pytest.skip("Need at least 2 tiles for mosaic test")
@@ -308,6 +314,6 @@ def test_laz_tile_url_accessible(self, frankfort_bbox):
308314
tiles = abovepy.search(bbox=frankfort_bbox, product="laz_phase2", max_items=1)
309315
if tiles.empty:
310316
pytest.skip("No COPC tiles found in Frankfort area")
311-
url = tiles.iloc[0]["asset_url"]
317+
url = tiles.tiles.iloc[0]["asset_url"]
312318
resp = httpx.head(url, follow_redirects=True, timeout=30)
313319
assert resp.status_code == 200

0 commit comments

Comments
 (0)