import pandas as pd
import folium
from folium.plugins import HeatMap
import json
import zipfile
import tempfile
import os
import xml.etree.ElementTree as ET
from collections import Counter
from datetime import datetime
def cargar_y_analizar_todos_datos(ruta_incidencias):
"""
Carga datos y analiza TODOS los tipos de delitos
"""
df = pd.read_csv(ruta_incidencias)
print(f"📊 Datos cargados: {len(df)} registros")
# Análisis de TODOS los tipos de delitos
contador_delitos = Counter(df['delito'])
total_incidencias = len(df)
# Ordenar por frecuencia descendente
todos_tipos = []
for delito, count in contador_delitos.most_common():
porcentaje = (count / total_incidencias) * 100
todos_tipos.append({
'tipo': delito,
'count': count,
'porcentaje': porcentaje
})
print(f"🎯 Se analizaron {len(todos_tipos)} tipos de delitos diferentes")
# Análisis por municipio
contador_municipios = Counter(df['municipio'])
top_municipios = contador_municipios.most_common(10)
# Procesar coordenadas para TODOS los tipos
coordenadas_por_tipo = {}
for item in todos_tipos:
coordenadas_por_tipo[item['tipo']] = []
for idx, row in df.iterrows():
try:
lat = row['latitud']
lon = row['longitud']
tipo_delito = row['delito']
# Validar coordenadas
if pd.notna(lat) and pd.notna(lon) and 15.0 < lat < 18.0 and -98.0 < lon < -94.0:
coordenadas_por_tipo[tipo_delito].append([lat, lon])
except:
continue
return df, coordenadas_por_tipo, todos_tipos, top_municipios
def kmz_a_geojson_simple(ruta_kmz):
"""
Convierte KMZ a GeoJSON de forma simple y segura
"""
try:
with zipfile.ZipFile(ruta_kmz, 'r') as kmz:
kml_files = [f for f in kmz.namelist() if f.endswith('.kml')]
if not kml_files:
return None
with tempfile.TemporaryDirectory() as temp_dir:
kmz.extract(kml_files[0], temp_dir)
kml_path = os.path.join(temp_dir, kml_files[0])
return parsear_kml_manual(kml_path)
except Exception as e:
print(f"❌ Error procesando KMZ: {e}")
return None
def parsear_kml_manual(ruta_kml):
"""
Parsea KML manualmente
"""
try:
tree = ET.parse(ruta_kml)
root = tree.getroot()
ns = {'kml': 'http://www.opengis.net/kml/2.2'}
features = []
for placemark in root.findall('.//kml:Placemark', ns):
feature = {
'type': 'Feature',
'properties': {},
'geometry': {'type': 'Point', 'coordinates': [0, 0]}
}
name_elem = placemark.find('kml:name', ns)
if name_elem is not None:
feature['properties']['name'] = name_elem.text
coords_elem = placemark.find('.//kml:coordinates', ns)
if coords_elem is not None and coords_elem.text:
try:
coords_text = coords_elem.text.strip()
parts = coords_text.split(',')
if len(parts) >= 2:
lon = float(parts[0])
lat = float(parts[1])
feature['geometry']['coordinates'] = [lon, lat]
features.append(feature)
except ValueError:
continue
return {
'type': 'FeatureCollection',
'features': features
}
except Exception as e:
print(f"❌ Error parseando KML: {e}")
return None
def crear_mapa_completo(ruta_incidencias, ruta_camaras_kmz=None, ruta_vehiculos_kmz=None):
"""
Crea mapa con análisis completo de TODOS los tipos de delitos
"""
df, coordenadas_por_tipo, todos_tipos, top_municipios = cargar_y_analizar_todos_datos(ruta_incidencias)
# Calcular centro del mapa basado en las coordenadas
todas_coordenadas = []
for coords in coordenadas_por_tipo.values():
todas_coordenadas.extend(coords)
if todas_coordenadas:
lats = [coord[0] for coord in todas_coordenadas]
lons = [coord[1] for coord in todas_coordenadas]
centro = [sum(lats)/len(lats), sum(lons)/len(lons)]
else:
# Centro por defecto para Oaxaca
centro = [17.0594, -96.7216]
m = folium.Map(location=centro, zoom_start=12)
# Generar paleta de colores más amplia
colores_base = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7',
'#DDA0DD', '#98D8C8', '#F7DC6F', '#BB8FCE', '#85C1E9',
'#FFA07A', '#20B2AA', '#778899', '#DEB887', '#5F9EA0',
'#FF69B4', '#BA55D3', '#9370DB', '#3CB371', '#FFD700']
# Extender paleta si hay más tipos
colores = colores_base
if len(todos_tipos) > len(colores_base):
import colorsys
colores_extra = []
for i in range(len(todos_tipos) - len(colores_base)):
hue = i / (len(todos_tipos) - len(colores_base))
rgb = colorsys.hsv_to_rgb(hue, 0.8, 0.9)
color = '#{:02x}{:02x}{:02x}'.format(int(rgb[0]*255), int(rgb[1]*255), int(rgb[2]*255))
colores_extra.append(color)
colores = colores_base + colores_extra
# Capa base de heatmap (todos los tipos)
todas_las_coordenadas = []
for coords in coordenadas_por_tipo.values():
todas_las_coordenadas.extend(coords)
if todas_las_coordenadas:
HeatMap(todas_las_coordenadas, radius=15, blur=12, min_opacity=0.3).add_to(m)
# Añadir marcadores individuales para los puntos
for idx, row in df.iterrows():
try:
if pd.notna(row['latitud']) and pd.notna(row['longitud']):
folium.CircleMarker(
location=[row['latitud'], row['longitud']],
radius=4,
popup=f"{row['delito']}
Municipio: {row['municipio']}",
color='red',
fill=True,
fillOpacity=0.6
).add_to(m)
except:
continue
# Cargar capas KMZ
capas_kmz = {}
if ruta_camaras_kmz and os.path.exists(ruta_camaras_kmz):
print("📷 Cargando cámaras KMZ...")
capas_kmz['camaras'] = kmz_a_geojson_simple(ruta_camaras_kmz)
if ruta_vehiculos_kmz and os.path.exists(ruta_vehiculos_kmz):
print("🚗 Cargando vehículos KMZ...")
capas_kmz['vehiculos'] = kmz_a_geojson_simple(ruta_vehiculos_kmz)
# Añadir marcadores KMZ
for capa_nombre, geojson in capas_kmz.items():
if geojson:
color = 'black' if capa_nombre == 'camaras' else 'blue'
icono = 'camera' if capa_nombre == 'camaras' else 'car'
etiqueta = '📹' if capa_nombre == 'camaras' else '🚗'
for feature in geojson['features']:
coords = feature['geometry']['coordinates']
nombre = feature['properties'].get('name', capa_nombre.title())
folium.Marker(
[coords[1], coords[0]],
popup=f"{etiqueta} {nombre}",
icon=folium.Icon(color=color, icon=icono, prefix='fa')
).add_to(m)
# Crear HTML para panel completo
crear_panel_completo(m, todos_tipos, top_municipios, colores, len(df))
return m, df, todos_tipos, top_municipios
def crear_panel_completo(m, todos_tipos, top_municipios, colores, total_incidencias):
"""
Crea panel de análisis completo de TODOS los tipos
"""
# HTML para el panel completo
panel_html = f'''