terrakio-core 0.3.8__tar.gz → 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of terrakio-core might be problematic. Click here for more details.
- {terrakio_core-0.3.8 → terrakio_core-0.4.0}/PKG-INFO +2 -1
- {terrakio_core-0.3.8 → terrakio_core-0.4.0}/pyproject.toml +2 -1
- {terrakio_core-0.3.8 → terrakio_core-0.4.0}/terrakio_core/__init__.py +3 -3
- {terrakio_core-0.3.8 → terrakio_core-0.4.0}/terrakio_core/async_client.py +22 -7
- terrakio_core-0.4.0/terrakio_core/client.py +134 -0
- terrakio_core-0.4.0/terrakio_core/convenience_functions/convenience_functions.py +529 -0
- {terrakio_core-0.3.8 → terrakio_core-0.4.0}/terrakio_core/endpoints/mass_stats.py +71 -16
- {terrakio_core-0.3.8 → terrakio_core-0.4.0}/terrakio_core/endpoints/model_management.py +388 -217
- {terrakio_core-0.3.8 → terrakio_core-0.4.0}/terrakio_core/endpoints/user_management.py +5 -5
- {terrakio_core-0.3.8 → terrakio_core-0.4.0}/terrakio_core/sync_client.py +107 -188
- {terrakio_core-0.3.8 → terrakio_core-0.4.0}/terrakio_core.egg-info/PKG-INFO +2 -1
- {terrakio_core-0.3.8 → terrakio_core-0.4.0}/terrakio_core.egg-info/requires.txt +1 -0
- terrakio_core-0.3.8/terrakio_core/client.py +0 -36
- terrakio_core-0.3.8/terrakio_core/convenience_functions/convenience_functions.py +0 -296
- {terrakio_core-0.3.8 → terrakio_core-0.4.0}/README.md +0 -0
- {terrakio_core-0.3.8 → terrakio_core-0.4.0}/setup.cfg +0 -0
- {terrakio_core-0.3.8 → terrakio_core-0.4.0}/terrakio_core/config.py +0 -0
- {terrakio_core-0.3.8 → terrakio_core-0.4.0}/terrakio_core/endpoints/auth.py +0 -0
- {terrakio_core-0.3.8 → terrakio_core-0.4.0}/terrakio_core/endpoints/dataset_management.py +0 -0
- {terrakio_core-0.3.8 → terrakio_core-0.4.0}/terrakio_core/endpoints/group_management.py +0 -0
- {terrakio_core-0.3.8 → terrakio_core-0.4.0}/terrakio_core/endpoints/space_management.py +0 -0
- {terrakio_core-0.3.8 → terrakio_core-0.4.0}/terrakio_core/exceptions.py +0 -0
- {terrakio_core-0.3.8 → terrakio_core-0.4.0}/terrakio_core/helper/bounded_taskgroup.py +0 -0
- {terrakio_core-0.3.8 → terrakio_core-0.4.0}/terrakio_core/helper/decorators.py +0 -0
- {terrakio_core-0.3.8 → terrakio_core-0.4.0}/terrakio_core/helper/tiles.py +0 -0
- {terrakio_core-0.3.8 → terrakio_core-0.4.0}/terrakio_core.egg-info/SOURCES.txt +0 -0
- {terrakio_core-0.3.8 → terrakio_core-0.4.0}/terrakio_core.egg-info/dependency_links.txt +0 -0
- {terrakio_core-0.3.8 → terrakio_core-0.4.0}/terrakio_core.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: terrakio-core
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Core components for Terrakio API clients
|
|
5
5
|
Author-email: Yupeng Chao <yupeng@haizea.com.au>
|
|
6
6
|
Project-URL: Homepage, https://github.com/HaizeaAnalytics/terrakio-python-api
|
|
@@ -22,6 +22,7 @@ Requires-Dist: xarray>=2023.1.0
|
|
|
22
22
|
Requires-Dist: shapely>=2.0.0
|
|
23
23
|
Requires-Dist: geopandas>=0.13.0
|
|
24
24
|
Requires-Dist: google-cloud-storage>=2.0.0
|
|
25
|
+
Requires-Dist: scipy>=1.7.0
|
|
25
26
|
Requires-Dist: nest_asyncio
|
|
26
27
|
Provides-Extra: ml
|
|
27
28
|
Requires-Dist: torch>=2.7.1; extra == "ml"
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "terrakio-core"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.4.0"
|
|
8
8
|
authors = [
|
|
9
9
|
{name = "Yupeng Chao", email = "yupeng@haizea.com.au"},
|
|
10
10
|
]
|
|
@@ -29,6 +29,7 @@ dependencies = [
|
|
|
29
29
|
"shapely>=2.0.0",
|
|
30
30
|
"geopandas>=0.13.0",
|
|
31
31
|
"google-cloud-storage>=2.0.0",
|
|
32
|
+
"scipy>=1.7.0",
|
|
32
33
|
"nest_asyncio",
|
|
33
34
|
]
|
|
34
35
|
|
|
@@ -5,12 +5,12 @@ Terrakio Core
|
|
|
5
5
|
Core components for Terrakio API clients.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
-
__version__ = "0.
|
|
8
|
+
__version__ = "0.4.0"
|
|
9
9
|
|
|
10
10
|
from .async_client import AsyncClient
|
|
11
|
-
from .sync_client import SyncClient
|
|
11
|
+
from .sync_client import SyncClient as Client
|
|
12
12
|
|
|
13
13
|
__all__ = [
|
|
14
14
|
"AsyncClient",
|
|
15
|
-
"
|
|
15
|
+
"Client"
|
|
16
16
|
]
|
|
@@ -90,7 +90,6 @@ class AsyncClient(BaseClient):
|
|
|
90
90
|
"""Parse response based on content type."""
|
|
91
91
|
content_type = response.headers.get('content-type', '').lower()
|
|
92
92
|
content = await response.read()
|
|
93
|
-
|
|
94
93
|
if 'json' in content_type:
|
|
95
94
|
return json.loads(content.decode('utf-8'))
|
|
96
95
|
elif 'csv' in content_type:
|
|
@@ -117,7 +116,6 @@ class AsyncClient(BaseClient):
|
|
|
117
116
|
except aiohttp.ClientError as e:
|
|
118
117
|
raise APIError(f"Request failed: {e}")
|
|
119
118
|
else:
|
|
120
|
-
# this means that we used the with statement, and we already have a session
|
|
121
119
|
try:
|
|
122
120
|
async with self._session.request(method, url, **kwargs) as response:
|
|
123
121
|
response.raise_for_status()
|
|
@@ -131,7 +129,6 @@ class AsyncClient(BaseClient):
|
|
|
131
129
|
feature: Union[Dict[str, Any], ShapelyGeometry],
|
|
132
130
|
in_crs: str = "epsg:4326",
|
|
133
131
|
out_crs: str = "epsg:4326",
|
|
134
|
-
output: str = "csv",
|
|
135
132
|
resolution: int = -1,
|
|
136
133
|
geom_fix: bool = False,
|
|
137
134
|
validated: bool = True,
|
|
@@ -145,7 +142,6 @@ class AsyncClient(BaseClient):
|
|
|
145
142
|
feature (Union[Dict[str, Any], ShapelyGeometry]): The geographic feature
|
|
146
143
|
in_crs (str): Input coordinate reference system
|
|
147
144
|
out_crs (str): Output coordinate reference system
|
|
148
|
-
output (str): Output format ('csv' or 'netcdf')
|
|
149
145
|
resolution (int): Resolution parameter
|
|
150
146
|
geom_fix (bool): Whether to fix the geometry (default False)
|
|
151
147
|
validated (bool): Whether to use validated data (default True)
|
|
@@ -167,7 +163,7 @@ class AsyncClient(BaseClient):
|
|
|
167
163
|
"feature": feature,
|
|
168
164
|
"in_crs": in_crs,
|
|
169
165
|
"out_crs": out_crs,
|
|
170
|
-
"output":
|
|
166
|
+
"output": "netcdf",
|
|
171
167
|
"resolution": resolution,
|
|
172
168
|
"expr": expr,
|
|
173
169
|
"buffer": geom_fix,
|
|
@@ -186,6 +182,11 @@ class AsyncClient(BaseClient):
|
|
|
186
182
|
out_crs: str = "epsg:4326",
|
|
187
183
|
resolution: int = -1,
|
|
188
184
|
geom_fix: bool = False,
|
|
185
|
+
drop_nan: bool = False,
|
|
186
|
+
spatial_reduction: str = None,
|
|
187
|
+
temporal_reduction: str = None,
|
|
188
|
+
max_memory_mb: int = 500,
|
|
189
|
+
stream_to_disk: bool = False,
|
|
189
190
|
):
|
|
190
191
|
"""
|
|
191
192
|
Compute zonal statistics for all geometries in a GeoDataFrame.
|
|
@@ -199,11 +200,20 @@ class AsyncClient(BaseClient):
|
|
|
199
200
|
out_crs (str): Output coordinate reference system
|
|
200
201
|
resolution (int): Resolution parameter
|
|
201
202
|
geom_fix (bool): Whether to fix the geometry (default False)
|
|
203
|
+
drop_nan (bool): Whether to drop NaN values from the results (default False)
|
|
204
|
+
spatial_reduction (str): Reduction operation for spatial dimensions (x, y).
|
|
205
|
+
Options: 'mean', 'median', 'min', 'max', 'std', 'var', 'sum', 'count'
|
|
206
|
+
temporal_reduction (str): Reduction operation for temporal dimension (time).
|
|
207
|
+
Options: 'mean', 'median', 'min', 'max', 'std', 'var', 'sum', 'count'
|
|
208
|
+
max_memory_mb (int): Maximum memory threshold in MB (default 500MB)
|
|
209
|
+
stream_to_disk (bool): Whether to stream datasets to disk as NetCDF files (default False)
|
|
210
|
+
|
|
202
211
|
Returns:
|
|
203
212
|
geopandas.GeoDataFrame: GeoDataFrame with added columns for results, or None if inplace=True
|
|
213
|
+
If stream_to_disk=True, large datasets are saved as NetCDF files with file paths stored.
|
|
204
214
|
|
|
205
215
|
Raises:
|
|
206
|
-
ValueError: If concurrency is too high
|
|
216
|
+
ValueError: If concurrency is too high or if data exceeds memory limit without streaming
|
|
207
217
|
APIError: If the API request fails
|
|
208
218
|
"""
|
|
209
219
|
return await _zonal_stats(
|
|
@@ -215,7 +225,12 @@ class AsyncClient(BaseClient):
|
|
|
215
225
|
in_crs=in_crs,
|
|
216
226
|
out_crs=out_crs,
|
|
217
227
|
resolution=resolution,
|
|
218
|
-
geom_fix=geom_fix
|
|
228
|
+
geom_fix=geom_fix,
|
|
229
|
+
drop_nan=drop_nan,
|
|
230
|
+
spatial_reduction=spatial_reduction,
|
|
231
|
+
temporal_reduction=temporal_reduction,
|
|
232
|
+
max_memory_mb=max_memory_mb,
|
|
233
|
+
stream_to_disk=stream_to_disk
|
|
219
234
|
)
|
|
220
235
|
|
|
221
236
|
async def create_dataset_file(
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
import logging
|
|
3
|
+
import warnings
|
|
4
|
+
from terrakio_core.config import read_config_file, DEFAULT_CONFIG_FILE
|
|
5
|
+
from abc import abstractmethod
|
|
6
|
+
import xarray as xr
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class BaseClient():
|
|
10
|
+
def __init__(self, url: Optional[str] = None, api_key: Optional[str] = None, verbose: bool = False):
|
|
11
|
+
|
|
12
|
+
self.verbose = verbose
|
|
13
|
+
self.logger = logging.getLogger("terrakio")
|
|
14
|
+
if verbose:
|
|
15
|
+
self.logger.setLevel(logging.INFO)
|
|
16
|
+
else:
|
|
17
|
+
self.logger.setLevel(logging.WARNING)
|
|
18
|
+
|
|
19
|
+
self.timeout = 300
|
|
20
|
+
self.retry = 3
|
|
21
|
+
|
|
22
|
+
self.session = None
|
|
23
|
+
|
|
24
|
+
self.url = url
|
|
25
|
+
self.key = api_key
|
|
26
|
+
|
|
27
|
+
config = read_config_file(DEFAULT_CONFIG_FILE, logger=self.logger)
|
|
28
|
+
|
|
29
|
+
if self.url is None:
|
|
30
|
+
self.url = config.get('url')
|
|
31
|
+
|
|
32
|
+
if self.key is None:
|
|
33
|
+
self.key = config.get('key')
|
|
34
|
+
|
|
35
|
+
self.token = config.get('token')
|
|
36
|
+
|
|
37
|
+
# Apply xarray printing fix to prevent crashes with GeoDataFrames
|
|
38
|
+
self._apply_xarray_fix()
|
|
39
|
+
|
|
40
|
+
def _apply_xarray_fix(self):
|
|
41
|
+
"""
|
|
42
|
+
Apply xarray printing fix to prevent crashes when GeoDataFrames contain xarray objects.
|
|
43
|
+
This fix is applied automatically when the client is initialized.
|
|
44
|
+
"""
|
|
45
|
+
try:
|
|
46
|
+
|
|
47
|
+
# Check if fix is already applied globally
|
|
48
|
+
if hasattr(xr.DataArray, '_terrakio_fix_applied'):
|
|
49
|
+
if self.verbose:
|
|
50
|
+
self.logger.info("xarray printing fix already applied")
|
|
51
|
+
return
|
|
52
|
+
|
|
53
|
+
# Store original methods for potential restoration
|
|
54
|
+
if not hasattr(xr.DataArray, '_original_iter'):
|
|
55
|
+
xr.DataArray._original_iter = xr.DataArray.__iter__
|
|
56
|
+
xr.Dataset._original_iter = xr.Dataset.__iter__
|
|
57
|
+
|
|
58
|
+
# Define safe iteration methods that prevent pandas from iterating
|
|
59
|
+
# but leave __repr__ and __str__ untouched for normal xarray printing
|
|
60
|
+
def safe_dataarray_iter(self):
|
|
61
|
+
# Return infinite iterator that always yields the same safe value
|
|
62
|
+
name = getattr(self, 'name', None) or 'unnamed'
|
|
63
|
+
shape_str = 'x'.join(map(str, self.shape)) if hasattr(self, 'shape') else 'unknown'
|
|
64
|
+
placeholder = f"<DataArray '{name}' {shape_str}>"
|
|
65
|
+
while True:
|
|
66
|
+
yield placeholder
|
|
67
|
+
|
|
68
|
+
def safe_dataset_iter(self):
|
|
69
|
+
# Return infinite iterator that always yields the same safe value
|
|
70
|
+
num_vars = len(self.data_vars) if hasattr(self, 'data_vars') else 0
|
|
71
|
+
num_dims = len(self.dims) if hasattr(self, 'dims') else 0
|
|
72
|
+
placeholder = f"<Dataset: {num_vars} vars, {num_dims} dims>"
|
|
73
|
+
while True:
|
|
74
|
+
yield placeholder
|
|
75
|
+
|
|
76
|
+
# Apply only the iteration fix - leave __repr__ and __str__ untouched
|
|
77
|
+
xr.DataArray.__iter__ = safe_dataarray_iter
|
|
78
|
+
xr.Dataset.__iter__ = safe_dataset_iter
|
|
79
|
+
|
|
80
|
+
# Mark as applied to avoid duplicate applications
|
|
81
|
+
xr.DataArray._terrakio_fix_applied = True
|
|
82
|
+
xr.Dataset._terrakio_fix_applied = True
|
|
83
|
+
|
|
84
|
+
if self.verbose:
|
|
85
|
+
self.logger.info("xarray iteration fix applied - GeoDataFrames with xarray objects will print safely, direct xarray printing unchanged")
|
|
86
|
+
|
|
87
|
+
except ImportError:
|
|
88
|
+
# xarray not installed, skip the fix
|
|
89
|
+
if self.verbose:
|
|
90
|
+
self.logger.info("xarray not installed, skipping printing fix")
|
|
91
|
+
except Exception as e:
|
|
92
|
+
# Log warning but don't fail initialization
|
|
93
|
+
warning_msg = f"Failed to apply xarray printing fix: {e}"
|
|
94
|
+
warnings.warn(warning_msg)
|
|
95
|
+
if self.verbose:
|
|
96
|
+
self.logger.warning(warning_msg)
|
|
97
|
+
|
|
98
|
+
def restore_xarray_printing(self):
|
|
99
|
+
"""
|
|
100
|
+
Restore original xarray printing behavior.
|
|
101
|
+
Call this method if you want to see full xarray representations again.
|
|
102
|
+
"""
|
|
103
|
+
try:
|
|
104
|
+
import xarray as xr
|
|
105
|
+
|
|
106
|
+
if hasattr(xr.DataArray, '_original_iter'):
|
|
107
|
+
xr.DataArray.__iter__ = xr.DataArray._original_iter
|
|
108
|
+
xr.Dataset.__iter__ = xr.Dataset._original_iter
|
|
109
|
+
|
|
110
|
+
# Remove the fix markers
|
|
111
|
+
if hasattr(xr.DataArray, '_terrakio_fix_applied'):
|
|
112
|
+
delattr(xr.DataArray, '_terrakio_fix_applied')
|
|
113
|
+
if hasattr(xr.Dataset, '_terrakio_fix_applied'):
|
|
114
|
+
delattr(xr.Dataset, '_terrakio_fix_applied')
|
|
115
|
+
|
|
116
|
+
if self.verbose:
|
|
117
|
+
self.logger.info("Original xarray iteration behavior restored")
|
|
118
|
+
else:
|
|
119
|
+
if self.verbose:
|
|
120
|
+
self.logger.info("No xarray fix to restore")
|
|
121
|
+
|
|
122
|
+
except ImportError:
|
|
123
|
+
if self.verbose:
|
|
124
|
+
self.logger.info("xarray not available")
|
|
125
|
+
except Exception as e:
|
|
126
|
+
warning_msg = f"Failed to restore xarray printing: {e}"
|
|
127
|
+
warnings.warn(warning_msg)
|
|
128
|
+
if self.verbose:
|
|
129
|
+
self.logger.warning(warning_msg)
|
|
130
|
+
|
|
131
|
+
@abstractmethod
|
|
132
|
+
def _setup_session(self):
|
|
133
|
+
"""Initialize the HTTP session - implemented by sync/async clients"""
|
|
134
|
+
pass
|