terrakio-core 0.3.7__tar.gz → 0.3.9__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.

Files changed (26) hide show
  1. {terrakio_core-0.3.7 → terrakio_core-0.3.9}/PKG-INFO +2 -1
  2. {terrakio_core-0.3.7 → terrakio_core-0.3.9}/pyproject.toml +2 -1
  3. {terrakio_core-0.3.7 → terrakio_core-0.3.9}/terrakio_core/__init__.py +3 -3
  4. {terrakio_core-0.3.7 → terrakio_core-0.3.9}/terrakio_core/async_client.py +4 -5
  5. {terrakio_core-0.3.7 → terrakio_core-0.3.9}/terrakio_core/client.py +4 -2
  6. {terrakio_core-0.3.7 → terrakio_core-0.3.9}/terrakio_core/convenience_functions/convenience_functions.py +51 -69
  7. {terrakio_core-0.3.7 → terrakio_core-0.3.9}/terrakio_core/sync_client.py +1 -3
  8. {terrakio_core-0.3.7 → terrakio_core-0.3.9}/terrakio_core.egg-info/PKG-INFO +2 -1
  9. {terrakio_core-0.3.7 → terrakio_core-0.3.9}/terrakio_core.egg-info/requires.txt +1 -0
  10. {terrakio_core-0.3.7 → terrakio_core-0.3.9}/README.md +0 -0
  11. {terrakio_core-0.3.7 → terrakio_core-0.3.9}/setup.cfg +0 -0
  12. {terrakio_core-0.3.7 → terrakio_core-0.3.9}/terrakio_core/config.py +0 -0
  13. {terrakio_core-0.3.7 → terrakio_core-0.3.9}/terrakio_core/endpoints/auth.py +0 -0
  14. {terrakio_core-0.3.7 → terrakio_core-0.3.9}/terrakio_core/endpoints/dataset_management.py +0 -0
  15. {terrakio_core-0.3.7 → terrakio_core-0.3.9}/terrakio_core/endpoints/group_management.py +0 -0
  16. {terrakio_core-0.3.7 → terrakio_core-0.3.9}/terrakio_core/endpoints/mass_stats.py +0 -0
  17. {terrakio_core-0.3.7 → terrakio_core-0.3.9}/terrakio_core/endpoints/model_management.py +0 -0
  18. {terrakio_core-0.3.7 → terrakio_core-0.3.9}/terrakio_core/endpoints/space_management.py +0 -0
  19. {terrakio_core-0.3.7 → terrakio_core-0.3.9}/terrakio_core/endpoints/user_management.py +0 -0
  20. {terrakio_core-0.3.7 → terrakio_core-0.3.9}/terrakio_core/exceptions.py +0 -0
  21. {terrakio_core-0.3.7 → terrakio_core-0.3.9}/terrakio_core/helper/bounded_taskgroup.py +0 -0
  22. {terrakio_core-0.3.7 → terrakio_core-0.3.9}/terrakio_core/helper/decorators.py +0 -0
  23. {terrakio_core-0.3.7 → terrakio_core-0.3.9}/terrakio_core/helper/tiles.py +0 -0
  24. {terrakio_core-0.3.7 → terrakio_core-0.3.9}/terrakio_core.egg-info/SOURCES.txt +0 -0
  25. {terrakio_core-0.3.7 → terrakio_core-0.3.9}/terrakio_core.egg-info/dependency_links.txt +0 -0
  26. {terrakio_core-0.3.7 → terrakio_core-0.3.9}/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.7
3
+ Version: 0.3.9
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.3.7"
7
+ version = "0.3.9"
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.3.7"
8
+ __version__ = "0.3.9"
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
- "SyncClient"
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,9 +129,9 @@ 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,
134
+ validated: bool = True,
137
135
  **kwargs
138
136
  ):
