cubexpress 0.1.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 cubexpress might be problematic. Click here for more details.

@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025, AndesDataCube
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
@@ -0,0 +1,305 @@
1
+ Metadata-Version: 2.1
2
+ Name: cubexpress
3
+ Version: 0.1.0
4
+ Summary: A Python package for efficient processing of cubic earth observation (EO) data
5
+ Home-page: https://github.com/andesdatacube/cubexpress/
6
+ License: MIT
7
+ Author: Julio Contreras
8
+ Author-email: contrerasnetk@gmail.com
9
+ Requires-Python: >=3.9,<4.0
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.9
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Requires-Dist: numpy (>=1.25.2)
17
+ Requires-Dist: pandas (>=2.0.3)
18
+ Requires-Dist: utm (>=0.8.0,<0.9.0)
19
+ Project-URL: Documentation, https://andesdatacube.github.io/cubexpress/
20
+ Project-URL: Repository, https://github.com/andesdatacube/cubexpress/
21
+ Description-Content-Type: text/markdown
22
+
23
+ <h1></h1>
24
+
25
+ <p align="center">
26
+ <img src="./docs/logo_cubexpress.png" width="39%">
27
+ </p>
28
+
29
+ <p align="center">
30
+ <em>A Python package for efficient processing of cubic earth observation (EO) data</em> 🚀
31
+ </p>
32
+
33
+ <p align="center">
34
+ <a href='https://pypi.python.org/pypi/cubexpress'>
35
+ <img src='https://img.shields.io/pypi/v/cubexpress.svg' alt='PyPI' />
36
+ </a>
37
+ <a href="https://opensource.org/licenses/MIT" target="_blank">
38
+ <img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="License">
39
+ </a>
40
+ <a href="https://github.com/psf/black" target="_blank">
41
+ <img src="https://img.shields.io/badge/code%20style-black-000000.svg" alt="Black">
42
+ </a>
43
+ <a href="https://pycqa.github.io/isort/" target="_blank">
44
+ <img src="https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336" alt="isort">
45
+ </a>
46
+ </p>
47
+
48
+ ---
49
+
50
+ **GitHub**: [https://github.com/andesdatacube/cubexpress/](https://github.com/andesdatacube/cubexpress/) 🌐
51
+
52
+ **PyPI**: [https://pypi.org/project/cubexpress/](https://pypi.org/project/cubexpress/) 🛠️
53
+
54
+ ---
55
+
56
+ ## **Overview**
57
+
58
+ **CubeXpress** is a Python package designed to **simplify and accelerate** the process of working with Google Earth Engine (GEE) data cubes. With features like multi-threaded downloads, automatic subdivision of large requests, and direct pixel-level computations on GEE, **CubeXpress** helps you handle massive datasets with ease.
59
+
60
+ ## **Key Features**
61
+ - **Fast Image and Collection Downloads**
62
+ Retrieve single images or entire collections at once, taking advantage of multi-threaded requests.
63
+ - **Automatic Tiling**
64
+ Large images are split ("quadsplit") into smaller sub-tiles, preventing errors with GEE’s size limits.
65
+ - **Direct Pixel Computations**
66
+ Perform computations (e.g., band math) directly on GEE, then fetch results in a single step.
67
+ - **Scalable & Efficient**
68
+ Optimized memory usage and parallelism let you handle complex tasks in big data environments.
69
+
70
+ ## **Installation**
71
+ Install the latest version from PyPI:
72
+
73
+ ```bash
74
+ pip install cubexpress
75
+ ```
76
+
77
+ > **Note**: You need a valid Google Earth Engine account and `earthengine-api` installed (`pip install earthengine-api`). Also run `ee.Initialize()` before using CubeXpress.
78
+
79
+ ---
80
+
81
+ ## **Basic Usage**
82
+
83
+ ### **Download a single `ee.Image`**
84
+
85
+ ```python
86
+ import ee
87
+ import cubexpress
88
+
89
+ # Initialize Earth Engine
90
+ ee.Initialize(project="your-project-id")
91
+
92
+ # Create a raster transform
93
+ geotransform = cubexpress.lonlat2rt(
94
+ lon=-76.5,
95
+ lat=-9.5,
96
+ edge_size=128, # Width=Height=128 pixels
97
+ scale=90 # 90m resolution
98
+ )
99
+
100
+ # Define a single Request
101
+ request = cubexpress.Request(
102
+ id="dem_test",
103
+ raster_transform=geotransform,
104
+ bands=["elevation"],
105
+ image="NASA/NASADEM_HGT/001" # Note: you can wrap with ee.Image("NASA/NASADEM_HGT/001").divide(10000) if needed
106
+
107
+ # Build the RequestSet
108
+ cube_requests = cubexpress.RequestSet(requestset=[request])
109
+
110
+ # Download with multi-threading
111
+ cubexpress.getcube(
112
+ request=cube_requests,
113
+ output_path="output_dem",
114
+ nworkers=4,
115
+ max_deep_level=5
116
+ )
117
+ ```
118
+
119
+ This will create a GeoTIFF named `dem_test.tif` in the `output_dem` folder, containing the elevation band.
120
+
121
+ ---
122
+
123
+
124
+ ### **Download pixel values from an ee.ImageCollection**
125
+
126
+ You can fetch multiple images by constructing a `RequestSet` with several `Request` objects. For example, filter Sentinel-2 images near a point:
127
+
128
+ ```python
129
+ import ee
130
+ import cubexpress
131
+
132
+ ee.Initialize(project="your-project-id")
133
+
134
+ # Filter a Sentinel-2 collection
135
+ point = ee.Geometry.Point([-97.59, 33.37])
136
+ collection = ee.ImageCollection("COPERNICUS/S2_SR_HARMONIZED") \
137
+ .filterBounds(point) \
138
+ .filterDate('2024-01-01', '2024-01-31')
139
+
140
+ # Extract image IDs
141
+ image_ids = collection.aggregate_array('system:id').getInfo()
142
+
143
+ # Set the geotransform
144
+ geotransform = cubexpress.lonlat2rt(
145
+ lon=-97.59,
146
+ lat=33.37,
147
+ edge_size=512,
148
+ scale=10
149
+ )
150
+
151
+ # Build multiple requests
152
+ requests = [
153
+ cubexpress.Request(
154
+ id=f"s2test_{i}",
155
+ raster_transform=geotransform,
156
+ bands=["B4", "B3", "B2"],
157
+ image=image_id # Note: you can wrap with ee.Image(image_id).divide(10000) if needed
158
+ )
159
+ for i, image_id in enumerate(image_ids)
160
+ ]
161
+
162
+ # Create the RequestSet
163
+ cube_requests = cubexpress.RequestSet(requestset=requests)
164
+
165
+ # Download
166
+ cubexpress.getcube(
167
+ request=cube_requests,
168
+ output_path="output_sentinel",
169
+ nworkers=4,
170
+ max_deep_level=5
171
+ )
172
+ ```
173
+
174
+ ---
175
+
176
+ ### **Process and extract a pixel from an ee.Image**
177
+ If you provide an `ee.Image` with custom calculations (e.g., `.divide(10000)`, `.normalizedDifference(...)`), CubeXpress can run those on GEE, then download the result. For large results, it automatically splits the image into sub-tiles.
178
+
179
+ ```python
180
+ import ee
181
+ import cubexpress
182
+
183
+ ee.Initialize(project="your-project-id")
184
+
185
+ # Example: NDVI from Sentinel-2
186
+ image = ee.Image("COPERNICUS/S2_HARMONIZED/20170804T154911_20170804T155116_T18SUJ") \
187
+ .normalizedDifference(["B8", "B4"]) \
188
+ .rename("NDVI")
189
+
190
+ geotransform = cubexpress.lonlat2rt(
191
+ lon=-76.59,
192
+ lat=38.89,
193
+ edge_size=256,
194
+ scale=10
195
+ )
196
+
197
+ request = cubexpress.Request(
198
+ id="ndvi_test",
199
+ raster_transform=geotransform,
200
+ bands=["NDVI"],
201
+ image=image # custom expression
202
+ )
203
+
204
+ cube_requests = cubexpress.RequestSet(requestset=[request])
205
+
206
+ cubexpress.getcube(
207
+ request=cube_requests,
208
+ output_path="output_ndvi",
209
+ nworkers=2,
210
+ max_deep_level=5
211
+ )
212
+ ```
213
+
214
+ ---
215
+
216
+ ## **Advanced Usage**
217
+
218
+ ### **Same Set of Sentinel-2 Images for Multiple Points**
219
+
220
+ Below is a **advanced example** demonstrating how to work with **multiple points** and a **Sentinel-2** image collection in one script. We first create a global collection but then filter it on a point-by-point basis, extracting only the images that intersect each coordinate. Finally, we download them in parallel using **CubeXpress**.
221
+
222
+
223
+ ```python
224
+ import ee
225
+ import cubexpress
226
+
227
+ # Initialize Earth Engine with your project
228
+ ee.Initialize(project="your-project-id")
229
+
230
+ # Define multiple points (longitude, latitude)
231
+ points = [
232
+ (-97.64, 33.37),
233
+ (-97.59, 33.37)
234
+ ]
235
+
236
+ # Start with a broad Sentinel-2 collection
237
+ collection = (
238
+ ee.ImageCollection("COPERNICUS/S2_SR_HARMONIZED")
239
+ .filterDate("2024-01-01", "2024-01-31")
240
+ )
241
+
242
+ # Build a list of Request objects
243
+ requestset = []
244
+ for i, (lon, lat) in enumerate(points):
245
+ # Create a point geometry for the current coordinates
246
+ point_geom = ee.Geometry.Point([lon, lat])
247
+ collection_filtered = collection.filterBounds(point_geom)
248
+
249
+ # Convert the filtered collection into a list of asset IDs
250
+ image_ids = collection_filtered.aggregate_array("system:id").getInfo()
251
+
252
+ # Define a geotransform for this point
253
+ geotransform = cubexpress.lonlat2rt(
254
+ lon=lon,
255
+ lat=lat,
256
+ edge_size=512, # Adjust the image size in pixels
257
+ scale=10 # 10m resolution for Sentinel-2
258
+ )
259
+
260
+ # Create one Request per image found for this point
261
+ requestset.extend([
262
+ cubexpress.Request(
263
+ id=f"s2test_{i}_{idx}",
264
+ raster_transform=geotransform,
265
+ bands=["B4", "B3", "B2"],
266
+ image=image_id
267
+ )
268
+ for idx, image_id in enumerate(image_ids)
269
+ ])
270
+
271
+ # Combine into a RequestSet
272
+ cube_requests = cubexpress.RequestSet(requestset=requestset)
273
+
274
+ # Download everything in parallel
275
+ results = cubexpress.getcube(
276
+ request=cube_requests,
277
+ nworkers=4,
278
+ output_path="images_s2",
279
+ max_deep_level=5
280
+ )
281
+
282
+ print("Downloaded files:", results)
283
+ ```
284
+
285
+
286
+ **How it works**:
287
+
288
+ 1. **Points:** We define multiple coordinates in `points`.
289
+ 2. **Global collection:** We retrieve a broad Sentinel-2 collection covering the desired date range.
290
+ 3. **Per-point filter:** For each point, we call `.filterBounds(...)` to get only images intersecting that location.
291
+ 4. **Geotransform:** We create a local geotransform (`edge_size`, `scale`) defining the spatial extent and resolution around each point.
292
+ 5. **Requests:** Each point-image pair becomes a `Request`, stored in a single list.
293
+ 6. **Parallel download:** With `cubexpress.getcube()`, all requests are fetched simultaneously, automatically splitting large outputs into sub-tiles if needed (up to `max_deep_level`).
294
+
295
+
296
+
297
+ ## **License**
298
+ This project is licensed under the [MIT License](https://opensource.org/licenses/MIT).
299
+
300
+ ---
301
+
302
+ <p align="center">
303
+ Built with 🌎 and ❤️ by the <strong>CubeXpress</strong> team
304
+ </p>
305
+
@@ -0,0 +1,282 @@
1
+ <h1></h1>
2
+
3
+ <p align="center">
4
+ <img src="./docs/logo_cubexpress.png" width="39%">
5
+ </p>
6
+
7
+ <p align="center">
8
+ <em>A Python package for efficient processing of cubic earth observation (EO) data</em> 🚀
9
+ </p>
10
+
11
+ <p align="center">
12
+ <a href='https://pypi.python.org/pypi/cubexpress'>
13
+ <img src='https://img.shields.io/pypi/v/cubexpress.svg' alt='PyPI' />
14
+ </a>
15
+ <a href="https://opensource.org/licenses/MIT" target="_blank">
16
+ <img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="License">
17
+ </a>
18
+ <a href="https://github.com/psf/black" target="_blank">
19
+ <img src="https://img.shields.io/badge/code%20style-black-000000.svg" alt="Black">
20
+ </a>
21
+ <a href="https://pycqa.github.io/isort/" target="_blank">
22
+ <img src="https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336" alt="isort">
23
+ </a>
24
+ </p>
25
+
26
+ ---
27
+
28
+ **GitHub**: [https://github.com/andesdatacube/cubexpress/](https://github.com/andesdatacube/cubexpress/) 🌐
29
+
30
+ **PyPI**: [https://pypi.org/project/cubexpress/](https://pypi.org/project/cubexpress/) 🛠️
31
+
32
+ ---
33
+
34
+ ## **Overview**
35
+
36
+ **CubeXpress** is a Python package designed to **simplify and accelerate** the process of working with Google Earth Engine (GEE) data cubes. With features like multi-threaded downloads, automatic subdivision of large requests, and direct pixel-level computations on GEE, **CubeXpress** helps you handle massive datasets with ease.
37
+
38
+ ## **Key Features**
39
+ - **Fast Image and Collection Downloads**
40
+ Retrieve single images or entire collections at once, taking advantage of multi-threaded requests.
41
+ - **Automatic Tiling**
42
+ Large images are split ("quadsplit") into smaller sub-tiles, preventing errors with GEE’s size limits.
43
+ - **Direct Pixel Computations**
44
+ Perform computations (e.g., band math) directly on GEE, then fetch results in a single step.
45
+ - **Scalable & Efficient**
46
+ Optimized memory usage and parallelism let you handle complex tasks in big data environments.
47
+
48
+ ## **Installation**
49
+ Install the latest version from PyPI:
50
+
51
+ ```bash
52
+ pip install cubexpress
53
+ ```
54
+
55
+ > **Note**: You need a valid Google Earth Engine account and `earthengine-api` installed (`pip install earthengine-api`). Also run `ee.Initialize()` before using CubeXpress.
56
+
57
+ ---
58
+
59
+ ## **Basic Usage**
60
+
61
+ ### **Download a single `ee.Image`**
62
+
63
+ ```python
64
+ import ee
65
+ import cubexpress
66
+
67
+ # Initialize Earth Engine
68
+ ee.Initialize(project="your-project-id")
69
+
70
+ # Create a raster transform
71
+ geotransform = cubexpress.lonlat2rt(
72
+ lon=-76.5,
73
+ lat=-9.5,
74
+ edge_size=128, # Width=Height=128 pixels
75
+ scale=90 # 90m resolution
76
+ )
77
+
78
+ # Define a single Request
79
+ request = cubexpress.Request(
80
+ id="dem_test",
81
+ raster_transform=geotransform,
82
+ bands=["elevation"],
83
+ image="NASA/NASADEM_HGT/001" # Note: you can wrap with ee.Image("NASA/NASADEM_HGT/001").divide(10000) if needed
84
+
85
+ # Build the RequestSet
86
+ cube_requests = cubexpress.RequestSet(requestset=[request])
87
+
88
+ # Download with multi-threading
89
+ cubexpress.getcube(
90
+ request=cube_requests,
91
+ output_path="output_dem",
92
+ nworkers=4,
93
+ max_deep_level=5
94
+ )
95
+ ```
96
+
97
+ This will create a GeoTIFF named `dem_test.tif` in the `output_dem` folder, containing the elevation band.
98
+
99
+ ---
100
+
101
+
102
+ ### **Download pixel values from an ee.ImageCollection**
103
+
104
+ You can fetch multiple images by constructing a `RequestSet` with several `Request` objects. For example, filter Sentinel-2 images near a point:
105
+
106
+ ```python
107
+ import ee
108
+ import cubexpress
109
+
110
+ ee.Initialize(project="your-project-id")
111
+
112
+ # Filter a Sentinel-2 collection
113
+ point = ee.Geometry.Point([-97.59, 33.37])
114
+ collection = ee.ImageCollection("COPERNICUS/S2_SR_HARMONIZED") \
115
+ .filterBounds(point) \
116
+ .filterDate('2024-01-01', '2024-01-31')
117
+
118
+ # Extract image IDs
119
+ image_ids = collection.aggregate_array('system:id').getInfo()
120
+
121
+ # Set the geotransform
122
+ geotransform = cubexpress.lonlat2rt(
123
+ lon=-97.59,
124
+ lat=33.37,
125
+ edge_size=512,
126
+ scale=10
127
+ )
128
+
129
+ # Build multiple requests
130
+ requests = [
131
+ cubexpress.Request(
132
+ id=f"s2test_{i}",
133
+ raster_transform=geotransform,
134
+ bands=["B4", "B3", "B2"],
135
+ image=image_id # Note: you can wrap with ee.Image(image_id).divide(10000) if needed
136
+ )
137
+ for i, image_id in enumerate(image_ids)
138
+ ]
139
+
140
+ # Create the RequestSet
141
+ cube_requests = cubexpress.RequestSet(requestset=requests)
142
+
143
+ # Download
144
+ cubexpress.getcube(
145
+ request=cube_requests,
146
+ output_path="output_sentinel",
147
+ nworkers=4,
148
+ max_deep_level=5
149
+ )
150
+ ```
151
+
152
+ ---
153
+
154
+ ### **Process and extract a pixel from an ee.Image**
155
+ If you provide an `ee.Image` with custom calculations (e.g., `.divide(10000)`, `.normalizedDifference(...)`), CubeXpress can run those on GEE, then download the result. For large results, it automatically splits the image into sub-tiles.
156
+
157
+ ```python
158
+ import ee
159
+ import cubexpress
160
+
161
+ ee.Initialize(project="your-project-id")
162
+
163
+ # Example: NDVI from Sentinel-2
164
+ image = ee.Image("COPERNICUS/S2_HARMONIZED/20170804T154911_20170804T155116_T18SUJ") \
165
+ .normalizedDifference(["B8", "B4"]) \
166
+ .rename("NDVI")
167
+
168
+ geotransform = cubexpress.lonlat2rt(
169
+ lon=-76.59,
170
+ lat=38.89,
171
+ edge_size=256,
172
+ scale=10
173
+ )
174
+
175
+ request = cubexpress.Request(
176
+ id="ndvi_test",
177
+ raster_transform=geotransform,
178
+ bands=["NDVI"],
179
+ image=image # custom expression
180
+ )
181
+
182
+ cube_requests = cubexpress.RequestSet(requestset=[request])
183
+
184
+ cubexpress.getcube(
185
+ request=cube_requests,
186
+ output_path="output_ndvi",
187
+ nworkers=2,
188
+ max_deep_level=5
189
+ )
190
+ ```
191
+
192
+ ---
193
+
194
+ ## **Advanced Usage**
195
+
196
+ ### **Same Set of Sentinel-2 Images for Multiple Points**
197
+
198
+ Below is a **advanced example** demonstrating how to work with **multiple points** and a **Sentinel-2** image collection in one script. We first create a global collection but then filter it on a point-by-point basis, extracting only the images that intersect each coordinate. Finally, we download them in parallel using **CubeXpress**.
199
+
200
+
201
+ ```python
202
+ import ee
203
+ import cubexpress
204
+
205
+ # Initialize Earth Engine with your project
206
+ ee.Initialize(project="your-project-id")
207
+
208
+ # Define multiple points (longitude, latitude)
209
+ points = [
210
+ (-97.64, 33.37),
211
+ (-97.59, 33.37)
212
+ ]
213
+
214
+ # Start with a broad Sentinel-2 collection
215
+ collection = (
216
+ ee.ImageCollection("COPERNICUS/S2_SR_HARMONIZED")
217
+ .filterDate("2024-01-01", "2024-01-31")
218
+ )
219
+
220
+ # Build a list of Request objects
221
+ requestset = []
222
+ for i, (lon, lat) in enumerate(points):
223
+ # Create a point geometry for the current coordinates
224
+ point_geom = ee.Geometry.Point([lon, lat])
225
+ collection_filtered = collection.filterBounds(point_geom)
226
+
227
+ # Convert the filtered collection into a list of asset IDs
228
+ image_ids = collection_filtered.aggregate_array("system:id").getInfo()
229
+
230
+ # Define a geotransform for this point
231
+ geotransform = cubexpress.lonlat2rt(
232
+ lon=lon,
233
+ lat=lat,
234
+ edge_size=512, # Adjust the image size in pixels
235
+ scale=10 # 10m resolution for Sentinel-2
236
+ )
237
+
238
+ # Create one Request per image found for this point
239
+ requestset.extend([
240
+ cubexpress.Request(
241
+ id=f"s2test_{i}_{idx}",
242
+ raster_transform=geotransform,
243
+ bands=["B4", "B3", "B2"],
244
+ image=image_id
245
+ )
246
+ for idx, image_id in enumerate(image_ids)
247
+ ])
248
+
249
+ # Combine into a RequestSet
250
+ cube_requests = cubexpress.RequestSet(requestset=requestset)
251
+
252
+ # Download everything in parallel
253
+ results = cubexpress.getcube(
254
+ request=cube_requests,
255
+ nworkers=4,
256
+ output_path="images_s2",
257
+ max_deep_level=5
258
+ )
259
+
260
+ print("Downloaded files:", results)
261
+ ```
262
+
263
+
264
+ **How it works**:
265
+
266
+ 1. **Points:** We define multiple coordinates in `points`.
267
+ 2. **Global collection:** We retrieve a broad Sentinel-2 collection covering the desired date range.
268
+ 3. **Per-point filter:** For each point, we call `.filterBounds(...)` to get only images intersecting that location.
269
+ 4. **Geotransform:** We create a local geotransform (`edge_size`, `scale`) defining the spatial extent and resolution around each point.
270
+ 5. **Requests:** Each point-image pair becomes a `Request`, stored in a single list.
271
+ 6. **Parallel download:** With `cubexpress.getcube()`, all requests are fetched simultaneously, automatically splitting large outputs into sub-tiles if needed (up to `max_deep_level`).
272
+
273
+
274
+
275
+ ## **License**
276
+ This project is licensed under the [MIT License](https://opensource.org/licenses/MIT).
277
+
278
+ ---
279
+
280
+ <p align="center">
281
+ Built with 🌎 and ❤️ by the <strong>CubeXpress</strong> team
282
+ </p>
@@ -0,0 +1,18 @@
1
+ from cubexpress.conversion import lonlat2rt
2
+ from cubexpress.download import getcube, getGeoTIFF
3
+ from cubexpress.geotyping import RasterTransform, Request, RequestSet
4
+
5
+ # Export the functions
6
+ __all__ = [
7
+ "lonlat2rt",
8
+ "RasterTransform",
9
+ "Request",
10
+ "RequestSet",
11
+ "getcube",
12
+ "getGeoTIFF",
13
+ ]
14
+
15
+ # Dynamic version import
16
+ import importlib.metadata
17
+
18
+ __version__ = importlib.metadata.version("cubexpress")
@@ -0,0 +1,73 @@
1
+ import utm
2
+
3
+ from cubexpress.geotyping import RasterTransform
4
+
5
+ # Define your GeotransformDict type if not already defined
6
+ GeotransformDict = dict[str, float]
7
+
8
+
9
+ def geo2utm(lon: float, lat: float) -> tuple[float, float, str]:
10
+ """
11
+ Converts latitude and longitude coordinates to UTM coordinates and returns the EPSG code.
12
+
13
+ Args:
14
+ lon (float): Longitude.
15
+ lat (float): Latitude.
16
+
17
+ Returns:
18
+ Tuple[float, float, str]: UTM coordinates (x, y) and the EPSG code.
19
+ """
20
+ x, y, zone, _ = utm.from_latlon(lat, lon)
21
+ epsg_code = f"326{zone:02d}" if lat >= 0 else f"327{zone:02d}"
22
+ return x, y, f"EPSG:{epsg_code}"
23
+
24
+
25
+ def lonlat2rt(lon: float, lat: float, edge_size: int, scale: int) -> RasterTransform:
26
+ """
27
+ Generates a ``RasterTransform`` for a given point by converting geographic (lon, lat) coordinates
28
+ to UTM projection and building the necessary geotransform metadata.
29
+
30
+ This function:
31
+ 1. Converts the input (lon, lat) to UTM coordinates using :func:`geo2utm`.
32
+ 2. Defines the extent of the raster in UTM meters based on the specified ``edge_size`` (width/height in pixels)
33
+ and ``scale`` (meters per pixel).
34
+ 3. Sets the Y-scale to be negative (``-scale``) because geospatial images typically consider the origin at
35
+ the top-left corner, resulting in a downward Y axis.
36
+
37
+ Args:
38
+ lon (float): The longitude coordinate.
39
+ lat (float): The latitude coordinate.
40
+ edge_size (int): Width and height of the output raster in pixels.
41
+ scale (int): Spatial resolution in meters per pixel.
42
+
43
+ Returns:
44
+ RasterTransform: A Pydantic model containing:
45
+ - ``crs``: The EPSG code in the form ``"EPSG:XYZ"``,
46
+ - ``geotransform``: A dictionary with the affine transform parameters,
47
+ - ``width`` and ``height``.
48
+
49
+ Example:
50
+ >>> import cubexpress
51
+ >>> rt = cubexpress.lonlat2rt(
52
+ ... lon=-76.0,
53
+ ... lat=40.0,
54
+ ... edge_size=512,
55
+ ... scale=30
56
+ ... )
57
+ >>> print(rt)
58
+ """
59
+ x, y, crs = geo2utm(lon, lat)
60
+ half_extent = (edge_size * scale) / 2
61
+
62
+ geotransform = GeotransformDict(
63
+ scaleX=scale,
64
+ shearX=0,
65
+ translateX=x - half_extent,
66
+ scaleY=-scale, # Y-axis is inverted in geospatial images
67
+ shearY=0,
68
+ translateY=y + half_extent,
69
+ )
70
+
71
+ return RasterTransform(
72
+ crs=crs, geotransform=geotransform, width=edge_size, height=edge_size
73
+ )