first commit
This commit is contained in:
commit
9603735519
17 changed files with 1665 additions and 0 deletions
188
README.md
Normal file
188
README.md
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
# Creazione MbTiles per tileserver GL utilizzando Copernicus glo30
|
||||
|
||||
## Download dei file
|
||||
|
||||
🚀 Scaricare i dati da mirror Copernicus GLO‑30
|
||||
```
|
||||
aws s3 sync s3://copernicus-dem-30m ./glo30 --no-sign-request
|
||||
```
|
||||
Crea glo30 dir con tutto il mondo
|
||||
|
||||
ma non servono tutti i dati quindi questi sono i tre programmi python per
|
||||
scaricare italia europa e mondo in base a quello che vuoi ottenere
|
||||
|
||||
```
|
||||
python3 i.py # italia
|
||||
python3 e.py # europa
|
||||
python3 w.py # mondo
|
||||
```
|
||||
|
||||
## Trasformare i DEM geotiff in tiff RGB
|
||||
|
||||
con la funzione rgbify_batch.py puoi creare le versioni tif in formato RGB
|
||||
|
||||
```
|
||||
python3 rgbify_batch.py glo30_italia glo30_italia_rgb
|
||||
```
|
||||
|
||||
il comando per la singola tiff sarebbe
|
||||
|
||||
```
|
||||
rio rgbify -b -10000 -i 0.1 a.tif a_rgb.tif
|
||||
```
|
||||
|
||||
## Trasformare gli RGB in tiles
|
||||
|
||||
corretto
|
||||
```
|
||||
python3 make_tiles_rio_tiler.py glo30_italia_rgb/Copernicus_DSM_COG_10_N46_00_E011_00_DEM_rgb.tif tiles_x
|
||||
```
|
||||
|
||||
## Trasformare i tiles in mbtiles
|
||||
|
||||
```
|
||||
mb-util --scheme=xyz --image_format=png tiles_x/ x.mbtiles
|
||||
```
|
||||
|
||||
## Inserire i metadata in mbtiles
|
||||
|
||||
```
|
||||
python3 set_mbtiles_metadata.py glo30_italia_rgb/Copernicus_DSM_COG_10_N46_00_E011_00_DEM_rgb.tif xxx.mbtiles "Terrain RGB Adamello"
|
||||
```
|
||||
|
||||
## Verificare
|
||||
|
||||
```
|
||||
python3 verify_mbtiles_metadata.py glo30_italia_rgb/Copernicus_DSM_COG_10_N46_00_E011_00_DEM_rgb.tif xxx.mbtiles
|
||||
python3 verify_and_fix_mbtiles.py glo30_italia_rgb/Copernicus_DSM_COG_10_N46_00_E011_00_DEM_rgb.tif xxx.mbtiles
|
||||
python3 verify_bounds_tileservergl.py glo30_italia_rgb/Copernicus_DSM_COG_10_N46_00_E011_00_DEM_rgb.tif xxx.mbtiles
|
||||
```
|
||||
|
||||
## multipli
|
||||
|
||||
multili con file
|
||||
```
|
||||
python3 make_tiles_rio_tiler.py glo30_italia_rgb/Copernicus_DSM_COG_10_N46_00_E011_00_DEM_rgb.tif tiles_m
|
||||
python3 make_tiles_rio_tiler.py glo30_italia_rgb/Copernicus_DSM_COG_10_N46_00_E010_00_DEM_rgb.tif tiles_m
|
||||
|
||||
mb-util --scheme=xyz --image_format=png tiles_m/ m.mbtiles
|
||||
|
||||
python3 set_mbtiles_metadata_multi.py glo30_italia_rgb/Copernicus_DSM_COG_10_N46_00_E011_00_DEM_rgb.tif glo30_italia_rgb/Copernicus_DSM_COG_10_N46_00_E010_00_DEM_rgb.tif m.mbtiles
|
||||
```
|
||||
|
||||
|
||||
multipli con dir
|
||||
```
|
||||
python3 make_tiles_rio_tiler_multi.py dir_tif tiles_mm
|
||||
mb-util --scheme=xyz --image_format=png tiles_mm/ mm.mbtiles
|
||||
python3 set_mbtiles_metadata_dir.py dir_tif mm.mbtiles
|
||||
```
|
||||
|
||||
ultima idea
|
||||
```
|
||||
python3 make_tiles_rio_tiler.py glo30_italia_rgb/Copernicus_DSM_COG_10_N46_00_E010_00_DEM_rgb.tif tiles_x
|
||||
python3 make_tiles_rio_tiler.py glo30_italia_rgb/Copernicus_DSM_COG_10_N46_00_E011_00_DEM_rgb.tif tiles_y
|
||||
rsync -av tiles_x/ tiles_m/
|
||||
rsync -av --ignore-existing tiles_y/ tiles_m/
|
||||
mb-util --scheme=xyz --image_format=png tiles_m/ m.mbtiles
|
||||
python3 set_mbtiles_metadata_dir.py dir_tif m.mbtiles
|
||||
scp m.mbtiles orangepi@192.168.1.4:/home/nvme/dockerdata/tileserver1/terrain.mbtiles
|
||||
```
|
||||
|
||||
## Controllo
|
||||
|
||||
|
||||
Monte Adamello
|
||||
|
||||
Latitude: 46° 9' 19" N
|
||||
Longitude: 10° 29' 47" E
|
||||
Lat/Long (dec): 46.1554,10.4966
|
||||
Köppen climate type: ET : Tundra
|
||||
Elevation: 3,539m
|
||||
|
||||
elevazione=-10000+(R*256^2+G*256+B)*0,1
|
||||
|
||||
estrarre l'altezza del monte dal geotiff
|
||||
```
|
||||
gdallocationinfo -wgs84 -valonly glo30_italia/Copernicus_DSM_COG_10_N46_00_E010_00_DEM.tif 10.4966 46.1554
|
||||
```
|
||||
è da come risultato
|
||||
|
||||
3496.90673828125
|
||||
|
||||
estrarre l'altezza del monte dal rgb
|
||||
```
|
||||
gdallocationinfo -wgs84 glo30_italia_rgb/Copernicus_DSM_COG_10_N46_00_E010_00_DEM_rgb.tif 10.4966 46.1554
|
||||
```
|
||||
|
||||
e da come report
|
||||
```
|
||||
Report:
|
||||
Location: (1788P,3041L)
|
||||
Band 1:
|
||||
Value: 2
|
||||
Band 2:
|
||||
Value: 15
|
||||
Band 3:
|
||||
Value: 57
|
||||
```
|
||||
utilizzando la funzione python che calcola altutudine da RGB si ottiene 3496.90
|
||||
```
|
||||
python3 rgb2alt.py 2 15 57
|
||||
```
|
||||
|
||||
verifichiamo con mbtiles
|
||||
|
||||
```
|
||||
gdallocationinfo -wgs84 -valonly \
|
||||
-b 1 -b 2 -b 3 \
|
||||
aa.mbtiles 10.4966 46.1554
|
||||
```
|
||||
|
||||
```
|
||||
gdallocationinfo -wgs84 -valonly \
|
||||
-b 1 -b 2 -b 3 \
|
||||
x.mbtiles 10.4966 46.1554
|
||||
```
|
||||
|
||||
Punta Penia
|
||||
|
||||
Dolomiti (3.343 m), sono approssimativamente:
|
||||
|
||||
Latitudine: 46° 26' 03.84" N (46.43429)
|
||||
|
||||
Longitudine: 11° 50' 58.58" E (11.85051)
|
||||
```
|
||||
gdallocationinfo -wgs84 -valonly glo30_italia/Copernicus_DSM_COG_10_N46_00_E011_00_DEM.tif 11.85051 46.43429
|
||||
3236.89526367188
|
||||
gdallocationinfo -wgs84 glo30_italia_rgb/Copernicus_DSM_COG_10_N46_00_E011_00_DEM_rgb.tif 11.85051 46.43429
|
||||
Report:
|
||||
Location: (3062P,2037L)
|
||||
Band 1:
|
||||
Value: 2
|
||||
Band 2:
|
||||
Value: 5
|
||||
Band 3:
|
||||
Value: 16
|
||||
python3 rgb2alt.py 2 5 16
|
||||
3236.800000000001
|
||||
gdallocationinfo -wgs84 -valonly -b 1 -b 2 -b 3 x.mbtiles 11.85051 46.43429
|
||||
2
|
||||
6
|
||||
68
|
||||
python3 rgb2alt.py 2 6 68
|
||||
3267.6
|
||||
```
|
||||
|
||||
|
||||
gdallocationinfo -wgs84 -valonly \
|
||||
-b 1 -b 2 -b 3 \
|
||||
aa.mbtiles 10.4966 46.1554
|
||||
2
|
||||
15
|
||||
147
|
||||
quindi 3505,9
|
||||
|
||||
|
||||
|
||||
|
||||
96
bounds.py
Normal file
96
bounds.py
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
#!/usr/bin/env python3
|
||||
import argparse, sqlite3, sys
|
||||
|
||||
def fetch_one(cur, sql, args=()):
|
||||
row = cur.execute(sql, args).fetchone()
|
||||
return row[0] if row else None
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser(
|
||||
description="Normalizza bounds (W,S,E,N) e imposta center/minzoom/maxzoom in un MBTiles."
|
||||
)
|
||||
ap.add_argument("mbtiles", help="Percorso al file .mbtiles")
|
||||
ap.add_argument("--zoom", type=int, default=None,
|
||||
help="Zoom del center da impostare (default: usa center esistente o media min/max dalle tile)")
|
||||
ap.add_argument("--minzoom", type=int, default=None,
|
||||
help="Forza minzoom (default: dai livelli in 'tiles')")
|
||||
ap.add_argument("--maxzoom", type=int, default=None,
|
||||
help="Forza maxzoom (default: dai livelli in 'tiles')")
|
||||
args = ap.parse_args()
|
||||
|
||||
conn = sqlite3.connect(args.mbtiles)
|
||||
cur = conn.cursor()
|
||||
|
||||
# --- 1) leggi bounds correnti
|
||||
b_raw = fetch_one(cur, "SELECT value FROM metadata WHERE name='bounds';")
|
||||
if not b_raw:
|
||||
sys.exit("ERRORE: 'bounds' mancante nella tabella metadata.")
|
||||
|
||||
try:
|
||||
x1, y1, x2, y2 = [float(t) for t in b_raw.split(",")]
|
||||
except Exception:
|
||||
sys.exit(f"ERRORE: bounds non parseable: {b_raw}")
|
||||
|
||||
# normalizza ordine W,S,E,N (min/max su lon e lat)
|
||||
W, E = sorted((x1, x2))
|
||||
S, N = sorted((y1, y2))
|
||||
bounds = f"{W:.6f},{S:.6f},{E:.6f},{N:.6f}"
|
||||
|
||||
# --- 2) min/max zoom: da tiles salvo override
|
||||
minz = fetch_one(cur, "SELECT MIN(zoom_level) FROM tiles;")
|
||||
maxz = fetch_one(cur, "SELECT MAX(zoom_level) FROM tiles;")
|
||||
|
||||
# fallback se tiles non esiste o vuota
|
||||
try:
|
||||
minz = int(minz) if minz is not None else None
|
||||
maxz = int(maxz) if maxz is not None else None
|
||||
except Exception:
|
||||
minz = maxz = None
|
||||
|
||||
if args.minzoom is not None: minz = int(args.minzoom)
|
||||
if args.maxzoom is not None: maxz = int(args.maxzoom)
|
||||
|
||||
# --- 3) center: lon/lat dal bbox; zoom = argomento > zoom esistente > media min/max > 0
|
||||
center_existing = fetch_one(cur, "SELECT value FROM metadata WHERE name='center';")
|
||||
center_z_existing = None
|
||||
if center_existing:
|
||||
parts = [p.strip() for p in center_existing.split(",")]
|
||||
if len(parts) >= 3 and parts[2] != "":
|
||||
center_z_existing = parts[2]
|
||||
|
||||
if args.zoom is not None:
|
||||
z = int(args.zoom)
|
||||
elif center_z_existing is not None:
|
||||
try:
|
||||
z = int(center_z_existing)
|
||||
except Exception:
|
||||
z = None
|
||||
else:
|
||||
z = int((minz + maxz) // 2) if (minz is not None and maxz is not None) else 0
|
||||
|
||||
ctr_lon = (W + E) / 2.0
|
||||
ctr_lat = (S + N) / 2.0
|
||||
center = f"{ctr_lon:.6f},{ctr_lat:.6f},{z}"
|
||||
|
||||
# --- 4) scrittura: crea metadata se manca, REPLACE dei valori
|
||||
cur.execute("CREATE TABLE IF NOT EXISTS metadata (name TEXT PRIMARY KEY, value TEXT)")
|
||||
cur.execute("REPLACE INTO metadata(name,value) VALUES(?,?)", ("bounds", bounds))
|
||||
cur.execute("REPLACE INTO metadata(name,value) VALUES(?,?)", ("center", center))
|
||||
if minz is not None:
|
||||
cur.execute("REPLACE INTO metadata(name,value) VALUES(?,?)", ("minzoom", str(minz)))
|
||||
if maxz is not None:
|
||||
cur.execute("REPLACE INTO metadata(name,value) VALUES(?,?)", ("maxzoom", str(maxz)))
|
||||
conn.commit()
|
||||
|
||||
# --- 5) stampa riepilogo
|
||||
print("OK")
|
||||
for k in ("bounds","center","minzoom","maxzoom"):
|
||||
v = fetch_one(cur, "SELECT value FROM metadata WHERE name=?", (k,))
|
||||
print(f"{k} = {v}")
|
||||
zminmax = cur.execute("SELECT MIN(zoom_level), MAX(zoom_level) FROM tiles;").fetchone()
|
||||
if zminmax and all(v is not None for v in zminmax):
|
||||
print(f"zoom (tiles) = {zminmax[0]}..{zminmax[1]}")
|
||||
conn.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
86
e.py
Normal file
86
e.py
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
import boto3
|
||||
import os
|
||||
import logging
|
||||
from botocore import UNSIGNED
|
||||
from botocore.config import Config
|
||||
|
||||
BUCKET = "copernicus-dem-30m"
|
||||
OUTPUT_DIR = "./glo30_europe"
|
||||
LOG_FILE = "download_europe.log"
|
||||
|
||||
# Europa approx:
|
||||
lat_range = range(34, 73) # N34–N72
|
||||
lon_east = range(0, 46) # E000–E045
|
||||
lon_west = range(1, 26) # W001–W025
|
||||
|
||||
logging.basicConfig(
|
||||
filename=LOG_FILE,
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s [%(levelname)s] %(message)s",
|
||||
)
|
||||
|
||||
console = logging.getLogger("console")
|
||||
console.setLevel(logging.INFO)
|
||||
console.addHandler(logging.StreamHandler())
|
||||
|
||||
def log(msg):
|
||||
logging.info(msg)
|
||||
console.info(msg)
|
||||
|
||||
os.makedirs(OUTPUT_DIR, exist_ok=True)
|
||||
|
||||
s3 = boto3.client("s3", config=Config(signature_version=UNSIGNED))
|
||||
|
||||
def folder_name(lat, lon, east=True):
|
||||
if east:
|
||||
return f"Copernicus_DSM_COG_10_N{lat:02d}_00_E{lon:03d}_00_DEM/"
|
||||
else:
|
||||
return f"Copernicus_DSM_COG_10_N{lat:02d}_00_W{lon:03d}_00_DEM/"
|
||||
|
||||
log("Inizio scansione directory europee...")
|
||||
|
||||
found_folders = []
|
||||
|
||||
paginator = s3.get_paginator("list_objects_v2")
|
||||
|
||||
for page in paginator.paginate(Bucket=BUCKET, Delimiter="/"):
|
||||
for prefix in page.get("CommonPrefixes", []):
|
||||
folder = prefix["Prefix"]
|
||||
|
||||
# EASTERN EUROPE
|
||||
for lat in lat_range:
|
||||
for lon in lon_east:
|
||||
if folder == folder_name(lat, lon, east=True):
|
||||
found_folders.append(folder)
|
||||
log(f"[FOUND] {folder}")
|
||||
|
||||
# WESTERN EUROPE (longitudes W)
|
||||
for lat in lat_range:
|
||||
for lon in lon_west:
|
||||
if folder == folder_name(lat, lon, east=False):
|
||||
found_folders.append(folder)
|
||||
log(f"[FOUND] {folder}")
|
||||
|
||||
log(f"Trovate {len(found_folders)} cartelle europee")
|
||||
|
||||
# -----------------------------
|
||||
# DOWNLOAD DEM PRINCIPALE
|
||||
# -----------------------------
|
||||
for folder in found_folders:
|
||||
tif_name = folder[:-1] + ".tif"
|
||||
key = folder + tif_name.split("/")[-1]
|
||||
out_path = os.path.join(OUTPUT_DIR, tif_name.split("/")[-1])
|
||||
|
||||
if os.path.exists(out_path):
|
||||
log(f"[SKIP] {out_path} già presente")
|
||||
continue
|
||||
|
||||
log(f"[DOWNLOAD] {key}")
|
||||
try:
|
||||
s3.download_file(BUCKET, key, out_path)
|
||||
log(f"[OK] {key} scaricato")
|
||||
except Exception as e:
|
||||
log(f"[ERROR] {key} → {e}")
|
||||
|
||||
log("Completato.")
|
||||
|
||||
74
i.py
Normal file
74
i.py
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
import boto3
|
||||
import os
|
||||
import logging
|
||||
from botocore import UNSIGNED
|
||||
from botocore.config import Config
|
||||
|
||||
BUCKET = "copernicus-dem-30m"
|
||||
OUTPUT_DIR = "./glo30_italia"
|
||||
LOG_FILE = "download.log"
|
||||
|
||||
lat_range = range(36, 48) # N36–N47
|
||||
lon_range = range(6, 19) # E006–E018
|
||||
|
||||
logging.basicConfig(
|
||||
filename=LOG_FILE,
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s [%(levelname)s] %(message)s",
|
||||
)
|
||||
|
||||
console = logging.getLogger("console")
|
||||
console.setLevel(logging.INFO)
|
||||
console.addHandler(logging.StreamHandler())
|
||||
|
||||
def log(msg):
|
||||
logging.info(msg)
|
||||
console.info(msg)
|
||||
|
||||
os.makedirs(OUTPUT_DIR, exist_ok=True)
|
||||
|
||||
s3 = boto3.client("s3", config=Config(signature_version=UNSIGNED))
|
||||
|
||||
def folder_name(lat, lon):
|
||||
return f"Copernicus_DSM_COG_10_N{lat:02d}_00_E{lon:03d}_00_DEM/"
|
||||
|
||||
log("Inizio scansione delle directory italiane...")
|
||||
|
||||
found_folders = []
|
||||
|
||||
paginator = s3.get_paginator("list_objects_v2")
|
||||
|
||||
for page in paginator.paginate(Bucket=BUCKET, Delimiter="/"):
|
||||
for prefix in page.get("CommonPrefixes", []):
|
||||
folder = prefix["Prefix"]
|
||||
|
||||
# Controlla se la cartella è italiana
|
||||
for lat in lat_range:
|
||||
for lon in lon_range:
|
||||
if folder == folder_name(lat, lon):
|
||||
found_folders.append(folder)
|
||||
log(f"[FOUND] {folder}")
|
||||
|
||||
log(f"Trovate {len(found_folders)} cartelle italiane")
|
||||
|
||||
# -----------------------------
|
||||
# DOWNLOAD DEM PRINCIPALE
|
||||
# -----------------------------
|
||||
for folder in found_folders:
|
||||
tif_name = folder[:-1] + ".tif" # rimuove "/" e aggiunge .tif
|
||||
key = folder + tif_name.split("/")[-1]
|
||||
out_path = os.path.join(OUTPUT_DIR, tif_name.split("/")[-1])
|
||||
|
||||
if os.path.exists(out_path):
|
||||
log(f"[SKIP] {out_path} già presente")
|
||||
continue
|
||||
|
||||
log(f"[DOWNLOAD] {key}")
|
||||
try:
|
||||
s3.download_file(BUCKET, key, out_path)
|
||||
log(f"[OK] {key} scaricato")
|
||||
except Exception as e:
|
||||
log(f"[ERROR] {key} → {e}")
|
||||
|
||||
log("Completato.")
|
||||
|
||||
96
make_tiles_rio_tiler.py
Normal file
96
make_tiles_rio_tiler.py
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
#!/usr/bin/env python3
|
||||
import os
|
||||
import sys
|
||||
import mercantile
|
||||
import numpy as np
|
||||
from rio_tiler.io import COGReader
|
||||
from rio_tiler.utils import render
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# USO:
|
||||
# python3 make_tiles_rio_tiler.py input_rgb.tif tiles_out/
|
||||
#
|
||||
# Versione finale:
|
||||
# - BUFFER per evitare buchi tra quadranti
|
||||
# - PADDING per generare tile anche se il TIFF copre solo parte della tile
|
||||
# - Logging avanzato
|
||||
# - Controllo tile vuote
|
||||
# ---------------------------------------------------------
|
||||
|
||||
ZOOM_MIN = 5
|
||||
ZOOM_MAX = 14
|
||||
BUFFER = 0.005 # espansione bounds per evitare tile mancanti
|
||||
TILESIZE = 256 # tile standard
|
||||
PADDING = 1 # genera tile anche se parziali
|
||||
|
||||
def generate_tiles(input_tif, output_dir):
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
|
||||
print(f"\n=== Generazione tile per: {input_tif} ===")
|
||||
print(f"Zoom: {ZOOM_MIN}..{ZOOM_MAX}")
|
||||
print(f"Buffer: {BUFFER}°")
|
||||
print(f"Padding: {PADDING} pixel\n")
|
||||
|
||||
with COGReader(input_tif) as cog:
|
||||
|
||||
for z in range(ZOOM_MIN, ZOOM_MAX + 1):
|
||||
print(f"\n== Zoom {z} ==")
|
||||
|
||||
# Bounds originali del TIFF
|
||||
bounds = cog.bounds
|
||||
|
||||
# Espansione per evitare buchi tra quadranti
|
||||
minx = bounds[0] - BUFFER
|
||||
miny = bounds[1] - BUFFER
|
||||
maxx = bounds[2] + BUFFER
|
||||
maxy = bounds[3] + BUFFER
|
||||
|
||||
# Tile da generare
|
||||
tiles = list(mercantile.tiles(minx, miny, maxx, maxy, z))
|
||||
print(f"Tile da generare: {len(tiles)}")
|
||||
|
||||
for t in tiles:
|
||||
out_dir_zxy = os.path.join(output_dir, str(z), str(t.x))
|
||||
os.makedirs(out_dir_zxy, exist_ok=True)
|
||||
out_path = os.path.join(out_dir_zxy, f"{t.y}.png")
|
||||
|
||||
try:
|
||||
tile, mask = cog.tile(
|
||||
t.x, t.y, z,
|
||||
tilesize=TILESIZE,
|
||||
padding=PADDING,
|
||||
resampling_method="nearest"
|
||||
)
|
||||
except Exception as e:
|
||||
print(f" ⚠️ Errore tile {z}/{t.x}/{t.y}: {e}")
|
||||
continue
|
||||
|
||||
# Controllo tile vuote (tutto zero o tutto trasparente)
|
||||
if mask is not None and np.all(mask == 0):
|
||||
print(f" ⚠️ Tile vuota saltata: {z}/{t.x}/{t.y}")
|
||||
continue
|
||||
|
||||
img = render(tile, mask=mask, img_format="PNG")
|
||||
|
||||
with open(out_path, "wb") as f:
|
||||
f.write(img)
|
||||
|
||||
print(f" ✓ {z}/{t.x}/{t.y}")
|
||||
|
||||
print("\n=== Completato senza errori ===\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) != 3:
|
||||
print("Uso: python3 make_tiles_rio_tiler.py <input_rgb.tif> <output_dir>")
|
||||
sys.exit(1)
|
||||
|
||||
INPUT_TIF = sys.argv[1]
|
||||
OUTPUT_DIR = sys.argv[2]
|
||||
|
||||
if not os.path.isfile(INPUT_TIF):
|
||||
print(f"Errore: file non trovato: {INPUT_TIF}")
|
||||
sys.exit(1)
|
||||
|
||||
generate_tiles(INPUT_TIF, OUTPUT_DIR)
|
||||
|
||||
58
make_tiles_rio_tiler.py.old
Normal file
58
make_tiles_rio_tiler.py.old
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
#!/usr/bin/env python3
|
||||
import os
|
||||
import sys
|
||||
import mercantile
|
||||
from rio_tiler.io import COGReader
|
||||
from rio_tiler.utils import render
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# USO:
|
||||
# python3 make_tiles_rio_tiler.py input_rgb.tif tiles_out/
|
||||
#
|
||||
# Genera tile PNG Terrain-RGB senza alterare i pixel.
|
||||
# ---------------------------------------------------------
|
||||
|
||||
ZOOM_MIN = 5
|
||||
ZOOM_MAX = 14
|
||||
|
||||
def generate_tiles(input_tif, output_dir):
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
|
||||
with COGReader(input_tif) as cog:
|
||||
for z in range(ZOOM_MIN, ZOOM_MAX + 1):
|
||||
print(f"== Zoom {z} ==")
|
||||
|
||||
bounds = cog.bounds
|
||||
tiles = list(mercantile.tiles(bounds[0], bounds[1], bounds[2], bounds[3], z))
|
||||
|
||||
for t in tiles:
|
||||
out_dir_zxy = os.path.join(output_dir, str(z), str(t.x))
|
||||
os.makedirs(out_dir_zxy, exist_ok=True)
|
||||
out_path = os.path.join(out_dir_zxy, f"{t.y}.png")
|
||||
|
||||
tile, mask = cog.tile(t.x, t.y, z, resampling_method="nearest")
|
||||
|
||||
img = render(tile, mask=mask, img_format="PNG")
|
||||
|
||||
with open(out_path, "wb") as f:
|
||||
f.write(img)
|
||||
|
||||
print(f"Tile {z}/{t.x}/{t.y} generata")
|
||||
|
||||
print("Completato.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) != 3:
|
||||
print("Uso: python3 make_tiles_rio_tiler.py <input_rgb.tif> <output_dir>")
|
||||
sys.exit(1)
|
||||
|
||||
INPUT_TIF = sys.argv[1]
|
||||
OUTPUT_DIR = sys.argv[2]
|
||||
|
||||
if not os.path.isfile(INPUT_TIF):
|
||||
print(f"Errore: file non trovato: {INPUT_TIF}")
|
||||
sys.exit(1)
|
||||
|
||||
generate_tiles(INPUT_TIF, OUTPUT_DIR)
|
||||
|
||||
71
make_tiles_rio_tiler.py.old2
Normal file
71
make_tiles_rio_tiler.py.old2
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
#!/usr/bin/env python3
|
||||
import os
|
||||
import sys
|
||||
import mercantile
|
||||
from rio_tiler.io import COGReader
|
||||
from rio_tiler.utils import render
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# USO:
|
||||
# python3 make_tiles_rio_tiler.py input_rgb.tif tiles_out/
|
||||
#
|
||||
# Genera tile PNG Terrain-RGB senza alterare i pixel.
|
||||
# Corretto per evitare buchi tra quadranti Copernicus.
|
||||
# ---------------------------------------------------------
|
||||
|
||||
ZOOM_MIN = 5
|
||||
ZOOM_MAX = 14
|
||||
BUFFER = 0.001 # espansione per evitare tile mancanti ai bordi
|
||||
|
||||
def generate_tiles(input_tif, output_dir):
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
|
||||
with COGReader(input_tif) as cog:
|
||||
for z in range(ZOOM_MIN, ZOOM_MAX + 1):
|
||||
print(f"== Zoom {z} ==")
|
||||
|
||||
# Bounds originali del TIFF
|
||||
bounds = cog.bounds
|
||||
|
||||
# Espansione per evitare buchi tra quadranti
|
||||
minx = bounds[0] - BUFFER
|
||||
miny = bounds[1] - BUFFER
|
||||
maxx = bounds[2] + BUFFER
|
||||
maxy = bounds[3] + BUFFER
|
||||
|
||||
# Tile da generare
|
||||
tiles = list(mercantile.tiles(minx, miny, maxx, maxy, z))
|
||||
|
||||
for t in tiles:
|
||||
out_dir_zxy = os.path.join(output_dir, str(z), str(t.x))
|
||||
os.makedirs(out_dir_zxy, exist_ok=True)
|
||||
out_path = os.path.join(out_dir_zxy, f"{t.y}.png")
|
||||
|
||||
# Estrazione tile
|
||||
tile, mask = cog.tile(t.x, t.y, z, resampling_method="nearest")
|
||||
|
||||
# Render PNG
|
||||
img = render(tile, mask=mask, img_format="PNG")
|
||||
|
||||
with open(out_path, "wb") as f:
|
||||
f.write(img)
|
||||
|
||||
print(f"Tile {z}/{t.x}/{t.y} generata")
|
||||
|
||||
print("Completato.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) != 3:
|
||||
print("Uso: python3 make_tiles_rio_tiler.py <input_rgb.tif> <output_dir>")
|
||||
sys.exit(1)
|
||||
|
||||
INPUT_TIF = sys.argv[1]
|
||||
OUTPUT_DIR = sys.argv[2]
|
||||
|
||||
if not os.path.isfile(INPUT_TIF):
|
||||
print(f"Errore: file non trovato: {INPUT_TIF}")
|
||||
sys.exit(1)
|
||||
|
||||
generate_tiles(INPUT_TIF, OUTPUT_DIR)
|
||||
|
||||
139
mbtiles_set_metadata.sh
Executable file
139
mbtiles_set_metadata.sh
Executable file
|
|
@ -0,0 +1,139 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# Imposta metadati essenziali in un file .mbtiles (schema XYZ)
|
||||
# - Calcola bounds (ovest,sud,est,nord) dal raster sorgente (GeoTIFF/COG)
|
||||
# - Calcola center (lon,lat,zoom)
|
||||
# - Legge min/max zoom dalla tabella 'tiles' e li scrive nella 'metadata'
|
||||
# - Imposta name/format/type/attribution
|
||||
#
|
||||
# Requisiti: gdalinfo (GDAL), jq, sqlite3
|
||||
# --------------------------------------------------------------------
|
||||
# USO:
|
||||
# ./mbtiles_set_metadata.sh \
|
||||
# --mbtiles aa.mbtiles \
|
||||
# --raster aa_rgb.tif \
|
||||
# [--name "Terrain RGB Adamello"] \
|
||||
# [--type overlay|baselayer] \
|
||||
# [--format png|jpg] \
|
||||
# [--zoom <Zcenter>] \
|
||||
# [--attribution " Copernicus DEM (GLO-30), ESA"]
|
||||
#
|
||||
# NOTE:
|
||||
# - 'bounds' e 'center' sono calcolati in EPSG:4326 (lon/lat).
|
||||
# - Se il raster non riassume esattamente lestensione delle tile,
|
||||
# passa un raster "clip" coerente con larea tilata.
|
||||
# - Lo script usa REPLACE INTO per compatibilit con SQLite < 3.24.
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
# ---------- parsing argomenti ----------
|
||||
MBTILES=""
|
||||
RASTER=""
|
||||
LAYER_NAME=""
|
||||
LAYER_TYPE="overlay"
|
||||
FORMAT="png"
|
||||
CENTER_Z=""
|
||||
ATTRIBUTION=""
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--mbtiles) MBTILES="$2"; shift 2;;
|
||||
--raster) RASTER="$2"; shift 2;;
|
||||
--name) LAYER_NAME="$2"; shift 2;;
|
||||
--type) LAYER_TYPE="$2"; shift 2;;
|
||||
--format) FORMAT="$2"; shift 2;;
|
||||
--zoom) CENTER_Z="$2"; shift 2;;
|
||||
--attribution) ATTRIBUTION="$2"; shift 2;;
|
||||
-h|--help)
|
||||
sed -n '1,80p' "$0"; exit 0;;
|
||||
*)
|
||||
echo "Argomento sconosciuto: $1" >&2; exit 1;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z "$MBTILES" || -z "$RASTER" ]]; then
|
||||
echo "Uso: $0 --mbtiles <file.mbtiles> --raster <raster.tif> [opzioni]" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "$MBTILES" ]]; then
|
||||
echo "File MBTiles non trovato: $MBTILES" >&2; exit 1
|
||||
fi
|
||||
if [[ ! -f "$RASTER" ]]; then
|
||||
echo "Raster non trovato: $RASTER" >&2; exit 1
|
||||
fi
|
||||
|
||||
command -v gdalinfo >/dev/null || { echo "gdalinfo non trovato"; exit 1; }
|
||||
command -v jq >/dev/null || { echo "jq non trovato"; exit 1; }
|
||||
command -v sqlite3 >/dev/null || { echo "sqlite3 non trovato"; exit 1; }
|
||||
|
||||
# ---------- step 1: calcolo bounds dal raster (EPSG:4326) ----------
|
||||
# Usiamo wgs84Extent se presente, altrimenti cornerCoordinates.
|
||||
# gdalinfo -json fornisce geometrie e corner coords in lon/lat.
|
||||
export LC_ALL=C
|
||||
export LANG=C
|
||||
|
||||
GDAL_JSON="$(gdalinfo -json "$RASTER")"
|
||||
|
||||
has_wgs84=$(echo "$GDAL_JSON" | jq 'has("wgs84Extent")')
|
||||
if [[ "$has_wgs84" == "true" ]]; then
|
||||
# Polygon ring: [ [minLon,minLat], [maxLon,minLat], [maxLon,maxLat], [minLon,maxLat], ... ]
|
||||
read -r MINLON MINLAT MAXLON MAXLAT <<<"$(echo "$GDAL_JSON" \
|
||||
| jq -r '.wgs84Extent.coordinates[0] |
|
||||
[.[0][0], .[0][1], .[2][0], .[2][1]] | @tsv')"
|
||||
else
|
||||
# cornerCoordinates: upperLeft, lowerLeft, upperRight, lowerRight (lon,lat)
|
||||
read -r MINLON MINLAT MAXLON MAXLAT <<<"$(echo "$GDAL_JSON" \
|
||||
| jq -r '.cornerCoordinates as $c |
|
||||
[$c.lowerLeft[0], $c.lowerLeft[1], $c.upperRight[0], $c.upperRight[1]] | @tsv')"
|
||||
fi
|
||||
|
||||
# Arrotonda a 6 decimali per compattare la stringa
|
||||
printf -v BOUNDS "%.6f,%.6f,%.6f,%.6f" "$MINLON" "$MINLAT" "$MAXLON" "$MAXLAT"
|
||||
|
||||
# ---------- step 2: calcolo center (lon,lat,zoom) ----------
|
||||
# lon,lat = met dei bounds; zoom: se non fornito, media tra min e max zoom presenti nelle tile
|
||||
CTR_LON=$(awk -v a="$MINLON" -v b="$MAXLON" 'BEGIN{printf "%.6f",(a+b)/2.0}')
|
||||
CTR_LAT=$(awk -v a="$MINLAT" -v b="$MAXLAT" 'BEGIN{printf "%.6f",(a+b)/2.0}')
|
||||
|
||||
# Leggi min/max zoom dalla tabella 'tiles'
|
||||
read -r TMIN TMAX <<<"$(sqlite3 "$MBTILES" "SELECT MIN(zoom_level), MAX(zoom_level) FROM tiles;")"
|
||||
if [[ -z "$TMIN" || -z "$TMAX" ]]; then
|
||||
echo "Attenzione: tabella 'tiles' vuota o mancante in $MBTILES" >&2
|
||||
TMIN=0; TMAX=0
|
||||
fi
|
||||
|
||||
if [[ -z "${CENTER_Z}" ]]; then
|
||||
CENTER_Z=$(( (TMIN + TMAX) / 2 ))
|
||||
fi
|
||||
|
||||
CENTER="${CTR_LON},${CTR_LAT},${CENTER_Z}"
|
||||
|
||||
# ---------- step 3: definisci name/format/type/attribution ----------
|
||||
if [[ -z "$LAYER_NAME" ]]; then
|
||||
# Usa il nome file come default
|
||||
base="$(basename "$MBTILES")"
|
||||
LAYER_NAME="${base%.*}"
|
||||
fi
|
||||
|
||||
# ---------- step 4: crea tabella metadata (se non esiste) e scrivi i metadati ----------
|
||||
sqlite3 "$MBTILES" <<SQL
|
||||
CREATE TABLE IF NOT EXISTS metadata (name TEXT PRIMARY KEY, value TEXT);
|
||||
REPLACE INTO metadata(name,value) VALUES('name', '$LAYER_NAME');
|
||||
REPLACE INTO metadata(name,value) VALUES('format', '$FORMAT');
|
||||
REPLACE INTO metadata(name,value) VALUES('type', '$LAYER_TYPE');
|
||||
REPLACE INTO metadata(name,value) VALUES('minzoom', '$TMIN');
|
||||
REPLACE INTO metadata(name,value) VALUES('maxzoom', '$TMAX');
|
||||
REPLACE INTO metadata(name,value) VALUES('bounds', '$BOUNDS');
|
||||
REPLACE INTO metadata(name,value) VALUES('center', '$CENTER');
|
||||
$( [[ -n "$ATTRIBUTION" ]] && echo "REPLACE INTO metadata(name,value) VALUES('attribution','$ATTRIBUTION');" )
|
||||
SQL
|
||||
|
||||
# ---------- step 5: stampa riepilogo ----------
|
||||
echo "==> METADATA scritti in: $MBTILES"
|
||||
sqlite3 "$MBTILES" "SELECT name,value FROM metadata ORDER BY name;"
|
||||
echo
|
||||
echo "Zoom effettivi nelle tile: $(sqlite3 "$MBTILES" "SELECT MIN(zoom_level)||'..'||MAX(zoom_level) FROM tiles;")"
|
||||
echo "Bounds: $BOUNDS"
|
||||
echo "Center: $CENTER"
|
||||
19
rgb2alt.py
Normal file
19
rgb2alt.py
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
def rgb_to_altitude(R, G, B, scale=0.1, offset=-10000):
|
||||
value = (R * 65536) + (G * 256) + B
|
||||
return value * scale + offset
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
if len(sys.argv) != 4:
|
||||
print("Uso: python3 rgb2alt.py R G B")
|
||||
sys.exit(1)
|
||||
|
||||
R = int(sys.argv[1])
|
||||
G = int(sys.argv[2])
|
||||
B = int(sys.argv[3])
|
||||
|
||||
alt = rgb_to_altitude(R, G, B)
|
||||
print(alt)
|
||||
|
||||
49
rgbify_batch.py
Normal file
49
rgbify_batch.py
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
#!/usr/bin/env python3
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 3:
|
||||
print("Uso: python3 rgbify_batch.py <input_dir> <output_dir>")
|
||||
sys.exit(1)
|
||||
|
||||
input_dir = sys.argv[1]
|
||||
output_dir = sys.argv[2]
|
||||
|
||||
if not os.path.isdir(input_dir):
|
||||
print(f"Errore: directory di input non trovata: {input_dir}")
|
||||
sys.exit(1)
|
||||
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
|
||||
# Scansiona tutti i .tif nella directory di input
|
||||
for filename in os.listdir(input_dir):
|
||||
if not filename.lower().endswith(".tif"):
|
||||
continue
|
||||
|
||||
in_path = os.path.join(input_dir, filename)
|
||||
out_name = filename.replace(".tif", "_rgb.tif")
|
||||
out_path = os.path.join(output_dir, out_name)
|
||||
|
||||
print(f"[PROCESS] {filename} → {out_name}")
|
||||
|
||||
cmd = [
|
||||
"rio", "rgbify",
|
||||
"-b", "-10000",
|
||||
"-i", "0.1",
|
||||
in_path,
|
||||
out_path
|
||||
]
|
||||
|
||||
try:
|
||||
subprocess.run(cmd, check=True)
|
||||
print(f"[OK] {out_name}")
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"[ERROR] {filename}: {e}")
|
||||
|
||||
print("Completato.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
130
set_mbtiles_metadata.py
Normal file
130
set_mbtiles_metadata.py
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
#!/usr/bin/env python3
|
||||
import sqlite3
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
def read_gdal_bounds(raster):
|
||||
"""Legge i bounds WGS84 dal TIFF usando gdalinfo -json."""
|
||||
info = subprocess.check_output(["gdalinfo", "-json", raster])
|
||||
info = json.loads(info)
|
||||
|
||||
if "wgs84Extent" in info:
|
||||
coords = info["wgs84Extent"]["coordinates"][0]
|
||||
minlon, minlat = coords[0]
|
||||
maxlon, maxlat = coords[2]
|
||||
else:
|
||||
cc = info["cornerCoordinates"]
|
||||
minlon, minlat = cc["lowerLeft"]
|
||||
maxlon, maxlat = cc["upperRight"]
|
||||
|
||||
# Normalizzazione latitudine
|
||||
if minlat > maxlat:
|
||||
minlat, maxlat = maxlat, minlat
|
||||
|
||||
return (minlon, minlat, maxlon, maxlat)
|
||||
|
||||
|
||||
def open_mbtiles(mbtiles):
|
||||
"""Apre MBTiles e garantisce che la tabella metadata esista."""
|
||||
conn = sqlite3.connect(mbtiles)
|
||||
cur = conn.cursor()
|
||||
|
||||
# Verifica tabella tiles
|
||||
cur.execute("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='tiles';")
|
||||
if cur.fetchone()[0] == 0:
|
||||
raise RuntimeError("La tabella 'tiles' non esiste nell'MBTiles.")
|
||||
|
||||
# Conta tile
|
||||
cur.execute("SELECT COUNT(*) FROM tiles;")
|
||||
tile_count = cur.fetchone()[0]
|
||||
if tile_count == 0:
|
||||
raise RuntimeError("La tabella 'tiles' è vuota.")
|
||||
|
||||
# Zoom min/max
|
||||
cur.execute("SELECT MIN(zoom_level), MAX(zoom_level) FROM tiles;")
|
||||
minzoom, maxzoom = cur.fetchone()
|
||||
|
||||
# Crea metadata se manca
|
||||
cur.execute("""
|
||||
CREATE TABLE IF NOT EXISTS metadata (
|
||||
name TEXT PRIMARY KEY,
|
||||
value TEXT
|
||||
);
|
||||
""")
|
||||
|
||||
# Leggi metadata esistenti
|
||||
cur.execute("SELECT name, value FROM metadata;")
|
||||
metadata = dict(cur.fetchall())
|
||||
|
||||
return conn, cur, tile_count, minzoom, maxzoom, metadata
|
||||
|
||||
|
||||
def write_metadata(cur, name, value):
|
||||
cur.execute("REPLACE INTO metadata (name, value) VALUES (?, ?)", (name, value))
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 3:
|
||||
print("Uso: python3 set_mbtiles_metadata.py <raster.tif> <file.mbtiles> [name]")
|
||||
sys.exit(1)
|
||||
|
||||
raster = Path(sys.argv[1])
|
||||
mbtiles = Path(sys.argv[2])
|
||||
layer_name = sys.argv[3] if len(sys.argv) > 3 else mbtiles.stem
|
||||
|
||||
if not raster.exists():
|
||||
print(f"Raster non trovato: {raster}")
|
||||
sys.exit(1)
|
||||
if not mbtiles.exists():
|
||||
print(f"MBTiles non trovato: {mbtiles}")
|
||||
sys.exit(1)
|
||||
|
||||
print("== Impostazione metadata MBTiles ==")
|
||||
|
||||
# 1) Bounds dal TIFF
|
||||
minlon, minlat, maxlon, maxlat = read_gdal_bounds(raster)
|
||||
bounds = f"{minlon:.6f},{minlat:.6f},{maxlon:.6f},{maxlat:.6f}"
|
||||
print(f"Bounds TIFF: {bounds}")
|
||||
|
||||
# 2) Lettura MBTiles
|
||||
conn, cur, tile_count, minzoom, maxzoom, metadata = open_mbtiles(mbtiles)
|
||||
|
||||
print(f"Tile count: {tile_count}")
|
||||
print(f"Zoom effettivi: {minzoom}..{maxzoom}")
|
||||
|
||||
# 3) Center
|
||||
ctr_lon = (minlon + maxlon) / 2
|
||||
ctr_lat = (minlat + maxlat) / 2
|
||||
ctr_zoom = (minzoom + maxzoom) // 2
|
||||
center = f"{ctr_lon:.6f},{ctr_lat:.6f},{ctr_zoom}"
|
||||
|
||||
# 4) Scrittura metadata
|
||||
write_metadata(cur, "name", layer_name)
|
||||
write_metadata(cur, "format", "png")
|
||||
write_metadata(cur, "type", "overlay")
|
||||
write_metadata(cur, "scheme", "xyz")
|
||||
write_metadata(cur, "bounds", bounds)
|
||||
write_metadata(cur, "center", center)
|
||||
write_metadata(cur, "minzoom", str(minzoom))
|
||||
write_metadata(cur, "maxzoom", str(maxzoom))
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
print("\n== Metadata scritti correttamente ==")
|
||||
print(f"name: {layer_name}")
|
||||
print(f"format: png")
|
||||
print(f"type: overlay")
|
||||
print(f"scheme: xyz")
|
||||
print(f"bounds: {bounds}")
|
||||
print(f"center: {center}")
|
||||
print(f"minzoom: {minzoom}")
|
||||
print(f"maxzoom: {maxzoom}")
|
||||
print("\nMBTiles ora è pronto per TileServer GL.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
118
set_mbtiles_metadata_dir.py
Normal file
118
set_mbtiles_metadata_dir.py
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
#!/usr/bin/env python3
|
||||
import sqlite3
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
def read_bounds(tif):
|
||||
"""Legge i bounds W,S,E,N dal TIFF usando gdalinfo -json, arrotondati a 6 decimali."""
|
||||
info = json.loads(subprocess.check_output(["gdalinfo", "-json", str(tif)]))
|
||||
|
||||
if "wgs84Extent" in info:
|
||||
coords = info["wgs84Extent"]["coordinates"][0]
|
||||
minlon, minlat = coords[0]
|
||||
maxlon, maxlat = coords[2]
|
||||
else:
|
||||
cc = info["cornerCoordinates"]
|
||||
minlon, minlat = cc["lowerLeft"]
|
||||
maxlon, maxlat = cc["upperRight"]
|
||||
|
||||
# Normalizzazione
|
||||
if minlon > maxlon:
|
||||
minlon, maxlon = maxlon, minlon
|
||||
if minlat > maxlat:
|
||||
minlat, maxlat = maxlat, minlat
|
||||
|
||||
return (
|
||||
round(minlon, 6),
|
||||
round(minlat, 6),
|
||||
round(maxlon, 6),
|
||||
round(maxlat, 6),
|
||||
)
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 3:
|
||||
print("Uso: python3 set_mbtiles_metadata_multi.py <dir_tif> <merged.mbtiles>")
|
||||
sys.exit(1)
|
||||
|
||||
tif_dir = Path(sys.argv[1])
|
||||
mbtiles = Path(sys.argv[2])
|
||||
|
||||
if not tif_dir.exists() or not tif_dir.is_dir():
|
||||
print(f"Directory TIFF non valida: {tif_dir}")
|
||||
sys.exit(1)
|
||||
|
||||
if not mbtiles.exists():
|
||||
print(f"MBTiles non trovato: {mbtiles}")
|
||||
sys.exit(1)
|
||||
|
||||
# Trova tutti i TIFF nella directory
|
||||
tifs = sorted([p for p in tif_dir.iterdir() if p.suffix.lower() in (".tif", ".tiff")])
|
||||
|
||||
if not tifs:
|
||||
print("Nessun TIFF trovato nella directory.")
|
||||
sys.exit(1)
|
||||
|
||||
print(f"Trovati {len(tifs)} TIFF:")
|
||||
for t in tifs:
|
||||
print(" -", t.name)
|
||||
|
||||
# Calcolo bounds unificati
|
||||
bounds_list = [read_bounds(t) for t in tifs]
|
||||
|
||||
W = min(b[0] for b in bounds_list)
|
||||
S = min(b[1] for b in bounds_list)
|
||||
E = max(b[2] for b in bounds_list)
|
||||
N = max(b[3] for b in bounds_list)
|
||||
|
||||
bounds = f"{W:.6f},{S:.6f},{E:.6f},{N:.6f}"
|
||||
print("\nBounds unificati:", bounds)
|
||||
|
||||
# Apri MBTiles
|
||||
conn = sqlite3.connect(mbtiles)
|
||||
cur = conn.cursor()
|
||||
|
||||
cur.execute("""
|
||||
CREATE TABLE IF NOT EXISTS metadata (
|
||||
name TEXT PRIMARY KEY,
|
||||
value TEXT
|
||||
);
|
||||
""")
|
||||
|
||||
# Zoom reali dal MBTiles
|
||||
cur.execute("SELECT MIN(zoom_level), MAX(zoom_level) FROM tiles;")
|
||||
minzoom, maxzoom = cur.fetchone()
|
||||
|
||||
print(f"Zoom reali: {minzoom}..{maxzoom}")
|
||||
|
||||
# Center
|
||||
centerLon = (W + E) / 2
|
||||
centerLat = (S + N) / 2
|
||||
centerZoom = (minzoom + maxzoom) // 2
|
||||
center = f"{centerLon:.6f},{centerLat:.6f},{centerZoom}"
|
||||
|
||||
# Scrittura metadata
|
||||
cur.execute("REPLACE INTO metadata VALUES ('bounds', ?)", (bounds,))
|
||||
cur.execute("REPLACE INTO metadata VALUES ('center', ?)", (center,))
|
||||
cur.execute("REPLACE INTO metadata VALUES ('minzoom', ?)", (str(minzoom),))
|
||||
cur.execute("REPLACE INTO metadata VALUES ('maxzoom', ?)", (str(maxzoom),))
|
||||
cur.execute("REPLACE INTO metadata VALUES ('format', 'png')")
|
||||
cur.execute("REPLACE INTO metadata VALUES ('type', 'overlay')")
|
||||
cur.execute("REPLACE INTO metadata VALUES ('scheme', 'xyz')")
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
print("\nMetadata scritti correttamente:")
|
||||
print("bounds:", bounds)
|
||||
print("center:", center)
|
||||
print("minzoom:", minzoom)
|
||||
print("maxzoom:", maxzoom)
|
||||
print("format: png")
|
||||
print("type: overlay")
|
||||
print("scheme: xyz")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
85
set_mbtiles_metadata_multi.py
Normal file
85
set_mbtiles_metadata_multi.py
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
#!/usr/bin/env python3
|
||||
import sqlite3
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
def read_bounds(tif):
|
||||
info = json.loads(subprocess.check_output(["gdalinfo", "-json", tif]))
|
||||
if "wgs84Extent" in info:
|
||||
coords = info["wgs84Extent"]["coordinates"][0]
|
||||
minlon, minlat = coords[0]
|
||||
maxlon, maxlat = coords[2]
|
||||
else:
|
||||
cc = info["cornerCoordinates"]
|
||||
minlon, minlat = cc["lowerLeft"]
|
||||
maxlon, maxlat = cc["upperRight"]
|
||||
|
||||
if minlat > maxlat:
|
||||
minlat, maxlat = maxlat, minlat
|
||||
if minlon > maxlon:
|
||||
minlon, maxlon = maxlon, minlon
|
||||
|
||||
return (
|
||||
round(minlon, 6),
|
||||
round(minlat, 6),
|
||||
round(maxlon, 6),
|
||||
round(maxlat, 6),
|
||||
)
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 4:
|
||||
print("Uso: python3 set_mbtiles_metadata_multi.py raster1.tif raster2.tif merged.mbtiles")
|
||||
sys.exit(1)
|
||||
|
||||
tif1 = Path(sys.argv[1])
|
||||
tif2 = Path(sys.argv[2])
|
||||
mbtiles = Path(sys.argv[3])
|
||||
|
||||
b1 = read_bounds(tif1)
|
||||
b2 = read_bounds(tif2)
|
||||
|
||||
W = min(b1[0], b2[0])
|
||||
S = min(b1[1], b2[1])
|
||||
E = max(b1[2], b2[2])
|
||||
N = max(b1[3], b2[3])
|
||||
|
||||
bounds = f"{W:.6f},{S:.6f},{E:.6f},{N:.6f}"
|
||||
|
||||
conn = sqlite3.connect(mbtiles)
|
||||
cur = conn.cursor()
|
||||
|
||||
cur.execute("CREATE TABLE IF NOT EXISTS metadata (name TEXT PRIMARY KEY, value TEXT);")
|
||||
|
||||
cur.execute("SELECT MIN(zoom_level), MAX(zoom_level) FROM tiles;")
|
||||
minzoom, maxzoom = cur.fetchone()
|
||||
|
||||
centerLon = (W + E) / 2
|
||||
centerLat = (S + N) / 2
|
||||
centerZoom = (minzoom + maxzoom) // 2
|
||||
center = f"{centerLon:.6f},{centerLat:.6f},{centerZoom}"
|
||||
|
||||
cur.execute("REPLACE INTO metadata VALUES ('bounds', ?)", (bounds,))
|
||||
cur.execute("REPLACE INTO metadata VALUES ('center', ?)", (center,))
|
||||
cur.execute("REPLACE INTO metadata VALUES ('minzoom', ?)", (str(minzoom),))
|
||||
cur.execute("REPLACE INTO metadata VALUES ('maxzoom', ?)", (str(maxzoom),))
|
||||
cur.execute("REPLACE INTO metadata VALUES ('format', 'png')")
|
||||
cur.execute("REPLACE INTO metadata VALUES ('type', 'overlay')")
|
||||
cur.execute("REPLACE INTO metadata VALUES ('scheme', 'xyz')")
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
print("Metadata scritti correttamente:")
|
||||
print("bounds:", bounds)
|
||||
print("center:", center)
|
||||
print("minzoom:", minzoom)
|
||||
print("maxzoom:", maxzoom)
|
||||
print("format: png")
|
||||
print("type: overlay")
|
||||
print("scheme: xyz")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
144
verify_and_fix_mbtiles.py
Normal file
144
verify_and_fix_mbtiles.py
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
#!/usr/bin/env python3
|
||||
import sqlite3
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
def read_gdal_bounds(raster):
|
||||
"""Legge i bounds WGS84 dal TIFF usando gdalinfo -json."""
|
||||
info = subprocess.check_output(["gdalinfo", "-json", raster])
|
||||
info = json.loads(info)
|
||||
|
||||
if "wgs84Extent" in info:
|
||||
coords = info["wgs84Extent"]["coordinates"][0]
|
||||
minlon, minlat = coords[0]
|
||||
maxlon, maxlat = coords[2]
|
||||
else:
|
||||
cc = info["cornerCoordinates"]
|
||||
minlon, minlat = cc["lowerLeft"]
|
||||
maxlon, maxlat = cc["upperRight"]
|
||||
|
||||
# Normalizzazione latitudine
|
||||
if minlat > maxlat:
|
||||
minlat, maxlat = maxlat, minlat
|
||||
|
||||
return (minlon, minlat, maxlon, maxlat)
|
||||
|
||||
|
||||
def read_mbtiles(mbtiles):
|
||||
"""Legge tile info e metadata da un MBTiles."""
|
||||
conn = sqlite3.connect(mbtiles)
|
||||
cur = conn.cursor()
|
||||
|
||||
# Verifica tabella tiles
|
||||
cur.execute("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='tiles';")
|
||||
if cur.fetchone()[0] == 0:
|
||||
raise RuntimeError("La tabella 'tiles' non esiste nell'MBTiles.")
|
||||
|
||||
# Conta tile
|
||||
cur.execute("SELECT COUNT(*) FROM tiles;")
|
||||
tile_count = cur.fetchone()[0]
|
||||
if tile_count == 0:
|
||||
raise RuntimeError("La tabella 'tiles' è vuota.")
|
||||
|
||||
# Zoom min/max
|
||||
cur.execute("SELECT MIN(zoom_level), MAX(zoom_level) FROM tiles;")
|
||||
minzoom, maxzoom = cur.fetchone()
|
||||
|
||||
# Metadata
|
||||
cur.execute("CREATE TABLE IF NOT EXISTS metadata (name TEXT PRIMARY KEY, value TEXT);")
|
||||
cur.execute("SELECT name, value FROM metadata;")
|
||||
metadata = dict(cur.fetchall())
|
||||
|
||||
return conn, cur, tile_count, minzoom, maxzoom, metadata
|
||||
|
||||
|
||||
def write_metadata(cur, name, value):
|
||||
cur.execute("REPLACE INTO metadata (name, value) VALUES (?, ?)", (name, value))
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 3:
|
||||
print("Uso: python3 verify_and_fix_mbtiles.py <raster.tif> <file.mbtiles>")
|
||||
sys.exit(1)
|
||||
|
||||
raster = Path(sys.argv[1])
|
||||
mbtiles = Path(sys.argv[2])
|
||||
|
||||
if not raster.exists():
|
||||
print(f"Raster non trovato: {raster}")
|
||||
sys.exit(1)
|
||||
if not mbtiles.exists():
|
||||
print(f"MBTiles non trovato: {mbtiles}")
|
||||
sys.exit(1)
|
||||
|
||||
print("== Verifica e correzione metadata MBTiles ==")
|
||||
|
||||
# 1) Bounds dal TIFF
|
||||
minlon, minlat, maxlon, maxlat = read_gdal_bounds(raster)
|
||||
expected_bounds = f"{minlon:.6f},{minlat:.6f},{maxlon:.6f},{maxlat:.6f}"
|
||||
print(f"Bounds TIFF: {expected_bounds}")
|
||||
|
||||
# 2) Lettura MBTiles
|
||||
conn, cur, tile_count, minzoom, maxzoom, metadata = read_mbtiles(mbtiles)
|
||||
|
||||
print(f"Tile count: {tile_count}")
|
||||
print(f"Zoom effettivi: {minzoom}..{maxzoom}")
|
||||
|
||||
# 3) Verifica/correzione bounds
|
||||
mb_bounds = metadata.get("bounds")
|
||||
if mb_bounds != expected_bounds:
|
||||
print(f"✘ Bounds NON corretti → FIX")
|
||||
write_metadata(cur, "bounds", expected_bounds)
|
||||
else:
|
||||
print("✔ Bounds OK")
|
||||
|
||||
# 4) Verifica/correzione minzoom/maxzoom
|
||||
mz = int(metadata.get("minzoom", -1))
|
||||
xz = int(metadata.get("maxzoom", -1))
|
||||
|
||||
if mz != minzoom or xz != maxzoom:
|
||||
print(f"✘ minzoom/maxzoom NON coerenti → FIX")
|
||||
write_metadata(cur, "minzoom", str(minzoom))
|
||||
write_metadata(cur, "maxzoom", str(maxzoom))
|
||||
else:
|
||||
print("✔ minzoom/maxzoom OK")
|
||||
|
||||
# 5) Verifica/correzione center
|
||||
ctr_lon = (minlon + maxlon) / 2
|
||||
ctr_lat = (minlat + maxlat) / 2
|
||||
ctr_zoom = (minzoom + maxzoom) // 2
|
||||
expected_center = f"{ctr_lon:.6f},{ctr_lat:.6f},{ctr_zoom}"
|
||||
|
||||
if metadata.get("center") != expected_center:
|
||||
print(f"✘ Center NON corretto → FIX")
|
||||
write_metadata(cur, "center", expected_center)
|
||||
else:
|
||||
print("✔ Center OK")
|
||||
|
||||
# 6) Verifica/correzione format
|
||||
if metadata.get("format") != "png":
|
||||
print("✘ Format NON corretto → FIX")
|
||||
write_metadata(cur, "format", "png")
|
||||
else:
|
||||
print("✔ Format OK")
|
||||
|
||||
# 7) Verifica/correzione scheme
|
||||
if metadata.get("scheme") != "xyz":
|
||||
print("✘ Scheme NON corretto → FIX")
|
||||
write_metadata(cur, "scheme", "xyz")
|
||||
else:
|
||||
print("✔ Scheme OK")
|
||||
|
||||
# 8) Commit
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
print("\n== Correzione completata ==")
|
||||
print("MBTiles ora ha metadata coerenti e validi.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
122
verify_bounds_tileservergl.py
Normal file
122
verify_bounds_tileservergl.py
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
#!/usr/bin/env python3
|
||||
import sqlite3
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
EPS = 1e-6 # tolleranza per confronti float
|
||||
|
||||
def almost_equal(a, b):
|
||||
return abs(a - b) < EPS
|
||||
|
||||
def read_gdal_bounds(raster):
|
||||
"""Legge i bounds W,S,E,N dal TIFF usando gdalinfo -json, arrotondati a 6 decimali."""
|
||||
info = subprocess.check_output(["gdalinfo", "-json", raster])
|
||||
info = json.loads(info)
|
||||
|
||||
if "wgs84Extent" in info:
|
||||
coords = info["wgs84Extent"]["coordinates"][0]
|
||||
minlon, minlat = coords[0]
|
||||
maxlon, maxlat = coords[2]
|
||||
else:
|
||||
cc = info["cornerCoordinates"]
|
||||
minlon, minlat = cc["lowerLeft"]
|
||||
maxlon, maxlat = cc["upperRight"]
|
||||
|
||||
# Normalizzazione
|
||||
if minlon > maxlon:
|
||||
minlon, maxlon = maxlon, minlon
|
||||
if minlat > maxlat:
|
||||
minlat, maxlat = maxlat, minlat
|
||||
|
||||
# Arrotondamento a 6 decimali (standard MBTiles)
|
||||
return (
|
||||
round(minlon, 6),
|
||||
round(minlat, 6),
|
||||
round(maxlon, 6),
|
||||
round(maxlat, 6),
|
||||
)
|
||||
|
||||
def read_mbtiles_bounds(cur):
|
||||
cur.execute("SELECT value FROM metadata WHERE name='bounds';")
|
||||
row = cur.fetchone()
|
||||
if not row:
|
||||
return None
|
||||
try:
|
||||
w, s, e, n = map(float, row[0].split(","))
|
||||
return (w, s, e, n)
|
||||
except:
|
||||
return None
|
||||
|
||||
def write_bounds(cur, bounds):
|
||||
w, s, e, n = bounds
|
||||
value = f"{w:.6f},{s:.6f},{e:.6f},{n:.6f}"
|
||||
cur.execute("REPLACE INTO metadata (name,value) VALUES ('bounds', ?)", (value,))
|
||||
return value
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 3:
|
||||
print("Uso: python3 verify_bounds_tileservergl.py <raster.tif> <file.mbtiles>")
|
||||
sys.exit(1)
|
||||
|
||||
raster = Path(sys.argv[1])
|
||||
mbtiles = Path(sys.argv[2])
|
||||
|
||||
if not raster.exists():
|
||||
print(f"Raster non trovato: {raster}")
|
||||
sys.exit(1)
|
||||
if not mbtiles.exists():
|
||||
print(f"MBTiles non trovato: {mbtiles}")
|
||||
sys.exit(1)
|
||||
|
||||
print("== Verifica bounds per TileServer GL (W,S,E,N) ==")
|
||||
|
||||
# Bounds dal TIFF
|
||||
tiff_bounds = read_gdal_bounds(raster)
|
||||
print(f"Bounds TIFF (W,S,E,N): {tiff_bounds}")
|
||||
|
||||
# Apri MBTiles
|
||||
conn = sqlite3.connect(mbtiles)
|
||||
cur = conn.cursor()
|
||||
|
||||
# Assicura tabella metadata
|
||||
cur.execute("""
|
||||
CREATE TABLE IF NOT EXISTS metadata (
|
||||
name TEXT PRIMARY KEY,
|
||||
value TEXT
|
||||
);
|
||||
""")
|
||||
|
||||
# Bounds MBTiles
|
||||
mb_bounds = read_mbtiles_bounds(cur)
|
||||
print(f"Bounds MBTiles: {mb_bounds}")
|
||||
|
||||
# Se mancano → scrivili
|
||||
if mb_bounds is None:
|
||||
print("✘ Bounds mancanti → FIX")
|
||||
new_value = write_bounds(cur, tiff_bounds)
|
||||
print(f"✔ Bounds scritti: {new_value}")
|
||||
conn.commit()
|
||||
conn.close()
|
||||
print("\n== Verifica completata ==")
|
||||
return
|
||||
|
||||
# Confronto con tolleranza
|
||||
w1, s1, e1, n1 = tiff_bounds
|
||||
w2, s2, e2, n2 = mb_bounds
|
||||
|
||||
if not (almost_equal(w1, w2) and almost_equal(s1, s2) and almost_equal(e1, e2) and almost_equal(n1, n2)):
|
||||
print("✘ Bounds NON corretti → FIX")
|
||||
new_value = write_bounds(cur, tiff_bounds)
|
||||
print(f"✔ Bounds aggiornati a: {new_value}")
|
||||
conn.commit()
|
||||
else:
|
||||
print("✔ Bounds OK (nessuna correzione necessaria)")
|
||||
|
||||
conn.close()
|
||||
print("\n== Verifica completata ==")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
122
verify_mbtiles_metadata.py
Normal file
122
verify_mbtiles_metadata.py
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
#!/usr/bin/env python3
|
||||
import sqlite3
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
def read_gdal_bounds(raster):
|
||||
"""Legge i bounds WGS84 dal TIFF usando gdalinfo -json."""
|
||||
info = subprocess.check_output(["gdalinfo", "-json", raster])
|
||||
info = json.loads(info)
|
||||
|
||||
if "wgs84Extent" in info:
|
||||
coords = info["wgs84Extent"]["coordinates"][0]
|
||||
minlon, minlat = coords[0]
|
||||
maxlon, maxlat = coords[2]
|
||||
else:
|
||||
cc = info["cornerCoordinates"]
|
||||
minlon, minlat = cc["lowerLeft"]
|
||||
maxlon, maxlat = cc["upperRight"]
|
||||
|
||||
# Normalizzazione latitudine
|
||||
if minlat > maxlat:
|
||||
minlat, maxlat = maxlat, minlat
|
||||
|
||||
return (minlon, minlat, maxlon, maxlat)
|
||||
|
||||
|
||||
def read_mbtiles_metadata(mbtiles):
|
||||
"""Legge metadata e tile info da un MBTiles."""
|
||||
conn = sqlite3.connect(mbtiles)
|
||||
cur = conn.cursor()
|
||||
|
||||
# Verifica tabella tiles
|
||||
cur.execute("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='tiles';")
|
||||
if cur.fetchone()[0] == 0:
|
||||
raise RuntimeError("La tabella 'tiles' non esiste nell'MBTiles.")
|
||||
|
||||
# Conta tile
|
||||
cur.execute("SELECT COUNT(*) FROM tiles;")
|
||||
tile_count = cur.fetchone()[0]
|
||||
if tile_count == 0:
|
||||
raise RuntimeError("La tabella 'tiles' è vuota.")
|
||||
|
||||
# Zoom min/max
|
||||
cur.execute("SELECT MIN(zoom_level), MAX(zoom_level) FROM tiles;")
|
||||
minzoom, maxzoom = cur.fetchone()
|
||||
|
||||
# Metadata
|
||||
cur.execute("SELECT name, value FROM metadata;")
|
||||
metadata = dict(cur.fetchall())
|
||||
|
||||
conn.close()
|
||||
return tile_count, minzoom, maxzoom, metadata
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 3:
|
||||
print("Uso: python3 verify_mbtiles_metadata.py <raster.tif> <file.mbtiles>")
|
||||
sys.exit(1)
|
||||
|
||||
raster = Path(sys.argv[1])
|
||||
mbtiles = Path(sys.argv[2])
|
||||
|
||||
if not raster.exists():
|
||||
print(f"Raster non trovato: {raster}")
|
||||
sys.exit(1)
|
||||
if not mbtiles.exists():
|
||||
print(f"MBTiles non trovato: {mbtiles}")
|
||||
sys.exit(1)
|
||||
|
||||
print("== Verifica metadata MBTiles ==")
|
||||
|
||||
# 1) Bounds dal TIFF
|
||||
minlon, minlat, maxlon, maxlat = read_gdal_bounds(raster)
|
||||
print(f"Bounds TIFF: {minlon},{minlat},{maxlon},{maxlat}")
|
||||
|
||||
# 2) Lettura MBTiles
|
||||
tile_count, minzoom, maxzoom, metadata = read_mbtiles_metadata(mbtiles)
|
||||
|
||||
print(f"Tile count: {tile_count}")
|
||||
print(f"Zoom effettivi: {minzoom}..{maxzoom}")
|
||||
|
||||
# 3) Confronto bounds
|
||||
mb_bounds = metadata.get("bounds", None)
|
||||
if mb_bounds:
|
||||
mb_minlon, mb_minlat, mb_maxlon, mb_maxlat = map(float, mb_bounds.split(","))
|
||||
print(f"Bounds MBTiles: {mb_bounds}")
|
||||
|
||||
if abs(mb_minlon - minlon) < 1e-4 and \
|
||||
abs(mb_minlat - minlat) < 1e-4 and \
|
||||
abs(mb_maxlon - maxlon) < 1e-4 and \
|
||||
abs(mb_maxlat - maxlat) < 1e-4:
|
||||
print("✔ Bounds OK")
|
||||
else:
|
||||
print("✘ Bounds NON corrispondono al TIFF")
|
||||
else:
|
||||
print("✘ Metadata 'bounds' mancante")
|
||||
|
||||
# 4) Confronto minzoom/maxzoom
|
||||
if "minzoom" in metadata and "maxzoom" in metadata:
|
||||
mz = int(metadata["minzoom"])
|
||||
xz = int(metadata["maxzoom"])
|
||||
if mz == minzoom and xz == maxzoom:
|
||||
print("✔ minzoom/maxzoom OK")
|
||||
else:
|
||||
print(f"✘ minzoom/maxzoom NON coerenti: metadata={mz}..{xz}, reali={minzoom}..{maxzoom}")
|
||||
else:
|
||||
print("✘ Metadata minzoom/maxzoom mancanti")
|
||||
|
||||
# 5) Center
|
||||
if "center" in metadata:
|
||||
print(f"Center: {metadata['center']}")
|
||||
else:
|
||||
print("✘ Metadata 'center' mancante")
|
||||
|
||||
print("\n== Verifica completata ==")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
68
w.py
Normal file
68
w.py
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
import boto3
|
||||
import os
|
||||
import logging
|
||||
from botocore import UNSIGNED
|
||||
from botocore.config import Config
|
||||
|
||||
BUCKET = "copernicus-dem-30m"
|
||||
OUTPUT_DIR = "./glo30_world"
|
||||
LOG_FILE = "download_world.log"
|
||||
|
||||
logging.basicConfig(
|
||||
filename=LOG_FILE,
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s [%(levelname)s] %(message)s",
|
||||
)
|
||||
|
||||
console = logging.getLogger("console")
|
||||
console.setLevel(logging.INFO)
|
||||
console.addHandler(logging.StreamHandler())
|
||||
|
||||
def log(msg):
|
||||
logging.info(msg)
|
||||
console.info(msg)
|
||||
|
||||
os.makedirs(OUTPUT_DIR, exist_ok=True)
|
||||
|
||||
s3 = boto3.client("s3", config=Config(signature_version=UNSIGNED))
|
||||
|
||||
log("Inizio scansione di tutte le cartelle globali...")
|
||||
|
||||
folders = []
|
||||
|
||||
paginator = s3.get_paginator("list_objects_v2")
|
||||
|
||||
# Legge SOLO le directory top-level
|
||||
for page in paginator.paginate(Bucket=BUCKET, Delimiter="/"):
|
||||
for prefix in page.get("CommonPrefixes", []):
|
||||
folder = prefix["Prefix"]
|
||||
|
||||
# Filtra solo i tile DEM
|
||||
if folder.startswith("Copernicus_DSM_COG_10_") and
|
||||
folder.endswith("_DEM/"):
|
||||
folders.append(folder)
|
||||
log(f"[FOUND] {folder}")
|
||||
|
||||
log(f"Trovate {len(folders)} cartelle globali")
|
||||
|
||||
# -----------------------------
|
||||
# DOWNLOAD SOLO IL DEM PRINCIPALE
|
||||
# -----------------------------
|
||||
for folder in folders:
|
||||
tif_name = folder[:-1] + ".tif" # rimuove "/" e aggiunge .tif
|
||||
key = folder + tif_name.split("/")[-1]
|
||||
out_path = os.path.join(OUTPUT_DIR, tif_name.split("/")[-1])
|
||||
|
||||
if os.path.exists(out_path):
|
||||
log(f"[SKIP] {out_path} già presente")
|
||||
continue
|
||||
|
||||
log(f"[DOWNLOAD] {key}")
|
||||
try:
|
||||
s3.download_file(BUCKET, key, out_path)
|
||||
log(f"[OK] {key} scaricato")
|
||||
except Exception as e:
|
||||
log(f"[ERROR] {key} → {e}")
|
||||
|
||||
log("Completato.")
|
||||
|
||||
Loading…
Reference in a new issue