139
137
  """
@@ -144,9 +142,9 @@ class AsyncClient(BaseClient):
144
142
  feature (Union[Dict[str, Any], ShapelyGeometry]): The geographic feature
145
143
  in_crs (str): Input coordinate reference system
146
144
  out_crs (str): Output coordinate reference system
147
- output (str): Output format ('csv' or 'netcdf')
148
145
  resolution (int): Resolution parameter
149
146
  geom_fix (bool): Whether to fix the geometry (default False)
147
+ validated (bool): Whether to use validated data (default True)
150
148
  **kwargs: Additional parameters to pass to the WCS request
151
149
 
152
150
  Returns:
@@ -165,10 +163,11 @@ class AsyncClient(BaseClient):
165
163
  "feature": feature,
166
164
  "in_crs": in_crs,
167
165
  "out_crs": out_crs,
168
- "output": output,
166
+ "output": "netcdf",
169
167
  "resolution": resolution,
170
168
  "expr": expr,
171
169
  "buffer": geom_fix,
170
+ "validated": validated,
172
171
  **kwargs
173
172
  }
174
173
  return await self._terrakio_request("POST", "geoquery", json=payload)
@@ -22,10 +22,12 @@ class BaseClient():
22
22
  self.key = api_key
23
23
 
24
24
  config = read_config_file( DEFAULT_CONFIG_FILE, logger = self.logger)
25
+
25
26
  if self.url is None:
26
27
  self.url = config.get('url')
27
-
28
- self.key = config.get('key')
28
+
29
+ if self.key is None:
30
+ self.key = config.get('key')
29
31
 
30
32
  self.token = config.get('token')
31
33
 
@@ -9,6 +9,8 @@ from ..exceptions import APIError, ConfigurationError
9
9
  from ..helper.bounded_taskgroup import BoundedTaskGroup
10
10
  from ..helper.tiles import tiles
11
11
  import uuid
12
+ import xarray as xr
13
+
12
14
 
13
15
  async def zonal_stats(
14
16
  client,
@@ -35,7 +37,8 @@ async def zonal_stats(
35
37
  resolution (int): Resolution parameter
36
38
  geom_fix (bool): Whether to fix the geometry (default False)
37
39
  Returns:
38
- geopandas.GeoDataFrame: GeoDataFrame with added columns for results, or None if inplace=True
40
+ geopandas.GeoDataFrame: GeoDataFrame with variable dataarrays in separate columns.
41
+ Each row represents one geometry with full time-dimensional dataarrays.
39
42
 
40
43
  Raises:
41
44
  ValueError: If concurrency is too high
@@ -51,7 +54,7 @@ async def zonal_stats(
51
54
  completed_count = 0
52
55
  lock = asyncio.Lock()
53
56
 
54
- async def process_geometry(geom, index):
57
+ async def process_geometry(geom):
55
58
  """Process a single geometry"""
56
59
  nonlocal completed_count
57
60
 
@@ -59,19 +62,20 @@ async def zonal_stats(
59
62
  feature = {
60
63
  "type": "Feature",
61
64
  "geometry": mapping(geom),
62
- "properties": {"index": index}
65
+ "properties": {}
63
66
  }
64
- result = await client.geoquery(expr=expr, feature=feature, output="csv",
67
+ # Request xarray dataset
68
+ result = await client.geoquery(expr=expr, feature=feature,
65
69
  in_crs=in_crs, out_crs=out_crs, resolution=resolution, geom_fix=geom_fix)
66
-
67
70
  if isinstance(result, dict) and result.get("error"):
68
- error_msg = f"Request {index} failed: {result.get('error_message', 'Unknown error')}"
71
+ error_msg = f"Request failed: {result.get('error_message', 'Unknown error')}"
69
72
  if result.get('status_code'):
70
- error_msg = f"Request {index} failed with status {result['status_code']}: {result.get('error_message', 'Unknown error')}"
73
+ error_msg = f"Request failed with status {result['status_code']}: {result.get('error_message', 'Unknown error')}"
71
74
  raise APIError(error_msg)
72
75
 
73
- if isinstance(result, pd.DataFrame):
74
- result['_geometry_index'] = index
76
+ # Ensure we got an xarray Dataset
77
+ if not isinstance(result, xr.Dataset):
78
+ raise ValueError(f"Expected xarray Dataset, got {type(result)}")
75
79
 
76
80
  async with lock:
77
81
  completed_count += 1
@@ -88,11 +92,11 @@ async def zonal_stats(
88
92
  try:
89
93
  async with BoundedTaskGroup(max_concurrency=conc) as tg:
90
94
  tasks = [
91
- tg.create_task(process_geometry(gdf.geometry.iloc[idx], idx))
95
+ tg.create_task(process_geometry(gdf.geometry.iloc[idx]))
92
96
  for idx in range(len(gdf))
93
97
  ]
94
98
  all_results = [task.result() for task in tasks]
95
-
99
+
96
100
  except* Exception as eg:
97
101
  for e in eg.exceptions:
98
102
  if hasattr(e, 'response'):
@@ -100,73 +104,51 @@ async def zonal_stats(
100
104
  raise
101
105
 
102
106
  client.logger.info("All requests completed! Processing results...")
107
+
103
108
 
104
109
  if not all_results:
105
110
  raise ValueError("No valid results were returned for any geometry")
106
-
107
- combined_df = pd.concat(all_results, ignore_index=True)
108
111
 
109
- has_time = 'time' in combined_df.columns
112
+
113
+ result_rows = []
114
+ geometries = []
110
115
 
111
- if has_time:
112
- if '_geometry_index' not in combined_df.columns:
113
- raise ValueError("Missing geometry index in results")
114
-
115
- combined_df.set_index(['_geometry_index', 'time'], inplace=True)
116
+ # Results should be in the same order as input geometries
117
+ for i, dataset in enumerate(all_results):
118
+ # Get the original geometry by index
119
+ original_geom = gdf.geometry.iloc[i]
120
+ # Create single row for this geometry
121
+ new_row = {}
122
+
123
+ # Copy original GeoDataFrame attributes
124
+ for col in gdf.columns:
125
+ if col != 'geometry':
126
+ new_row[col] = gdf.iloc[i][col]
127
+ # Store the full dataarray for each variable (with time dimension intact)
128
+ data_vars = list(dataset.data_vars.keys())
129
+ for var_name in data_vars:
130
+ var_data = dataset[var_name]
131
+ new_row[f'{var_name}_dataarray'] = var_data
132
+ result_rows.append(new_row)
133
+ geometries.append(original_geom)
134
+ # Create the result GeoDataFrame with default integer index
135
+ result_gdf = GeoDataFrame(result_rows, geometry=geometries)
136
+ if inplace:
137
+ # Clear original gdf and replace with result_gdf content
138
+ gdf.drop(gdf.index, inplace=True)
139
+ gdf.drop(gdf.columns, axis=1, inplace=True)
116
140
 
117
- result_cols = combined_df.columns
141
+ # Copy all data from result_gdf to gdf
142
+ for col in result_gdf.columns:
143
+ gdf[col] = result_gdf[col].values
118
144
 
119
- result_rows = []
120
- geometries = []
145
+ # Ensure it remains a GeoDataFrame with correct geometry
146
+ gdf.geometry = result_gdf.geometry
121
147
 
122
- for (geom_idx, time_val), row in combined_df.iterrows():
123
- new_row = {}
124
-
125
- for col in gdf.columns:
126
- if col != 'geometry':
127
- new_row[col] = gdf.loc[geom_idx, col]
128
-
129
- for col in result_cols:
130
- new_row[col] = row[col]
131
-
132
- result_rows.append(new_row)
133
- geometries.append(gdf.geometry.iloc[geom_idx])
134
-
135
- multi_index = pd.MultiIndex.from_tuples(
136
- combined_df.index.tolist(),
137
- names=['geometry_index', 'time']
138
- )
139
-
140
- result_gdf = GeoDataFrame(
141
- result_rows,
142
- geometry=geometries,
143
- index=multi_index
144
- )
145
-
146
- if inplace:
147
- return result_gdf
148
- else:
149
- return result_gdf
148
+ return None
150
149
  else:
151
- result_gdf = gdf.copy() if not inplace else gdf
152
-
153
- result_cols = [col for col in combined_df.columns if col not in ['_geometry_index']]
154
-
155
- geom_idx_to_row = {}
156
- for idx, row in combined_df.iterrows():
157
- geom_idx = int(row['_geometry_index'])
158
- geom_idx_to_row[geom_idx] = row
159
-
160
- for col in result_cols:
161
- if col not in result_gdf.columns:
162
- result_gdf[col] = None
163
-
164
- for geom_idx, row in geom_idx_to_row.items():
165
- result_gdf.loc[geom_idx, col] = row[col]
166
- if inplace:
167
- return None
168
- else:
169
- return result_gdf
150
+
151
+ return result_gdf
170
152
 
171
153
  async def create_dataset_file(
172
154
  client,
@@ -166,7 +166,6 @@ class SyncClient:
166
166
  feature: Union[Dict[str, Any], ShapelyGeometry],
167
167
  in_crs: str = "epsg:4326",
168
168
  out_crs: str = "epsg:4326",
169
- output: str = "csv",
170
169
  resolution: int = -1,
171
170
  geom_fix: bool = False,
172
171
  **kwargs
@@ -179,7 +178,6 @@ class SyncClient:
179
178
  feature (Union[Dict[str, Any], ShapelyGeometry]): The geographic feature
180
179
  in_crs (str): Input coordinate reference system
181
180
  out_crs (str): Output coordinate reference system
182
- output (str): Output format ('csv' or 'netcdf')
183
181
  resolution (int): Resolution parameter
184
182
  geom_fix (bool): Whether to fix the geometry (default False)
185
183
  **kwargs: Additional parameters to pass to the WCS request
@@ -195,7 +193,7 @@ class SyncClient:
195
193
  feature=feature,
196
194
  in_crs=in_crs,
197
195
  out_crs=out_crs,
198
- output=output,
196
+ output="netcdf",
199
197
  resolution=resolution,
200
198
  geom_fix=geom_fix,
201
199
  **kwargs
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: terrakio-core
3
- Version: 0.3.7
3
+ Version: 0.3.9
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"
@@ -5,6 +5,7 @@ xarray>=2023.1.0
5
5
  shapely>=2.0.0
6
6
  geopandas>=0.13.0
7
7
  google-cloud-storage>=2.0.0
8
+ scipy>=1.7.0
8
9
  nest_asyncio
9
10
 
10
11
  [ml]
File without changes
File without changes