Spaces:
Sleeping
Sleeping
Update src/streamlit_app.py
Browse files- src/streamlit_app.py +132 -150
src/streamlit_app.py
CHANGED
|
@@ -155,191 +155,173 @@ example_files = []
|
|
| 155 |
if os.path.exists(examples_folder):
|
| 156 |
example_files = [f for f in os.listdir(examples_folder) if f.endswith(".gpx")]
|
| 157 |
|
| 158 |
-
# ==========
|
| 159 |
|
| 160 |
-
# Opciones de selección de datos de ejemplo
|
| 161 |
st.subheader("Datos de Ejemplo")
|
| 162 |
-
|
| 163 |
-
|
| 164 |
|
|
|
|
| 165 |
if example_files:
|
| 166 |
-
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
archivo_ejemplo_seleccionado = example_file
|
| 172 |
|
| 173 |
-
# Inicializar
|
| 174 |
data = None
|
| 175 |
-
gpx_content = None
|
| 176 |
|
| 177 |
-
#
|
| 178 |
-
if
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
|
| 187 |
-
|
| 188 |
-
|
| 189 |
-
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 193 |
|
| 194 |
-
# Opción para cargar datos personalizados -
|
| 195 |
st.subheader("Carga tus Datos")
|
| 196 |
uploaded_file = st.file_uploader("Carga un archivo GPX con los datos experimentales:", type="gpx")
|
| 197 |
|
| 198 |
if uploaded_file is not None:
|
| 199 |
try:
|
| 200 |
-
#
|
| 201 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 202 |
try:
|
| 203 |
-
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
# Intentar decodificar con diferentes encodings como hace apptest.py internamente
|
| 207 |
-
gpx_content = None
|
| 208 |
-
for encoding in ['utf-8', 'latin-1', 'cp1252', 'iso-8859-1']:
|
| 209 |
-
try:
|
| 210 |
-
gpx_content = file_bytes.decode(encoding)
|
| 211 |
-
break
|
| 212 |
-
except UnicodeDecodeError:
|
| 213 |
-
continue
|
| 214 |
-
|
| 215 |
-
if gpx_content is None:
|
| 216 |
-
# Fallback: usar el método original de streamlit
|
| 217 |
-
uploaded_file.seek(0) # Reset file pointer
|
| 218 |
-
gpx_content = uploaded_file.read().decode('utf-8')
|
| 219 |
|
| 220 |
-
|
| 221 |
-
|
| 222 |
-
|
| 223 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 224 |
|
| 225 |
-
if gpx_content:
|
| 226 |
-
st.success("Datos cargados exitosamente desde el archivo proporcionado.")
|
| 227 |
-
|
| 228 |
-
# Crear DataFrame simulado para mantener consistencia con apptest.py
|
| 229 |
-
data = pd.DataFrame({
|
| 230 |
-
'status': ['loaded'],
|
| 231 |
-
'source': ['uploaded'],
|
| 232 |
-
'filename': [uploaded_file.name],
|
| 233 |
-
'size': [len(gpx_content)]
|
| 234 |
-
})
|
| 235 |
-
st.dataframe(data, use_container_width=True)
|
| 236 |
-
else:
|
| 237 |
-
st.error("No se pudo leer el contenido del archivo")
|
| 238 |
-
|
| 239 |
except Exception as e:
|
| 240 |
st.error(f"Error leyendo archivo: {e}")
|
| 241 |
-
|
| 242 |
-
|
| 243 |
-
st.write(f"Tipo de archivo: {type(uploaded_file)}")
|
| 244 |
-
st.write(f"Nombre de archivo: {uploaded_file.name if uploaded_file else 'None'}")
|
| 245 |
|
| 246 |
-
# Validación para asegurarse de que se cargaron datos -
|
| 247 |
if data is None:
|
| 248 |
st.warning("No se han cargado datos. Por favor, selecciona un archivo o usa datos de ejemplo.")
|
| 249 |
else:
|
| 250 |
st.write("### Datos listos para su análisis.")
|
| 251 |
|
| 252 |
-
#
|
| 253 |
-
|
| 254 |
-
|
| 255 |
-
|
| 256 |
-
#
|
| 257 |
-
|
| 258 |
-
|
| 259 |
-
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
# PROCESAR DATOS GPX
|
| 263 |
-
with st.spinner("🔄 Procesando datos GPX..."):
|
| 264 |
-
gpx_data = process_gpx_data(gpx_content)
|
| 265 |
|
| 266 |
-
|
| 267 |
-
|
| 268 |
-
|
| 269 |
-
|
| 270 |
-
|
| 271 |
-
|
| 272 |
-
|
| 273 |
-
|
| 274 |
-
|
| 275 |
-
|
| 276 |
-
|
| 277 |
-
|
| 278 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 279 |
|
| 280 |
with col1:
|
| 281 |
-
st.
|
| 282 |
-
|
| 283 |
-
hours = int(total_time // 3600) if total_time > 0 else 0
|
| 284 |
-
minutes = int((total_time % 3600) // 60) if total_time > 0 else 0
|
| 285 |
-
st.metric("⏱️ Tiempo", f"{hours}h {minutes}m" if total_time > 0 else "N/A")
|
| 286 |
-
with col3:
|
| 287 |
-
st.metric("📍 Puntos GPS", f"{len(heatmap_data):,}")
|
| 288 |
|
| 289 |
-
|
| 290 |
-
|
| 291 |
-
|
| 292 |
-
with st.spinner("🗺️ Generando mapas..."):
|
| 293 |
-
heat_map, spaghetti_map = create_maps(heatmap_data, track_segments, center)
|
| 294 |
-
|
| 295 |
-
if heat_map and spaghetti_map:
|
| 296 |
-
col1, col2 = st.columns(2)
|
| 297 |
-
|
| 298 |
-
with col1:
|
| 299 |
-
st.markdown("#### 🔥 Mapa de Calor")
|
| 300 |
-
folium_static(heat_map, width=600, height=500)
|
| 301 |
-
|
| 302 |
-
with col2:
|
| 303 |
-
st.markdown("#### 🍝 Diagrama de Spaghetti")
|
| 304 |
-
folium_static(spaghetti_map, width=600, height=500)
|
| 305 |
-
|
| 306 |
-
# Información técnica adicional
|
| 307 |
-
with st.expander("ℹ️ Información técnica"):
|
| 308 |
-
st.write(f"**Archivo:** {uploaded_file.name if uploaded_file else archivo_ejemplo_seleccionado}")
|
| 309 |
-
st.write(f"**Fuente:** {'Archivo cargado' if uploaded_file else 'Archivo de ejemplo'}")
|
| 310 |
-
st.write(f"**Tracks:** {len(set(seg['track_idx'] for seg in track_segments))}")
|
| 311 |
-
st.write(f"**Segmentos:** {len(track_segments)}")
|
| 312 |
-
st.write(f"**Centro:** {center[0]:.6f}, {center[1]:.6f}")
|
| 313 |
-
st.write(f"**Puntos totales:** {len(heatmap_data)}")
|
| 314 |
-
st.write(f"**Tamaño del archivo:** {len(gpx_content)} caracteres")
|
| 315 |
-
|
| 316 |
-
else:
|
| 317 |
-
st.error("❌ Error generando los mapas")
|
| 318 |
-
|
| 319 |
-
elif gpx_data and not gpx_data['success']:
|
| 320 |
-
st.error(f"❌ Error procesando GPX: {gpx_data['error']}")
|
| 321 |
-
|
| 322 |
-
# Debug completo
|
| 323 |
-
with st.expander("🔧 Información completa de debugging"):
|
| 324 |
-
st.write("**Información del archivo:**")
|
| 325 |
-
st.write(f"- Nombre: {uploaded_file.name if uploaded_file else archivo_ejemplo_seleccionado}")
|
| 326 |
-
st.write(f"- Tamaño: {len(gpx_content)} caracteres")
|
| 327 |
-
st.write(f"- Tipo de fuente: {'Cargado' if uploaded_file else 'Ejemplo'}")
|
| 328 |
|
| 329 |
-
|
| 330 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 331 |
|
| 332 |
-
st.write("**Últimos 500 caracteres del archivo:**")
|
| 333 |
-
st.text(gpx_content[-500:] if gpx_content and len(gpx_content) > 500 else "Archivo muy corto")
|
| 334 |
else:
|
| 335 |
-
st.error("❌ Error
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 336 |
|
| 337 |
-
# Mostrar ejemplos disponibles
|
| 338 |
-
if data is None
|
| 339 |
st.markdown("### 📋 Ejemplos disponibles:")
|
| 340 |
for i, file in enumerate(example_files, 1):
|
| 341 |
st.write(f"{i}. **{file}**")
|
| 342 |
|
| 343 |
# Footer
|
| 344 |
st.markdown("---")
|
| 345 |
-
st.markdown("🚀 **Optimizado para Hugging Face Spaces** | 📊 **
|
|
|
|
| 155 |
if os.path.exists(examples_folder):
|
| 156 |
example_files = [f for f in os.listdir(examples_folder) if f.endswith(".gpx")]
|
| 157 |
|
| 158 |
+
# ========== LÓGICA EXACTA DE APPTEST.PY ==========
|
| 159 |
|
| 160 |
+
# Opciones de selección de datos de ejemplo - EXACTAMENTE COMO APPTEST.PY
|
| 161 |
st.subheader("Datos de Ejemplo")
|
| 162 |
+
usar_datos_exp = st.checkbox("Usar datos de ejemplo: datos_exp.csv") if len(example_files) > 0 else False
|
| 163 |
+
usar_datos_process = st.checkbox("Usar datos de ejemplo: datos_process.csv") if len(example_files) > 1 else False
|
| 164 |
|
| 165 |
+
# Para GPX, adaptamos pero mantenemos la estructura:
|
| 166 |
if example_files:
|
| 167 |
+
for i, example_file in enumerate(example_files):
|
| 168 |
+
if i == 0:
|
| 169 |
+
usar_datos_exp = st.checkbox(f"Usar datos de ejemplo: {example_file}")
|
| 170 |
+
elif i == 1:
|
| 171 |
+
usar_datos_process = st.checkbox(f"Usar datos de ejemplo: {example_file}")
|
|
|
|
| 172 |
|
| 173 |
+
# Inicializar variable de datos - EXACTAMENTE COMO APPTEST.PY
|
| 174 |
data = None
|
|
|
|
| 175 |
|
| 176 |
+
# Verificar qué checkbox está seleccionado y cargar el archivo correspondiente - EXACTAMENTE COMO APPTEST.PY
|
| 177 |
+
if usar_datos_exp and not usar_datos_process:
|
| 178 |
+
if example_files:
|
| 179 |
+
try:
|
| 180 |
+
example_path = os.path.join(examples_folder, example_files[0])
|
| 181 |
+
with open(example_path, 'r', encoding='utf-8') as f:
|
| 182 |
+
gpx_content = f.read()
|
| 183 |
+
# Simular DataFrame como en apptest.py
|
| 184 |
+
data = pd.DataFrame({'source': ['example'], 'filename': [example_files[0]], 'loaded': [True]})
|
| 185 |
+
st.success(f"Datos de ejemplo ({example_files[0]}) cargados exitosamente.")
|
| 186 |
+
st.dataframe(data, use_container_width=True)
|
| 187 |
+
except Exception as e:
|
| 188 |
+
st.error(f"Error cargando ejemplo: {e}")
|
| 189 |
+
|
| 190 |
+
elif usar_datos_process and not usar_datos_exp:
|
| 191 |
+
if len(example_files) > 1:
|
| 192 |
+
try:
|
| 193 |
+
example_path = os.path.join(examples_folder, example_files[1])
|
| 194 |
+
with open(example_path, 'r', encoding='utf-8') as f:
|
| 195 |
+
gpx_content = f.read()
|
| 196 |
+
# Simular DataFrame como en apptest.py
|
| 197 |
+
data = pd.DataFrame({'source': ['example'], 'filename': [example_files[1]], 'loaded': [True]})
|
| 198 |
+
st.success(f"Datos de ejemplo ({example_files[1]}) cargados exitosamente.")
|
| 199 |
+
st.dataframe(data, use_container_width=True)
|
| 200 |
+
except Exception as e:
|
| 201 |
+
st.error(f"Error cargando ejemplo: {e}")
|
| 202 |
+
|
| 203 |
+
elif usar_datos_exp and usar_datos_process:
|
| 204 |
+
st.error("Por favor, selecciona solo un conjunto de datos de ejemplo a la vez.")
|
| 205 |
|
| 206 |
+
# Opción para cargar datos personalizados - EXACTAMENTE COMO APPTEST.PY
|
| 207 |
st.subheader("Carga tus Datos")
|
| 208 |
uploaded_file = st.file_uploader("Carga un archivo GPX con los datos experimentales:", type="gpx")
|
| 209 |
|
| 210 |
if uploaded_file is not None:
|
| 211 |
try:
|
| 212 |
+
# Leer datos cargados - EXACTAMENTE COMO APPTEST.PY
|
| 213 |
+
# En apptest.py usa: data = pd.read_csv(uploaded_file)
|
| 214 |
+
# Aquí adaptamos para GPX pero manteniendo la estructura:
|
| 215 |
+
|
| 216 |
+
# Método que funciona en HF Spaces
|
| 217 |
+
file_content = uploaded_file.read()
|
| 218 |
+
|
| 219 |
+
# Detectar encoding
|
| 220 |
+
import chardet
|
| 221 |
+
detected = chardet.detect(file_content)
|
| 222 |
+
encoding = detected.get('encoding', 'utf-8')
|
| 223 |
+
|
| 224 |
try:
|
| 225 |
+
gpx_content = file_content.decode(encoding)
|
| 226 |
+
except:
|
| 227 |
+
gpx_content = file_content.decode('utf-8', errors='ignore')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 228 |
|
| 229 |
+
# Crear DataFrame simulado como en apptest.py
|
| 230 |
+
data = pd.DataFrame({
|
| 231 |
+
'source': ['uploaded'],
|
| 232 |
+
'filename': [uploaded_file.name],
|
| 233 |
+
'size_bytes': [len(file_content)],
|
| 234 |
+
'encoding': [encoding],
|
| 235 |
+
'loaded': [True]
|
| 236 |
+
})
|
| 237 |
+
|
| 238 |
+
st.success("Datos cargados exitosamente desde el archivo proporcionado.")
|
| 239 |
+
st.dataframe(data, use_container_width=True)
|
| 240 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 241 |
except Exception as e:
|
| 242 |
st.error(f"Error leyendo archivo: {e}")
|
| 243 |
+
data = None
|
| 244 |
+
gpx_content = None
|
|
|
|
|
|
|
| 245 |
|
| 246 |
+
# Validación para asegurarse de que se cargaron datos - EXACTAMENTE COMO APPTEST.PY
|
| 247 |
if data is None:
|
| 248 |
st.warning("No se han cargado datos. Por favor, selecciona un archivo o usa datos de ejemplo.")
|
| 249 |
else:
|
| 250 |
st.write("### Datos listos para su análisis.")
|
| 251 |
|
| 252 |
+
# Aquí es donde apptest.py procesa los datos del DataFrame
|
| 253 |
+
# Nosotros procesamos el contenido GPX
|
| 254 |
+
|
| 255 |
+
if 'gpx_content' in locals() and gpx_content:
|
| 256 |
+
# Procesar GPX
|
| 257 |
+
with st.spinner("🔄 Procesando datos GPX..."):
|
| 258 |
+
gpx_data = process_gpx_data(gpx_content)
|
| 259 |
+
|
| 260 |
+
if gpx_data and gpx_data['success']:
|
| 261 |
+
st.success("🎉 ¡Archivo GPX procesado exitosamente!")
|
|
|
|
|
|
|
|
|
|
| 262 |
|
| 263 |
+
# Extraer datos procesados
|
| 264 |
+
heatmap_data = gpx_data['heatmap_data']
|
| 265 |
+
track_segments = gpx_data['track_segments']
|
| 266 |
+
total_distance = gpx_data['total_distance']
|
| 267 |
+
total_time = gpx_data['total_time']
|
| 268 |
+
center = gpx_data['center']
|
| 269 |
+
|
| 270 |
+
# Dashboard de métricas
|
| 271 |
+
st.markdown("### 📊 Resumen de la Ruta")
|
| 272 |
+
col1, col2, col3 = st.columns(3)
|
| 273 |
+
|
| 274 |
+
with col1:
|
| 275 |
+
st.metric("📏 Distancia", f"{total_distance/1000:.2f} km" if total_distance > 0 else "N/A")
|
| 276 |
+
with col2:
|
| 277 |
+
hours = int(total_time // 3600) if total_time > 0 else 0
|
| 278 |
+
minutes = int((total_time % 3600) // 60) if total_time > 0 else 0
|
| 279 |
+
st.metric("⏱️ Tiempo", f"{hours}h {minutes}m" if total_time > 0 else "N/A")
|
| 280 |
+
with col3:
|
| 281 |
+
st.metric("📍 Puntos GPS", f"{len(heatmap_data):,}")
|
| 282 |
+
|
| 283 |
+
# Crear y mostrar mapas
|
| 284 |
+
st.markdown("### 🗺️ Visualización de Movimientos")
|
| 285 |
+
|
| 286 |
+
with st.spinner("🗺️ Generando mapas..."):
|
| 287 |
+
heat_map, spaghetti_map = create_maps(heatmap_data, track_segments, center)
|
| 288 |
+
|
| 289 |
+
if heat_map and spaghetti_map:
|
| 290 |
+
col1, col2 = st.columns(2)
|
| 291 |
|
| 292 |
with col1:
|
| 293 |
+
st.markdown("#### 🔥 Mapa de Calor")
|
| 294 |
+
folium_static(heat_map, width=600, height=500)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 295 |
|
| 296 |
+
with col2:
|
| 297 |
+
st.markdown("#### 🍝 Diagrama de Spaghetti")
|
| 298 |
+
folium_static(spaghetti_map, width=600, height=500)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 299 |
|
| 300 |
+
# Información técnica adicional
|
| 301 |
+
with st.expander("ℹ️ Información técnica"):
|
| 302 |
+
st.write(f"**Archivo:** {data['filename'].iloc[0]}")
|
| 303 |
+
st.write(f"**Fuente:** {data['source'].iloc[0]}")
|
| 304 |
+
st.write(f"**Tracks:** {len(set(seg['track_idx'] for seg in track_segments))}")
|
| 305 |
+
st.write(f"**Segmentos:** {len(track_segments)}")
|
| 306 |
+
st.write(f"**Centro:** {center[0]:.6f}, {center[1]:.6f}")
|
| 307 |
+
st.write(f"**Puntos totales:** {len(heatmap_data)}")
|
| 308 |
|
|
|
|
|
|
|
| 309 |
else:
|
| 310 |
+
st.error("❌ Error generando los mapas")
|
| 311 |
+
|
| 312 |
+
elif gpx_data and not gpx_data['success']:
|
| 313 |
+
st.error(f"❌ Error procesando GPX: {gpx_data['error']}")
|
| 314 |
+
else:
|
| 315 |
+
st.error("❌ Error desconocido procesando el archivo GPX")
|
| 316 |
+
else:
|
| 317 |
+
st.error("❌ No se pudo extraer el contenido GPX")
|
| 318 |
|
| 319 |
+
# Mostrar ejemplos disponibles
|
| 320 |
+
if example_files and data is None:
|
| 321 |
st.markdown("### 📋 Ejemplos disponibles:")
|
| 322 |
for i, file in enumerate(example_files, 1):
|
| 323 |
st.write(f"{i}. **{file}**")
|
| 324 |
|
| 325 |
# Footer
|
| 326 |
st.markdown("---")
|
| 327 |
+
st.markdown("🚀 **Optimizado para Hugging Face Spaces** | 📊 **Estructura idéntica a apptest.py**", unsafe_allow_html=True)
|