terrakio-core 0.4.3__py3-none-any.whl → 0.4.5__py3-none-any.whl

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.

@@ -17,7 +17,7 @@ from .endpoints.group_management import GroupManagement
17
17
  from .endpoints.space_management import SpaceManagement
18
18
  from .endpoints.model_management import ModelManagement
19
19
  from .endpoints.auth import AuthClient
20
- from .convenience_functions.convenience_functions import zonal_stats as _zonal_stats, create_dataset_file as _create_dataset_file
20
+ from .convenience_functions.convenience_functions import zonal_stats as _zonal_stats, create_dataset_file as _create_dataset_file, request_geoquery_list as _request_geoquery_list
21
21
 
22
22
  class AsyncClient(BaseClient):
23
23
  def __init__(self, url: Optional[str] = None, api_key: Optional[str] = None, verbose: bool = False, session: Optional[aiohttp.ClientSession] = None):
@@ -33,7 +33,6 @@ class AsyncClient(BaseClient):
33
33
  self._owns_session = session is None
34
34
 
35
35
  async def _terrakio_request(self, method: str, endpoint: str, **kwargs):
36
- # if self.session is None:
37
36
  if self.session is None:
38
37
  headers = {
39
38
  'Content-Type': 'application/json',
@@ -49,7 +48,6 @@ class AsyncClient(BaseClient):
49
48
  async def _make_request_with_retry(self, session: aiohttp.ClientSession, method: str, endpoint: str, **kwargs) -> Dict[Any, Any]:
50
49
  url = f"{self.url}/{endpoint.lstrip('/')}"
51
50
  last_exception = None
52
-
53
51
  for attempt in range(self.retry + 1):
54
52
  try:
55
53
  async with session.request(method, url, **kwargs) as response:
@@ -103,23 +101,38 @@ class AsyncClient(BaseClient):
103
101
  return xr.open_dataset(BytesIO(content))
104
102
  except:
105
103
  raise APIError(f"Unknown response format: {content_type}", status_code=response.status)
106
-
104
+
107
105
  async def _regular_request(self, method: str, endpoint: str, **kwargs):
108
106
  url = endpoint.lstrip('/')
107
+
109
108
  if self._session is None:
110
-
111
109
  async with aiohttp.ClientSession() as session:
112
110
  try:
113
111
  async with session.request(method, url, **kwargs) as response:
114
112
  response.raise_for_status()
115
- return response
113
+
114
+ content = await response.read()
115
+
116
+ return type('Response', (), {
117
+ 'status': response.status,
118
+ 'content': content,
119
+ 'text': lambda: content.decode('utf-8'),
120
+ 'json': lambda: json.loads(content.decode('utf-8'))
121
+ })()
116
122
  except aiohttp.ClientError as e:
117
123
  raise APIError(f"Request failed: {e}")
118
124
  else:
119
125
  try:
120
126
  async with self._session.request(method, url, **kwargs) as response:
121
127
  response.raise_for_status()
122
- return response
128
+ content = await response.read()
129
+
130
+ return type('Response', (), {
131
+ 'status': response.status,
132
+ 'content': content,
133
+ 'text': lambda: content.decode('utf-8'),
134
+ 'json': lambda: json.loads(content.decode('utf-8'))
135
+ })()
123
136
  except aiohttp.ClientError as e:
124
137
  raise APIError(f"Request failed: {e}")
125
138
 
@@ -170,23 +183,19 @@ class AsyncClient(BaseClient):
170
183
  "validated": validated,
171
184
  **kwargs
172
185
  }
173
- return await self._terrakio_request("POST", "geoquery", json=payload)
186
+ result = await self._terrakio_request("POST", "geoquery", json=payload)
187
+
188
+ return result
174
189
 
175
190
  async def zonal_stats(
176
191
  self,
177
192
  gdf: GeoDataFrame,
178
193
  expr: str,
179
194
  conc: int = 20,
180
- inplace: bool = False,
181
195
  in_crs: str = "epsg:4326",
182
196
  out_crs: str = "epsg:4326",
183
197
  resolution: int = -1,
184
198
  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,
190
199
  ):
