In [1]:
# Installazione dipendenze per funzionalità PDF
# Esegui questo chunk solo se non hai le librerie PDF
import subprocess
import sys
def install_pdf_dependencies():
"""Installa le librerie necessarie per processare PDF"""
libraries = [
'PyPDF2', # Estrazione testo base
'PyMuPDF', # pymupdf - migliore per PDF complessi
]
print("📦 Installazione dipendenze PDF...")
for lib in libraries:
try:
__import__(lib.lower().replace('-', '_'))
print(f"✅ {lib} già installato")
except ImportError:
print(f"🔄 Installando {lib}...")
subprocess.check_call([sys.executable, "-m", "pip", "install", lib])
print(f"✅ {lib} installato con successo")
# Decommentare per installare le dipendenze
install_pdf_dependencies()
# Test import
try:
import PyPDF2
import fitz # PyMuPDF
print("✅ Tutte le librerie PDF sono disponibili!")
print(f" • PyPDF2 versione: {PyPDF2.__version__}")
print(f" • PyMuPDF versione: {fitz.version[0]}")
except ImportError as e:
print(f"❌ Libreria mancante: {e}")
print("💡 Decommenta install_pdf_dependencies() sopra per installarle")
📦 Installazione dipendenze PDF... 🔄 Installando PyPDF2...
Requirement already satisfied: PyPDF2 in /opt/iz/ilab_venv/lib/python3.11/site-packages (3.0.1)
✅ PyPDF2 installato con successo ✅ PyMuPDF già installato ✅ Tutte le librerie PDF sono disponibili! • PyPDF2 versione: 3.0.1 • PyMuPDF versione: 1.26.0
In [2]:
import os
import json
import pandas as pd
import geopandas as gpd
import numpy as np
import folium
from folium import plugins
from dotenv import load_dotenv, find_dotenv
import anthropic
from IPython.display import display
load_dotenv(find_dotenv())
# Configurazione percorsi
ilab_path = os.path.join(os.path.expanduser('~'), 'ILAB_DATA')
istat_path = os.path.join(ilab_path, 'ISTAT', 'DATA')
work_path = os.path.join(ilab_path, 'OCPR', 'DATA')
out_path = os.path.join(ilab_path, 'OCPR', 'OUT')
pdf_path = os.path.join(ilab_path, 'OCPR', 'DOCS')
# Configurazione API e file
CLAUDE_API_KEY = os.getenv('ANTHROPIC_API_KEY_GK')
geoparquet_path = os.path.join(work_path, 'grid_06_adv.geoparquet')
metadata_path = os.path.join(out_path, 'metadati_dataset_06.csv')
print("✅ Ambiente configurato")
print(f"📂 Percorso GeoParquet: {geoparquet_path}")
print(f"📄 Percorso Metadata: {metadata_path}")
print(f"🔑 API Key: {'✅ Presente' if CLAUDE_API_KEY else '❌ Mancante'}")
✅ Ambiente configurato 📂 Percorso GeoParquet: /home/ilab/ILAB_DATA/OCPR/DATA/grid_06_adv.geoparquet 📄 Percorso Metadata: /home/ilab/ILAB_DATA/OCPR/OUT/metadati_dataset_06.csv 🔑 API Key: ✅ Presente
In [3]:
class ClaudeClient:
def __init__(self, api_key: str):
self.client = anthropic.Anthropic(api_key=api_key)
def query_interpreter(self, user_query: str, available_columns: dict) -> dict:
prompt = f"""
Analizza questa domanda sui dati geografici e restituisci un JSON con le operazioni da eseguire.
Colonne disponibili per categoria:
{json.dumps(available_columns, indent=2)}
Domanda utente: "{user_query}"
Restituisci solo un JSON con questa struttura:
{{
"operation": "filter|aggregate|spatial",
"columns": ["lista_colonne_coinvolte"],
"conditions": [
{{"column": "nome_colonna", "operator": ">|<|=|!=", "value": valore}}
],
"aggregation": "count|sum|mean|max|min|null",
"spatial_operation": "within|distance|intersects|null",
"explanation": "breve spiegazione operazione"
}}
"""
response = self.client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1000,
messages=[{"role": "user", "content": prompt}]
)
try:
return json.loads(response.content[0].text)
except:
return {"error": "Parsing fallito", "raw_response": response.content[0].text}
def generate_response(self, question: str, query_spec: dict, result) -> str:
prompt = f"""
Domanda originale: "{question}"
Operazione eseguita: {query_spec.get('explanation', 'N/A')}
Risultato: {result}
Genera una risposta naturale e comprensibile per l'utente.
"""
response = self.client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=300,
messages=[{"role": "user", "content": prompt}]
)
return response.content[0].text
In [4]:
class DataAnalyzer:
def __init__(self, geoparquet_path: str, metadata_path: str):
self.metadata = pd.read_csv(metadata_path)
self.gdf = gpd.read_parquet(geoparquet_path)
self.columns_by_category = self._organize_columns()
def _organize_columns(self) -> dict:
categories = {}
for _, row in self.metadata.iterrows():
cat = row['category']
if cat not in categories:
categories[cat] = []
categories[cat].append({
'name_adv': row['name_adv'],
'name': row['name'],
'description': row['label_adv']
})
return categories
def execute_query(self, query_spec: dict):
try:
df = self.gdf.copy()
if 'conditions' in query_spec and query_spec['conditions']:
for condition in query_spec['conditions']:
col = condition['column']
op = condition['operator']
val = condition['value']
if col not in df.columns:
continue
if op == '>':
df = df[df[col] > val]
elif op == '<':
df = df[df[col] < val]
elif op == '=':
df = df[df[col] == val]
elif op == '!=':
df = df[df[col] != val]
if query_spec.get('aggregation'):
agg = query_spec['aggregation']
cols = query_spec.get('columns', [])
if agg == 'count':
return len(df)
elif cols and agg in ['sum', 'mean', 'max', 'min']:
col = cols[0] if cols[0] in df.columns else df.select_dtypes(include='number').columns[0]
return getattr(df[col], agg)()
return df
except Exception as e:
return f"Errore: {str(e)}"
In [5]:
class MapVisualizer:
def __init__(self, gdf, metadata):
self.gdf = gdf
self.metadata = metadata
self.center_lat, self.center_lon = self._get_center()
def _get_center(self):
if self.gdf.crs != 'EPSG:4326':
gdf_wgs84 = self.gdf.to_crs('EPSG:4326')
else:
gdf_wgs84 = self.gdf
bounds = gdf_wgs84.total_bounds
center_lat = (bounds[1] + bounds[3]) / 2
center_lon = (bounds[0] + bounds[2]) / 2
return center_lat, center_lon
def create_map(self, filtered_gdf=None, title="Mappa Risultati", viz_type="markers"):
m = folium.Map(
location=[self.center_lat, self.center_lon],
zoom_start=10,
tiles='OpenStreetMap'
)
if filtered_gdf is None or len(filtered_gdf) == 0:
folium.Marker(
[self.center_lat, self.center_lon],
popup="Nessun risultato trovato",
icon=folium.Icon(color='red')
).add_to(m)
return m
if viz_type == "heatmap":
self._add_heatmap(m, filtered_gdf)
else:
self._add_markers(m, filtered_gdf)
title_html = f'<h3 align="center">{title}</h3>'
m.get_root().html.add_child(folium.Element(title_html))
return m
def _add_heatmap(self, m, gdf):
if gdf.crs != 'EPSG:4326':
gdf_wgs84 = gdf.to_crs('EPSG:4326')
else:
gdf_wgs84 = gdf.copy()
points = [[geom.centroid.y, geom.centroid.x] for geom in gdf_wgs84.geometry if geom is not None]
if points:
plugins.HeatMap(points).add_to(m)
def _add_markers(self, m, gdf):
if gdf.crs != 'EPSG:4326':
gdf_wgs84 = gdf.to_crs('EPSG:4326')
else:
gdf_wgs84 = gdf.copy()
for idx, row in gdf_wgs84.iterrows():
if row.geometry is None:
continue
centroid = row.geometry.centroid
popup_text = f"Cella: {idx}"
folium.CircleMarker(
location=[centroid.y, centroid.x],
radius=5,
popup=popup_text,
color='red',
fill=True,
opacity=0.7
).add_to(m)
In [6]:
class GeoAgent:
def __init__(self, claude_api_key: str, geoparquet_path: str, metadata_path: str):
self.claude = ClaudeClient(claude_api_key)
self.analyzer = DataAnalyzer(geoparquet_path, metadata_path)
self.visualizer = MapVisualizer(self.analyzer.gdf, self.analyzer.metadata)
self.context = self._build_context()
def _build_context(self) -> str:
stats = {
'total_cells': len(self.analyzer.gdf),
'total_columns': len(self.analyzer.gdf.columns),
'categories': list(self.analyzer.columns_by_category.keys()),
'sample_columns': {}
}
for cat, cols in self.analyzer.columns_by_category.items():
stats['sample_columns'][cat] = [c['name'] for c in cols[:3]]
return f"""Dataset: {stats['total_cells']} celle esagonali con {stats['total_columns']} attributi
Categorie: {', '.join(stats['categories'])}
Esempi colonne per categoria: {stats['sample_columns']}"""
def ask(self, question: str, show_map: bool = True):
query_spec = self.claude.query_interpreter(
question,
self.analyzer.columns_by_category
)
if 'error' in query_spec:
return f"❌ Non riesco a interpretare: {query_spec['error']}"
result = self.analyzer.execute_query(query_spec)
response = self.claude.generate_response(question, query_spec, result)
print(f"🤔 DOMANDA: {question}")
print(f"🤖 RISPOSTA: {response}")
if show_map and hasattr(result, 'geometry'):
print("🗺️ MAPPA:")
viz_type = "heatmap" if len(result) > 100 else "markers"
mappa = self.visualizer.create_map(result, question[:40], viz_type)
display(mappa)
return response, mappa
return response
def debug_query(self, question: str):
print(f"🔍 DEBUG: {question}")
query_spec = self.claude.query_interpreter(
question,
self.analyzer.columns_by_category
)
print("📋 Interpretazione Claude:")
for k, v in query_spec.items():
print(f" {k}: {v}")
if 'error' not in query_spec:
result = self.analyzer.execute_query(query_spec)
print(f"📊 Risultato: {result}")
return query_spec
In [7]:
# Inizializzazione del GeoAgent
agent = GeoAgent(
claude_api_key=CLAUDE_API_KEY,
geoparquet_path=geoparquet_path,
metadata_path=metadata_path
)
print("✅ GeoAgent inizializzato!")
print(f"📊 {agent.context}")
# Test funzionalità di base
print("\n" + "="*50)
print("🔍 TEST COLONNE DISPONIBILI")
print("="*50)
for categoria, colonne in agent.analyzer.columns_by_category.items():
print(f"\n📂 {categoria.upper()}:")
for col in colonne[:5]: # Prime 5 colonne per categoria
print(f" • {col['name']}: {col['description'][:60]}...")
print(f"\n📈 STATISTICHE DATASET:")
print(f" • Celle totali: {len(agent.analyzer.gdf):,}")
print(f" • Colonne totali: {len(agent.analyzer.gdf.columns)}")
print(f" • Categorie: {len(agent.analyzer.columns_by_category)}")
✅ GeoAgent inizializzato!
📊 Dataset: 704 celle esagonali con 311 attributi
Categorie: Griglia, Copertura del suolo, Accessibilità trasporti (geoportale sardegna), Demografia, Accessibilità trasporti, Accessibilità cultura, Accessibilità istruzione, Accessibilità ristorazione, Accessibilità sanità, Accessibilità sport, Accessibilità strutture ricettive, Accessibilità sanità - Ospedali
Esempi colonne per categoria: {'Griglia': ['geometry', 'h3_06', 'cell_a_m2'], 'Copertura del suolo': ['agricultural', 'artificial_non_agricultural_vegetated', 'forest_and_semi_natural'], 'Accessibilità trasporti (geoportale sardegna)': ['traffic__15__aeroporto_militare', 'traffic__15__aeroporto_principale', 'traffic__15__aeroporto_secondario'], 'Demografia': ['p_0_14__01', 'p_0_14__11', 'p_0_14__21'], 'Accessibilità trasporti': ['traffic__15__aeroporto', 'traffic__15__bus', 'traffic__15__stazione'], 'Accessibilità cultura': ['traffic__15__biblioteca', 'traffic__15__cinema', 'traffic__15__evento_culturale'], 'Accessibilità istruzione': ['traffic__15__formazione_alternativa', 'traffic__15__formazione_professionale', 'traffic__15__istituti_scolastici'], 'Accessibilità ristorazione': ['traffic__15__altri_servizi_alimentari', 'traffic__15__bar', 'traffic__15__ristorante'], 'Accessibilità sanità': ['traffic__15__centri_sanitari', 'traffic__15__cliniche_mediche', 'traffic__15__servizi_diagnostici'], 'Accessibilità sport': ['traffic__15__altre_attivita_sportive', 'traffic__15__club_e_squadre_sportive', 'traffic__15__fitness_e_allenamento'], 'Accessibilità strutture ricettive': ['traffic__15__strutture_alberghiere', 'traffic__15__strutture_extra_alberghiere', 'traffic__15__total_strutture_ricettive'], 'Accessibilità sanità - Ospedali': ['traffic__15__ao_integrata_con_il_ssn', 'traffic__15__azienda_ospedaliera', 'traffic__15__osped_a_gestione_diretta_presidio_asl']}
==================================================
🔍 TEST COLONNE DISPONIBILI
==================================================
📂 GRIGLIA:
• geometry: Geometria poligonale dell'area rappresentata...
• h3_06: Indice di geolocalizzazione H3 con risoluzione xx...
• cell_a_m2: Area della cella in metri quadrati...
📂 COPERTURA DEL SUOLO:
• agricultural: Area agricola...
• artificial_non_agricultural_vegetated: Area con vegetazione artificiale, non agricola...
• forest_and_semi_natural: Area forestale e semi naturale...
• industrial_commercial_and_transport: Area Industriale, commerciale e dei trasporti...
• int_a_m2: Area di intersezione in metri quadrati...
📂 ACCESSIBILITÀ TRASPORTI (GEOPORTALE SARDEGNA):
• traffic__15__aeroporto_militare: Aeroporti militari raggiungibili in 15 minuti con trasporto ...
• traffic__15__aeroporto_principale: Aeroporti principali raggiungibili in 15 minuti con trasport...
• traffic__15__aeroporto_secondario: Aeroporti secondari raggiungibili in 15 minuti con trasporto...
• traffic__15__porto_commerciale: Porti commerciali raggiungibili in 15 minuti con trasporto p...
• traffic__15__porto_commerciale_turistico: Porti commerciale-turistico raggiungibili in 15 minuti con t...
📂 DEMOGRAFIA:
• p_0_14__01: Popolazione residente di età 0 - 14 anni nel 2001...
• p_0_14__11: Popolazione residente di età 0 - 14 anni nel 2011...
• p_0_14__21: Popolazione residente di età 0 - 14 anni nel 2021...
• p_0_14__91: Popolazione residente di età 0 - 14 anni nel 1991...
• p_15_19__01: Popolazione residente di età 15 - 19 anni nel 2001...
📂 ACCESSIBILITÀ TRASPORTI:
• traffic__15__aeroporto: Aeroporti raggiungibili in 15 minuti con trasporto privato (...
• traffic__15__bus: Fermate autobus raggiungibili in 15 minuti con trasporto pri...
• traffic__15__stazione: Stazioni ferroviarie raggiungibili in 15 minuti con trasport...
• traffic__15__total_infrastrutture: Totale infrastrutture OVM raggiungibili in 15 minuti con tra...
• traffic__30__aeroporto: Aeroporti raggiungibili in 30 minuti con trasporto privato (...
📂 ACCESSIBILITÀ CULTURA:
• traffic__15__biblioteca: Biblioteche raggiungibili in 15 minuti con trasporto privato...
• traffic__15__cinema: Cinema raggiungibili in 15 minuti con trasporto privato (tra...
• traffic__15__evento_culturale: Eventi culturale raggiungibili in 15 minuti con trasporto pr...
• traffic__15__museo: Musei raggiungibili in 15 minuti con trasporto privato (traf...
• traffic__15__teatro_sala_concerto: Teatri, sale concerto raggiungibili in 15 minuti con traspor...
📂 ACCESSIBILITÀ ISTRUZIONE:
• traffic__15__formazione_alternativa: Centri di formazione alternativa raggiungibili in 15 minuti ...
• traffic__15__formazione_professionale: Centri di formazione professionale raggiungibili in 15 minut...
• traffic__15__istituti_scolastici: Istituti scolastici raggiungibili in 15 minuti con trasporto...
• traffic__15__istruzione_generale: Strutture di istruzione generale raggiungibili in 15 minuti ...
• traffic__15__istruzione_infanzia: Strutture per l'istruzione dell'infanzia raggiungibili in 15...
📂 ACCESSIBILITÀ RISTORAZIONE:
• traffic__15__altri_servizi_alimentari: Altri servizi alimentari raggiungibili in 15 minuti con tras...
• traffic__15__bar: Bar raggiungibili in 15 minuti con trasporto privato (traffi...
• traffic__15__ristorante: Ristoranti raggiungibili in 15 minuti con trasporto privato ...
• traffic__15__total_ristorazione: Totale luoghi di ristorazione raggiungibili in 15 minuti con...
• traffic__30__altri_servizi_alimentari: Altri servizi alimentari raggiungibili in 30 minuti con tras...
📂 ACCESSIBILITÀ SANITÀ:
• traffic__15__centri_sanitari: Centri sanitari raggiungibili in 15 minuti con trasporto pri...
• traffic__15__cliniche_mediche: Cliniche mediche raggiungibili in 15 minuti con trasporto pr...
• traffic__15__servizi_diagnostici: Servizi diagnostici raggiungibili in 15 minuti con trasporto...
• traffic__15__servizi_sanitari: Servizi sanitari raggiungibili in 15 minuti con trasporto pr...
• traffic__15__total_sanita: Totale servizi sanitari raggiungibili in 15 minuti con trasp...
📂 ACCESSIBILITÀ SPORT:
• traffic__15__altre_attivita_sportive: Altre attività sportive raggiungibili in 15 minuti con trasp...
• traffic__15__club_e_squadre_sportive: Club e squadre sportive raggiungibili in 15 minuti con trasp...
• traffic__15__fitness_e_allenamento: Fitness e allenamento raggiungibili in 15 minuti con traspor...
• traffic__15__formazione_sportiva: Formazione sportiva raggiungibili in 15 minuti con trasporto...
• traffic__15__luoghi_sportivi: Luoghi sportivi raggiungibili in 15 minuti con trasporto pri...
📂 ACCESSIBILITÀ STRUTTURE RICETTIVE:
• traffic__15__strutture_alberghiere: strutture alberghiere raggiungibili in 15 minuti con traspor...
• traffic__15__strutture_extra_alberghiere: strutture extra-alberghiere raggiungibili in 15 minuti con t...
• traffic__15__total_strutture_ricettive: Totale strutture ricettive raggiungibili in 15 minuti con tr...
• traffic__30__strutture_alberghiere: strutture alberghiere raggiungibili in 30 minuti con traspor...
• traffic__30__strutture_extra_alberghiere: strutture extra-alberghiere raggiungibili in 30 minuti con t...
📂 ACCESSIBILITÀ SANITÀ - OSPEDALI:
• traffic__15__ao_integrata_con_il_ssn: Aziende ospedaliere integrate con il SSN raggiungibili in 15...
• traffic__15__azienda_ospedaliera: Aziende ospedaliere raggiungibili in 15 minuti con trasporto...
• traffic__15__osped_a_gestione_diretta_presidio_asl: Ospedali a gestione diretta presidio ASL raggiungibili in 15...
• traffic__15__total_ospedale: Totale ospedali raggiungibili in 15 minuti con trasporto pri...
• traffic__30__ao_integrata_con_il_ssn: Aziende ospedaliere integrate con il SSN raggiungibili in 30...
📈 STATISTICHE DATASET:
• Celle totali: 704
• Colonne totali: 311
• Categorie: 12
In [8]:
# Tutorial - Esempi di utilizzo del GeoAgent
print("🚀 TUTORIAL GEOAGENT - ESEMPI PRATICI")
print("="*60)
# Esempio 1: Query demografiche
print("\n1️⃣ QUERY DEMOGRAFICHE")
print("-"*30)
domande_demografiche = [
"Quante celle hanno popolazione superiore a 5000 abitanti?",
"Qual è la media della popolazione femminile?",
"Zone con alta densità di popolazione"
]
for domanda in domande_demografiche:
try:
agent.ask(domanda, show_map=False)
print()
except Exception as e:
print(f"❌ Errore: {e}\n")
# Esempio 2: Query con mappe
print("\n2️⃣ QUERY CON VISUALIZZAZIONI")
print("-"*30)
# Questa query mostrerà anche la mappa
agent.ask("Celle con popolazione superiore a 10000", show_map=True)
🚀 TUTORIAL GEOAGENT - ESEMPI PRATICI ============================================================ 1️⃣ QUERY DEMOGRAFICHE ------------------------------
🤔 DOMANDA: Quante celle hanno popolazione superiore a 5000 abitanti? 🤖 RISPOSTA: In base all'analisi effettuata, ci sono 60 celle del territorio che ospitano più di 5000 abitanti. Questo dato ci mostra le aree più densamente popolate della zona in esame.
🤔 DOMANDA: Qual è la media della popolazione femminile? 🤖 RISPOSTA: La popolazione femminile media nel periodo considerato (dal 1991 al 2021) è stata di circa 1.183 abitanti. Questo valore rappresenta la media delle presenze femminili registrate nei quattro censimenti decennali presi in esame.
🤔 DOMANDA: Zone con alta densità di popolazione 🤖 RISPOSTA: In base all'analisi della densità di popolazione effettuata sui dati del 2021, è possibile identificare le aree con maggiore concentrazione abitativa. Il calcolo è stato eseguito considerando il rapporto tra la popolazione totale e la superficie di ogni cella esagonale H3. I risultati mostrano una distribuzione non uniforme della popolazione, con alcune zone che presentano valori di densità significativamente più elevati rispetto alla media. In particolare, si notano concentrazioni maggiori nelle aree urbane e nei principali centri abitati, mentre le zone forestali e agricole tendono ad avere densità abitative più basse. La presenza di attività commerciali, industriali e infrastrutture di trasporto sembra correlata con le aree a maggiore densità di popolazione, suggerendo un legame tra sviluppo urbano e concentrazione demografica. È interessante notare come le aree classificate come "Agricultural" e "Forest and semi natural" mostrano generalmente densità di popolazione più contenute, confermando il tipico pattern di urbanizzazione che vede le maggiori concentrazioni abitative nelle zone più sviluppate dal punto di vista delle infrastrutture e dei servizi. 2️⃣ QUERY CON VISUALIZZAZIONI ------------------------------
🤔 DOMANDA: Celle con popolazione superiore a 10000 🤖 RISPOSTA: In base all'analisi dei dati, sono state identificate 30 celle con una popolazione residente superiore a 10.000 abitanti nel 2021. Queste celle sono distribuite in diverse aree del territorio e presentano caratteristiche variegate: - La maggior parte delle celle ha una significativa presenza di aree agricole o forestali - Alcune celle contengono importanti strutture sanitarie, con 13 celle che hanno almeno un ospedale raggiungibile entro 60 minuti - Si nota una distribuzione mista dell'uso del suolo, con presenza di aree urbane, industriali e commerciali - Diverse celle si trovano in zone con buona accessibilità ai servizi sanitari È interessante notare come queste aree ad alta densità abitativa tendano a concentrarsi in zone con una buona dotazione di servizi e infrastrutture, confermando il tipico pattern di sviluppo urbano. 🗺️ MAPPA:
Make this Notebook Trusted to load map: File -> Trust Notebook
Out[8]:
("In base all'analisi dei dati, sono state identificate 30 celle con una popolazione residente superiore a 10.000 abitanti nel 2021. Queste celle sono distribuite in diverse aree del territorio e presentano caratteristiche variegate:\n\n- La maggior parte delle celle ha una significativa presenza di aree agricole o forestali\n- Alcune celle contengono importanti strutture sanitarie, con 13 celle che hanno almeno un ospedale raggiungibile entro 60 minuti\n- Si nota una distribuzione mista dell'uso del suolo, con presenza di aree urbane, industriali e commerciali\n- Diverse celle si trovano in zone con buona accessibilità ai servizi sanitari\n\nÈ interessante notare come queste aree ad alta densità abitativa tendano a concentrarsi in zone con una buona dotazione di servizi e infrastrutture, confermando il tipico pattern di sviluppo urbano.",
<folium.folium.Map at 0x7f959975de50>)
In [9]:
# Esempi avanzati e test del sistema
print("🔬 ESEMPI AVANZATI")
print("="*40)
# Test su diverse tipologie di domande
domande_test = [
# Demografia
"Quante celle hanno più di 5000 residenti?",
"Zone con popolazione femminile alta",
# Servizi e accessibilità
"Aree con buona accessibilità ai servizi",
"Zone con molti negozi raggiungibili",
# Territorio e ambiente
"Celle con copertura agricola estesa",
"Aree urbane dense"
]
print("🧪 TEST AUTOMATICO SU DIVERSE QUERY")
print("-"*40)
risultati_test = {}
for i, domanda in enumerate(domande_test, 1):
print(f"\n{i}. {domanda}")
try:
# Test solo interpretazione (senza mappa per velocità)
query_spec = agent.debug_query(domanda)
if 'error' not in query_spec:
risultati_test[domanda] = "✅ Successo"
else:
risultati_test[domanda] = "❌ Fallito"
except Exception as e:
risultati_test[domanda] = f"❌ Errore: {str(e)[:50]}"
print("-"*30)
# Riassunto risultati
print(f"\n📊 RIASSUNTO TEST:")
successi = sum(1 for r in risultati_test.values() if "✅" in r)
print(f"Successi: {successi}/{len(domande_test)}")
for domanda, risultato in risultati_test.items():
print(f" {risultato} {domanda[:40]}...")
🔬 ESEMPI AVANZATI ======================================== 🧪 TEST AUTOMATICO SU DIVERSE QUERY ---------------------------------------- 1. Quante celle hanno più di 5000 residenti? 🔍 DEBUG: Quante celle hanno più di 5000 residenti?
📋 Interpretazione Claude:
operation: aggregate
columns: ['istat||pop_tot__21']
conditions: [{'column': 'istat||pop_tot__21', 'operator': '>', 'value': 5000}]
aggregation: count
spatial_operation: None
explanation: Conta il numero di celle che hanno una popolazione residente totale nel 2021 superiore a 5000 abitanti
📊 Risultato: 60
------------------------------
2. Zone con popolazione femminile alta
🔍 DEBUG: Zone con popolazione femminile alta
📋 Interpretazione Claude:
operation: filter
columns: ['istat||pop_f__21']
conditions: [{'column': 'istat||pop_f__21', 'operator': '>', 'value': 'mean'}]
aggregation: None
spatial_operation: None
explanation: Filtra le aree con popolazione femminile nel 2021 superiore alla media, identificando così le zone con alta presenza femminile
📊 Risultato: Errore: Invalid comparison between dtype=float64 and str
------------------------------
3. Aree con buona accessibilità ai servizi
🔍 DEBUG: Aree con buona accessibilità ai servizi
📋 Interpretazione Claude:
operation: filter
columns: ['ovm_places_sanita||traffic__15__total_sanita', 'ovm_places_istruzione||traffic__15__total_istruzione', 'ovm_places_cultura||traffic__15__total_cultura', 'ovm_places_sport||traffic__15__total_sport', 'ovm_places_ristorazione||traffic__15__total_ristorazione', 'ovm_infr_infrastrutture||traffic__15__total_infrastrutture']
conditions: [{'column': 'ovm_places_sanita||traffic__15__total_sanita', 'operator': '>', 'value': 5}, {'column': 'ovm_places_istruzione||traffic__15__total_istruzione', 'operator': '>', 'value': 5}, {'column': 'ovm_places_cultura||traffic__15__total_cultura', 'operator': '>', 'value': 3}, {'column': 'ovm_places_sport||traffic__15__total_sport', 'operator': '>', 'value': 3}, {'column': 'ovm_places_ristorazione||traffic__15__total_ristorazione', 'operator': '>', 'value': 5}, {'column': 'ovm_infr_infrastrutture||traffic__15__total_infrastrutture', 'operator': '>', 'value': 2}]
aggregation: None
spatial_operation: None
explanation: Filtra le aree che hanno buona accessibilità (raggiungibili in 15 minuti in auto) a diversi servizi essenziali: sanità, istruzione, cultura, sport, ristorazione e infrastrutture di trasporto
📊 Risultato: geometry h3_06 \
21 POLYGON ((498906.262 4359915.765, 497941.186 4... 861e96657ffffff
121 POLYGON ((463606.163 4421357.513, 462641.208 4... 861e96107ffffff
129 POLYGON ((513615.251 4350695.732, 512650.51 43... 861e92987ffffff
140 POLYGON ((524905.944 4461668.374, 523945.082 4... 861e946b7ffffff
152 POLYGON ((520083.904 4349272.523, 519119.381 4... 861e9298fffffff
177 POLYGON ((459089.681 4350739.174, 458122.798 4... 863865b67ffffff
183 POLYGON ((444225.254 4490901.259, 443261.153 4... 863949217ffffff
272 POLYGON ((480668.833 4381849.655, 479703.593 4... 861e9672fffffff
277 POLYGON ((508934.877 4345737.285, 507969.809 4... 861e92997ffffff
280 POLYGON ((462820.689 4510613.023, 461858.079 4... 86394934fffffff
295 POLYGON ((492449.935 4361334.747, 491484.636 4... 861e9660fffffff
321 POLYGON ((494229.835 4354960.964, 493264.436 4... 861e966e7ffffff
322 POLYGON ((461839.5 4427695.817, 460874.627 442... 861e96127ffffff
325 POLYGON ((487115.524 4380437.495, 486150.518 4... 861e96777ffffff
351 POLYGON ((458171.7 4505684.989, 457208.709 450... 86394935fffffff
361 POLYGON ((496010.608 4348584.097, 495045.109 4... 861e966c7ffffff
389 POLYGON ((437821.763 4492277.311, 436857.373 4... 86394938fffffff
392 POLYGON ((458944.462 4416416.559, 457979.172 4... 861e96117ffffff
493 POLYGON ((515402.147 4344312.78, 514437.295 43... 861e9299fffffff
552 POLYGON ((540385.332 4535560.989, 539427.8 453... 861e9414fffffff
598 POLYGON ((505366.022 4358496.051, 504401.168 4... 861e9665fffffff
640 POLYGON ((456411.982 4511983.749, 455449.071 4... 863949347ffffff
650 POLYGON ((529286.744 4531990.982, 528328.452 4... 861e94157ffffff
655 POLYGON ((507150.019 4352118.209, 506185.059 4... 861e929b7ffffff
656 POLYGON ((457178.981 4422756.015, 456213.772 4... 861e96137ffffff
662 POLYGON ((461059.75 4516910.499, 460097.221 45... 86394936fffffff
677 POLYGON ((502471.029 4347161.057, 501505.746 4... 861e966cfffffff
679 POLYGON ((460710.878 4410073.923, 459745.507 4... 861e961afffffff
cell_a_m2 clc||agricultural \
21 3.865636e+07 3.338488e+07
121 3.830668e+07 2.984119e+07
129 3.874597e+07 2.840007e+07
140 3.841214e+07 2.743749e+07
152 3.877670e+07 3.223367e+07
177 3.852491e+07 2.446151e+07
183 3.798254e+07 2.075221e+07
272 3.850985e+07 3.483803e+07
277 3.874325e+07 4.476001e+06
280 3.798568e+07 1.961409e+07
295 3.862559e+07 3.306194e+07
321 3.865371e+07 3.167425e+07
322 3.827764e+07 3.732719e+07
325 3.854066e+07 3.582148e+07
351 3.798502e+07 3.179008e+07
361 3.868175e+07 2.595422e+07
389 3.795163e+07 3.330308e+06
392 3.830477e+07 1.176099e+07
493 3.877399e+07 5.436167e+06
552 3.820313e+07 9.574884e+06
598 3.868712e+07 3.349985e+07
640 3.795481e+07 2.856556e+07
650 3.817242e+07 2.298489e+07
655 3.871523e+07 3.518479e+07
656 3.827581e+07 2.619343e+07
662 3.795538e+07 3.144773e+07
677 3.871251e+07 7.057166e+06
679 3.833363e+07 2.497049e+07
clc||artificial_non_agricultural_vegetated clc||forest_and_semi_natural \
21 0.000000e+00 1.106240e+06
121 0.000000e+00 0.000000e+00
129 0.000000e+00 5.993800e+05
140 0.000000e+00 1.051451e+07
152 9.455910e+04 1.707216e+06
177 0.000000e+00 9.137436e+06
183 0.000000e+00 1.720911e+07
272 0.000000e+00 7.117293e+05
277 2.885492e+06 0.000000e+00
280 3.106665e+05 7.285006e+06
295 0.000000e+00 2.584749e+05
321 0.000000e+00 0.000000e+00
322 0.000000e+00 0.000000e+00
325 0.000000e+00 0.000000e+00
351 6.472635e+02 4.517826e+06
361 0.000000e+00 1.238039e+06
389 2.618976e+05 2.185202e+05
392 0.000000e+00 3.278147e+06
493 2.789177e+05 7.457195e+05
552 3.233366e+05 1.128873e+07
598 0.000000e+00 3.626975e+06
640 0.000000e+00 3.213563e+05
650 0.000000e+00 1.518753e+07
655 0.000000e+00 0.000000e+00
656 0.000000e+00 1.198675e+06
662 0.000000e+00 3.155563e+06
677 5.889145e+05 0.000000e+00
679 0.000000e+00 3.762256e+06
clc||industrial_commercial_and_transport clc||int_a_m2 \
21 1.287459e+06 3.338488e+07
121 1.630136e+06 2.984119e+07
129 2.143252e+06 2.840007e+07
140 0.000000e+00 2.743749e+07
152 0.000000e+00 3.223367e+07
177 1.637986e+06 2.446151e+07
183 0.000000e+00 2.075221e+07
272 5.502070e+05 3.483803e+07
277 2.406575e+06 2.265437e+07
280 4.555945e+05 1.961409e+07
295 2.910857e+06 3.306194e+07
321 2.890404e+06 3.167425e+07
322 1.744193e+04 3.732719e+07
325 4.308084e+05 3.582148e+07
351 0.000000e+00 3.179008e+07
361 3.420546e+06 2.595422e+07
389 3.886982e+05 2.941065e+07
392 3.212411e+06 1.934884e+07
493 0.000000e+00 1.914922e+07
552 6.441963e+06 1.128873e+07
598 6.009575e+05 3.349985e+07
640 6.044878e+06 2.856556e+07
650 0.000000e+00 2.298489e+07
655 9.540088e+05 3.518479e+07
656 2.923898e+05 2.619343e+07
662 0.000000e+00 3.144773e+07
677 1.242865e+07 1.389398e+07
679 0.000000e+00 2.497049e+07
clc||label clc||mine_dump_and_construction ... \
21 Agricultural 5.529853e+03 ...
121 Agricultural 0.000000e+00 ...
129 Agricultural 0.000000e+00 ...
140 Agricultural 0.000000e+00 ...
152 Agricultural 1.529059e+06 ...
177 Agricultural 1.424388e+06 ...
183 Agricultural 0.000000e+00 ...
272 Agricultural 0.000000e+00 ...
277 Urban 0.000000e+00 ...
280 Agricultural 0.000000e+00 ...
295 Agricultural 4.497590e+05 ...
321 Agricultural 0.000000e+00 ...
322 Agricultural 0.000000e+00 ...
325 Agricultural 0.000000e+00 ...
351 Agricultural 0.000000e+00 ...
361 Agricultural 3.816677e+05 ...
389 Water bodies 0.000000e+00 ...
392 Water bodies 6.118155e+05 ...
493 Water bodies 0.000000e+00 ...
552 Forest and semi natural 0.000000e+00 ...
598 Agricultural 6.935244e+05 ...
640 Agricultural 0.000000e+00 ...
650 Agricultural 0.000000e+00 ...
655 Agricultural 0.000000e+00 ...
656 Agricultural 0.000000e+00 ...
662 Agricultural 0.000000e+00 ...
677 Water bodies 0.000000e+00 ...
679 Agricultural 0.000000e+00 ...
sanita_ospedale||transit__60__osped_a_gestione_diretta_presidio_asl \
21 1.0
121 1.0
129 0.0
140 2.0
152 0.0
177 0.0
183 0.0
272 1.0
277 1.0
280 0.0
295 1.0
321 1.0
322 0.0
325 1.0
351 0.0
361 0.0
389 0.0
392 0.0
493 0.0
552 0.0
598 0.0
640 0.0
650 0.0
655 1.0
656 1.0
662 0.0
677 0.0
679 0.0
sanita_ospedale||transit__60__total_ospedale \
21 1.0
121 1.0
129 1.0
140 2.0
152 0.0
177 0.0
183 0.0
272 1.0
277 2.0
280 0.0
295 2.0
321 2.0
322 0.0
325 1.0
351 1.0
361 0.0
389 0.0
392 0.0
493 0.0
552 0.0
598 0.0
640 1.0
650 0.0
655 2.0
656 1.0
662 1.0
677 0.0
679 0.0
sanita_ospedale||walking__15__ao_integrata_con_il_ssn \
21 0
121 0
129 0
140 0
152 0
177 0
183 0
272 0
277 0
280 0
295 0
321 0
322 0
325 0
351 0
361 0
389 0
392 0
493 0
552 0
598 0
640 0
650 0
655 0
656 0
662 0
677 0
679 0
sanita_ospedale||walking__15__azienda_ospedaliera \
21 0
121 0
129 0
140 0
152 0
177 0
183 0
272 0
277 0
280 0
295 0
321 0
322 0
325 0
351 0
361 0
389 0
392 0
493 0
552 0
598 0
640 0
650 0
655 0
656 0
662 0
677 0
679 0
sanita_ospedale||walking__15__osped_a_gestione_diretta_presidio_asl \
21 0
121 0
129 0
140 0
152 0
177 0
183 0
272 0
277 0
280 0
295 0
321 0
322 0
325 0
351 0
361 0
389 0
392 0
493 0
552 0
598 0
640 0
650 0
655 0
656 0
662 0
677 0
679 0
sanita_ospedale||walking__15__total_ospedale \
21 0
121 0
129 0
140 0
152 0
177 0
183 0
272 0
277 0
280 0
295 0
321 0
322 0
325 0
351 0
361 0
389 0
392 0
493 0
552 0
598 0
640 0
650 0
655 0
656 0
662 0
677 0
679 0
sanita_ospedale||walking__30__ao_integrata_con_il_ssn \
21 0
121 0
129 0
140 0
152 0
177 0
183 0
272 0
277 0
280 0
295 0
321 0
322 0
325 0
351 0
361 0
389 0
392 0
493 0
552 0
598 0
640 0
650 0
655 0
656 0
662 0
677 0
679 0
sanita_ospedale||walking__30__azienda_ospedaliera \
21 0
121 0
129 0
140 0
152 0
177 0
183 0
272 0
277 0
280 0
295 0
321 0
322 0
325 0
351 0
361 0
389 0
392 0
493 0
552 0
598 0
640 0
650 0
655 0
656 0
662 0
677 0
679 0
sanita_ospedale||walking__30__osped_a_gestione_diretta_presidio_asl \
21 0.0
121 0.0
129 0.0
140 0.0
152 0.0
177 0.0
183 0.0
272 0.0
277 0.0
280 0.0
295 0.0
321 0.0
322 0.0
325 0.0
351 0.0
361 0.0
389 0.0
392 0.0
493 0.0
552 0.0
598 0.0
640 0.0
650 0.0
655 0.0
656 0.0
662 0.0
677 0.0
679 0.0
sanita_ospedale||walking__30__total_ospedale
21 0.0
121 0.0
129 0.0
140 0.0
152 0.0
177 0.0
183 0.0
272 0.0
277 0.0
280 0.0
295 0.0
321 0.0
322 0.0
325 0.0
351 0.0
361 0.0
389 0.0
392 0.0
493 0.0
552 0.0
598 0.0
640 0.0
650 0.0
655 0.0
656 0.0
662 0.0
677 0.0
679 0.0
[28 rows x 311 columns]
------------------------------
4. Zone con molti negozi raggiungibili
🔍 DEBUG: Zone con molti negozi raggiungibili
📋 Interpretazione Claude:
operation: filter
columns: ['ovm_places_ristorazione||traffic__15__total_ristorazione', 'ovm_places_ristorazione||traffic__30__total_ristorazione', 'ovm_places_ristorazione||walking__15__total_ristorazione', 'ovm_places_ristorazione||walking__30__total_ristorazione']
conditions: [{'column': 'ovm_places_ristorazione||traffic__15__total_ristorazione', 'operator': '>', 'value': 10}, {'column': 'ovm_places_ristorazione||walking__15__total_ristorazione', 'operator': '>', 'value': 5}]
aggregation: None
spatial_operation: None
explanation: Filtra le aree che hanno un buon numero di attività commerciali/ristorazione raggiungibili sia in auto (>10) che a piedi (>5) entro 15 minuti
📊 Risultato: geometry h3_06 \
7 POLYGON ((465531.492 4349318.5, 464564.824 434... 863865b6fffffff
23 POLYGON ((499574.757 4335821.156, 498609.053 4... 861e92d77ffffff
95 POLYGON ((431953.574 4534949.407, 430989.956 4... 863949a17ffffff
129 POLYGON ((513615.251 4350695.732, 512650.51 43... 861e92987ffffff
185 POLYGON ((553240.252 4425749.365, 552279.601 4... 861e90c1fffffff
263 POLYGON ((476943.022 4460113.997, 475979.729 4... 861e96867ffffff
277 POLYGON ((508934.877 4345737.285, 507969.809 4... 861e92997ffffff
295 POLYGON ((492449.935 4361334.747, 491484.636 4... 861e9660fffffff
317 POLYGON ((443314.135 4342264.159, 442346.461 4... 863865b07ffffff
340 POLYGON ((438645.959 4337317.061, 437678.006 4... 863865b17ffffff
342 POLYGON ((531358.876 4460278.858, 530398.296 4... 861e94687ffffff
389 POLYGON ((437821.763 4492277.311, 436857.373 4... 86394938fffffff
437 POLYGON ((456188.656 4339418.382, 455221.399 4... 863865b57ffffff
442 POLYGON ((505332.609 4531157.476, 504372.935 4... 861e94c4fffffff
443 POLYGON ((530378.49 4549525.249, 529420.867 45... 861e948cfffffff
493 POLYGON ((515402.147 4344312.78, 514437.295 43... 861e9299fffffff
518 POLYGON ((543101.188 4332213.277, 542137.068 4... 861e928dfffffff
550 POLYGON ((564035.904 4494914.117, 563078.241 4... 861e94287ffffff
552 POLYGON ((540385.332 4535560.989, 539427.8 453... 861e9414fffffff
587 POLYGON ((461202.272 4451627.911, 460237.99 44... 861e96807ffffff
637 POLYGON ((477121.18 4394565.147, 476156.123 43... 861e960f7ffffff
645 POLYGON ((541471.263 4553098.08, 540514.419 45... 861e94b87ffffff
656 POLYGON ((457178.981 4422756.015, 456213.772 4... 861e96137ffffff
677 POLYGON ((502471.029 4347161.057, 501505.746 4... 861e966cfffffff
695 POLYGON ((559714.061 4424347.412, 558753.673 4... 861e90ce7ffffff
cell_a_m2 clc||agricultural \
7 3.855574e+07 2.760774e+07
23 3.873754e+07 9.018384e+06
95 3.777098e+07 6.133860e+06
129 3.874597e+07 2.840007e+07
185 3.865180e+07 2.426498e+07
263 3.822505e+07 2.879676e+07
277 3.874325e+07 4.476001e+06
295 3.862559e+07 3.306194e+07
317 3.848809e+07 3.397551e+06
340 3.848496e+07 3.404099e+06
342 3.844289e+07 1.945460e+07
389 3.795163e+07 3.330308e+06
437 3.854978e+07 1.255508e+07
442 3.808003e+07 1.468803e+07
443 3.811062e+07 2.396901e+07
493 3.877399e+07 5.436167e+06
518 3.892467e+07 3.469928e+06
550 3.844785e+07 6.601547e+06
552 3.820313e+07 9.574884e+06
587 3.819150e+07 1.537172e+07
637 3.845277e+07 2.920445e+07
645 3.814108e+07 3.027018e+05
656 3.827581e+07 2.619343e+07
677 3.871251e+07 7.057166e+06
695 3.868249e+07 9.695557e+03
clc||artificial_non_agricultural_vegetated clc||forest_and_semi_natural \
7 0.000000e+00 9.941729e+06
23 0.000000e+00 9.934706e+04
95 0.000000e+00 1.082007e+07
129 0.000000e+00 5.993800e+05
185 0.000000e+00 5.178557e+06
263 0.000000e+00 5.349459e+06
277 2.885492e+06 0.000000e+00
295 0.000000e+00 2.584749e+05
317 0.000000e+00 4.394598e+06
340 0.000000e+00 4.709515e+06
342 0.000000e+00 1.704381e+07
389 2.618976e+05 2.185202e+05
437 0.000000e+00 1.707639e+07
442 0.000000e+00 2.037542e+07
443 0.000000e+00 1.148000e+07
493 2.789177e+05 7.457195e+05
518 7.391426e+05 4.703881e+06
550 0.000000e+00 4.509134e+06
552 3.233366e+05 1.128873e+07
587 0.000000e+00 2.151376e+07
637 0.000000e+00 7.393940e+06
645 1.417495e+06 2.712233e+07
656 0.000000e+00 1.198675e+06
677 5.889145e+05 0.000000e+00
695 0.000000e+00 5.388235e+05
clc||industrial_commercial_and_transport clc||int_a_m2 \
7 0.000000e+00 2.760774e+07
23 1.243225e+05 2.631433e+07
95 0.000000e+00 1.770545e+07
129 2.143252e+06 2.840007e+07
185 9.881478e+05 2.426498e+07
263 1.559521e+06 2.879676e+07
277 2.406575e+06 2.265437e+07
295 2.910857e+06 3.306194e+07
317 4.907432e+06 2.353434e+07
340 0.000000e+00 2.788826e+07
342 3.184809e+05 1.945460e+07
389 3.886982e+05 2.941065e+07
437 2.148763e+06 1.707639e+07
442 0.000000e+00 2.037542e+07
443 3.887533e+05 2.396901e+07
493 0.000000e+00 1.914922e+07
518 0.000000e+00 2.734895e+07
550 0.000000e+00 2.589592e+07
552 6.441963e+06 1.128873e+07
587 0.000000e+00 2.151376e+07
637 0.000000e+00 2.920445e+07
645 0.000000e+00 2.712233e+07
656 2.923898e+05 2.619343e+07
677 1.242865e+07 1.389398e+07
695 1.285735e+06 3.518731e+07
clc||label clc||mine_dump_and_construction ... \
7 Agricultural 0.000000 ...
23 Water bodies 0.000000 ...
95 Water bodies 0.000000 ...
129 Agricultural 0.000000 ...
185 Agricultural 0.000000 ...
263 Agricultural 0.000000 ...
277 Urban 0.000000 ...
295 Agricultural 449759.023331 ...
317 Water bodies 287996.305164 ...
340 Water bodies 0.000000 ...
342 Agricultural 0.000000 ...
389 Water bodies 0.000000 ...
437 Forest and semi natural 647733.750695 ...
442 Forest and semi natural 0.000000 ...
443 Agricultural 0.000000 ...
493 Water bodies 0.000000 ...
518 Water bodies 0.000000 ...
550 Water bodies 0.000000 ...
552 Forest and semi natural 0.000000 ...
587 Forest and semi natural 0.000000 ...
637 Agricultural 0.000000 ...
645 Forest and semi natural 0.000000 ...
656 Agricultural 0.000000 ...
677 Water bodies 0.000000 ...
695 Water bodies 0.000000 ...
sanita_ospedale||transit__60__osped_a_gestione_diretta_presidio_asl \
7 1.0
23 2.0
95 0.0
129 0.0
185 0.0
263 0.0
277 1.0
295 1.0
317 0.0
340 0.0
342 2.0
389 0.0
437 2.0
442 1.0
443 0.0
493 0.0
518 0.0
550 0.0
552 0.0
587 0.0
637 0.0
645 0.0
656 1.0
677 0.0
695 0.0
sanita_ospedale||transit__60__total_ospedale \
7 1.0
23 3.0
95 0.0
129 1.0
185 0.0
263 0.0
277 2.0
295 2.0
317 0.0
340 0.0
342 2.0
389 0.0
437 2.0
442 1.0
443 0.0
493 0.0
518 0.0
550 0.0
552 0.0
587 0.0
637 0.0
645 0.0
656 1.0
677 0.0
695 0.0
sanita_ospedale||walking__15__ao_integrata_con_il_ssn \
7 0
23 0
95 0
129 0
185 0
263 0
277 0
295 0
317 0
340 0
342 0
389 0
437 0
442 0
443 0
493 0
518 0
550 0
552 0
587 0
637 0
645 0
656 0
677 0
695 0
sanita_ospedale||walking__15__azienda_ospedaliera \
7 0
23 0
95 0
129 0
185 0
263 0
277 0
295 0
317 0
340 0
342 0
389 0
437 0
442 0
443 0
493 0
518 0
550 0
552 0
587 0
637 0
645 0
656 0
677 0
695 0
sanita_ospedale||walking__15__osped_a_gestione_diretta_presidio_asl \
7 0
23 0
95 0
129 0
185 0
263 0
277 0
295 0
317 0
340 0
342 0
389 0
437 0
442 0
443 0
493 0
518 0
550 0
552 0
587 0
637 0
645 0
656 0
677 0
695 0
sanita_ospedale||walking__15__total_ospedale \
7 0
23 0
95 0
129 0
185 0
263 0
277 0
295 0
317 0
340 0
342 0
389 0
437 0
442 0
443 0
493 0
518 0
550 0
552 0
587 0
637 0
645 0
656 0
677 0
695 0
sanita_ospedale||walking__30__ao_integrata_con_il_ssn \
7 0
23 0
95 0
129 0
185 0
263 0
277 0
295 0
317 0
340 0
342 0
389 0
437 0
442 0
443 0
493 0
518 0
550 0
552 0
587 0
637 0
645 0
656 0
677 0
695 0
sanita_ospedale||walking__30__azienda_ospedaliera \
7 0
23 0
95 0
129 0
185 0
263 0
277 0
295 0
317 0
340 0
342 0
389 0
437 0
442 0
443 0
493 0
518 0
550 0
552 0
587 0
637 0
645 0
656 0
677 0
695 0
sanita_ospedale||walking__30__osped_a_gestione_diretta_presidio_asl \
7 0.0
23 0.0
95 0.0
129 0.0
185 0.0
263 0.0
277 0.0
295 0.0
317 0.0
340 0.0
342 0.0
389 0.0
437 0.0
442 1.0
443 0.0
493 0.0
518 0.0
550 0.0
552 0.0
587 0.0
637 0.0
645 0.0
656 0.0
677 0.0
695 0.0
sanita_ospedale||walking__30__total_ospedale
7 0.0
23 0.0
95 0.0
129 0.0
185 0.0
263 0.0
277 0.0
295 0.0
317 0.0
340 0.0
342 0.0
389 0.0
437 0.0
442 1.0
443 0.0
493 0.0
518 0.0
550 0.0
552 0.0
587 0.0
637 0.0
645 0.0
656 0.0
677 0.0
695 0.0
[25 rows x 311 columns]
------------------------------
5. Celle con copertura agricola estesa
🔍 DEBUG: Celle con copertura agricola estesa
📋 Interpretazione Claude:
operation: filter
columns: ['clc||agricultural', 'clc||perc']
conditions: [{'column': 'clc||agricultural', 'operator': '>', 'value': 0}, {'column': 'clc||perc', 'operator': '>', 'value': 50}]
aggregation: None
spatial_operation: None
explanation: Filtra le celle che hanno una superficie agricola maggiore di 0 e una percentuale di copertura agricola superiore al 50%
📊 Risultato: Empty GeoDataFrame
Columns: [geometry, h3_06, cell_a_m2, clc||agricultural, clc||artificial_non_agricultural_vegetated, clc||forest_and_semi_natural, clc||industrial_commercial_and_transport, clc||int_a_m2, clc||label, clc||mine_dump_and_construction, clc||perc, clc||urban, clc||water_bodies, clc||wetlands, geoportale_infr_infrastrutture||traffic__15__aeroporto_militare, geoportale_infr_infrastrutture||traffic__15__aeroporto_principale, geoportale_infr_infrastrutture||traffic__15__aeroporto_secondario, geoportale_infr_infrastrutture||traffic__15__porto_commerciale, geoportale_infr_infrastrutture||traffic__15__porto_commerciale_turistico, geoportale_infr_infrastrutture||traffic__15__porto_industriale, geoportale_infr_infrastrutture||traffic__15__porto_turistico, geoportale_infr_infrastrutture||traffic__15__stazione_ferroviaria, geoportale_infr_infrastrutture||traffic__15__terminal_industriale, geoportale_infr_infrastrutture||traffic__15__total_infrastrutture, geoportale_infr_infrastrutture||traffic__30__aeroporto_militare, geoportale_infr_infrastrutture||traffic__30__aeroporto_principale, geoportale_infr_infrastrutture||traffic__30__aeroporto_secondario, geoportale_infr_infrastrutture||traffic__30__porto_commerciale, geoportale_infr_infrastrutture||traffic__30__porto_commerciale_turistico, geoportale_infr_infrastrutture||traffic__30__porto_industriale, geoportale_infr_infrastrutture||traffic__30__porto_turistico, geoportale_infr_infrastrutture||traffic__30__stazione_ferroviaria, geoportale_infr_infrastrutture||traffic__30__terminal_industriale, geoportale_infr_infrastrutture||traffic__30__total_infrastrutture, geoportale_infr_infrastrutture||walking__15__aeroporto_militare, geoportale_infr_infrastrutture||walking__15__aeroporto_principale, geoportale_infr_infrastrutture||walking__15__aeroporto_secondario, geoportale_infr_infrastrutture||walking__15__porto_commerciale, geoportale_infr_infrastrutture||walking__15__porto_commerciale_turistico, geoportale_infr_infrastrutture||walking__15__porto_turistico, geoportale_infr_infrastrutture||walking__15__stazione_ferroviaria, geoportale_infr_infrastrutture||walking__15__terminal_industriale, geoportale_infr_infrastrutture||walking__15__total_infrastrutture, geoportale_infr_infrastrutture||walking__30__aeroporto_militare, geoportale_infr_infrastrutture||walking__30__aeroporto_principale, geoportale_infr_infrastrutture||walking__30__aeroporto_secondario, geoportale_infr_infrastrutture||walking__30__porto_commerciale, geoportale_infr_infrastrutture||walking__30__porto_commerciale_turistico, geoportale_infr_infrastrutture||walking__30__porto_industriale, geoportale_infr_infrastrutture||walking__30__porto_turistico, geoportale_infr_infrastrutture||walking__30__stazione_ferroviaria, geoportale_infr_infrastrutture||walking__30__terminal_industriale, geoportale_infr_infrastrutture||walking__30__total_infrastrutture, istat||p_0_14__01, istat||p_0_14__11, istat||p_0_14__21, istat||p_0_14__91, istat||p_15_19__01, istat||p_15_19__11, istat||p_15_19__21, istat||p_15_19__91, istat||p_20_39__01, istat||p_20_39__11, istat||p_20_39__21, istat||p_20_39__91, istat||p_40_64__01, istat||p_40_64__11, istat||p_40_64__21, istat||p_40_64__91, istat||p_65_99__01, istat||p_65_99__11, istat||p_65_99__21, istat||p_65_99__91, istat||pop_f__01, istat||pop_f__11, istat||pop_f__21, istat||pop_f__91, istat||pop_m__01, istat||pop_m__11, istat||pop_m__21, istat||pop_m__91, istat||pop_tot__01, istat||pop_tot__11, istat||pop_tot__21, istat||pop_tot__21__01, istat||pop_tot__21__11, istat||pop_tot__91, ovm_infr_infrastrutture||traffic__15__aeroporto, ovm_infr_infrastrutture||traffic__15__bus, ovm_infr_infrastrutture||traffic__15__stazione, ovm_infr_infrastrutture||traffic__15__total_infrastrutture, ovm_infr_infrastrutture||traffic__30__aeroporto, ovm_infr_infrastrutture||traffic__30__bus, ovm_infr_infrastrutture||traffic__30__stazione, ovm_infr_infrastrutture||traffic__30__total_infrastrutture, ovm_infr_infrastrutture||walking__15__aeroporto, ovm_infr_infrastrutture||walking__15__bus, ovm_infr_infrastrutture||walking__15__stazione, ovm_infr_infrastrutture||walking__15__total_infrastrutture, ovm_infr_infrastrutture||walking__30__aeroporto, ...]
Index: []
[0 rows x 311 columns]
------------------------------
6. Aree urbane dense
🔍 DEBUG: Aree urbane dense
📋 Interpretazione Claude:
operation: filter
columns: ['clc||urban', 'clc||label', 'cell_a_m2']
conditions: [{'column': 'clc||urban', 'operator': '>', 'value': 0.7}, {'column': 'clc||label', 'operator': '=', 'value': 'urban'}]
aggregation: None
spatial_operation: None
explanation: Filtra le aree con una percentuale di tessuto urbano superiore al 70% e dove la categoria di uso del suolo predominante è urbana
📊 Risultato: Empty GeoDataFrame
Columns: [geometry, h3_06, cell_a_m2, clc||agricultural, clc||artificial_non_agricultural_vegetated, clc||forest_and_semi_natural, clc||industrial_commercial_and_transport, clc||int_a_m2, clc||label, clc||mine_dump_and_construction, clc||perc, clc||urban, clc||water_bodies, clc||wetlands, geoportale_infr_infrastrutture||traffic__15__aeroporto_militare, geoportale_infr_infrastrutture||traffic__15__aeroporto_principale, geoportale_infr_infrastrutture||traffic__15__aeroporto_secondario, geoportale_infr_infrastrutture||traffic__15__porto_commerciale, geoportale_infr_infrastrutture||traffic__15__porto_commerciale_turistico, geoportale_infr_infrastrutture||traffic__15__porto_industriale, geoportale_infr_infrastrutture||traffic__15__porto_turistico, geoportale_infr_infrastrutture||traffic__15__stazione_ferroviaria, geoportale_infr_infrastrutture||traffic__15__terminal_industriale, geoportale_infr_infrastrutture||traffic__15__total_infrastrutture, geoportale_infr_infrastrutture||traffic__30__aeroporto_militare, geoportale_infr_infrastrutture||traffic__30__aeroporto_principale, geoportale_infr_infrastrutture||traffic__30__aeroporto_secondario, geoportale_infr_infrastrutture||traffic__30__porto_commerciale, geoportale_infr_infrastrutture||traffic__30__porto_commerciale_turistico, geoportale_infr_infrastrutture||traffic__30__porto_industriale, geoportale_infr_infrastrutture||traffic__30__porto_turistico, geoportale_infr_infrastrutture||traffic__30__stazione_ferroviaria, geoportale_infr_infrastrutture||traffic__30__terminal_industriale, geoportale_infr_infrastrutture||traffic__30__total_infrastrutture, geoportale_infr_infrastrutture||walking__15__aeroporto_militare, geoportale_infr_infrastrutture||walking__15__aeroporto_principale, geoportale_infr_infrastrutture||walking__15__aeroporto_secondario, geoportale_infr_infrastrutture||walking__15__porto_commerciale, geoportale_infr_infrastrutture||walking__15__porto_commerciale_turistico, geoportale_infr_infrastrutture||walking__15__porto_turistico, geoportale_infr_infrastrutture||walking__15__stazione_ferroviaria, geoportale_infr_infrastrutture||walking__15__terminal_industriale, geoportale_infr_infrastrutture||walking__15__total_infrastrutture, geoportale_infr_infrastrutture||walking__30__aeroporto_militare, geoportale_infr_infrastrutture||walking__30__aeroporto_principale, geoportale_infr_infrastrutture||walking__30__aeroporto_secondario, geoportale_infr_infrastrutture||walking__30__porto_commerciale, geoportale_infr_infrastrutture||walking__30__porto_commerciale_turistico, geoportale_infr_infrastrutture||walking__30__porto_industriale, geoportale_infr_infrastrutture||walking__30__porto_turistico, geoportale_infr_infrastrutture||walking__30__stazione_ferroviaria, geoportale_infr_infrastrutture||walking__30__terminal_industriale, geoportale_infr_infrastrutture||walking__30__total_infrastrutture, istat||p_0_14__01, istat||p_0_14__11, istat||p_0_14__21, istat||p_0_14__91, istat||p_15_19__01, istat||p_15_19__11, istat||p_15_19__21, istat||p_15_19__91, istat||p_20_39__01, istat||p_20_39__11, istat||p_20_39__21, istat||p_20_39__91, istat||p_40_64__01, istat||p_40_64__11, istat||p_40_64__21, istat||p_40_64__91, istat||p_65_99__01, istat||p_65_99__11, istat||p_65_99__21, istat||p_65_99__91, istat||pop_f__01, istat||pop_f__11, istat||pop_f__21, istat||pop_f__91, istat||pop_m__01, istat||pop_m__11, istat||pop_m__21, istat||pop_m__91, istat||pop_tot__01, istat||pop_tot__11, istat||pop_tot__21, istat||pop_tot__21__01, istat||pop_tot__21__11, istat||pop_tot__91, ovm_infr_infrastrutture||traffic__15__aeroporto, ovm_infr_infrastrutture||traffic__15__bus, ovm_infr_infrastrutture||traffic__15__stazione, ovm_infr_infrastrutture||traffic__15__total_infrastrutture, ovm_infr_infrastrutture||traffic__30__aeroporto, ovm_infr_infrastrutture||traffic__30__bus, ovm_infr_infrastrutture||traffic__30__stazione, ovm_infr_infrastrutture||traffic__30__total_infrastrutture, ovm_infr_infrastrutture||walking__15__aeroporto, ovm_infr_infrastrutture||walking__15__bus, ovm_infr_infrastrutture||walking__15__stazione, ovm_infr_infrastrutture||walking__15__total_infrastrutture, ovm_infr_infrastrutture||walking__30__aeroporto, ...]
Index: []
[0 rows x 311 columns]
------------------------------
📊 RIASSUNTO TEST:
Successi: 6/6
✅ Successo Quante celle hanno più di 5000 residenti...
✅ Successo Zone con popolazione femminile alta...
✅ Successo Aree con buona accessibilità ai servizi...
✅ Successo Zone con molti negozi raggiungibili...
✅ Successo Celle con copertura agricola estesa...
✅ Successo Aree urbane dense...
In [10]:
# Funzioni di utilità per il tutorial
def quick_ask(question):
"""Modo veloce per fare una domanda senza output verboso"""
return agent.ask(question, show_map=False)
def map_only(question):
"""Mostra solo la mappa per una query"""
query_spec = agent.claude.query_interpreter(question, agent.analyzer.columns_by_category)
if 'error' not in query_spec:
result = agent.analyzer.execute_query(query_spec)
if hasattr(result, 'geometry'):
return agent.visualizer.create_map(result, question)
return None
def explore_category(category_name):
"""Esplora una categoria specifica del dataset"""
if category_name in agent.analyzer.columns_by_category:
colonne = agent.analyzer.columns_by_category[category_name]
print(f"📂 CATEGORIA: {category_name.upper()}")
print(f"📊 Colonne disponibili: {len(colonne)}")
print("-"*40)
for col in colonne:
print(f"• {col['name']}")
print(f" └─ {col['description']}")
return colonne
else:
print(f"❌ Categoria '{category_name}' non trovata")
print(f"Categorie disponibili: {list(agent.analyzer.columns_by_category.keys())}")
return None
def get_sample_data(n_rows=5):
"""Mostra un campione dei dati"""
return agent.analyzer.gdf.head(n_rows)
# Esempi di utilizzo rapido
print("🛠️ FUNZIONI DI UTILITÀ CARICATE")
print("\nEsempi di utilizzo:")
print("• quick_ask('domanda') - Query veloce")
print("• map_only('domanda') - Solo mappa")
print("• explore_category('nome_categoria') - Esplora categoria")
print("• get_sample_data(10) - Campione dati")
# Test rapido delle utilità
print(f"\n📈 Esempio explore_category:")
explore_category('Demografia')
🛠️ FUNZIONI DI UTILITÀ CARICATE
Esempi di utilizzo:
• quick_ask('domanda') - Query veloce
• map_only('domanda') - Solo mappa
• explore_category('nome_categoria') - Esplora categoria
• get_sample_data(10) - Campione dati
📈 Esempio explore_category:
📂 CATEGORIA: DEMOGRAFIA
📊 Colonne disponibili: 34
----------------------------------------
• p_0_14__01
└─ Popolazione residente di età 0 - 14 anni nel 2001
• p_0_14__11
└─ Popolazione residente di età 0 - 14 anni nel 2011
• p_0_14__21
└─ Popolazione residente di età 0 - 14 anni nel 2021
• p_0_14__91
└─ Popolazione residente di età 0 - 14 anni nel 1991
• p_15_19__01
└─ Popolazione residente di età 15 - 19 anni nel 2001
• p_15_19__11
└─ Popolazione residente di età 15 - 19 anni nel 2011
• p_15_19__21
└─ Popolazione residente di età 15 - 19 anni nel 2021
• p_15_19__91
└─ Popolazione residente di età 15 - 19 anni nel 1991
• p_20_39__01
└─ Popolazione residente di età 20 - 39 anni nel 2001
• p_20_39__11
└─ Popolazione residente di età 20 - 39 anni nel 2011
• p_20_39__21
└─ Popolazione residente di età 20 - 39 anni nel 2021
• p_20_39__91
└─ Popolazione residente di età 20 - 39 anni nel 1991
• p_40_64__01
└─ Popolazione residente di età 40 - 64 anni nel 2001
• p_40_64__11
└─ Popolazione residente di età 40 - 64 anni nel 2011
• p_40_64__21
└─ Popolazione residente di età 40 - 64 anni nel 2021
• p_40_64__91
└─ Popolazione residente di età 40 - 64 anni nel 1991
• p_65_99__01
└─ Popolazione residente di età over 65 nel 2001
• p_65_99__11
└─ Popolazione residente di età over 65 nel 2011
• p_65_99__21
└─ Popolazione residente di età over 65 nel 2021
• p_65_99__91
└─ Popolazione residente di età over 65 nel 1991
• pop_f__01
└─ Popolazione residente femminile nel 2001
• pop_f__11
└─ Popolazione residente femminile nel 2011
• pop_f__21
└─ Popolazione residente femminile nel 2021
• pop_f__91
└─ Popolazione residente femminile nel 1991
• pop_m__01
└─ Popolazione residente maschile nel 2001
• pop_m__11
└─ Popolazione residente maschile nel 2011
• pop_m__21
└─ Popolazione residente maschile nel 2021
• pop_m__91
└─ Popolazione residente maschile nel 1991
• pop_tot__01
└─ Popolazione residente nel 2001
• pop_tot__11
└─ Popolazione residente nel 2011
• pop_tot__21
└─ Popolazione residente nel 2021
• pop_tot__21__01
└─ Tasso di variazione della popolazione 2001-2021
• pop_tot__21__11
└─ Tasso di variazione della popolazione 2011-2021
• pop_tot__91
└─ Popolazione residente nel 1991
Out[10]:
[{'name_adv': 'istat||p_0_14__01',
'name': 'p_0_14__01',
'description': 'Popolazione residente di età 0 - 14 anni nel 2001'},
{'name_adv': 'istat||p_0_14__11',
'name': 'p_0_14__11',
'description': 'Popolazione residente di età 0 - 14 anni nel 2011'},
{'name_adv': 'istat||p_0_14__21',
'name': 'p_0_14__21',
'description': 'Popolazione residente di età 0 - 14 anni nel 2021'},
{'name_adv': 'istat||p_0_14__91',
'name': 'p_0_14__91',
'description': 'Popolazione residente di età 0 - 14 anni nel 1991'},
{'name_adv': 'istat||p_15_19__01',
'name': 'p_15_19__01',
'description': 'Popolazione residente di età 15 - 19 anni nel 2001'},
{'name_adv': 'istat||p_15_19__11',
'name': 'p_15_19__11',
'description': 'Popolazione residente di età 15 - 19 anni nel 2011'},
{'name_adv': 'istat||p_15_19__21',
'name': 'p_15_19__21',
'description': 'Popolazione residente di età 15 - 19 anni nel 2021'},
{'name_adv': 'istat||p_15_19__91',
'name': 'p_15_19__91',
'description': 'Popolazione residente di età 15 - 19 anni nel 1991'},
{'name_adv': 'istat||p_20_39__01',
'name': 'p_20_39__01',
'description': 'Popolazione residente di età 20 - 39 anni nel 2001'},
{'name_adv': 'istat||p_20_39__11',
'name': 'p_20_39__11',
'description': 'Popolazione residente di età 20 - 39 anni nel 2011'},
{'name_adv': 'istat||p_20_39__21',
'name': 'p_20_39__21',
'description': 'Popolazione residente di età 20 - 39 anni nel 2021'},
{'name_adv': 'istat||p_20_39__91',
'name': 'p_20_39__91',
'description': 'Popolazione residente di età 20 - 39 anni nel 1991'},
{'name_adv': 'istat||p_40_64__01',
'name': 'p_40_64__01',
'description': 'Popolazione residente di età 40 - 64 anni nel 2001'},
{'name_adv': 'istat||p_40_64__11',
'name': 'p_40_64__11',
'description': 'Popolazione residente di età 40 - 64 anni nel 2011'},
{'name_adv': 'istat||p_40_64__21',
'name': 'p_40_64__21',
'description': 'Popolazione residente di età 40 - 64 anni nel 2021'},
{'name_adv': 'istat||p_40_64__91',
'name': 'p_40_64__91',
'description': 'Popolazione residente di età 40 - 64 anni nel 1991'},
{'name_adv': 'istat||p_65_99__01',
'name': 'p_65_99__01',
'description': 'Popolazione residente di età over 65 nel 2001'},
{'name_adv': 'istat||p_65_99__11',
'name': 'p_65_99__11',
'description': 'Popolazione residente di età over 65 nel 2011'},
{'name_adv': 'istat||p_65_99__21',
'name': 'p_65_99__21',
'description': 'Popolazione residente di età over 65 nel 2021'},
{'name_adv': 'istat||p_65_99__91',
'name': 'p_65_99__91',
'description': 'Popolazione residente di età over 65 nel 1991'},
{'name_adv': 'istat||pop_f__01',
'name': 'pop_f__01',
'description': 'Popolazione residente femminile nel 2001'},
{'name_adv': 'istat||pop_f__11',
'name': 'pop_f__11',
'description': 'Popolazione residente femminile nel 2011'},
{'name_adv': 'istat||pop_f__21',
'name': 'pop_f__21',
'description': 'Popolazione residente femminile nel 2021'},
{'name_adv': 'istat||pop_f__91',
'name': 'pop_f__91',
'description': 'Popolazione residente femminile nel 1991'},
{'name_adv': 'istat||pop_m__01',
'name': 'pop_m__01',
'description': 'Popolazione residente maschile nel 2001'},
{'name_adv': 'istat||pop_m__11',
'name': 'pop_m__11',
'description': 'Popolazione residente maschile nel 2011'},
{'name_adv': 'istat||pop_m__21',
'name': 'pop_m__21',
'description': 'Popolazione residente maschile nel 2021'},
{'name_adv': 'istat||pop_m__91',
'name': 'pop_m__91',
'description': 'Popolazione residente maschile nel 1991'},
{'name_adv': 'istat||pop_tot__01',
'name': 'pop_tot__01',
'description': 'Popolazione residente nel 2001'},
{'name_adv': 'istat||pop_tot__11',
'name': 'pop_tot__11',
'description': 'Popolazione residente nel 2011'},
{'name_adv': 'istat||pop_tot__21',
'name': 'pop_tot__21',
'description': 'Popolazione residente nel 2021'},
{'name_adv': 'istat||pop_tot__21__01',
'name': 'pop_tot__21__01',
'description': 'Tasso di variazione della popolazione 2001-2021'},
{'name_adv': 'istat||pop_tot__21__11',
'name': 'pop_tot__21__11',
'description': 'Tasso di variazione della popolazione 2011-2021'},
{'name_adv': 'istat||pop_tot__91',
'name': 'pop_tot__91',
'description': 'Popolazione residente nel 1991'}]
In [11]:
import PyPDF2
import fitz # pymupdf
from pathlib import Path
import re
from typing import List, Dict
class PDFProcessor:
def __init__(self, pdf_directory: str = None):
self.pdf_directory = Path(pdf_directory) if pdf_directory else Path(out_path)
self.documents = {}
self.context_summary = ""
def load_pdfs(self, pdf_paths: List[str] = None) -> Dict[str, str]:
"""Carica tutti i PDF dalla directory o dai percorsi specificati"""
if pdf_paths:
files_to_process = [Path(p) for p in pdf_paths]
else:
files_to_process = list(self.pdf_directory.glob("*.pdf"))
print(f"🔍 Cercando PDF in: {self.pdf_directory}")
print(f"📄 Trovati {len(files_to_process)} file PDF")
for pdf_path in files_to_process:
try:
text = self._extract_text_pymupdf(pdf_path)
if text.strip():
self.documents[pdf_path.name] = text
print(f"✅ Caricato: {pdf_path.name} ({len(text)} caratteri)")
else:
print(f"⚠️ PDF vuoto: {pdf_path.name}")
except Exception as e:
print(f"❌ Errore con {pdf_path.name}: {e}")
if self.documents:
self._create_context_summary()
return self.documents
def _extract_text_pymupdf(self, pdf_path: Path) -> str:
"""Estrae testo usando PyMuPDF (migliore per layout complessi)"""
doc = fitz.open(pdf_path)
text = ""
for page_num in range(doc.page_count):
page = doc[page_num]
text += page.get_text()
doc.close()
return self._clean_text(text)
def _extract_text_pypdf2(self, pdf_path: Path) -> str:
"""Metodo alternativo con PyPDF2"""
text = ""
with open(pdf_path, 'rb') as file:
reader = PyPDF2.PdfReader(file)
for page in reader.pages:
text += page.extract_text()
return self._clean_text(text)
def _clean_text(self, text: str) -> str:
"""Pulisce il testo estratto"""
# Rimuovi caratteri di controllo e normalizza spazi
text = re.sub(r'\s+', ' ', text)
text = re.sub(r'[^\w\s\.\,\;\:\!\?\-\(\)]', ' ', text)
return text.strip()
def _create_context_summary(self):
"""Crea un riassunto del contesto dai PDF"""
all_text = " ".join(self.documents.values())
# Statistiche base
total_chars = len(all_text)
total_words = len(all_text.split())
self.context_summary = f"""
CONTESTO DAI PDF ({len(self.documents)} documenti):
- Caratteri totali: {total_chars:,}
- Parole totali: {total_words:,}
- File caricati: {', '.join(self.documents.keys())}
"""
def get_relevant_context(self, query: str, max_chars: int = 4000) -> str:
"""Estrae contesto rilevante per una query specifica"""
if not self.documents:
return ""
query_lower = query.lower()
relevant_chunks = []
for doc_name, content in self.documents.items():
# Trova frasi che contengono parole della query
sentences = re.split(r'[.!?]+', content)
for sentence in sentences:
if any(word in sentence.lower() for word in query_lower.split() if len(word) > 3):
relevant_chunks.append((doc_name, sentence.strip()))
# Seleziona i chunk più rilevanti
context = ""
for doc_name, chunk in relevant_chunks[:10]: # Max 10 chunk
if len(context) + len(chunk) < max_chars:
context += f"[{doc_name}] {chunk}\n\n"
else:
break
return context
# Inizializza processor
pdf_processor = PDFProcessor(pdf_path)
print("📚 PDFProcessor inizializzato!")
📚 PDFProcessor inizializzato!
In [12]:
# Caricamento dei PDF dello studio
# Opzione 1: Carica tutti i PDF dalla directory
documents = pdf_processor.load_pdfs()
# Opzione 2: Carica PDF specifici (decommenta se necessario)
# pdf_files = [
# "/path/to/studio_popolazione.pdf",
# "/path/to/metodologia.pdf",
# "/path/to/risultati_analisi.pdf"
# ]
# documents = pdf_processor.load_pdfs(pdf_files)
print(f"\n{pdf_processor.context_summary}")
# Mostra anteprima di ogni documento
if documents:
print("\n📖 ANTEPRIMA DOCUMENTI:")
print("="*50)
for filename, content in documents.items():
preview = content[:300] + "..." if len(content) > 300 else content
print(f"\n📄 {filename}")
print("-" * len(filename))
print(preview)
print(f"\n└─ Lunghezza totale: {len(content):,} caratteri")
else:
print("\n⚠️ Nessun PDF caricato. Verifica i percorsi!")
print(f"Directory controllata: {pdf_processor.pdf_directory}")
# Lista file nella directory per debug
try:
all_files = list(pdf_processor.pdf_directory.glob("*"))
print(f"File presenti: {[f.name for f in all_files]}")
except:
print("Directory non trovata o inaccessibile")
🔍 Cercando PDF in: /home/ilab/ILAB_DATA/OCPR/DOCS 📄 Trovati 1 file PDF ✅ Caricato: Relazione finale.pdf (53338 caratteri) CONTESTO DAI PDF (1 documenti): - Caratteri totali: 53,338 - Parole totali: 7,375 - File caricati: Relazione finale.pdf 📖 ANTEPRIMA DOCUMENTI: ================================================== 📄 Relazione finale.pdf -------------------- Descrizione del sistema informativo Il sistema informativo sviluppato nell ambito del progetto rappresenta una soluzione integrata per l analisi dell accessibilità ai servizi per i cittadini e le cittadine della Sardegna. La piattaforma è stata progettata per supportare il decisore pubblico nelle sc... └─ Lunghezza totale: 53,338 caratteri
In [13]:
class EnhancedClaudeClient(ClaudeClient):
def __init__(self, api_key: str, pdf_processor: PDFProcessor = None):
super().__init__(api_key)
self.pdf_processor = pdf_processor
def query_interpreter_with_context(self, user_query: str, available_columns: dict) -> dict:
"""Interpreta query con contesto dai PDF"""
# Ottieni contesto rilevante dai PDF
context = ""
if self.pdf_processor and self.pdf_processor.documents:
context = self.pdf_processor.get_relevant_context(user_query)
prompt = f"""
CONTESTO DELLO STUDIO:
{context}
Colonne disponibili per categoria:
{json.dumps(available_columns, indent=2)}
Domanda utente: "{user_query}"
Considerando il contesto dello studio, analizza la domanda e restituisci un JSON:
{{
"operation": "filter|aggregate|spatial",
"columns": ["lista_colonne_coinvolte"],
"conditions": [
{{"column": "nome_colonna", "operator": ">|<|=|!=", "value": valore}}
],
"aggregation": "count|sum|mean|max|min|null",
"spatial_operation": "within|distance|intersects|null",
"explanation": "breve spiegazione operazione con riferimento al contesto"
}}
"""
response = self.client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1200,
messages=[{"role": "user", "content": prompt}]
)
try:
return json.loads(response.content[0].text)
except:
return {"error": "Parsing fallito", "raw_response": response.content[0].text}
def generate_response_with_context(self, question: str, query_spec: dict, result) -> str:
"""Genera risposta arricchita dal contesto dello studio"""
# Contesto rilevante per la risposta
context = ""
if self.pdf_processor and self.pdf_processor.documents:
context = self.pdf_processor.get_relevant_context(question, max_chars=2000)
prompt = f"""
CONTESTO DELLO STUDIO:
{context}
Domanda originale: "{question}"
Operazione eseguita: {query_spec.get('explanation', 'N/A')}
Risultato numerico: {result}
Genera una risposta che:
1. Risponde alla domanda con il risultato
2. Collega il risultato al contesto dello studio quando rilevante
3. Fornisce interpretazioni basate sulla ricerca documentata
4. Mantiene un tono professionale ma accessibile
"""
response = self.client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=500,
messages=[{"role": "user", "content": prompt}]
)
return response.content[0].text
In [14]:
class ContextAwareGeoAgent(GeoAgent):
def __init__(self, claude_api_key: str, geoparquet_path: str, metadata_path: str, pdf_processor: PDFProcessor):
# Inizializza con il client enhanced
self.claude = EnhancedClaudeClient(claude_api_key, pdf_processor)
self.analyzer = DataAnalyzer(geoparquet_path, metadata_path)
self.visualizer = MapVisualizer(self.analyzer.gdf, self.analyzer.metadata)
self.pdf_processor = pdf_processor
self.context = self._build_enhanced_context()
def _build_enhanced_context(self) -> str:
base_context = super()._build_context()
if self.pdf_processor.documents:
pdf_context = f"""
DOCUMENTI STUDIO CARICATI:
{self.pdf_processor.context_summary}
"""
return base_context + "\n" + pdf_context
return base_context
def ask_with_context(self, question: str, show_map: bool = True):
"""Query con contesto arricchito dai PDF"""
query_spec = self.claude.query_interpreter_with_context(
question,
self.analyzer.columns_by_category
)
if 'error' in query_spec:
return f"❌ Non riesco a interpretare: {query_spec['error']}"
result = self.analyzer.execute_query(query_spec)
response = self.claude.generate_response_with_context(question, query_spec, result)
print(f"🤔 DOMANDA: {question}")
print(f"🤖 RISPOSTA CONTESTUALIZZATA: {response}")
# Mostra contesto utilizzato
if self.pdf_processor.documents:
relevant_context = self.pdf_processor.get_relevant_context(question, max_chars=500)
if relevant_context.strip():
print(f"\n📚 CONTESTO UTILIZZATO:")
print(relevant_context[:200] + "..." if len(relevant_context) > 200 else relevant_context)
if show_map and hasattr(result, 'geometry'):
print("\n🗺️ MAPPA:")
viz_type = "heatmap" if len(result) > 100 else "markers"
mappa = self.visualizer.create_map(result, question[:40], viz_type)
display(mappa)
return response, mappa
return response
def compare_responses(self, question: str):
"""Confronta risposta normale vs contestualizzata"""
print(f"🔍 CONFRONTO RISPOSTE: {question}")
print("="*60)
# Risposta normale
print("\n1️⃣ RISPOSTA STANDARD:")
print("-"*30)
standard_response = super().ask(question, show_map=False)
# Risposta con contesto
print("\n2️⃣ RISPOSTA CON CONTESTO STUDIO:")
print("-"*30)
context_response = self.ask_with_context(question, show_map=False)
return standard_response, context_response
In [15]:
# Inizializzazione dell'agente con contesto PDF
# Crea l'agente potenziato con contesto
context_agent = ContextAwareGeoAgent(
claude_api_key=CLAUDE_API_KEY,
geoparquet_path=geoparquet_path,
metadata_path=metadata_path,
pdf_processor=pdf_processor
)
print("🚀 ContextAwareGeoAgent inizializzato!")
print(f"📊 {context_agent.context}")
# Test delle capacità avanzate
if pdf_processor.documents:
print("\n" + "="*50)
print("🧪 TEST CAPACITÀ CONTESTUALI")
print("="*50)
# Test contesto rilevante
test_query = "popolazione urbana"
relevant = pdf_processor.get_relevant_context(test_query, max_chars=300)
if relevant.strip():
print(f"\n🔍 Contesto per '{test_query}':")
print(relevant)
else:
print(f"\n⚠️ Nessun contesto rilevante trovato per '{test_query}'")
print(f"\n✅ Agente con contesto pronto per domande avanzate!")
print(f"📚 Documenti disponibili: {len(pdf_processor.documents)}")
else:
print("\n⚠️ Nessun PDF caricato - l'agente funzionerà in modalità standard")
print("Per abilitare il contesto, carica prima i PDF con il chunk 12")
🚀 ContextAwareGeoAgent inizializzato!
📊 Dataset: 704 celle esagonali con 311 attributi
Categorie: Griglia, Copertura del suolo, Accessibilità trasporti (geoportale sardegna), Demografia, Accessibilità trasporti, Accessibilità cultura, Accessibilità istruzione, Accessibilità ristorazione, Accessibilità sanità, Accessibilità sport, Accessibilità strutture ricettive, Accessibilità sanità - Ospedali
Esempi colonne per categoria: {'Griglia': ['geometry', 'h3_06', 'cell_a_m2'], 'Copertura del suolo': ['agricultural', 'artificial_non_agricultural_vegetated', 'forest_and_semi_natural'], 'Accessibilità trasporti (geoportale sardegna)': ['traffic__15__aeroporto_militare', 'traffic__15__aeroporto_principale', 'traffic__15__aeroporto_secondario'], 'Demografia': ['p_0_14__01', 'p_0_14__11', 'p_0_14__21'], 'Accessibilità trasporti': ['traffic__15__aeroporto', 'traffic__15__bus', 'traffic__15__stazione'], 'Accessibilità cultura': ['traffic__15__biblioteca', 'traffic__15__cinema', 'traffic__15__evento_culturale'], 'Accessibilità istruzione': ['traffic__15__formazione_alternativa', 'traffic__15__formazione_professionale', 'traffic__15__istituti_scolastici'], 'Accessibilità ristorazione': ['traffic__15__altri_servizi_alimentari', 'traffic__15__bar', 'traffic__15__ristorante'], 'Accessibilità sanità': ['traffic__15__centri_sanitari', 'traffic__15__cliniche_mediche', 'traffic__15__servizi_diagnostici'], 'Accessibilità sport': ['traffic__15__altre_attivita_sportive', 'traffic__15__club_e_squadre_sportive', 'traffic__15__fitness_e_allenamento'], 'Accessibilità strutture ricettive': ['traffic__15__strutture_alberghiere', 'traffic__15__strutture_extra_alberghiere', 'traffic__15__total_strutture_ricettive'], 'Accessibilità sanità - Ospedali': ['traffic__15__ao_integrata_con_il_ssn', 'traffic__15__azienda_ospedaliera', 'traffic__15__osped_a_gestione_diretta_presidio_asl']}
DOCUMENTI STUDIO CARICATI:
CONTESTO DAI PDF (1 documenti):
- Caratteri totali: 53,338
- Parole totali: 7,375
- File caricati: Relazione finale.pdf
==================================================
🧪 TEST CAPACITÀ CONTESTUALI
==================================================
🔍 Contesto per 'popolazione urbana':
[Relazione finale.pdf] Infine, integrando i dati demografici, si elaborano indicatori compositi che esprimono l accessibilità effettiva, considerando non solo la distanza fisica ma anche la distribuzione della popolazione
✅ Agente con contesto pronto per domande avanzate!
📚 Documenti disponibili: 1
In [16]:
# Esempi di utilizzo con contesto PDF
print("📚 ESEMPI CON CONTESTO DELLO STUDIO")
print("="*50)
# Esempi che beneficiano del contesto
domande_contestuali = [
"Qual è la metodologia usata per calcolare la popolazione?",
"Come è stata definita l'accessibilità ai servizi?",
"Quali sono i principali risultati dello studio?",
"Zone ad alta densità secondo i criteri dello studio",
"Distribuzione demografica come descritto nella ricerca"
]
if pdf_processor.documents:
print("📖 Testando domande che utilizzano il contesto...")
for i, domanda in enumerate(domande_contestuali[:3], 1): # Prime 3 per brevità
print(f"\n{i}. {domanda}")
print("-"*60)
try:
context_agent.ask_with_context(domanda, show_map=False)
except Exception as e:
print(f"❌ Errore: {e}")
print()
# Confronto diretto
print("\n🔍 CONFRONTO RISPOSTE (Standard vs Contestualizzata)")
print("="*60)
test_question = "Zone con alta densità di popolazione"
context_agent.compare_responses(test_question)
else:
print("⚠️ PDF non caricati - esempi non disponibili")
print("Carica prima i PDF con il chunk 12 per testare questa funzionalità")
# Funzione helper per test rapidi
def test_context_query(question):
"""Test rapido di una query con contesto"""
if pdf_processor.documents:
return context_agent.ask_with_context(question, show_map=False)
else:
print("⚠️ PDF non caricati")
return None
print(f"\n🛠️ Funzione helper disponibile:")
print("test_context_query('tua domanda') - Test rapido con contesto")
📚 ESEMPI CON CONTESTO DELLO STUDIO ================================================== 📖 Testando domande che utilizzano il contesto... 1. Qual è la metodologia usata per calcolare la popolazione? ------------------------------------------------------------
2. Come è stata definita l'accessibilità ai servizi? ------------------------------------------------------------
3. Quali sono i principali risultati dello studio? ------------------------------------------------------------
🔍 CONFRONTO RISPOSTE (Standard vs Contestualizzata) ============================================================ 🔍 CONFRONTO RISPOSTE: Zone con alta densità di popolazione ============================================================ 1️⃣ RISPOSTA STANDARD: ------------------------------
🤔 DOMANDA: Zone con alta densità di popolazione 🤖 RISPOSTA: Mi sembra ci sia stato un problema tecnico nel calcolo della densità di popolazione. L'errore indica che il sistema sta cercando di confrontare dei numeri decimali con del testo, cosa che non è possibile fare matematicamente. Per identificare le zone ad alta densità di popolazione, suggerirei di: 1. Verificare che i dati della popolazione e dell'area siano effettivamente in formato numerico 2. Assicurarsi che non ci siano valori mancanti o caratteri speciali nei dati 3. Riprovare il calcolo dopo aver controllato e corretto il formato dei dati Vuoi che proviamo insieme a risolvere il problema? 2️⃣ RISPOSTA CON CONTESTO STUDIO: ------------------------------
🛠️ Funzione helper disponibile:
test_context_query('tua domanda') - Test rapido con contesto
In [ ]: