portolan-cli 0.1.3__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 CHANGED
@@ -0,0 +1,5 @@
1
+ """Portolan CLI - Publish and manage cloud-native geospatial data catalogs."""
2
+
3
+ from portolan_cli.cli import cli
4
+
5
+ __all__ = ["cli"]
@@ -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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: portolan-cli
3
- Version: 0.1.3
3
+ Version: 0.2.0
4
4
  Summary: A CLI tool for managing cloud-native geospatial data
5
5
  Project-URL: Homepage, https://github.com/portolan-sdi/portolan-cli
6
6
  Project-URL: Bug Tracker, https://github.com/portolan-sdi/portolan-cli/issues
@@ -22,6 +22,9 @@ Classifier: Programming Language :: Python :: 3.13
22
22
  Classifier: Topic :: Scientific/Engineering :: GIS
23
23
  Requires-Python: >=3.10
24
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'
25
28
  Provides-Extra: dev
26
29
  Requires-Dist: bandit>=1.9.3; extra == 'dev'
27
30
  Requires-Dist: codespell>=2.4.1; extra == 'dev'
@@ -46,14 +49,14 @@ Requires-Dist: mkdocstrings[python]>=1.0.0; extra == 'docs'
46
49
  Description-Content-Type: text/markdown
47
50
 
48
51
  <div align="center">
49
- <img src="docs/assets/images/logo.svg" alt="Portolan Logo" width="200"/>
50
- <h1>Portolan CLI</h1>
51
- <p><strong>Cloud-native geospatial data catalogs, simplified</strong></p>
52
+ <img src="docs/assets/images/cover.png" alt="Portolan" width="600"/>
52
53
  </div>
53
54
 
54
55
  ---
55
56
 
56
- A CLI for publishing and managing **cloud-native geospatial data catalogs**. Portolan orchestrates format conversion (GeoParquet, COG), versioning, and sync to object storage—no running servers, just static files.
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.
57
60
 
58
61
  ## Why Portolan?
59
62
 
@@ -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,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.3.dist-info/METADATA,sha256=bk5cHzQxh69F_5fn-UrmHqDkwlUjU0-h6SMVsiJo1Ns,4472
5
- portolan_cli-0.1.3.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
6
- portolan_cli-0.1.3.dist-info/entry_points.txt,sha256=qcxazmDwTESEd44qYETCxUy-mJPcNs3Tyk8PRrl9Shs,46
7
- portolan_cli-0.1.3.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
8
- portolan_cli-0.1.3.dist-info/RECORD,,