191
200
  """
192
201
  Compute zonal statistics for all geometries in a GeoDataFrame.
@@ -195,22 +204,13 @@ class AsyncClient(BaseClient):
195
204
  gdf (GeoDataFrame): GeoDataFrame containing geometries
196
205
  expr (str): Terrakio expression to evaluate, can include spatial aggregations
197
206
  conc (int): Number of concurrent requests to make
198
- inplace (bool): Whether to modify the input GeoDataFrame in place
199
207
  in_crs (str): Input coordinate reference system
200
208
  out_crs (str): Output coordinate reference system
201
209
  resolution (int): Resolution parameter
202
210
  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
211
 
211
212
  Returns:
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.
213
+ geopandas.GeoDataFrame: GeoDataFrame with added columns for results
214
214
 
215
215
  Raises:
216
216
  ValueError: If concurrency is too high or if data exceeds memory limit without streaming
@@ -221,16 +221,10 @@ class AsyncClient(BaseClient):
221
221
  gdf=gdf,
222
222
  expr=expr,
223
223
  conc=conc,
224
- inplace=inplace,
225
224
  in_crs=in_crs,
226
225
  out_crs=out_crs,
227
226
  resolution=resolution,
228
227
  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
234
228
  )
235
229
 
236
230
  async def create_dataset_file(
@@ -288,33 +282,67 @@ class AsyncClient(BaseClient):
288
282
  download_path=download_path,
289
283
  )
290
284
 
285
+ async def geo_queries(
286
+ self,
287
+ queries: list[dict],
288
+ conc: int = 20,
289
+ ):
290
+ """
291
+ Execute multiple geo queries concurrently.
292
+
293
+ Args:
294
+ queries (list[dict]): List of dictionaries containing query parameters.
295
+ Each query must have 'expr', 'feature', and 'in_crs' keys.
296
+ conc (int): Number of concurrent requests to make (default 20, max 100)
297
+
298
+ Returns:
299
+ Union[float, geopandas.GeoDataFrame]:
300
+ - float: Average of all results if results are integers
301
+ - GeoDataFrame: GeoDataFrame with geometry and dataset columns if results are xarray datasets
302
+
303
+ Raises:
304
+ ValueError: If queries list is empty, concurrency is too high, or queries are malformed
305
+ APIError: If the API request fails
306
+
307
+ Example:
308
+ queries = [
309
+ {
310
+ 'expr': 'WCF.wcf',
311
+ 'feature': {'type': 'Feature', 'geometry': {...}, 'properties': {}},
312
+ 'in_crs': 'epsg:4326'
313
+ },
314
+ {
315
+ 'expr': 'NDVI.ndvi',
316
+ 'feature': {'type': 'Feature', 'geometry': {...}, 'properties': {}},
317
+ 'in_crs': 'epsg:4326'
318
+ }
319
+ ]
320
+ result = await client.geo_queries(queries)
321
+ """
322
+ return await _request_geoquery_list(
323
+ client=self,
324
+ quries=queries, # Note: keeping original parameter name for compatibility
325
+ conc=conc,
326
+ )
327
+
291
328
  async def __aenter__(self):
292
- # if there is no session, we create a session
293
329
  if self._session is None:
294
330
  headers = {
295
331
  'Content-Type': 'application/json',
296
332
  'x-api-key': self.key,
297
333
  'Authorization': self.token
298
334
  }
299
- # Remove None values from headers
300
335
  clean_headers = {k: v for k, v in headers.items() if v is not None}
301
- # we are creating the header and clean any value that is none
302
- # now we create the session
303
336
  self._session = aiohttp.ClientSession(
304
337
  headers=clean_headers,
305
338
  timeout=aiohttp.ClientTimeout(total=self.timeout)
306
339
  )
307
340
  return self
308
- # if there is no session, we create a session
309
341
 
310
- # now lets create the aexit function, this function is used when a user uses with, and this function will be automatically called when the with statement is done
311
342
  async def __aexit__(self, exc_type, exc_val, exc_tb):
312
- # so if the session is not being passed in, and we created it by ourselves, we are responsible for closing the session
313
- # if the session is being passed in, we are not responsible for closing the session
314
343
  if self._owns_session and self._session:
315
344
  await self._session.close()
316
345
  self._session = None
317
- # we close the session and set the session value to none
318
346
 
319
347
  async def close(self):
320
348
  if self._owns_session and self._session:
terrakio_core/client.py CHANGED
@@ -8,7 +8,6 @@ import xarray as xr
8
8
 
9
9
  class BaseClient():
10
10
  def __init__(self, url: Optional[str] = None, api_key: Optional[str] = None, verbose: bool = False):
11
-
12
11
  self.verbose = verbose
13
12
  self.logger = logging.getLogger("terrakio")
14
13
  if verbose:
@@ -34,101 +33,101 @@ class BaseClient():
34
33
 
35
34
  self.token = config.get('token')
36
35
 
37
- # Apply xarray printing fix to prevent crashes with GeoDataFrames
38
- self._apply_xarray_fix()
36
+ # # Apply xarray printing fix to prevent crashes with GeoDataFrames
37
+ # self._apply_xarray_fix()
39
38
 
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:
39
+ # def _apply_xarray_fix(self):
40
+ # """
41
+ # Apply xarray printing fix to prevent crashes when GeoDataFrames contain xarray objects.
42
+ # This fix is applied automatically when the client is initialized.
43
+ # """
44
+ # try:
46
45
 
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
46
+ # # Check if fix is already applied globally
47
+ # if hasattr(xr.DataArray, '_terrakio_fix_applied'):
48
+ # if self.verbose:
49
+ # self.logger.info("xarray printing fix already applied")
50
+ # return
52
51
 
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__
52
+ # # Store original methods for potential restoration
53
+ # if not hasattr(xr.DataArray, '_original_iter'):
54
+ # xr.DataArray._original_iter = xr.DataArray.__iter__
55
+ # xr.Dataset._original_iter = xr.Dataset.__iter__
57
56
 
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
57
+ # # Define safe iteration methods that prevent pandas from iterating
58
+ # # but leave __repr__ and __str__ untouched for normal xarray printing
59
+ # def safe_dataarray_iter(self):
60
+ # # Return infinite iterator that always yields the same safe value
61
+ # name = getattr(self, 'name', None) or 'unnamed'
62
+ # shape_str = 'x'.join(map(str, self.shape)) if hasattr(self, 'shape') else 'unknown'
63
+ # placeholder = f"<DataArray '{name}' {shape_str}>"
64
+ # while True:
65
+ # yield placeholder
67
66
 
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
67
+ # def safe_dataset_iter(self):
68
+ # # Return infinite iterator that always yields the same safe value
69
+ # num_vars = len(self.data_vars) if hasattr(self, 'data_vars') else 0
70
+ # num_dims = len(self.dims) if hasattr(self, 'dims') else 0
71
+ # placeholder = f"<Dataset: {num_vars} vars, {num_dims} dims>"
72
+ # while True:
73
+ # yield placeholder
75
74
 
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
75
+ # # Apply only the iteration fix - leave __repr__ and __str__ untouched
76
+ # xr.DataArray.__iter__ = safe_dataarray_iter
77
+ # xr.Dataset.__iter__ = safe_dataset_iter
79
78
 
