Backup automatico script del 2026-09-19 07:00
This commit is contained in:
1 parent
35677497dc
commit
f347badf3b
4 files changed
+144
-39
No files matched your search
@@ -0,0 +1,112 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Basemap tiles for matplotlib maps (road weather, ice, snow radar).
|
||||
|
||||
Prefer tile sources that do **not** require an API key:
|
||||
|
||||
- OpenStreetMap Mapnik — OK with an identifying User-Agent
|
||||
(contextily's default UA gets "403 Access blocked" tiles)
|
||||
- Esri WorldStreetMap — free for reasonable use, good road detail
|
||||
- OpenTopoMap — terrain fallback
|
||||
|
||||
CARTO basemaps are avoided: they now overlay "API KEY REQUIRED".
|
||||
|
||||
Compatible with contextily 1.6.x (loogle-casa) and 1.7.x (loogle-bot):
|
||||
in 1.6, ``headers=`` is forwarded to ``imshow`` and breaks the call,
|
||||
so we set ``contextily.tile.USER_AGENT`` instead.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import inspect
|
||||
import logging
|
||||
from typing import Any, Optional
|
||||
|
||||
LOGGER = logging.getLogger("basemap_tiles")
|
||||
|
||||
BASEMAP_USER_AGENT = "LoogleCasa/1.0 (https://casa.loogle.it; meteo maps)"
|
||||
BASEMAP_HEADERS = {"User-Agent": BASEMAP_USER_AGENT}
|
||||
|
||||
|
||||
def _configure_user_agent() -> None:
|
||||
try:
|
||||
import contextily.tile as tile_mod
|
||||
|
||||
tile_mod.USER_AGENT = BASEMAP_USER_AGENT
|
||||
except Exception as exc: # noqa: BLE001
|
||||
LOGGER.debug("Impossibile impostare USER_AGENT contextily: %s", exc)
|
||||
|
||||
|
||||
def _providers(ctx) -> list:
|
||||
"""Ordered list of free (no API key) basemap providers."""
|
||||
out = []
|
||||
candidates = [
|
||||
("OpenStreetMap", "Mapnik"),
|
||||
("Esri", "WorldStreetMap"),
|
||||
("OpenTopoMap",),
|
||||
("OpenStreetMap", "DE"),
|
||||
("OpenStreetMap", "HOT"),
|
||||
]
|
||||
for path in candidates:
|
||||
try:
|
||||
node = ctx.providers
|
||||
for part in path:
|
||||
node = getattr(node, part)
|
||||
# skip leaf namespaces that aren't a provider dict
|
||||
if hasattr(node, "url") or (isinstance(node, dict) and "url" in node):
|
||||
out.append(node)
|
||||
elif path == ("OpenTopoMap",):
|
||||
# xyzservices LeafMap style
|
||||
out.append(node)
|
||||
except AttributeError:
|
||||
continue
|
||||
return out
|
||||
|
||||
|
||||
def add_basemap(
|
||||
ax: Any,
|
||||
*,
|
||||
alpha: float = 0.85,
|
||||
attribution_size: int = 6,
|
||||
crs: str = "EPSG:4326",
|
||||
) -> bool:
|
||||
"""Add a web basemap under *ax*. Returns True on success."""
|
||||
try:
|
||||
import contextily as ctx
|
||||
except ImportError:
|
||||
LOGGER.warning("contextily non disponibile: mappa senza basemap")
|
||||
return False
|
||||
|
||||
_configure_user_agent()
|
||||
providers = _providers(ctx)
|
||||
if not providers:
|
||||
LOGGER.warning("Nessun provider basemap trovato in contextily")
|
||||
return False
|
||||
|
||||
call_kwargs: dict = {
|
||||
"crs": crs,
|
||||
"alpha": alpha,
|
||||
"attribution_size": attribution_size,
|
||||
}
|
||||
try:
|
||||
if "headers" in inspect.signature(ctx.add_basemap).parameters:
|
||||
call_kwargs["headers"] = BASEMAP_HEADERS
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
|
||||
last_err: Optional[BaseException] = None
|
||||
for source in providers:
|
||||
name = getattr(source, "name", None) or (
|
||||
source.get("name") if isinstance(source, dict) else str(source)
|
||||
)
|
||||
try:
|
||||
ctx.add_basemap(ax, source=source, **call_kwargs)
|
||||
LOGGER.info("Basemap OK: %s", name)
|
||||
return True
|
||||
except Exception as exc: # noqa: BLE001
|
||||
last_err = exc
|
||||
LOGGER.warning("Basemap %s fallita: %s", name, exc)
|
||||
|
||||
if last_err:
|
||||
LOGGER.warning("Nessuna basemap disponibile: %s", last_err)
|
||||
return False
|
||||
@@ -1673,13 +1673,14 @@ def generate_route_ice_map(df: pd.DataFrame, city1: str, city2: str, output_path
|
||||
print(f"matplotlib non disponibile: {e}. Mappa non generata.")
|
||||
return False
|
||||
|
||||
# Prova a importare contextily per mappa di sfondo
|
||||
# Prova a importare basemap (CARTO / OSM HOT; non tile.openstreetmap.org)
|
||||
try:
|
||||
import contextily as ctx
|
||||
from basemap_tiles import add_basemap
|
||||
CONTEXTILY_AVAILABLE = True
|
||||
except ImportError:
|
||||
CONTEXTILY_AVAILABLE = False
|
||||
print("contextily non disponibile. Mappa generata senza sfondo geografico.")
|
||||
add_basemap = None # type: ignore
|
||||
print("contextily/basemap_tiles non disponibile. Mappa generata senza sfondo geografico.")
|
||||
|
||||
if df.empty:
|
||||
return False
|
||||
@@ -1720,13 +1721,10 @@ def generate_route_ice_map(df: pd.DataFrame, city1: str, city2: str, output_path
|
||||
ax.set_ylim(lat_min, lat_max)
|
||||
ax.set_aspect('equal', adjustable='box')
|
||||
|
||||
# Aggiungi mappa di sfondo OpenStreetMap se disponibile
|
||||
if CONTEXTILY_AVAILABLE:
|
||||
try:
|
||||
ctx.add_basemap(ax, crs='EPSG:4326', source=ctx.providers.OpenStreetMap.Mapnik,
|
||||
alpha=0.6, attribution_size=6)
|
||||
except Exception as e:
|
||||
print(f"Errore aggiunta mappa sfondo: {e}")
|
||||
# Aggiungi mappa di sfondo (CARTO; OSM Mapnik è bloccato per lo UA di contextily)
|
||||
if CONTEXTILY_AVAILABLE and add_basemap is not None:
|
||||
if not add_basemap(ax, alpha=0.85, attribution_size=6):
|
||||
print("Errore aggiunta mappa sfondo")
|
||||
CONTEXTILY_AVAILABLE = False
|
||||
|
||||
# Disegna linea del percorso
|
||||
@@ -1871,13 +1869,14 @@ def generate_ice_risk_map(points_data: List[Dict], output_path: str) -> bool:
|
||||
print(f"matplotlib non disponibile: {e}. Mappa non generata.")
|
||||
return False
|
||||
|
||||
# Prova a importare contextily per mappa di sfondo
|
||||
# Prova a importare basemap (CARTO / OSM HOT; non tile.openstreetmap.org)
|
||||
try:
|
||||
import contextily as ctx
|
||||
from basemap_tiles import add_basemap
|
||||
CONTEXTILY_AVAILABLE = True
|
||||
except ImportError:
|
||||
CONTEXTILY_AVAILABLE = False
|
||||
print("contextily non disponibile. Mappa generata senza sfondo geografico.")
|
||||
add_basemap = None # type: ignore
|
||||
print("contextily/basemap_tiles non disponibile. Mappa generata senza sfondo geografico.")
|
||||
|
||||
if not points_data:
|
||||
return False
|
||||
@@ -1901,13 +1900,10 @@ def generate_ice_risk_map(points_data: List[Dict], output_path: str) -> bool:
|
||||
ax.set_ylim(lat_min, lat_max)
|
||||
ax.set_aspect('equal', adjustable='box')
|
||||
|
||||
# Aggiungi mappa di sfondo OpenStreetMap se disponibile
|
||||
if CONTEXTILY_AVAILABLE:
|
||||
try:
|
||||
ctx.add_basemap(ax, crs='EPSG:4326', source=ctx.providers.OpenStreetMap.Mapnik,
|
||||
alpha=0.6, attribution_size=6)
|
||||
except Exception as e:
|
||||
print(f"Errore aggiunta mappa sfondo: {e}")
|
||||
# Aggiungi mappa di sfondo (CARTO; OSM Mapnik è bloccato per lo UA di contextily)
|
||||
if CONTEXTILY_AVAILABLE and add_basemap is not None:
|
||||
if not add_basemap(ax, alpha=0.85, attribution_size=6):
|
||||
print("Errore aggiunta mappa sfondo")
|
||||
CONTEXTILY_AVAILABLE = False
|
||||
|
||||
# Colori per livelli di rischio: verde (0), giallo (1), arancione (2), rosso scuro (3), azzurro (4=neve)
|
||||
|
||||
@@ -1793,10 +1793,11 @@ def generate_route_weather_map(df: pd.DataFrame, city1: str, city2: str, output_
|
||||
return False
|
||||
|
||||
try:
|
||||
import contextily as ctx
|
||||
from basemap_tiles import add_basemap
|
||||
CONTEXTILY_AVAILABLE = True
|
||||
except ImportError:
|
||||
CONTEXTILY_AVAILABLE = False
|
||||
add_basemap = None # type: ignore
|
||||
|
||||
if df.empty:
|
||||
return False
|
||||
@@ -1868,11 +1869,10 @@ def generate_route_weather_map(df: pd.DataFrame, city1: str, city2: str, output_
|
||||
ax.set_ylim(lat_min, lat_max)
|
||||
ax.set_aspect('equal', adjustable='box')
|
||||
|
||||
if CONTEXTILY_AVAILABLE:
|
||||
try:
|
||||
ctx.add_basemap(ax, crs='EPSG:4326', source=ctx.providers.OpenStreetMap.Mapnik,
|
||||
alpha=0.6, attribution_size=6)
|
||||
except:
|
||||
if CONTEXTILY_AVAILABLE and add_basemap is not None:
|
||||
# Non usare tile.openstreetmap.org: OSM blocca lo User-Agent di contextily
|
||||
# e restituisce tile "403 Access blocked" (vedi osm.wiki/Blocked).
|
||||
if not add_basemap(ax, alpha=0.85, attribution_size=6):
|
||||
CONTEXTILY_AVAILABLE = False
|
||||
|
||||
# Linea percorso
|
||||
|
||||
@@ -340,13 +340,14 @@ def generate_snow_map(results: List[Dict], center_lat: float, center_lon: float,
|
||||
LOGGER.warning("matplotlib non disponibile: %s. Mappa non generata.", e)
|
||||
return False
|
||||
|
||||
# Prova a importare contextily per mappa di sfondo
|
||||
# Prova a importare basemap (CARTO / OSM HOT; non tile.openstreetmap.org)
|
||||
try:
|
||||
import contextily as ctx
|
||||
from basemap_tiles import add_basemap
|
||||
CONTEXTILY_AVAILABLE = True
|
||||
except ImportError:
|
||||
CONTEXTILY_AVAILABLE = False
|
||||
LOGGER.warning("contextily non disponibile. Mappa generata senza sfondo geografico.")
|
||||
add_basemap = None # type: ignore
|
||||
LOGGER.warning("contextily/basemap_tiles non disponibile. Mappa generata senza sfondo geografico.")
|
||||
|
||||
if not results:
|
||||
return False
|
||||
@@ -377,16 +378,12 @@ def generate_snow_map(results: List[Dict], center_lat: float, center_lon: float,
|
||||
ax.set_ylim(lat_min, lat_max)
|
||||
ax.set_aspect('equal', adjustable='box')
|
||||
|
||||
# Aggiungi mappa di sfondo OpenStreetMap se disponibile
|
||||
if CONTEXTILY_AVAILABLE:
|
||||
try:
|
||||
# Aggiungi tile OpenStreetMap (contextily gestisce automaticamente la conversione)
|
||||
ctx.add_basemap(ax, crs='EPSG:4326', source=ctx.providers.OpenStreetMap.Mapnik,
|
||||
alpha=0.6, attribution_size=6)
|
||||
LOGGER.debug("Mappa OpenStreetMap aggiunta come sfondo")
|
||||
except Exception as e:
|
||||
LOGGER.warning("Errore aggiunta mappa sfondo: %s. Continuo senza sfondo.", e)
|
||||
# Non reimpostare CONTEXTILY_AVAILABLE qui, solo logga l'errore
|
||||
# Aggiungi mappa di sfondo (CARTO; OSM Mapnik è bloccato per lo UA di contextily)
|
||||
if CONTEXTILY_AVAILABLE and add_basemap is not None:
|
||||
if add_basemap(ax, alpha=0.85, attribution_size=6):
|
||||
LOGGER.debug("Basemap aggiunta come sfondo")
|
||||
else:
|
||||
LOGGER.warning("Errore aggiunta mappa sfondo. Continuo senza sfondo.")
|
||||
|
||||
# Disegna punti con colore basato su accumulo totale
|
||||
# Colori: blu (poco) -> verde -> giallo -> arancione -> rosso (molto)
|
||||
|
||||