edown 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.
- edown-0.1.0/.github/workflows/build.yml +29 -0
- edown-0.1.0/.github/workflows/ci.yml +42 -0
- edown-0.1.0/.github/workflows/docs.yml +45 -0
- edown-0.1.0/.github/workflows/publish.yml +29 -0
- edown-0.1.0/.github/workflows/smoke-live-gee.yml +40 -0
- edown-0.1.0/.gitignore +26 -0
- edown-0.1.0/CHANGELOG.md +8 -0
- edown-0.1.0/CONTRIBUTING.md +22 -0
- edown-0.1.0/LICENSE +21 -0
- edown-0.1.0/PKG-INFO +217 -0
- edown-0.1.0/PLAN.md +114 -0
- edown-0.1.0/README.md +149 -0
- edown-0.1.0/docs/api.md +19 -0
- edown-0.1.0/docs/cli.md +15 -0
- edown-0.1.0/docs/examples/aoi.geojson +23 -0
- edown-0.1.0/docs/getting-started.md +65 -0
- edown-0.1.0/docs/index.md +12 -0
- edown-0.1.0/docs/manifest.md +13 -0
- edown-0.1.0/examples/s2_find_download_stack.py +124 -0
- edown-0.1.0/mkdocs.yml +22 -0
- edown-0.1.0/pyproject.toml +101 -0
- edown-0.1.0/src/edown/__init__.py +33 -0
- edown-0.1.0/src/edown/__main__.py +4 -0
- edown-0.1.0/src/edown/aoi.py +82 -0
- edown-0.1.0/src/edown/auth.py +107 -0
- edown-0.1.0/src/edown/cli.py +325 -0
- edown-0.1.0/src/edown/constants.py +25 -0
- edown-0.1.0/src/edown/discovery.py +246 -0
- edown-0.1.0/src/edown/download.py +431 -0
- edown-0.1.0/src/edown/errors.py +22 -0
- edown-0.1.0/src/edown/grid.py +235 -0
- edown-0.1.0/src/edown/logging_utils.py +13 -0
- edown-0.1.0/src/edown/manifest.py +45 -0
- edown-0.1.0/src/edown/models.py +175 -0
- edown-0.1.0/src/edown/plugins.py +21 -0
- edown-0.1.0/src/edown/progress.py +587 -0
- edown-0.1.0/src/edown/stack.py +289 -0
- edown-0.1.0/src/edown/utils.py +161 -0
- edown-0.1.0/tests/__init__.py +1 -0
- edown-0.1.0/tests/conftest.py +62 -0
- edown-0.1.0/tests/test_aoi.py +13 -0
- edown-0.1.0/tests/test_auth.py +85 -0
- edown-0.1.0/tests/test_cli.py +100 -0
- edown-0.1.0/tests/test_discovery.py +77 -0
- edown-0.1.0/tests/test_download.py +54 -0
- edown-0.1.0/tests/test_download_runtime.py +234 -0
- edown-0.1.0/tests/test_grid.py +32 -0
- edown-0.1.0/tests/test_live_s2.py +128 -0
- edown-0.1.0/tests/test_manifest.py +30 -0
- edown-0.1.0/tests/test_progress.py +283 -0
- edown-0.1.0/tests/test_stack.py +146 -0
- edown-0.1.0/uv.lock +3125 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
name: Build
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: ["main"]
|
|
6
|
+
pull_request:
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
build:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v6
|
|
14
|
+
- uses: actions/setup-python@v6
|
|
15
|
+
with:
|
|
16
|
+
python-version: "3.12"
|
|
17
|
+
- name: Install build tooling
|
|
18
|
+
run: |
|
|
19
|
+
python -m pip install --upgrade pip
|
|
20
|
+
python -m pip install build twine
|
|
21
|
+
- name: Build artifacts
|
|
22
|
+
run: python -m build
|
|
23
|
+
- name: Validate artifacts
|
|
24
|
+
run: twine check dist/*
|
|
25
|
+
- name: Upload dist
|
|
26
|
+
uses: actions/upload-artifact@v4
|
|
27
|
+
with:
|
|
28
|
+
name: python-dist
|
|
29
|
+
path: dist/
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: ["main"]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
quality:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v6
|
|
13
|
+
- uses: actions/setup-python@v6
|
|
14
|
+
with:
|
|
15
|
+
python-version: "3.9"
|
|
16
|
+
- name: Install lint and type-check tooling
|
|
17
|
+
run: |
|
|
18
|
+
python -m pip install --upgrade pip
|
|
19
|
+
python -m pip install -e ".[dev]"
|
|
20
|
+
- name: Ruff
|
|
21
|
+
run: ruff check .
|
|
22
|
+
- name: Mypy
|
|
23
|
+
run: mypy src
|
|
24
|
+
|
|
25
|
+
test:
|
|
26
|
+
needs: quality
|
|
27
|
+
runs-on: ubuntu-latest
|
|
28
|
+
strategy:
|
|
29
|
+
fail-fast: false
|
|
30
|
+
matrix:
|
|
31
|
+
python-version: ["3.9", "3.10", "3.11", "3.12"]
|
|
32
|
+
steps:
|
|
33
|
+
- uses: actions/checkout@v6
|
|
34
|
+
- uses: actions/setup-python@v6
|
|
35
|
+
with:
|
|
36
|
+
python-version: ${{ matrix.python-version }}
|
|
37
|
+
- name: Install package and tooling
|
|
38
|
+
run: |
|
|
39
|
+
python -m pip install --upgrade pip
|
|
40
|
+
python -m pip install -e ".[dev,stack,dask]"
|
|
41
|
+
- name: Pytest
|
|
42
|
+
run: pytest
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
name: Docs
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: ["main"]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
pages: write
|
|
11
|
+
id-token: write
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
build:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v6
|
|
18
|
+
- uses: actions/setup-python@v6
|
|
19
|
+
with:
|
|
20
|
+
python-version: "3.12"
|
|
21
|
+
- name: Install docs dependencies
|
|
22
|
+
run: |
|
|
23
|
+
python -m pip install --upgrade pip
|
|
24
|
+
python -m pip install -e ".[docs]"
|
|
25
|
+
- name: Build docs
|
|
26
|
+
run: mkdocs build --strict
|
|
27
|
+
- name: Setup Pages
|
|
28
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
29
|
+
uses: actions/configure-pages@v6
|
|
30
|
+
- name: Upload Pages artifact
|
|
31
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
32
|
+
uses: actions/upload-pages-artifact@v4
|
|
33
|
+
with:
|
|
34
|
+
path: site/
|
|
35
|
+
|
|
36
|
+
deploy:
|
|
37
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
38
|
+
needs: build
|
|
39
|
+
runs-on: ubuntu-latest
|
|
40
|
+
environment:
|
|
41
|
+
name: github-pages
|
|
42
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
43
|
+
steps:
|
|
44
|
+
- id: deployment
|
|
45
|
+
uses: actions/deploy-pages@v5
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
name: Publish
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
id-token: write
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
publish:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
environment: pypi
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v6
|
|
19
|
+
- uses: actions/setup-python@v6
|
|
20
|
+
with:
|
|
21
|
+
python-version: "3.12"
|
|
22
|
+
- name: Install build tooling
|
|
23
|
+
run: |
|
|
24
|
+
python -m pip install --upgrade pip
|
|
25
|
+
python -m pip install build
|
|
26
|
+
- name: Build artifacts
|
|
27
|
+
run: python -m build
|
|
28
|
+
- name: Publish to PyPI
|
|
29
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
name: Live GEE Smoke Test
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
|
|
6
|
+
jobs:
|
|
7
|
+
smoke:
|
|
8
|
+
runs-on: ubuntu-latest
|
|
9
|
+
steps:
|
|
10
|
+
- uses: actions/checkout@v6
|
|
11
|
+
- uses: actions/setup-python@v6
|
|
12
|
+
with:
|
|
13
|
+
python-version: "3.12"
|
|
14
|
+
- name: Install package
|
|
15
|
+
run: |
|
|
16
|
+
python -m pip install --upgrade pip
|
|
17
|
+
python -m pip install -e ".[dev,stack]"
|
|
18
|
+
- name: Materialize GEE service-account key
|
|
19
|
+
env:
|
|
20
|
+
GEE_SERVICE_ACCOUNT_SECRET: ${{ secrets.GEE_SERVICE_ACCOUNT }}
|
|
21
|
+
GEE_SERVICE_ACCOUNT_KEY_JSON: ${{ secrets.GEE_SERVICE_ACCOUNT_KEY }}
|
|
22
|
+
run: |
|
|
23
|
+
if [ -z "$GEE_SERVICE_ACCOUNT_SECRET" ]; then
|
|
24
|
+
echo "GEE_SERVICE_ACCOUNT secret is not set" >&2
|
|
25
|
+
exit 1
|
|
26
|
+
fi
|
|
27
|
+
if [ -z "$GEE_SERVICE_ACCOUNT_KEY_JSON" ]; then
|
|
28
|
+
echo "GEE_SERVICE_ACCOUNT_KEY secret is not set" >&2
|
|
29
|
+
exit 1
|
|
30
|
+
fi
|
|
31
|
+
key_path="$RUNNER_TEMP/gee-service-account.json"
|
|
32
|
+
printf '%s' "$GEE_SERVICE_ACCOUNT_KEY_JSON" > "$key_path"
|
|
33
|
+
chmod 600 "$key_path"
|
|
34
|
+
echo "GEE_SERVICE_ACCOUNT_KEY=$key_path" >> "$GITHUB_ENV"
|
|
35
|
+
- name: Run live smoke test
|
|
36
|
+
env:
|
|
37
|
+
GEE_SERVICE_ACCOUNT: ${{ secrets.GEE_SERVICE_ACCOUNT }}
|
|
38
|
+
EDOWN_RUN_LIVE_TESTS: "1"
|
|
39
|
+
run: |
|
|
40
|
+
python -m pytest -s tests/test_live_s2.py
|
edown-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
.DS_Store
|
|
2
|
+
.pytest_cache/
|
|
3
|
+
.ruff_cache/
|
|
4
|
+
.mypy_cache/
|
|
5
|
+
.coverage
|
|
6
|
+
coverage.xml
|
|
7
|
+
dist/
|
|
8
|
+
build/
|
|
9
|
+
site/
|
|
10
|
+
htmlcov/
|
|
11
|
+
.venv/
|
|
12
|
+
venv/
|
|
13
|
+
__pycache__/
|
|
14
|
+
*.pyc
|
|
15
|
+
*.pyo
|
|
16
|
+
*.egg-info/
|
|
17
|
+
|
|
18
|
+
# Legacy local reference scripts; intentionally not tracked.
|
|
19
|
+
gee_downloader.py
|
|
20
|
+
access_GEE_generic.py
|
|
21
|
+
|
|
22
|
+
# Runtime outputs
|
|
23
|
+
data/
|
|
24
|
+
manifests/
|
|
25
|
+
images/
|
|
26
|
+
stacks/
|
edown-0.1.0/CHANGELOG.md
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0
|
|
4
|
+
|
|
5
|
+
- Initial package scaffold for `edown`.
|
|
6
|
+
- Native-grid Google Earth Engine GeoTIFF downloader with run manifest generation.
|
|
7
|
+
- Optional Zarr stacking for grid-compatible image groups.
|
|
8
|
+
- CLI, tests, documentation, and GitHub Actions release pipeline.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
## Local Setup
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
python -m pip install -e ".[dev,stack,dask]"
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Checks
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
ruff check .
|
|
13
|
+
mypy src
|
|
14
|
+
pytest
|
|
15
|
+
python -m build
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Notes
|
|
19
|
+
|
|
20
|
+
- Keep the legacy `gee_downloader.py` and `access_GEE_generic.py` scripts ignored; they are local references only.
|
|
21
|
+
- Favor pure functions in the discovery and grid-planning layers so they remain easy to test without live Earth Engine access.
|
|
22
|
+
- Live Earth Engine checks belong in the optional `smoke-live-gee.yml` workflow, not in the default unit test suite.
|
edown-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Marc Yin
|
|
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.
|
edown-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: edown
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Native-grid Google Earth Engine downloader with optional alignment-aware Zarr stacking.
|
|
5
|
+
Project-URL: Homepage, https://github.com/MarcYin/edown
|
|
6
|
+
Project-URL: Documentation, https://marcyin.github.io/edown/
|
|
7
|
+
Project-URL: Issues, https://github.com/MarcYin/edown/issues
|
|
8
|
+
Author: Marc Yin
|
|
9
|
+
License: MIT License
|
|
10
|
+
|
|
11
|
+
Copyright (c) 2026 Marc Yin
|
|
12
|
+
|
|
13
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
14
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
15
|
+
in the Software without restriction, including without limitation the rights
|
|
16
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
17
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
18
|
+
furnished to do so, subject to the following conditions:
|
|
19
|
+
|
|
20
|
+
The above copyright notice and this permission notice shall be included in all
|
|
21
|
+
copies or substantial portions of the Software.
|
|
22
|
+
|
|
23
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
24
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
25
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
26
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
27
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
28
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
29
|
+
SOFTWARE.
|
|
30
|
+
License-File: LICENSE
|
|
31
|
+
Keywords: download,earth-engine,gee,geospatial,raster
|
|
32
|
+
Classifier: Development Status :: 3 - Alpha
|
|
33
|
+
Classifier: Intended Audience :: Science/Research
|
|
34
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
35
|
+
Classifier: Programming Language :: Python :: 3
|
|
36
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
37
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
38
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
39
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
40
|
+
Classifier: Topic :: Scientific/Engineering :: GIS
|
|
41
|
+
Requires-Python: <3.13,>=3.9
|
|
42
|
+
Requires-Dist: click>=8.1
|
|
43
|
+
Requires-Dist: earthengine-api>=0.1.397
|
|
44
|
+
Requires-Dist: numpy>=1.24
|
|
45
|
+
Requires-Dist: pyproj>=3.5
|
|
46
|
+
Requires-Dist: rasterio>=1.3
|
|
47
|
+
Requires-Dist: shapely>=2.0
|
|
48
|
+
Requires-Dist: tenacity>=8.2
|
|
49
|
+
Provides-Extra: dask
|
|
50
|
+
Requires-Dist: dask-jobqueue>=0.8.5; extra == 'dask'
|
|
51
|
+
Requires-Dist: distributed>=2024.1.0; extra == 'dask'
|
|
52
|
+
Requires-Dist: tqdm>=4.66; extra == 'dask'
|
|
53
|
+
Provides-Extra: dev
|
|
54
|
+
Requires-Dist: build>=1.2; extra == 'dev'
|
|
55
|
+
Requires-Dist: mypy>=1.8; extra == 'dev'
|
|
56
|
+
Requires-Dist: pytest-mock>=3.12; extra == 'dev'
|
|
57
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
58
|
+
Requires-Dist: ruff>=0.4.0; extra == 'dev'
|
|
59
|
+
Requires-Dist: twine>=5.0; extra == 'dev'
|
|
60
|
+
Provides-Extra: docs
|
|
61
|
+
Requires-Dist: mkdocs-material>=9.5; extra == 'docs'
|
|
62
|
+
Requires-Dist: mkdocstrings[python]>=0.24; extra == 'docs'
|
|
63
|
+
Provides-Extra: stack
|
|
64
|
+
Requires-Dist: dask[array]>=2024.1.0; extra == 'stack'
|
|
65
|
+
Requires-Dist: xarray>=2024.1.0; extra == 'stack'
|
|
66
|
+
Requires-Dist: zarr>=2.17; extra == 'stack'
|
|
67
|
+
Description-Content-Type: text/markdown
|
|
68
|
+
|
|
69
|
+
# edown
|
|
70
|
+
|
|
71
|
+
`edown` is a Google Earth Engine downloader that discovers images for a location and time range, downloads each image in its native grid as GeoTIFF, and can optionally build Zarr stacks for grid-compatible groups.
|
|
72
|
+
|
|
73
|
+
## What It Does
|
|
74
|
+
|
|
75
|
+
- Searches an ImageCollection by date range and AOI.
|
|
76
|
+
- Preserves each image's native CRS and transform instead of forcing a common projection.
|
|
77
|
+
- Downloads intersecting chunks in parallel across multiple images.
|
|
78
|
+
- Writes a run manifest with discovery, download, and stack metadata.
|
|
79
|
+
- Builds Zarr outputs only when images share an alignment signature.
|
|
80
|
+
|
|
81
|
+
## Installation
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
python -m pip install edown
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
For local development:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
python -m pip install -e ".[dev,stack,dask]"
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Authentication
|
|
94
|
+
|
|
95
|
+
`edown` prefers Earth Engine service-account credentials when both of these environment variables are set:
|
|
96
|
+
|
|
97
|
+
- `GEE_SERVICE_ACCOUNT`
|
|
98
|
+
- `GEE_SERVICE_ACCOUNT_KEY`
|
|
99
|
+
|
|
100
|
+
Otherwise it tries, in order:
|
|
101
|
+
|
|
102
|
+
- persistent Earth Engine user credentials
|
|
103
|
+
- Google application default credentials
|
|
104
|
+
|
|
105
|
+
If both user-auth and ADC refresh tokens are stale, reauthenticate before running downloads.
|
|
106
|
+
|
|
107
|
+
## CLI
|
|
108
|
+
|
|
109
|
+
Search only:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
edown search \
|
|
113
|
+
--collection-id COPERNICUS/S2_SR_HARMONIZED \
|
|
114
|
+
--start-date 2024-06-01 \
|
|
115
|
+
--end-date 2024-06-07 \
|
|
116
|
+
--bbox -0.15 51.48 0.02 51.56 \
|
|
117
|
+
--band B4 \
|
|
118
|
+
--band B8 \
|
|
119
|
+
--manifest-path manifests/search.json
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Download native-grid GeoTIFFs:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
edown download \
|
|
126
|
+
--collection-id COPERNICUS/S2_SR_HARMONIZED \
|
|
127
|
+
--start-date 2024-06-01 \
|
|
128
|
+
--end-date 2024-06-07 \
|
|
129
|
+
--geojson docs/examples/aoi.geojson \
|
|
130
|
+
--band B4 \
|
|
131
|
+
--band B8 \
|
|
132
|
+
--output-root ./data
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Build Zarr stacks from compatible groups:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
edown stack \
|
|
139
|
+
--manifest-path manifests/run.json \
|
|
140
|
+
--output-root ./data
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Run the bundled end-to-end Sentinel-2 example:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
python examples/s2_find_download_stack.py --output-root ./data/live-s2
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Python API
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
from pathlib import Path
|
|
153
|
+
|
|
154
|
+
from edown import AOI, DownloadConfig, download_images
|
|
155
|
+
|
|
156
|
+
config = DownloadConfig(
|
|
157
|
+
collection_id="COPERNICUS/S2_SR_HARMONIZED",
|
|
158
|
+
start_date="2024-06-01",
|
|
159
|
+
end_date="2024-06-07",
|
|
160
|
+
aoi=AOI.from_bbox((-0.15, 51.48, 0.02, 51.56)),
|
|
161
|
+
bands=("B4", "B8"),
|
|
162
|
+
output_root=Path("data"),
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
summary = download_images(config)
|
|
166
|
+
print(summary.manifest_path)
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Live Integration Test
|
|
170
|
+
|
|
171
|
+
The default test suite is mocked and offline. For a real end-to-end Sentinel-2 smoke test, install the stack extras:
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
python -m pip install -e ".[dev,stack]"
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Then run:
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
export EDOWN_RUN_LIVE_TESTS=1
|
|
181
|
+
python -m pytest -s tests/test_live_s2.py
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Default live settings:
|
|
185
|
+
|
|
186
|
+
- collection: `COPERNICUS/S2_SR_HARMONIZED`
|
|
187
|
+
- dates: `2024-06-01` through `2024-06-03`
|
|
188
|
+
- bbox: `-0.1278,51.5072,-0.1270,51.5078`
|
|
189
|
+
- bands: `B4,B8`
|
|
190
|
+
|
|
191
|
+
You can override them with:
|
|
192
|
+
|
|
193
|
+
- `EDOWN_LIVE_COLLECTION_ID`
|
|
194
|
+
- `EDOWN_LIVE_START_DATE`
|
|
195
|
+
- `EDOWN_LIVE_END_DATE`
|
|
196
|
+
- `EDOWN_LIVE_BBOX`
|
|
197
|
+
- `EDOWN_LIVE_BANDS`
|
|
198
|
+
|
|
199
|
+
This requires valid Earth Engine authentication, via either:
|
|
200
|
+
|
|
201
|
+
- `GEE_SERVICE_ACCOUNT` and `GEE_SERVICE_ACCOUNT_KEY` (path to a service-account JSON key file)
|
|
202
|
+
- existing local Earth Engine credentials
|
|
203
|
+
- valid Google application default credentials
|
|
204
|
+
|
|
205
|
+
## Notes
|
|
206
|
+
|
|
207
|
+
- `--chunk-size` is only used as an exact size when `--chunk-size-mode fixed` is set.
|
|
208
|
+
- In the default `auto` mode, `edown` estimates chunk sizes per image from the AOI window and request byte limits.
|
|
209
|
+
|
|
210
|
+
## Development
|
|
211
|
+
|
|
212
|
+
- `python -m pytest`
|
|
213
|
+
- `ruff check .`
|
|
214
|
+
- `mypy src`
|
|
215
|
+
- `python -m build`
|
|
216
|
+
|
|
217
|
+
Docs are built with MkDocs Material and published to GitHub Pages from GitHub Actions.
|
edown-0.1.0/PLAN.md
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# Edown Bootstrap And Release Plan
|
|
2
|
+
|
|
3
|
+
## Summary
|
|
4
|
+
- Bootstrap `/Users/fengyin/Documents/edown` into a git repo, add `origin` as `git@github.com:MarcYin/edown.git`, use `main` as the default branch, and push the initial scaffold to the empty GitHub repository.
|
|
5
|
+
- Build `edown` as a `src`-layout Python package that combines the useful logic from the two current scripts, but does not commit those scripts to the repository.
|
|
6
|
+
- Make GeoTIFF the primary output: one file per discovered image, clipped to the AOI in that image’s native CRS/grid.
|
|
7
|
+
- Add an optional stacking stage that creates Zarr outputs only for grid-compatible images; mixed-CRS or mixed-transform collections are split into separate alignment groups instead of being forced into one CRS.
|
|
8
|
+
- First implementation artifact: add `PLAN.md` at repo root containing this plan, then scaffold `src/edown/`, tests, docs, and `.github/workflows/`.
|
|
9
|
+
|
|
10
|
+
## Repository And Packaging
|
|
11
|
+
- Initialize local git in `/Users/fengyin/Documents/edown`, set remote `origin` to `git@github.com:MarcYin/edown.git`, create branch `main`, and use that as the default branch.
|
|
12
|
+
- Add `.gitignore` before the first commit and explicitly ignore:
|
|
13
|
+
- `gee_downloader.py`
|
|
14
|
+
- `access_GEE_generic.py`
|
|
15
|
+
- Treat those two scripts as local reference material only: read from them during implementation, extract/rewrite the needed logic into package modules, but do not track or publish the original files.
|
|
16
|
+
- Use `src` layout with distribution/import name `edown`.
|
|
17
|
+
- Python support: `>=3.9,<3.13`.
|
|
18
|
+
- Build backend: `hatchling`.
|
|
19
|
+
- Standard project files: `README.md`, `LICENSE` (MIT), `CHANGELOG.md`, `pyproject.toml`, `.gitignore`, docs config, test config, and contributor docs.
|
|
20
|
+
|
|
21
|
+
## Public APIs And CLI
|
|
22
|
+
- Public Python API:
|
|
23
|
+
- `search_images(config: SearchConfig) -> SearchResult`
|
|
24
|
+
- `download_images(config: DownloadConfig) -> DownloadSummary`
|
|
25
|
+
- `stack_images(config: StackConfig) -> list[StackResult]`
|
|
26
|
+
- Public types:
|
|
27
|
+
- `AOI` with `bbox` or `geojson_path`
|
|
28
|
+
- `SearchConfig` for collection, time range, AOI, band selection, rename/scale, and auth/server settings
|
|
29
|
+
- `DownloadConfig` for search config plus output root, worker counts, retry policy, chunk size mode, resume/overwrite, and manifest path
|
|
30
|
+
- `StackConfig` for manifest/input source, backend (`threads`, `dask-local`, `dask-slurm`), and output root
|
|
31
|
+
- `ImageRecord`, `DownloadResult`, `DownloadSummary`, `AlignmentGroup`, and `StackResult`
|
|
32
|
+
- CLI commands:
|
|
33
|
+
- `edown search ...` discovers images and writes a manifest without downloading
|
|
34
|
+
- `edown download ...` performs search plus native-grid GeoTIFF download
|
|
35
|
+
- `edown stack ...` builds one Zarr store per compatible alignment group from a manifest or download directory
|
|
36
|
+
- AOI input:
|
|
37
|
+
- support `--bbox xmin ymin xmax ymax`
|
|
38
|
+
- support `--geojson path`
|
|
39
|
+
- GeoJSON is used for AOI geometry/bounds in discovery and clipping logic; v1 does not do polygon-mask rasterization
|
|
40
|
+
- Authentication:
|
|
41
|
+
- auto mode uses `GEE_SERVICE_ACCOUNT` and `GEE_SERVICE_ACCOUNT_KEY` when both exist
|
|
42
|
+
- otherwise fall back to normal Earth Engine user/application-default auth
|
|
43
|
+
- default endpoint is the high-volume Earth Engine URL, with override support
|
|
44
|
+
- Keep band rename/scale behavior, but replace raw `mask_eval` with a safer plugin hook: Python callable API and CLI `--transform-plugin module:function`
|
|
45
|
+
|
|
46
|
+
## Implementation Changes
|
|
47
|
+
- Split the current script logic into focused modules for auth, config/models, discovery, projection/grid math, chunk planning, downloader, manifest I/O, stacking, CLI, and errors.
|
|
48
|
+
- Reuse the generic script’s discovery features:
|
|
49
|
+
- temporal chunking around the 5000-image limit
|
|
50
|
+
- band selection and include/exclude regex filters
|
|
51
|
+
- rename/scale mapping
|
|
52
|
+
- request-size-based chunk estimation
|
|
53
|
+
- Reuse the downloader script’s native-grid strategy:
|
|
54
|
+
- compute AOI intersection in each image’s own CRS
|
|
55
|
+
- plan chunks only for intersecting windows
|
|
56
|
+
- fetch chunks in parallel across many images
|
|
57
|
+
- Replace the current “submit everything at once” behavior with a bounded global task queue so large runs stay memory-safe.
|
|
58
|
+
- Write GeoTIFFs from the coordinator thread; worker threads only fetch chunk data.
|
|
59
|
+
- Record each image’s native CRS, affine transform, dimensions, selected bands, and alignment signature in the manifest.
|
|
60
|
+
- Define stack compatibility as matching CRS, affine transform, AOI window shape, output band order, and dtype.
|
|
61
|
+
- Build one Zarr store per alignment group; incompatible groups are skipped with explicit manifest/report entries.
|
|
62
|
+
- Keep Dask local and SLURM as optional extras and optional backends, not core requirements.
|
|
63
|
+
- Standardize output layout:
|
|
64
|
+
- `manifests/run-<timestamp>.json`
|
|
65
|
+
- `images/<collection>/<safe-image-id>.tif`
|
|
66
|
+
- `images/<collection>/<safe-image-id>.tif.metadata.json`
|
|
67
|
+
- `stacks/<collection>/<alignment-group>.zarr`
|
|
68
|
+
|
|
69
|
+
## CI, Docs, And Release
|
|
70
|
+
- Dependencies:
|
|
71
|
+
- base: `earthengine-api`, `numpy`, `rasterio`, `shapely`, `pyproj`, `click`, `tenacity`
|
|
72
|
+
- stack extra: `xarray`, `zarr`, `dask[array]`
|
|
73
|
+
- dask extra: `distributed`, `dask_jobqueue`, `tqdm`
|
|
74
|
+
- dev/docs: `pytest`, `pytest-mock`, `ruff`, `mypy`, `mkdocs-material`, `mkdocstrings[python]`
|
|
75
|
+
- GitHub Actions workflows:
|
|
76
|
+
- `ci.yml` for Ruff, mypy, unit tests, and mocked integration tests on Python 3.9, 3.10, 3.11, and 3.12
|
|
77
|
+
- `build.yml` for wheel/sdist build plus `twine check`
|
|
78
|
+
- `docs.yml` for MkDocs build on PRs and deploy to GitHub Pages on `main`
|
|
79
|
+
- `publish.yml` for PyPI release on tags like `v0.1.0`
|
|
80
|
+
- `smoke-live-gee.yml` as an optional `workflow_dispatch` live smoke test using repo secrets
|
|
81
|
+
- Docs stack:
|
|
82
|
+
- MkDocs Material with `mkdocstrings`
|
|
83
|
+
- publish to GitHub Pages at `https://marcyin.github.io/edown/`
|
|
84
|
+
- include install, auth, quickstart, CLI reference, Python API reference, manifest format, and mixed-grid stacking behavior
|
|
85
|
+
- Publishing:
|
|
86
|
+
- configure PyPI Trusted Publishing from GitHub Actions with `id-token: write`
|
|
87
|
+
- publish on version tags from `main`
|
|
88
|
+
|
|
89
|
+
## Test Plan
|
|
90
|
+
- Unit tests:
|
|
91
|
+
- AOI parsing for bbox and GeoJSON
|
|
92
|
+
- collection discovery chunking around the 5000-image threshold
|
|
93
|
+
- band selection, rename, scale, and missing-band handling
|
|
94
|
+
- native-grid AOI intersection and chunk window planning
|
|
95
|
+
- chunk-size estimation against request-size limits
|
|
96
|
+
- alignment-signature grouping for stack compatibility
|
|
97
|
+
- manifest serialization plus resume/overwrite behavior
|
|
98
|
+
- Mocked integration tests:
|
|
99
|
+
- threaded multi-image download writes valid GeoTIFFs and metadata sidecars
|
|
100
|
+
- mixed-grid collections download successfully and split into separate alignment groups
|
|
101
|
+
- stack creation succeeds for compatible groups and skips incompatible ones cleanly
|
|
102
|
+
- retry and partial-failure cases are surfaced correctly in the manifest and CLI exit code
|
|
103
|
+
- transform plugin modifies server-side image preparation without unsafe eval
|
|
104
|
+
- Release validation:
|
|
105
|
+
- wheel and sdist build cleanly
|
|
106
|
+
- docs build without broken references
|
|
107
|
+
- optional live smoke workflow downloads a tiny public AOI and verifies outputs
|
|
108
|
+
|
|
109
|
+
## Assumptions And Defaults
|
|
110
|
+
- `edown` remains the package/distribution name unless PyPI availability changes before first release.
|
|
111
|
+
- GeoTIFF is the main artifact; Zarr is derived and only built for compatible native-grid groups.
|
|
112
|
+
- No automatic reprojection or “single common CRS” behavior is allowed in the default workflow.
|
|
113
|
+
- `click` remains the CLI framework.
|
|
114
|
+
- The two current scripts are intentionally excluded from git via `.gitignore` and are not part of the published package.
|