80
- # Mark as applied to avoid duplicate applications
81
- xr.DataArray._terrakio_fix_applied = True
82
- xr.Dataset._terrakio_fix_applied = True
79
+ # # Mark as applied to avoid duplicate applications
80
+ # xr.DataArray._terrakio_fix_applied = True
81
+ # xr.Dataset._terrakio_fix_applied = True
83
82
 
84
- if self.verbose:
85
- self.logger.info("xarray iteration fix applied - GeoDataFrames with xarray objects will print safely, direct xarray printing unchanged")
83
+ # if self.verbose:
84
+ # self.logger.info("xarray iteration fix applied - GeoDataFrames with xarray objects will print safely, direct xarray printing unchanged")
86
85
 
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)
86
+ # except ImportError:
87
+ # # xarray not installed, skip the fix
88
+ # if self.verbose:
89
+ # self.logger.info("xarray not installed, skipping printing fix")
90
+ # except Exception as e:
91
+ # # Log warning but don't fail initialization
92
+ # warning_msg = f"Failed to apply xarray printing fix: {e}"
93
+ # warnings.warn(warning_msg)
94
+ # if self.verbose:
95
+ # self.logger.warning(warning_msg)
97
96
 
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
97
+ # def restore_xarray_printing(self):
98
+ # """
99
+ # Restore original xarray printing behavior.
100
+ # Call this method if you want to see full xarray representations again.
101
+ # """
102
+ # try:
103
+ # import xarray as xr
105
104
 
106
- if hasattr(xr.DataArray, '_original_iter'):
107
- xr.DataArray.__iter__ = xr.DataArray._original_iter
108
- xr.Dataset.__iter__ = xr.Dataset._original_iter
105
+ # if hasattr(xr.DataArray, '_original_iter'):
106
+ # xr.DataArray.__iter__ = xr.DataArray._original_iter
107
+ # xr.Dataset.__iter__ = xr.Dataset._original_iter
109
108
 
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')
109
+ # # Remove the fix markers
110
+ # if hasattr(xr.DataArray, '_terrakio_fix_applied'):
111
+ # delattr(xr.DataArray, '_terrakio_fix_applied')
112
+ # if hasattr(xr.Dataset, '_terrakio_fix_applied'):
113
+ # delattr(xr.Dataset, '_terrakio_fix_applied')
115
114
 
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")
115
+ # if self.verbose:
116
+ # self.logger.info("Original xarray iteration behavior restored")
117
+ # else:
118
+ # if self.verbose:
119
+ # self.logger.info("No xarray fix to restore")
121
120
 
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)
121
+ # except ImportError:
122
+ # if self.verbose:
123
+ # self.logger.info("xarray not available")
124
+ # except Exception as e:
125
+ # warning_msg = f"Failed to restore xarray printing: {e}"
126
+ # warnings.warn(warning_msg)
127
+ # if self.verbose:
128
+ # self.logger.warning(warning_msg)
130
129
 
131
- @abstractmethod
132
- def _setup_session(self):
133
- """Initialize the HTTP session - implemented by sync/async clients"""
134
- pass
130
+ # @abstractmethod
131
+ # def _setup_session(self):
132
+ # """Initialize the HTTP session - implemented by sync/async clients"""
133
+ # pass