portolan-cli 0.1.2__py3-none-any.whl → 0.2.0__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.
- portolan_cli/__init__.py +5 -0
- portolan_cli/catalog.py +94 -0
- portolan_cli/cli.py +38 -0
- portolan_cli-0.2.0.dist-info/METADATA +133 -0
- portolan_cli-0.2.0.dist-info/RECORD +9 -0
- portolan_cli-0.1.2.dist-info/METADATA +0 -49
- portolan_cli-0.1.2.dist-info/RECORD +0 -8
- {portolan_cli-0.1.2.dist-info → portolan_cli-0.2.0.dist-info}/WHEEL +0 -0
- {portolan_cli-0.1.2.dist-info → portolan_cli-0.2.0.dist-info}/entry_points.txt +0 -0
- {portolan_cli-0.1.2.dist-info → portolan_cli-0.2.0.dist-info}/licenses/LICENSE +0 -0
portolan_cli/__init__.py
CHANGED
portolan_cli/catalog.py
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"""Catalog management for Portolan.
|
|
2
|
+
|
|
3
|
+
The Catalog class is the primary interface for working with Portolan catalogs.
|
|
4
|
+
It wraps all catalog operations as methods, following ADR-0007 (CLI wraps API).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
import sys
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
if sys.version_info >= (3, 11):
|
|
14
|
+
from typing import Self
|
|
15
|
+
else:
|
|
16
|
+
from typing_extensions import Self
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class CatalogExistsError(Exception):
|
|
20
|
+
"""Raised when attempting to initialize a catalog that already exists."""
|
|
21
|
+
|
|
22
|
+
def __init__(self, path: Path) -> None:
|
|
23
|
+
self.path = path
|
|
24
|
+
super().__init__(f"Catalog already exists at {path}")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class Catalog:
|
|
28
|
+
"""A Portolan catalog backed by a .portolan directory.
|
|
29
|
+
|
|
30
|
+
The Catalog class provides the Python API for all catalog operations.
|
|
31
|
+
The CLI commands are thin wrappers around these methods.
|
|
32
|
+
|
|
33
|
+
Attributes:
|
|
34
|
+
root: The root directory containing the .portolan folder.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
PORTOLAN_DIR = ".portolan"
|
|
38
|
+
CATALOG_FILE = "catalog.json"
|
|
39
|
+
STAC_VERSION = "1.0.0"
|
|
40
|
+
|
|
41
|
+
def __init__(self, root: Path) -> None:
|
|
42
|
+
"""Initialize a Catalog instance.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
root: The root directory containing the .portolan folder.
|
|
46
|
+
"""
|
|
47
|
+
self.root = root
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def portolan_path(self) -> Path:
|
|
51
|
+
"""Path to the .portolan directory."""
|
|
52
|
+
return self.root / self.PORTOLAN_DIR
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
def catalog_file(self) -> Path:
|
|
56
|
+
"""Path to the catalog.json file."""
|
|
57
|
+
return self.portolan_path / self.CATALOG_FILE
|
|
58
|
+
|
|
59
|
+
@classmethod
|
|
60
|
+
def init(cls, root: Path) -> Self:
|
|
61
|
+
"""Initialize a new Portolan catalog.
|
|
62
|
+
|
|
63
|
+
Creates the .portolan directory and a minimal STAC catalog.json file.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
root: The directory where the catalog should be created.
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
A Catalog instance for the newly created catalog.
|
|
70
|
+
|
|
71
|
+
Raises:
|
|
72
|
+
CatalogExistsError: If a .portolan directory already exists.
|
|
73
|
+
"""
|
|
74
|
+
portolan_path = root / cls.PORTOLAN_DIR
|
|
75
|
+
|
|
76
|
+
if portolan_path.exists():
|
|
77
|
+
raise CatalogExistsError(portolan_path)
|
|
78
|
+
|
|
79
|
+
# Create the .portolan directory
|
|
80
|
+
portolan_path.mkdir(parents=True)
|
|
81
|
+
|
|
82
|
+
# Create minimal STAC catalog
|
|
83
|
+
catalog_data = {
|
|
84
|
+
"type": "Catalog",
|
|
85
|
+
"stac_version": cls.STAC_VERSION,
|
|
86
|
+
"id": "portolan-catalog",
|
|
87
|
+
"description": "A Portolan-managed STAC catalog",
|
|
88
|
+
"links": [],
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
catalog_file = portolan_path / cls.CATALOG_FILE
|
|
92
|
+
catalog_file.write_text(json.dumps(catalog_data, indent=2))
|
|
93
|
+
|
|
94
|
+
return cls(root)
|
portolan_cli/cli.py
CHANGED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Portolan CLI - Command-line interface for managing cloud-native geospatial data.
|
|
2
|
+
|
|
3
|
+
The CLI is a thin wrapper around the Python API (see catalog.py).
|
|
4
|
+
All business logic lives in the library; the CLI handles user interaction.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
import click
|
|
12
|
+
|
|
13
|
+
from portolan_cli.catalog import Catalog, CatalogExistsError
|
|
14
|
+
from portolan_cli.output import error, success
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@click.group()
|
|
18
|
+
@click.version_option()
|
|
19
|
+
def cli() -> None:
|
|
20
|
+
"""Portolan - Publish and manage cloud-native geospatial data catalogs."""
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@cli.command()
|
|
25
|
+
@click.argument("path", type=click.Path(path_type=Path), default=".")
|
|
26
|
+
def init(path: Path) -> None:
|
|
27
|
+
"""Initialize a new Portolan catalog.
|
|
28
|
+
|
|
29
|
+
Creates a .portolan directory with a STAC catalog.json file.
|
|
30
|
+
|
|
31
|
+
PATH is the directory where the catalog should be created (default: current directory).
|
|
32
|
+
"""
|
|
33
|
+
try:
|
|
34
|
+
Catalog.init(path)
|
|
35
|
+
success(f"Initialized Portolan catalog in {path.resolve()}")
|
|
36
|
+
except CatalogExistsError as err:
|
|
37
|
+
error(f"Catalog already exists at {path.resolve()}")
|
|
38
|
+
raise SystemExit(1) from err
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: portolan-cli
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: A CLI tool for managing cloud-native geospatial data
|
|
5
|
+
Project-URL: Homepage, https://github.com/portolan-sdi/portolan-cli
|
|
6
|
+
Project-URL: Bug Tracker, https://github.com/portolan-sdi/portolan-cli/issues
|
|
7
|
+
Project-URL: Documentation, https://github.com/portolan-sdi/portolan-cli#readme
|
|
8
|
+
Project-URL: Source, https://github.com/portolan-sdi/portolan-cli
|
|
9
|
+
Author-email: Nissim Lebovits <nlebovits@pm.me>
|
|
10
|
+
License-Expression: Apache-2.0
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: cloud-native,geospatial,gis,io
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Intended Audience :: Science/Research
|
|
16
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Scientific/Engineering :: GIS
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Requires-Dist: click>=8.3.1
|
|
25
|
+
Requires-Dist: geoparquet-io>=0.3.0
|
|
26
|
+
Requires-Dist: pyarrow<22.0.0,>=12.0.0
|
|
27
|
+
Requires-Dist: typing-extensions>=4.0.0; python_version < '3.11'
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: bandit>=1.9.3; extra == 'dev'
|
|
30
|
+
Requires-Dist: codespell>=2.4.1; extra == 'dev'
|
|
31
|
+
Requires-Dist: commitizen>=4.13.4; extra == 'dev'
|
|
32
|
+
Requires-Dist: hypothesis>=6.151.5; extra == 'dev'
|
|
33
|
+
Requires-Dist: mutmut>=3.4.0; extra == 'dev'
|
|
34
|
+
Requires-Dist: mypy>=1.19.1; extra == 'dev'
|
|
35
|
+
Requires-Dist: pip-audit>=2.10.0; extra == 'dev'
|
|
36
|
+
Requires-Dist: pre-commit>=4.5.1; extra == 'dev'
|
|
37
|
+
Requires-Dist: pytest-benchmark>=5.0.0; extra == 'dev'
|
|
38
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
|
|
39
|
+
Requires-Dist: pytest-timeout>=2.3.1; extra == 'dev'
|
|
40
|
+
Requires-Dist: pytest>=9.0.2; extra == 'dev'
|
|
41
|
+
Requires-Dist: radon>=6.0.1; extra == 'dev'
|
|
42
|
+
Requires-Dist: ruff>=0.14.11; extra == 'dev'
|
|
43
|
+
Requires-Dist: vulture>=2.14; extra == 'dev'
|
|
44
|
+
Requires-Dist: xenon>=0.9.3; extra == 'dev'
|
|
45
|
+
Provides-Extra: docs
|
|
46
|
+
Requires-Dist: mkdocs-material>=9.7.1; extra == 'docs'
|
|
47
|
+
Requires-Dist: mkdocs>=1.6.1; extra == 'docs'
|
|
48
|
+
Requires-Dist: mkdocstrings[python]>=1.0.0; extra == 'docs'
|
|
49
|
+
Description-Content-Type: text/markdown
|
|
50
|
+
|
|
51
|
+
<div align="center">
|
|
52
|
+
<img src="docs/assets/images/cover.png" alt="Portolan" width="600"/>
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
Portolan enables organizations to share geospatial data in a low-cost, accessible, sovereign, and reliable way. Built on [cloud-native geospatial](https://cloudnativegeo.org) formats, a Portolan catalog is as interactive as any geospatial portal—but faster, more scalable, and much cheaper to run. A small government's vector data costs a few dollars a month; even full imagery and point clouds typically stay under $50/month.
|
|
58
|
+
|
|
59
|
+
This CLI converts data to cloud-native formats (GeoParquet, COG), generates rich STAC metadata, and syncs to any object storage—no servers required.
|
|
60
|
+
|
|
61
|
+
## Why Portolan?
|
|
62
|
+
|
|
63
|
+
| Benefit | How |
|
|
64
|
+
|---------|-----|
|
|
65
|
+
| **Scalable** | Cloud object storage that scales to petabytes |
|
|
66
|
+
| **Open** | 100% open source, open formats (GeoParquet, COG, STAC, Iceberg) |
|
|
67
|
+
| **AI-Ready** | STAC metadata enables semantic search and LLM integration |
|
|
68
|
+
| **Cheap** | Pay only for storage + egress — no servers to run |
|
|
69
|
+
| **Sovereign** | Host anywhere (AWS, GCS, Azure, MinIO, Cloudflare R2) |
|
|
70
|
+
| **Breaks the GIS silo** | Query with DuckDB, Snowflake, BigQuery, Databricks, Pandas — not just GIS tools |
|
|
71
|
+
|
|
72
|
+
## What It Does
|
|
73
|
+
|
|
74
|
+
- **Convert** vector/raster data to cloud-native formats (GeoParquet, COG)
|
|
75
|
+
- **Generate** STAC catalogs with rich metadata, thumbnails, and MapLibre styles
|
|
76
|
+
- **Version** datasets with checksums and history tracking
|
|
77
|
+
- **Sync** to S3, GCS, Azure, or any S3-compatible storage
|
|
78
|
+
|
|
79
|
+
## Quick Example
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
portolan init
|
|
83
|
+
portolan dataset add census.parquet --title "Census 2022" --auto
|
|
84
|
+
portolan remote add prod s3://my-bucket/catalog
|
|
85
|
+
portolan sync
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Installation
|
|
89
|
+
|
|
90
|
+
### Recommended: pipx (for global use)
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
pipx install portolan-cli
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
This installs `portolan` in an isolated environment while making the command globally available.
|
|
97
|
+
|
|
98
|
+
If you don't have pipx installed:
|
|
99
|
+
```bash
|
|
100
|
+
python3 -m pip install --user pipx
|
|
101
|
+
python3 -m pipx ensurepath
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Alternative: pip
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
pip install portolan-cli
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**Note:** This installs into your global or user site-packages and may conflict with other packages.
|
|
111
|
+
|
|
112
|
+
### For Development
|
|
113
|
+
|
|
114
|
+
Use [uv](https://github.com/astral-sh/uv) for local development:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
git clone https://github.com/portolan-sdi/portolan-cli.git
|
|
118
|
+
cd portolan-cli
|
|
119
|
+
uv sync --all-extras
|
|
120
|
+
uv run portolan --help
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
See [Contributing Guide](docs/contributing.md) for full development setup.
|
|
124
|
+
|
|
125
|
+
## Documentation
|
|
126
|
+
|
|
127
|
+
- [Contributing Guide](docs/contributing.md)
|
|
128
|
+
- [Architecture](context/architecture.md)
|
|
129
|
+
- [Roadmap](ROADMAP.md)
|
|
130
|
+
|
|
131
|
+
## License
|
|
132
|
+
|
|
133
|
+
Apache 2.0 — see [LICENSE](LICENSE)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
portolan_cli/__init__.py,sha256=k2RodeI9pun6dtZsitWo_4TCFdDH513Uxp5vrpEYJ3A,132
|
|
2
|
+
portolan_cli/catalog.py,sha256=Z5I6O0sZtAL2oiTof63P-HWc2nh-GleuPF3CR9xdw6Y,2634
|
|
3
|
+
portolan_cli/cli.py,sha256=Oq8HCKDp3vSXB6cPGgU1avoKcJUSAPEToM46nwDY6K4,1130
|
|
4
|
+
portolan_cli/output.py,sha256=3aNXrQIXYSnGrFEBnYGQNZzgN5B6tgt1uC_4tekBxII,3694
|
|
5
|
+
portolan_cli-0.2.0.dist-info/METADATA,sha256=RQwTK3OUxUfSIiEpAkzXz5RP8LMloDcKqzK8P9XVvbE,4873
|
|
6
|
+
portolan_cli-0.2.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
7
|
+
portolan_cli-0.2.0.dist-info/entry_points.txt,sha256=qcxazmDwTESEd44qYETCxUy-mJPcNs3Tyk8PRrl9Shs,46
|
|
8
|
+
portolan_cli-0.2.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
9
|
+
portolan_cli-0.2.0.dist-info/RECORD,,
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: portolan-cli
|
|
3
|
-
Version: 0.1.2
|
|
4
|
-
Summary: A CLI tool for managing cloud-native geospatial data
|
|
5
|
-
Project-URL: Homepage, https://github.com/portolan-sdi/portolan-cli
|
|
6
|
-
Project-URL: Bug Tracker, https://github.com/portolan-sdi/portolan-cli/issues
|
|
7
|
-
Project-URL: Documentation, https://github.com/portolan-sdi/portolan-cli#readme
|
|
8
|
-
Project-URL: Source, https://github.com/portolan-sdi/portolan-cli
|
|
9
|
-
Author-email: Nissim Lebovits <nlebovits@pm.me>
|
|
10
|
-
License-Expression: Apache-2.0
|
|
11
|
-
License-File: LICENSE
|
|
12
|
-
Keywords: cloud-native,geospatial,gis,io
|
|
13
|
-
Classifier: Development Status :: 3 - Alpha
|
|
14
|
-
Classifier: Intended Audience :: Developers
|
|
15
|
-
Classifier: Intended Audience :: Science/Research
|
|
16
|
-
Classifier: License :: OSI Approved :: Apache Software License
|
|
17
|
-
Classifier: Programming Language :: Python :: 3
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
-
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
-
Classifier: Topic :: Scientific/Engineering :: GIS
|
|
23
|
-
Requires-Python: >=3.10
|
|
24
|
-
Requires-Dist: click>=8.3.1
|
|
25
|
-
Provides-Extra: dev
|
|
26
|
-
Requires-Dist: bandit>=1.9.3; extra == 'dev'
|
|
27
|
-
Requires-Dist: codespell>=2.4.1; extra == 'dev'
|
|
28
|
-
Requires-Dist: commitizen>=4.13.4; extra == 'dev'
|
|
29
|
-
Requires-Dist: hypothesis>=6.151.5; extra == 'dev'
|
|
30
|
-
Requires-Dist: mutmut>=3.4.0; extra == 'dev'
|
|
31
|
-
Requires-Dist: mypy>=1.19.1; extra == 'dev'
|
|
32
|
-
Requires-Dist: pip-audit>=2.10.0; extra == 'dev'
|
|
33
|
-
Requires-Dist: pre-commit>=4.5.1; extra == 'dev'
|
|
34
|
-
Requires-Dist: pytest-benchmark>=5.0.0; extra == 'dev'
|
|
35
|
-
Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
|
|
36
|
-
Requires-Dist: pytest-timeout>=2.3.1; extra == 'dev'
|
|
37
|
-
Requires-Dist: pytest>=9.0.2; extra == 'dev'
|
|
38
|
-
Requires-Dist: radon>=6.0.1; extra == 'dev'
|
|
39
|
-
Requires-Dist: ruff>=0.14.11; extra == 'dev'
|
|
40
|
-
Requires-Dist: vulture>=2.14; extra == 'dev'
|
|
41
|
-
Requires-Dist: xenon>=0.9.3; extra == 'dev'
|
|
42
|
-
Provides-Extra: docs
|
|
43
|
-
Requires-Dist: mkdocs-material>=9.7.1; extra == 'docs'
|
|
44
|
-
Requires-Dist: mkdocs>=1.6.1; extra == 'docs'
|
|
45
|
-
Requires-Dist: mkdocstrings[python]>=1.0.0; extra == 'docs'
|
|
46
|
-
Description-Content-Type: text/markdown
|
|
47
|
-
|
|
48
|
-
# portolan-cli
|
|
49
|
-
A CLI tool for managing cloud-native geospatial data.
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
portolan_cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
portolan_cli/cli.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
-
portolan_cli/output.py,sha256=3aNXrQIXYSnGrFEBnYGQNZzgN5B6tgt1uC_4tekBxII,3694
|
|
4
|
-
portolan_cli-0.1.2.dist-info/METADATA,sha256=UWtB-aBi-p6XZYTf6OsI0Iu3fFPwzSMmvckJ-ygDNG4,2181
|
|
5
|
-
portolan_cli-0.1.2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
6
|
-
portolan_cli-0.1.2.dist-info/entry_points.txt,sha256=qcxazmDwTESEd44qYETCxUy-mJPcNs3Tyk8PRrl9Shs,46
|
|
7
|
-
portolan_cli-0.1.2.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
8
|
-
portolan_cli-0.1.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|