Skip to content

Commit c1585d3

Browse files
authored
Merge pull request #171 from opendatacube/switch_to_geodatasets
Update so tests pass with latest versions of upstream libraries.
2 parents d020f21 + 65a16cb commit c1585d3

File tree

5 files changed

+71
-26
lines changed

5 files changed

+71
-26
lines changed

odc/geo/_blocks.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,15 @@
1313
from .types import Chunks2d
1414

1515

16-
def _find_common_type(array_types, scalar_types):
17-
# TODO: don't use find_common_type as it's being removed from numpy
18-
return np.find_common_type(array_types, scalar_types)
16+
def _find_common_type(array_types, scalar_type=None):
17+
if scalar_type is None:
18+
return np.result_type(*array_types)
19+
20+
if np.issubdtype(scalar_type, np.floating):
21+
array_types = array_types + [0.0]
22+
elif np.issubdtype(scalar_type, np.complexfloating):
23+
array_types = array_types + [0j]
24+
return np.result_type(*array_types)
1925

2026

2127
class BlockAssembler:
@@ -33,7 +39,7 @@ def __init__(
3339
self._dtype = (
3440
np.dtype("float32")
3541
if len(blocks) == 0
36-
else _find_common_type([b.dtype for b in blocks.values()], [])
42+
else _find_common_type([b.dtype for b in blocks.values()])
3743
)
3844
self._axis = axis
3945
self._blocks = blocks
@@ -126,7 +132,7 @@ def extract(
126132
dtype = self._dtype
127133
if fill_value is not None:
128134
# possibly upgrade to float based on fill_value
129-
dtype = _find_common_type([dtype], [np.min_scalar_type(fill_value)])
135+
dtype = _find_common_type([dtype], np.min_scalar_type(fill_value))
130136
else:
131137
dtype = np.dtype(dtype)
132138

odc/geo/_xr_interop.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from __future__ import annotations
99

1010
import functools
11+
import json
1112
import math
1213
import warnings
1314
from dataclasses import dataclass
@@ -209,7 +210,8 @@ def _mk_crs_coord(
209210
crs_wkt = cf.get("crs_wkt", None) or crs.wkt
210211

211212
if gcps is not None:
212-
cf["gcps"] = _gcps_to_json(gcps)
213+
# Store as string
214+
cf["gcps"] = json.dumps(_gcps_to_json(gcps))
213215

214216
if transform is not None:
215217
cf["GeoTransform"] = _render_geo_transform(transform, precision=24)
@@ -523,13 +525,15 @@ def _extract_gcps(crs_coord: xarray.DataArray) -> Optional[GCPMapping]:
523525
return None
524526
crs = _extract_crs(crs_coord)
525527
try:
528+
if isinstance(gcps, str):
529+
gcps = json.loads(gcps)
526530
wld = Geometry(gcps, crs=crs)
527531
pix = [
528532
xy_(f["properties"]["col"], f["properties"]["row"])
529533
for f in gcps["features"]
530534
]
531535
return GCPMapping(pix, wld)
532-
except (IndexError, KeyError, ValueError):
536+
except (IndexError, KeyError, ValueError, json.JSONDecodeError):
533537
return None
534538

535539

odc/geo/data/__init__.py

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import json
22
import lzma
3+
import threading
4+
35
from functools import lru_cache
46
from pathlib import Path
57
from typing import Any, Dict, Optional, Tuple
@@ -46,19 +48,54 @@ def gbox_css() -> str:
4648
return src.read()
4749

4850

51+
class _CachedGeoDataFrame:
52+
_instance = None
53+
54+
# Override in sub-classes
55+
_lock = threading.Lock()
56+
_data_url = ""
57+
58+
def __init__(self):
59+
# Thread safe class-cached dataload
60+
if self._instance is None:
61+
with self._lock:
62+
if self._instance is None:
63+
self.__class__._instance = self._load_from_url()
64+
65+
def _load_from_url(self):
66+
# pylint: disable=import-outside-toplevel
67+
import geopandas as gpd
68+
69+
with catch_warnings():
70+
filterwarnings("ignore", category=FutureWarning)
71+
df = gpd.read_file(self._data_url)
72+
return df
73+
74+
75+
class Countries(_CachedGeoDataFrame):
76+
"""
77+
Cache-wrapper around the Natural Earth low-res countries geodataset.
78+
"""
79+
80+
_lock = threading.Lock()
81+
_data_url = (
82+
"https://naciscdn.org/naturalearth/110m/cultural/ne_110m_admin_0_countries.zip"
83+
)
84+
85+
def frame_by_iso3(self, iso3):
86+
df = self._instance
87+
return df[df.ISO_A3 == iso3]
88+
89+
4990
def country_geom(iso3: str, crs: MaybeCRS = None) -> Geometry:
5091
"""
5192
Extract geometry for a country from geopandas sample data.
5293
"""
5394
# pylint: disable=import-outside-toplevel
54-
import geopandas as gpd
55-
5695
from ..converters import from_geopandas
5796

58-
with catch_warnings():
59-
filterwarnings("ignore", category=FutureWarning)
60-
df = gpd.read_file(gpd.datasets.get_path("naturalearth_lowres"))
61-
(gg,) = from_geopandas(df[df.iso_a3 == iso3])
97+
countries = Countries()
98+
(gg,) = from_geopandas(countries.frame_by_iso3(iso3))
6299
crs = norm_crs(crs)
63100
if crs is not None:
64101
gg = gg.to_crs(crs)

tests/test_converters.py

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77

88
rasterio = pytest.importorskip("rasterio")
99
gpd = pytest.importorskip("geopandas")
10-
gpd_datasets = pytest.importorskip("geopandas.datasets")
1110

1211
from odc.geo._interop import have
1312
from odc.geo.converters import extract_gcps, from_geopandas, map_crs, rio_geobox
@@ -16,27 +15,26 @@
1615

1716

1817
@pytest.fixture
19-
def ne_lowres_path():
20-
with catch_warnings():
21-
filterwarnings("ignore")
22-
path = gpd_datasets.get_path("naturalearth_lowres")
23-
yield path
18+
def countries_geodataframe():
19+
from odc.geo.data import Countries
2420

21+
yield Countries()._instance
2522

26-
def test_from_geopandas(ne_lowres_path):
27-
df = gpd.read_file(ne_lowres_path)
23+
24+
def test_from_geopandas(countries_geodataframe):
25+
df = countries_geodataframe
2826
gg = from_geopandas(df)
2927
assert isinstance(gg, list)
3028
assert len(gg) == len(df)
3129
assert gg[0].crs == "epsg:4326"
3230

33-
(au,) = from_geopandas(df[df.iso_a3 == "AUS"].to_crs(epsg=3577))
31+
(au,) = from_geopandas(df[df.ISO_A3 == "AUS"].to_crs(epsg=3577))
3432
assert au.crs.epsg == 3577
3533

36-
(au,) = from_geopandas(df[df.iso_a3 == "AUS"].to_crs(epsg=3857).geometry)
34+
(au,) = from_geopandas(df[df.ISO_A3 == "AUS"].to_crs(epsg=3857).geometry)
3735
assert au.crs.epsg == 3857
3836

39-
assert from_geopandas(df.continent) == []
37+
assert from_geopandas(df.CONTINENT) == []
4038

4139

4240
def test_have():

tests/test_gcp.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,8 @@ def test_gcp_geobox_xr(au_gcp_geobox: GCPGeoBox):
125125
assert _gbox.crs == gbox.crs
126126
assert (_gbox.extent ^ gbox.extent).is_empty
127127

128-
# corrupt some gcps
129-
yy.spatial_ref.attrs["gcps"]["features"][0].pop("properties")
128+
# corrupt gcps
129+
yy.spatial_ref.attrs["gcps"] = "not even geojson"
130130
# should not throw, just return None
131131
assert yy.odc.uncached.geobox is None
132132

0 commit comments

Comments
 (0)