openrndt 1.0.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.
- openrndt-1.0.0/PKG-INFO +315 -0
- openrndt-1.0.0/README.md +287 -0
- openrndt-1.0.0/pyproject.toml +61 -0
- openrndt-1.0.0/src/openrndt/__init__.py +8 -0
- openrndt-1.0.0/src/openrndt/cli.py +234 -0
- openrndt-1.0.0/src/openrndt/client.py +43 -0
- openrndt-1.0.0/src/openrndt/codelists.py +147 -0
- openrndt-1.0.0/src/openrndt/config.py +32 -0
- openrndt-1.0.0/src/openrndt/item.py +49 -0
- openrndt-1.0.0/src/openrndt/output.py +80 -0
- openrndt-1.0.0/src/openrndt/py.typed +0 -0
- openrndt-1.0.0/src/openrndt/search.py +158 -0
openrndt-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: openrndt
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: CLI Python per il Repertorio Nazionale dei Dati Territoriali (RNDT) — pensata per essere orchestrata da un'AI
|
|
5
|
+
Keywords: rndt,geodati,inspire,metadata,iso19115,iso19139,open-data,italy
|
|
6
|
+
Author: Andrea Borruso
|
|
7
|
+
Author-email: Andrea Borruso <aborruso@gmail.com>
|
|
8
|
+
License: MIT
|
|
9
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
10
|
+
Classifier: Intended Audience :: Science/Research
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering :: GIS
|
|
17
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
18
|
+
Requires-Dist: httpx>=0.28.1
|
|
19
|
+
Requires-Dist: typer>=0.24.1
|
|
20
|
+
Requires-Dist: rich>=14.3.3
|
|
21
|
+
Requires-Dist: tenacity>=9.1.4
|
|
22
|
+
Requires-Python: >=3.12
|
|
23
|
+
Project-URL: Issues, https://github.com/ondata/openrndt/issues
|
|
24
|
+
Project-URL: RNDT Portal, https://geodati.gov.it/geoportale/
|
|
25
|
+
Project-URL: RNDT REST API, https://geodati.gov.it/geoportale/eng/strumenti-en/rest-api
|
|
26
|
+
Project-URL: Repository, https://github.com/ondata/openrndt
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# openrndt
|
|
30
|
+
|
|
31
|
+
CLI Python e libreria per accedere al **Repertorio Nazionale dei Dati Territoriali (RNDT)** —
|
|
32
|
+
pensata per essere orchestrata da un'AI.
|
|
33
|
+
|
|
34
|
+
> Stato: v1.0 — read-only.
|
|
35
|
+
|
|
36
|
+
## Cos'è il RNDT
|
|
37
|
+
|
|
38
|
+
Il [Repertorio Nazionale dei Dati Territoriali](https://geodati.gov.it/geoportale/) è
|
|
39
|
+
il catalogo ufficiale italiano dei metadati geografici (ISO 19115/19139). Espone REST
|
|
40
|
+
API per cercare e scaricare i metadati.
|
|
41
|
+
|
|
42
|
+
## Installazione
|
|
43
|
+
|
|
44
|
+
### Da PyPI
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
uv tool install openrndt
|
|
48
|
+
# oppure, senza installazione persistente:
|
|
49
|
+
uvx openrndt --help
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
> Non ancora pubblicato su PyPI (roadmap verso la v1.0). Nel frattempo installa da locale.
|
|
53
|
+
|
|
54
|
+
### Da locale
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
git clone https://github.com/ondata/openrndt.git
|
|
58
|
+
cd openrndt
|
|
59
|
+
|
|
60
|
+
# CLI globale: venv isolato, eseguibile in PATH
|
|
61
|
+
uv tool install .
|
|
62
|
+
|
|
63
|
+
# Aggiornamento dopo modifiche al codice
|
|
64
|
+
uv tool install --reinstall .
|
|
65
|
+
|
|
66
|
+
# Disinstallazione
|
|
67
|
+
uv tool uninstall openrndt
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Per sviluppo (modifiche con ricarica immediata)
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
git clone https://github.com/ondata/openrndt.git
|
|
74
|
+
cd openrndt
|
|
75
|
+
uv sync
|
|
76
|
+
uv run openrndt --help
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Uso
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
# Ricerca testuale
|
|
83
|
+
openrndt search --q "catasto" --num 5
|
|
84
|
+
|
|
85
|
+
# Filtro per bounding box (Piemonte sud)
|
|
86
|
+
openrndt search --q "cartografia" --bbox 7,44,8,45 --num 10
|
|
87
|
+
|
|
88
|
+
# Per categoria tematica ISO 19115
|
|
89
|
+
openrndt search --data-category planningCadastre --num 5
|
|
90
|
+
|
|
91
|
+
# Singolo metadato
|
|
92
|
+
openrndt get age:D_E973_MARSAGLIA
|
|
93
|
+
|
|
94
|
+
# XML ISO 19139 grezzo
|
|
95
|
+
openrndt get age:D_E973_MARSAGLIA --xml > meta.xml
|
|
96
|
+
|
|
97
|
+
# Codelist disponibili (no rete)
|
|
98
|
+
openrndt discover
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Il timeout HTTP per singolo tentativo è configurabile con `--timeout` (default 30s);
|
|
102
|
+
con i retry su timeout/5xx (3 tentativi) il caso peggiore è ~3x questo valore:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
openrndt --timeout 5 search --q "catasto" --num 5
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Tutti i comandi accettano `--format json` (default), `--format table`, `--format csv`.
|
|
109
|
+
Per `search` c'è anche `--format compact`: una riga NDJSON per record con i soli
|
|
110
|
+
campi ad alto segnale (`id`, `title`, `org`, `type`, `category`, `updated`,
|
|
111
|
+
`resources`), pensata per agenti AI e pipe a basso consumo di token.
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
openrndt --format compact search --q "catasto" --num 3
|
|
115
|
+
# {"id":"…","title":"…","org":"Comune di Torino","type":"service","category":null,"updated":"…","resources":["WMS"]}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Esempi di conversazione con un'AI
|
|
119
|
+
|
|
120
|
+
openrndt è pensato per essere **pilotato da un assistente AI**. Non devi imparare la
|
|
121
|
+
sintassi: descrivi a parole quello che ti serve e l'AI compone i comandi, legge i
|
|
122
|
+
risultati e ti restituisce ciò che conta — di solito **un URL WMS/WFS da incollare in
|
|
123
|
+
QGIS** o un dato da scaricare. Tutti gli esempi qui sotto usano risposte reali del
|
|
124
|
+
catalogo RNDT.
|
|
125
|
+
|
|
126
|
+
> Per provarli serve un assistente (es. Claude) a cui è stata installata la skill
|
|
127
|
+
> `skills/rndt-explorer/` o che può eseguire `openrndt` da terminale.
|
|
128
|
+
|
|
129
|
+
### «Mi serve l'uso del suolo dell'Emilia-Romagna da caricare in QGIS»
|
|
130
|
+
|
|
131
|
+
> **Tu:** Sto lavorando su un progetto QGIS in Emilia-Romagna. Mi trovi un servizio
|
|
132
|
+
> con l'uso del suolo regionale, così lo aggiungo come layer?
|
|
133
|
+
|
|
134
|
+
L'AI cerca i servizi pubblicati sul tema:
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
openrndt search --q "uso del suolo WMS" --num 20
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
> **AI:** Trovato. La **Regione Emilia-Romagna** pubblica un WMS *Uso del Suolo* che
|
|
141
|
+
> copre più annate (2020, 2017, 2014, 2008, 2003, 1994, 1976) più l'uso storico dal
|
|
142
|
+
> 1853. In QGIS aggiungi un nuovo layer **WMS/WMTS** con questo URL:
|
|
143
|
+
>
|
|
144
|
+
> ```
|
|
145
|
+
> http://servizigis.regione.emilia-romagna.it/wms/uso_del_suolo?request=GetCapabilities&service=WMS
|
|
146
|
+
> ```
|
|
147
|
+
>
|
|
148
|
+
> Scegli poi l'annata che ti interessa fra i layer offerti dal servizio.
|
|
149
|
+
|
|
150
|
+
### «Voglio il catasto di un comune piemontese»
|
|
151
|
+
|
|
152
|
+
> **Tu:** Mi serve il catasto urbano per un'analisi in Piemonte.
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
openrndt search --q "catasto" --num 10
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
> **AI:** C'è il **Catasto Urbano 1:1500** come geo-servizio WMS della Regione
|
|
159
|
+
> Piemonte. URL per QGIS:
|
|
160
|
+
>
|
|
161
|
+
> ```
|
|
162
|
+
> https://geomap.reteunitaria.piemonte.it/ws/siccms/coto-01/wmsg01/wms_sicc17bis_catasto_urbano?service=WMS&version=1.1.1&request=getCapabilities
|
|
163
|
+
> ```
|
|
164
|
+
|
|
165
|
+
### «Cerco ortofoto da usare come base»
|
|
166
|
+
|
|
167
|
+
> **Tu:** Mi servono delle ortofoto come sfondo per la mia mappa. Cosa c'è disponibile?
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
openrndt search --q "ortofoto" --num 50
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
> **AI:** Diverse amministrazioni espongono ortofoto via WMS, pronte da usare come
|
|
174
|
+
> base in QGIS. Per esempio:
|
|
175
|
+
>
|
|
176
|
+
> - **Regione Sardegna** — `https://webgis.regione.sardegna.it/geoserverraster/ows?service=wms&version=1.3.0&request=GetCapabilities`
|
|
177
|
+
> - **Provincia di Lodi** — `http://sdi.provincia.lodi.it/arcgis/services/DBTL/dbtl_ortofoto_digitali/MapServer/WMSServer?request=GetCapabilities&service=WMS`
|
|
178
|
+
> - **Regione Piemonte** — `https://geomap.reteunitaria.piemonte.it/mapproxy/service?service=WMS&version=1.3.0&request=getCapabilities`
|
|
179
|
+
|
|
180
|
+
### «Il reticolo idrografico, ma vettoriale (per editing)»
|
|
181
|
+
|
|
182
|
+
> **Tu:** Non voglio un'immagine: mi serve il reticolo idrografico come vettoriale,
|
|
183
|
+
> così posso interrogarlo e modificarlo.
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
openrndt search --q "reticolo idrografico" --num 50
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
> **AI:** Per i vettoriali ti serve un **WFS** (in QGIS: *Aggiungi layer WFS*). Alcuni
|
|
190
|
+
> servizi disponibili:
|
|
191
|
+
>
|
|
192
|
+
> - **ISPRA** — `http://sdi.isprambiente.it/geoserver/hy/wfs?service=wfs&version=2.0.0&request=GetCapabilities`
|
|
193
|
+
> - **ARPA Veneto** — `http://gaia.arpa.veneto.it/geoserver/ows?service=WFS&version=1.0.0&request=GetCapabilities`
|
|
194
|
+
> - **Regione Basilicata** — `http://rsdi.regione.basilicata.it/rbgeoserver2016/dbgt_0404/nd_idr/wfs?request=GetCapabilities&service=WFS&version=2.0.0`
|
|
195
|
+
|
|
196
|
+
### «Tutto quello che pubblica un certo ente»
|
|
197
|
+
|
|
198
|
+
> **Tu:** Quanti e quali dataset ha pubblicato la Regione Lombardia?
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
openrndt search --q 'apiso_OrganizationName_txt:"Regione Lombardia"' \
|
|
202
|
+
--sort "apiso_Modified_dt:desc" --num 10
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
> **AI:** La **Regione Lombardia** ha **430 dataset** nel RNDT. Ecco i 10 aggiornati
|
|
206
|
+
> più di recente… (l'AI elenca titoli e date). Posso filtrarli per tema — uso del
|
|
207
|
+
> suolo, idrografia, trasporti — o mostrarti solo quelli con un servizio WMS/WFS.
|
|
208
|
+
|
|
209
|
+
### «Cosa copre la mia area di studio»
|
|
210
|
+
|
|
211
|
+
> **Tu:** Sto studiando l'area attorno a Bologna. Quali dataset la coprono?
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
openrndt search --q "edificato" --bbox "11.2,44.4,11.5,44.6" --num 10
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
> **AI:** Nel riquadro che copre l'area bolognese ci sono **40 dataset** che la
|
|
218
|
+
> intersecano: il Database Topografico Regionale (edifici, unità volumetriche, falde)
|
|
219
|
+
> e anche cartografie storiche (Ducato di Modena 1821, territorio ferrarese 1814)
|
|
220
|
+
> disponibili come WMS. Nota: il filtro è per *sovrapposizione* al riquadro, quindi
|
|
221
|
+
> include dataset a copertura regionale che toccano l'area.
|
|
222
|
+
|
|
223
|
+
### «Solo dati con licenza aperta»
|
|
224
|
+
|
|
225
|
+
> **Tu:** Mi servono dati sulle frane, ma solo open data che posso riusare liberamente.
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
openrndt search --q "frane AND isOpendata:*" --num 10
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
> **AI:** Ci sono **259 dataset open data** sulle frane — dall'inventario IFFI della
|
|
232
|
+
> Regione Toscana ai rilievi con drone dell'emergenza alluvione 2023 in
|
|
233
|
+
> Emilia-Romagna. Te li elenco con la licenza di ciascuno?
|
|
234
|
+
|
|
235
|
+
## Uso come libreria Python
|
|
236
|
+
|
|
237
|
+
```python
|
|
238
|
+
from openrndt import search, get_item, get_item_xml, ItemNotFoundError
|
|
239
|
+
|
|
240
|
+
# Ricerca
|
|
241
|
+
results = search(q="catasto", num=5)
|
|
242
|
+
for r in results["results"]:
|
|
243
|
+
print(r["id"], r["title"])
|
|
244
|
+
|
|
245
|
+
# Filtro per categoria e bbox
|
|
246
|
+
results = search(data_category="planningCadastre", bbox="7,44,8,45", num=10)
|
|
247
|
+
|
|
248
|
+
# Dettaglio singolo metadato
|
|
249
|
+
item = get_item("age:D_E973_MARSAGLIA")
|
|
250
|
+
print(item["_source"]["title"])
|
|
251
|
+
|
|
252
|
+
# XML ISO 19139
|
|
253
|
+
xml = get_item_xml("age:D_E973_MARSAGLIA")
|
|
254
|
+
|
|
255
|
+
# Gestione ID inesistente
|
|
256
|
+
try:
|
|
257
|
+
item = get_item("id_inesistente")
|
|
258
|
+
except ItemNotFoundError:
|
|
259
|
+
print("metadato non trovato")
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Le funzioni propagano le eccezioni `httpx`: `httpx.HTTPStatusError` per le
|
|
263
|
+
risposte 4xx/5xx e `httpx.ConnectError` / `httpx.TimeoutException` per i
|
|
264
|
+
problemi di rete. I retry interni coprono i timeout e i 5xx (3 tentativi),
|
|
265
|
+
mentre gli errori di connessione/DNS (`ConnectError`) vengono propagati subito.
|
|
266
|
+
Tutte derivano da `httpx.HTTPError`, comodo per catturarle insieme:
|
|
267
|
+
|
|
268
|
+
```python
|
|
269
|
+
import httpx
|
|
270
|
+
from openrndt import search
|
|
271
|
+
|
|
272
|
+
try:
|
|
273
|
+
results = search(q="catasto")
|
|
274
|
+
except httpx.HTTPError as exc:
|
|
275
|
+
print(f"richiesta fallita: {exc}")
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
Il base URL è configurabile via variabile d'ambiente o parametro:
|
|
279
|
+
|
|
280
|
+
```python
|
|
281
|
+
from openrndt.config import set_base_url
|
|
282
|
+
set_base_url("https://mio-mirror.example.com/RNDT")
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
## Per agenti AI
|
|
286
|
+
|
|
287
|
+
L'utente primario di questa CLI è un agente che legge `stdout` e compone i comandi
|
|
288
|
+
passo passo. Da qui i principi di design (sul modello di
|
|
289
|
+
[opensdmx](https://github.com/aborruso/opensdmx)):
|
|
290
|
+
|
|
291
|
+
- **Output strutturato, mai oggetti Python.** Default JSON su `stdout`; `--format
|
|
292
|
+
table` per la lettura umana, `--format csv` per i risultati tabellari, `--format
|
|
293
|
+
compact` (NDJSON, una riga per record) per scremare molti risultati a basso costo.
|
|
294
|
+
- **In modalità JSON, `stdout` contiene solo JSON.** Errori e avvisi vanno su
|
|
295
|
+
`stderr`: si può fare pipe diretta in `jq`.
|
|
296
|
+
- **Errori leggibili e self-contained: mai stack trace.** Un errore di rete o HTTP
|
|
297
|
+
produce un messaggio comprensibile su `stderr` ed exit code `1`, non un traceback.
|
|
298
|
+
- **Exit code chiari.** `0` successo, `1` errore (rete, HTTP, ID inesistente),
|
|
299
|
+
`2` parametri non validi.
|
|
300
|
+
- **Niente formati ambigui.** `get <id> --format csv` (dettaglio non tabellare)
|
|
301
|
+
fallisce con un messaggio esplicito invece di restituire output vuoto.
|
|
302
|
+
|
|
303
|
+
Il progetto include inoltre una skill Claude Code in `skills/rndt-explorer/` che guida
|
|
304
|
+
un agente attraverso le 4 fasi: scoperta delle codelist, ricerca con filtri progressivi,
|
|
305
|
+
lettura del dettaglio, download delle risorse collegate (WMS/WFS/download).
|
|
306
|
+
|
|
307
|
+
## Riferimenti
|
|
308
|
+
|
|
309
|
+
- Pagina ufficiale REST API: <https://geodati.gov.it/geoportale/eng/strumenti-en/rest-api>
|
|
310
|
+
- Spec completa (sito di test Esri Geoportal Server, non produzione): <https://gpt.geocloud.com/geoportal3/api/gpt_api.json>
|
|
311
|
+
- Documentazione raccolta nella cartella [`ref/`](./ref).
|
|
312
|
+
|
|
313
|
+
## Licenza
|
|
314
|
+
|
|
315
|
+
MIT.
|
openrndt-1.0.0/README.md
ADDED
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
# openrndt
|
|
2
|
+
|
|
3
|
+
CLI Python e libreria per accedere al **Repertorio Nazionale dei Dati Territoriali (RNDT)** —
|
|
4
|
+
pensata per essere orchestrata da un'AI.
|
|
5
|
+
|
|
6
|
+
> Stato: v1.0 — read-only.
|
|
7
|
+
|
|
8
|
+
## Cos'è il RNDT
|
|
9
|
+
|
|
10
|
+
Il [Repertorio Nazionale dei Dati Territoriali](https://geodati.gov.it/geoportale/) è
|
|
11
|
+
il catalogo ufficiale italiano dei metadati geografici (ISO 19115/19139). Espone REST
|
|
12
|
+
API per cercare e scaricare i metadati.
|
|
13
|
+
|
|
14
|
+
## Installazione
|
|
15
|
+
|
|
16
|
+
### Da PyPI
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
uv tool install openrndt
|
|
20
|
+
# oppure, senza installazione persistente:
|
|
21
|
+
uvx openrndt --help
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
> Non ancora pubblicato su PyPI (roadmap verso la v1.0). Nel frattempo installa da locale.
|
|
25
|
+
|
|
26
|
+
### Da locale
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
git clone https://github.com/ondata/openrndt.git
|
|
30
|
+
cd openrndt
|
|
31
|
+
|
|
32
|
+
# CLI globale: venv isolato, eseguibile in PATH
|
|
33
|
+
uv tool install .
|
|
34
|
+
|
|
35
|
+
# Aggiornamento dopo modifiche al codice
|
|
36
|
+
uv tool install --reinstall .
|
|
37
|
+
|
|
38
|
+
# Disinstallazione
|
|
39
|
+
uv tool uninstall openrndt
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Per sviluppo (modifiche con ricarica immediata)
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
git clone https://github.com/ondata/openrndt.git
|
|
46
|
+
cd openrndt
|
|
47
|
+
uv sync
|
|
48
|
+
uv run openrndt --help
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Uso
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
# Ricerca testuale
|
|
55
|
+
openrndt search --q "catasto" --num 5
|
|
56
|
+
|
|
57
|
+
# Filtro per bounding box (Piemonte sud)
|
|
58
|
+
openrndt search --q "cartografia" --bbox 7,44,8,45 --num 10
|
|
59
|
+
|
|
60
|
+
# Per categoria tematica ISO 19115
|
|
61
|
+
openrndt search --data-category planningCadastre --num 5
|
|
62
|
+
|
|
63
|
+
# Singolo metadato
|
|
64
|
+
openrndt get age:D_E973_MARSAGLIA
|
|
65
|
+
|
|
66
|
+
# XML ISO 19139 grezzo
|
|
67
|
+
openrndt get age:D_E973_MARSAGLIA --xml > meta.xml
|
|
68
|
+
|
|
69
|
+
# Codelist disponibili (no rete)
|
|
70
|
+
openrndt discover
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Il timeout HTTP per singolo tentativo è configurabile con `--timeout` (default 30s);
|
|
74
|
+
con i retry su timeout/5xx (3 tentativi) il caso peggiore è ~3x questo valore:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
openrndt --timeout 5 search --q "catasto" --num 5
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Tutti i comandi accettano `--format json` (default), `--format table`, `--format csv`.
|
|
81
|
+
Per `search` c'è anche `--format compact`: una riga NDJSON per record con i soli
|
|
82
|
+
campi ad alto segnale (`id`, `title`, `org`, `type`, `category`, `updated`,
|
|
83
|
+
`resources`), pensata per agenti AI e pipe a basso consumo di token.
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
openrndt --format compact search --q "catasto" --num 3
|
|
87
|
+
# {"id":"…","title":"…","org":"Comune di Torino","type":"service","category":null,"updated":"…","resources":["WMS"]}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Esempi di conversazione con un'AI
|
|
91
|
+
|
|
92
|
+
openrndt è pensato per essere **pilotato da un assistente AI**. Non devi imparare la
|
|
93
|
+
sintassi: descrivi a parole quello che ti serve e l'AI compone i comandi, legge i
|
|
94
|
+
risultati e ti restituisce ciò che conta — di solito **un URL WMS/WFS da incollare in
|
|
95
|
+
QGIS** o un dato da scaricare. Tutti gli esempi qui sotto usano risposte reali del
|
|
96
|
+
catalogo RNDT.
|
|
97
|
+
|
|
98
|
+
> Per provarli serve un assistente (es. Claude) a cui è stata installata la skill
|
|
99
|
+
> `skills/rndt-explorer/` o che può eseguire `openrndt` da terminale.
|
|
100
|
+
|
|
101
|
+
### «Mi serve l'uso del suolo dell'Emilia-Romagna da caricare in QGIS»
|
|
102
|
+
|
|
103
|
+
> **Tu:** Sto lavorando su un progetto QGIS in Emilia-Romagna. Mi trovi un servizio
|
|
104
|
+
> con l'uso del suolo regionale, così lo aggiungo come layer?
|
|
105
|
+
|
|
106
|
+
L'AI cerca i servizi pubblicati sul tema:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
openrndt search --q "uso del suolo WMS" --num 20
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
> **AI:** Trovato. La **Regione Emilia-Romagna** pubblica un WMS *Uso del Suolo* che
|
|
113
|
+
> copre più annate (2020, 2017, 2014, 2008, 2003, 1994, 1976) più l'uso storico dal
|
|
114
|
+
> 1853. In QGIS aggiungi un nuovo layer **WMS/WMTS** con questo URL:
|
|
115
|
+
>
|
|
116
|
+
> ```
|
|
117
|
+
> http://servizigis.regione.emilia-romagna.it/wms/uso_del_suolo?request=GetCapabilities&service=WMS
|
|
118
|
+
> ```
|
|
119
|
+
>
|
|
120
|
+
> Scegli poi l'annata che ti interessa fra i layer offerti dal servizio.
|
|
121
|
+
|
|
122
|
+
### «Voglio il catasto di un comune piemontese»
|
|
123
|
+
|
|
124
|
+
> **Tu:** Mi serve il catasto urbano per un'analisi in Piemonte.
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
openrndt search --q "catasto" --num 10
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
> **AI:** C'è il **Catasto Urbano 1:1500** come geo-servizio WMS della Regione
|
|
131
|
+
> Piemonte. URL per QGIS:
|
|
132
|
+
>
|
|
133
|
+
> ```
|
|
134
|
+
> https://geomap.reteunitaria.piemonte.it/ws/siccms/coto-01/wmsg01/wms_sicc17bis_catasto_urbano?service=WMS&version=1.1.1&request=getCapabilities
|
|
135
|
+
> ```
|
|
136
|
+
|
|
137
|
+
### «Cerco ortofoto da usare come base»
|
|
138
|
+
|
|
139
|
+
> **Tu:** Mi servono delle ortofoto come sfondo per la mia mappa. Cosa c'è disponibile?
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
openrndt search --q "ortofoto" --num 50
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
> **AI:** Diverse amministrazioni espongono ortofoto via WMS, pronte da usare come
|
|
146
|
+
> base in QGIS. Per esempio:
|
|
147
|
+
>
|
|
148
|
+
> - **Regione Sardegna** — `https://webgis.regione.sardegna.it/geoserverraster/ows?service=wms&version=1.3.0&request=GetCapabilities`
|
|
149
|
+
> - **Provincia di Lodi** — `http://sdi.provincia.lodi.it/arcgis/services/DBTL/dbtl_ortofoto_digitali/MapServer/WMSServer?request=GetCapabilities&service=WMS`
|
|
150
|
+
> - **Regione Piemonte** — `https://geomap.reteunitaria.piemonte.it/mapproxy/service?service=WMS&version=1.3.0&request=getCapabilities`
|
|
151
|
+
|
|
152
|
+
### «Il reticolo idrografico, ma vettoriale (per editing)»
|
|
153
|
+
|
|
154
|
+
> **Tu:** Non voglio un'immagine: mi serve il reticolo idrografico come vettoriale,
|
|
155
|
+
> così posso interrogarlo e modificarlo.
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
openrndt search --q "reticolo idrografico" --num 50
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
> **AI:** Per i vettoriali ti serve un **WFS** (in QGIS: *Aggiungi layer WFS*). Alcuni
|
|
162
|
+
> servizi disponibili:
|
|
163
|
+
>
|
|
164
|
+
> - **ISPRA** — `http://sdi.isprambiente.it/geoserver/hy/wfs?service=wfs&version=2.0.0&request=GetCapabilities`
|
|
165
|
+
> - **ARPA Veneto** — `http://gaia.arpa.veneto.it/geoserver/ows?service=WFS&version=1.0.0&request=GetCapabilities`
|
|
166
|
+
> - **Regione Basilicata** — `http://rsdi.regione.basilicata.it/rbgeoserver2016/dbgt_0404/nd_idr/wfs?request=GetCapabilities&service=WFS&version=2.0.0`
|
|
167
|
+
|
|
168
|
+
### «Tutto quello che pubblica un certo ente»
|
|
169
|
+
|
|
170
|
+
> **Tu:** Quanti e quali dataset ha pubblicato la Regione Lombardia?
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
openrndt search --q 'apiso_OrganizationName_txt:"Regione Lombardia"' \
|
|
174
|
+
--sort "apiso_Modified_dt:desc" --num 10
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
> **AI:** La **Regione Lombardia** ha **430 dataset** nel RNDT. Ecco i 10 aggiornati
|
|
178
|
+
> più di recente… (l'AI elenca titoli e date). Posso filtrarli per tema — uso del
|
|
179
|
+
> suolo, idrografia, trasporti — o mostrarti solo quelli con un servizio WMS/WFS.
|
|
180
|
+
|
|
181
|
+
### «Cosa copre la mia area di studio»
|
|
182
|
+
|
|
183
|
+
> **Tu:** Sto studiando l'area attorno a Bologna. Quali dataset la coprono?
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
openrndt search --q "edificato" --bbox "11.2,44.4,11.5,44.6" --num 10
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
> **AI:** Nel riquadro che copre l'area bolognese ci sono **40 dataset** che la
|
|
190
|
+
> intersecano: il Database Topografico Regionale (edifici, unità volumetriche, falde)
|
|
191
|
+
> e anche cartografie storiche (Ducato di Modena 1821, territorio ferrarese 1814)
|
|
192
|
+
> disponibili come WMS. Nota: il filtro è per *sovrapposizione* al riquadro, quindi
|
|
193
|
+
> include dataset a copertura regionale che toccano l'area.
|
|
194
|
+
|
|
195
|
+
### «Solo dati con licenza aperta»
|
|
196
|
+
|
|
197
|
+
> **Tu:** Mi servono dati sulle frane, ma solo open data che posso riusare liberamente.
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
openrndt search --q "frane AND isOpendata:*" --num 10
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
> **AI:** Ci sono **259 dataset open data** sulle frane — dall'inventario IFFI della
|
|
204
|
+
> Regione Toscana ai rilievi con drone dell'emergenza alluvione 2023 in
|
|
205
|
+
> Emilia-Romagna. Te li elenco con la licenza di ciascuno?
|
|
206
|
+
|
|
207
|
+
## Uso come libreria Python
|
|
208
|
+
|
|
209
|
+
```python
|
|
210
|
+
from openrndt import search, get_item, get_item_xml, ItemNotFoundError
|
|
211
|
+
|
|
212
|
+
# Ricerca
|
|
213
|
+
results = search(q="catasto", num=5)
|
|
214
|
+
for r in results["results"]:
|
|
215
|
+
print(r["id"], r["title"])
|
|
216
|
+
|
|
217
|
+
# Filtro per categoria e bbox
|
|
218
|
+
results = search(data_category="planningCadastre", bbox="7,44,8,45", num=10)
|
|
219
|
+
|
|
220
|
+
# Dettaglio singolo metadato
|
|
221
|
+
item = get_item("age:D_E973_MARSAGLIA")
|
|
222
|
+
print(item["_source"]["title"])
|
|
223
|
+
|
|
224
|
+
# XML ISO 19139
|
|
225
|
+
xml = get_item_xml("age:D_E973_MARSAGLIA")
|
|
226
|
+
|
|
227
|
+
# Gestione ID inesistente
|
|
228
|
+
try:
|
|
229
|
+
item = get_item("id_inesistente")
|
|
230
|
+
except ItemNotFoundError:
|
|
231
|
+
print("metadato non trovato")
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Le funzioni propagano le eccezioni `httpx`: `httpx.HTTPStatusError` per le
|
|
235
|
+
risposte 4xx/5xx e `httpx.ConnectError` / `httpx.TimeoutException` per i
|
|
236
|
+
problemi di rete. I retry interni coprono i timeout e i 5xx (3 tentativi),
|
|
237
|
+
mentre gli errori di connessione/DNS (`ConnectError`) vengono propagati subito.
|
|
238
|
+
Tutte derivano da `httpx.HTTPError`, comodo per catturarle insieme:
|
|
239
|
+
|
|
240
|
+
```python
|
|
241
|
+
import httpx
|
|
242
|
+
from openrndt import search
|
|
243
|
+
|
|
244
|
+
try:
|
|
245
|
+
results = search(q="catasto")
|
|
246
|
+
except httpx.HTTPError as exc:
|
|
247
|
+
print(f"richiesta fallita: {exc}")
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
Il base URL è configurabile via variabile d'ambiente o parametro:
|
|
251
|
+
|
|
252
|
+
```python
|
|
253
|
+
from openrndt.config import set_base_url
|
|
254
|
+
set_base_url("https://mio-mirror.example.com/RNDT")
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
## Per agenti AI
|
|
258
|
+
|
|
259
|
+
L'utente primario di questa CLI è un agente che legge `stdout` e compone i comandi
|
|
260
|
+
passo passo. Da qui i principi di design (sul modello di
|
|
261
|
+
[opensdmx](https://github.com/aborruso/opensdmx)):
|
|
262
|
+
|
|
263
|
+
- **Output strutturato, mai oggetti Python.** Default JSON su `stdout`; `--format
|
|
264
|
+
table` per la lettura umana, `--format csv` per i risultati tabellari, `--format
|
|
265
|
+
compact` (NDJSON, una riga per record) per scremare molti risultati a basso costo.
|
|
266
|
+
- **In modalità JSON, `stdout` contiene solo JSON.** Errori e avvisi vanno su
|
|
267
|
+
`stderr`: si può fare pipe diretta in `jq`.
|
|
268
|
+
- **Errori leggibili e self-contained: mai stack trace.** Un errore di rete o HTTP
|
|
269
|
+
produce un messaggio comprensibile su `stderr` ed exit code `1`, non un traceback.
|
|
270
|
+
- **Exit code chiari.** `0` successo, `1` errore (rete, HTTP, ID inesistente),
|
|
271
|
+
`2` parametri non validi.
|
|
272
|
+
- **Niente formati ambigui.** `get <id> --format csv` (dettaglio non tabellare)
|
|
273
|
+
fallisce con un messaggio esplicito invece di restituire output vuoto.
|
|
274
|
+
|
|
275
|
+
Il progetto include inoltre una skill Claude Code in `skills/rndt-explorer/` che guida
|
|
276
|
+
un agente attraverso le 4 fasi: scoperta delle codelist, ricerca con filtri progressivi,
|
|
277
|
+
lettura del dettaglio, download delle risorse collegate (WMS/WFS/download).
|
|
278
|
+
|
|
279
|
+
## Riferimenti
|
|
280
|
+
|
|
281
|
+
- Pagina ufficiale REST API: <https://geodati.gov.it/geoportale/eng/strumenti-en/rest-api>
|
|
282
|
+
- Spec completa (sito di test Esri Geoportal Server, non produzione): <https://gpt.geocloud.com/geoportal3/api/gpt_api.json>
|
|
283
|
+
- Documentazione raccolta nella cartella [`ref/`](./ref).
|
|
284
|
+
|
|
285
|
+
## Licenza
|
|
286
|
+
|
|
287
|
+
MIT.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "openrndt"
|
|
3
|
+
version = "1.0.0"
|
|
4
|
+
description = "CLI Python per il Repertorio Nazionale dei Dati Territoriali (RNDT) — pensata per essere orchestrata da un'AI"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
authors = [
|
|
7
|
+
{ name = "Andrea Borruso", email = "aborruso@gmail.com" }
|
|
8
|
+
]
|
|
9
|
+
license = { text = "MIT" }
|
|
10
|
+
requires-python = ">=3.12"
|
|
11
|
+
keywords = ["rndt", "geodati", "inspire", "metadata", "iso19115", "iso19139", "open-data", "italy"]
|
|
12
|
+
classifiers = [
|
|
13
|
+
"Development Status :: 5 - Production/Stable",
|
|
14
|
+
"Intended Audience :: Science/Research",
|
|
15
|
+
"Intended Audience :: Developers",
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Programming Language :: Python :: 3.12",
|
|
19
|
+
"Programming Language :: Python :: 3.13",
|
|
20
|
+
"Topic :: Scientific/Engineering :: GIS",
|
|
21
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
22
|
+
]
|
|
23
|
+
dependencies = [
|
|
24
|
+
"httpx>=0.28.1",
|
|
25
|
+
"typer>=0.24.1",
|
|
26
|
+
"rich>=14.3.3",
|
|
27
|
+
"tenacity>=9.1.4",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[project.scripts]
|
|
31
|
+
openrndt = "openrndt:main"
|
|
32
|
+
|
|
33
|
+
[project.urls]
|
|
34
|
+
Repository = "https://github.com/ondata/openrndt"
|
|
35
|
+
Issues = "https://github.com/ondata/openrndt/issues"
|
|
36
|
+
"RNDT Portal" = "https://geodati.gov.it/geoportale/"
|
|
37
|
+
"RNDT REST API" = "https://geodati.gov.it/geoportale/eng/strumenti-en/rest-api"
|
|
38
|
+
|
|
39
|
+
[dependency-groups]
|
|
40
|
+
dev = [
|
|
41
|
+
"mypy>=2.3.0",
|
|
42
|
+
"pytest>=8",
|
|
43
|
+
"pytest-cov>=7.1.0",
|
|
44
|
+
"responses>=0.25",
|
|
45
|
+
"respx>=0.21",
|
|
46
|
+
"ruff>=0.11",
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
[tool.pytest.ini_options]
|
|
50
|
+
testpaths = ["tests"]
|
|
51
|
+
|
|
52
|
+
[tool.ruff.lint]
|
|
53
|
+
ignore = ["E402"]
|
|
54
|
+
|
|
55
|
+
[tool.mypy]
|
|
56
|
+
python_version = "3.12"
|
|
57
|
+
strict = true
|
|
58
|
+
|
|
59
|
+
[build-system]
|
|
60
|
+
requires = ["uv_build>=0.9.7,<0.10.0"]
|
|
61
|
+
build-backend = "uv_build"
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"""openrndt — CLI Python per il Repertorio Nazionale dei Dati Territoriali."""
|
|
2
|
+
|
|
3
|
+
from openrndt.cli import main
|
|
4
|
+
from openrndt.item import ItemNotFoundError, get_item, get_item_html, get_item_xml
|
|
5
|
+
from openrndt.search import search
|
|
6
|
+
|
|
7
|
+
__version__ = "1.0.0"
|
|
8
|
+
__all__ = ["main", "search", "get_item", "get_item_xml", "get_item_html", "ItemNotFoundError", "__version__"]
|