import streamlit as st import folium from streamlit_folium import folium_static from folium.plugins import HeatMap import gpxpy import gpxpy.gpx import numpy as np from PIL import Image import pandas as pd import random import os import io import tempfile # Configuración de la página st.set_page_config(page_title="🌍 Analizador de tiempos y movimientos Avanzado", layout="wide") # Función para cargar imagen def load_app_image(): image_path = os.path.join(os.path.dirname(__file__), "app.jpg") if os.path.exists(image_path): try: return Image.open(image_path) except: return None return None # Mostrar imagen si existe app_image = load_app_image() if app_image: st.image(app_image, use_container_width=True) # Función para procesar GPX sin caché primero para debugging def process_gpx_simple(file_content_or_path, is_file_path=False): """Procesa archivo GPX de manera simple y directa""" try: if is_file_path: # Es una ruta de archivo (ejemplo) with open(file_content_or_path, 'r', encoding='utf-8') as f: gpx = gpxpy.parse(f) else: # Es contenido de archivo (cargado) gpx = gpxpy.parse(file_content_or_path) # Extraer datos heatmap_data = [] total_distance = 0 total_time = 0 track_segments = [] if gpx.tracks: for track_idx, track in enumerate(gpx.tracks): for segment_idx, segment in enumerate(track.segments): segment_points = [] # Calcular distancia y tiempo for i in range(1, len(segment.points)): point1 = segment.points[i-1] point2 = segment.points[i] try: distance = point1.distance_2d(point2) if distance: total_distance += distance except: pass if point1.time and point2.time: try: time_diff = (point2.time - point1.time).total_seconds() total_time += time_diff except: pass # Extraer puntos for point in segment.points: if point.latitude and point.longitude: heatmap_data.append([point.latitude, point.longitude]) segment_points.append([point.latitude, point.longitude]) if segment_points: track_segments.append({ 'points': segment_points, 'track_idx': track_idx, 'segment_idx': segment_idx }) if heatmap_data: center_lat = sum(p[0] for p in heatmap_data) / len(heatmap_data) center_lon = sum(p[1] for p in heatmap_data) / len(heatmap_data) return { 'success': True, 'heatmap_data': heatmap_data, 'track_segments': track_segments, 'total_distance': total_distance, 'total_time': total_time, 'center': [center_lat, center_lon] } else: return {'success': False, 'error': 'No se encontraron puntos GPS válidos'} except Exception as e: return {'success': False, 'error': f'Error procesando GPX: {str(e)}'} def create_maps(heatmap_data, track_segments, center): """Crear ambos mapas""" try: # Mapa de calor m_heat = folium.Map( location=center, zoom_start=13, tiles='https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', attr='Esri Satellite' ) HeatMap(heatmap_data, radius=15, blur=10).add_to(m_heat) # Ajustar vista if len(heatmap_data) > 1: sw = [min(p[0] for p in heatmap_data), min(p[1] for p in heatmap_data)] ne = [max(p[0] for p in heatmap_data), max(p[1] for p in heatmap_data)] m_heat.fit_bounds([sw, ne], padding=[20, 20]) # Mapa spaghetti m_spaghetti = folium.Map(location=center, zoom_start=13, tiles='CartoDB positron') colors = ['red', 'blue', 'green', 'purple', 'orange', 'darkred', 'darkblue', 'darkgreen'] for i, segment in enumerate(track_segments): color = colors[i % len(colors)] folium.PolyLine( segment['points'], color=color, weight=4, opacity=0.8 ).add_to(m_spaghetti) # Marcadores inicio y fin if segment['points']: folium.Marker(segment['points'][0], icon=folium.Icon(color='green', icon='play')).add_to(m_spaghetti) folium.Marker(segment['points'][-1], icon=folium.Icon(color='red', icon='stop')).add_to(m_spaghetti) # Ajustar vista if len(heatmap_data) > 1: m_spaghetti.fit_bounds([sw, ne], padding=[20, 20]) return m_heat, m_spaghetti except Exception as e: st.error(f"Error creando mapas: {e}") return None, None # Título st.title("🌍 Análisis de tiempo y movimientos avanzado") st.markdown("Sube un archivo GPX para visualizar mapas de calor y diagramas de spaghetti de tus rutas.") # Obtener archivos de ejemplo examples_folder = os.path.join(os.path.dirname(__file__), "Examples") example_files = [] if os.path.exists(examples_folder): example_files = [f for f in os.listdir(examples_folder) if f.endswith(".gpx")] # Interface de carga uploaded_file = st.file_uploader("📁 Cargar archivo GPX", type="gpx") if uploaded_file is not None: selected_example = "Ninguno" # Forzar que no se use el ejemplo si se carga archivo else: selected_example = st.selectbox("📋 O selecciona un ejemplo:", ["Ninguno"] + example_files) gpx_data = None file_name = None if uploaded_file is not None: selected_example = "Ninguno" # Ignorar ejemplos si se carga archivo file_name = uploaded_file.name st.info(f"📄 Archivo seleccionado: {file_name}") try: # Leer contenido como texto directamente file_bytes = uploaded_file.read() file_content = file_bytes.decode("utf-8", errors="ignore") if file_content and " 0 else "N/A") with col2: hours = int(total_time // 3600) minutes = int((total_time % 3600) // 60) st.metric("⏱️ Tiempo", f"{hours}h {minutes}m" if total_time > 0 else "N/A") with col3: st.metric("📍 Puntos GPS", f"{len(heatmap_data):,}") # Crear y mostrar mapas st.markdown("### 🗺️ Visualización de Movimientos") with st.spinner("🗺️ Generando mapas..."): heat_map, spaghetti_map = create_maps(heatmap_data, track_segments, center) if heat_map and spaghetti_map: col1, col2 = st.columns(2) with col1: st.markdown("#### 🔥 Mapa de Calor") folium_static(heat_map, width=600, height=500) with col2: st.markdown("#### 🍝 Diagrama de Spaghetti") folium_static(spaghetti_map, width=600, height=500) # Info adicional with st.expander("ℹ️ Información técnica"): st.write(f"**Tracks:** {len(set(seg['track_idx'] for seg in track_segments))}") st.write(f"**Segmentos:** {len(track_segments)}") st.write(f"**Centro:** {center[0]:.6f}, {center[1]:.6f}") else: st.error("❌ Error generando los mapas") elif gpx_data and not gpx_data['success']: st.error(f"❌ Error: {gpx_data['error']}") else: st.info("📤 Por favor, carga un archivo GPX o selecciona un ejemplo para comenzar.") if example_files: st.markdown("### 📋 Ejemplos disponibles:") for i, file in enumerate(example_files, 1): st.write(f"{i}. **{file}**") # Footer st.markdown("---") st.markdown("🚀 **Optimizado para Hugging Face Spaces**", unsafe_allow_html=True)