mapa/index.php
2025-07-04 12:34:51 -04:00

369 lines
11 KiB
PHP
Executable File

<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mapa de Conexiones IComunica</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" />
<style>
.map-layout {
display: flex;
height: 100vh;
}
.map-sidebar {
width: 300px;
padding: 15px;
overflow-y: auto;
background: #fff;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
}
#map-container {
flex: 1;
height: 100%;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body, html { height: 100%; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; }
#map { height: 100vh; width: 100%; }
.info-panel {
position: absolute;
top: 15px;
right: 15px;
z-index: 1000;
background: rgba(255, 255, 255, 0.93);
padding: 20px;
border-radius: 10px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.25);
width: 300px;
backdrop-filter: blur(5px);
}
.panel-title {
font-size: 1.4rem;
margin-bottom: 15px;
color: #2c3e50;
border-bottom: 2px solid #3498db;
padding-bottom: 8px;
}
.stats-container {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
margin-bottom: 15px;
}
.stat-box {
background: #f8f9fa;
padding: 12px;
border-radius: 8px;
text-align: center;
box-shadow: 0 2px 5px rgba(0,0,0,0.05);
}
.stat-value {
font-size: 1.8rem;
font-weight: 700;
color: #2980b9;
}
.stat-label {
font-size: 0.85rem;
color: #7f8c8d;
margin-top: 3px;
}
.refresh-info {
background: #e1f5fe;
padding: 12px;
border-radius: 8px;
text-align: center;
font-size: 1.1rem;
color: #0288d1;
}
.refresh-counter {
font-weight: 700;
font-size: 1.3rem;
color: #d35400;
}
.last-updated {
margin-top: 15px;
text-align: center;
font-size: 0.9rem;
color: #7f8c8d;
}
.connection-marker {
position: relative;
width: 16px !important;
height: 24px !important;
}
.marker-pin {
width: 100%;
height: 100%;
background: #3498db; /* Azul */
border-radius: 50% 50% 50% 0;
transform: rotate(-45deg) scale(0.65); /* Más pequeño */
position: absolute;
top: 0;
left: 0;
box-shadow: 0 2px 4px rgba(0,0,0,0.2); /* Opcional: sombra suave */
}
.marker-count {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) rotate(45deg);
color: white;
font-weight: bold;
font-size: 8px; /* Texto más pequeño */
text-align: center;
z-index: 10;
text-shadow: 0 0 2px #000; /* Mejor contraste */
}
.custom-connection-marker {
position: relative !important;
width: 16px !important;
height: 24px !important;
z-index: 1000;
}
.custom-marker-pin {
width: 100% !important;
height: 100% !important;
background: #3498db !important; /* Azul */
border-radius: 50% 50% 50% 0 !important;
transform: rotate(-45deg) scale(0.65) !important;
position: absolute !important;
top: 0 !important;
left: 0 !important;
box-shadow: none !important;
}
.custom-marker-count {
position: absolute !important;
top: 50% !important;
left: 50% !important;
transform: translate(-50%, -50%) rotate(45deg) !important;
color: white !important;
font-weight: bold !important;
font-size: 8px !important;
text-align: center !important;
z-index: 10 !important;
text-shadow: 0 0 2px #000 !important;
margin: 0 !important;
padding: 0 !important;
}
</style>
</head>
<body>
<div id="map"></div>
<div class="info-panel">
<div class="panel-title">Conexiones IComunica</div>
<div class="stats-container">
<div class="stat-box">
<div class="stat-value" id="connection-count">0</div>
<div class="stat-label">Conexiones</div>
</div>
<div class="stat-box">
<div class="stat-value" id="location-count">0</div>
<div class="stat-label">Ubicaciones</div>
</div>
</div>
<div class="refresh-info">
Actualizando en <span class="refresh-counter" id="refresh-counter">5</span>s
</div>
<div class="last-updated">
Última actualización: <span id="last-updated">--:--:--</span>
</div>
</div>
<script
//src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js">
src="https://cdn.jsdelivr.net/gh/Leaflet/Leaflet.heat@gh-pages/dist/leaflet-heat.js">
</script>
<script>
// Configuración inicial del mapa
const map = L.map('map').setView([20, 0], 2);
// Capa de OpenStreetMap
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>'
}).addTo(map);
// Capa para marcadores
const markersLayer = L.layerGroup().addTo(map);
// Variables de estado
let connectionCount = 0;
let refreshTimer = 5;
let updateTimer;
// Icono personalizado
function createCustomIcon(count) {
return L.divIcon({
className: 'force-styles',
html: `
<div style="
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: #3498db;
border-radius: 50% 50% 50% 0;
transform: rotate(-45deg) scale(0.65);
"></div>
<div style="
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) rotate(45deg);
color: white;
font-weight: bold;
font-size: 8px;
text-align: center;
z-index: 10;
text-shadow: 0 0 2px #000;
">${count}</div>
`,
iconSize: [16, 24],
iconAnchor: [8, 24],
popupAnchor: [0, -18]
});
}
// Crear contenido para popup
function createPopupContent(conn) {
return `
<div class="popup-content">
<div style="font-weight:bold; margin-bottom:8px; font-size:16px;">
${conn.country}
</div>
<div style="margin-bottom:5px;">
<strong>IP:</strong> ${conn.ip}
</div>
<div>
<strong>Conexiones:</strong> ${conn.connections}
</div>
</div>
`;
}
// Actualizar datos del mapa
async function updateMapData() {
try {
const response = await fetch('data.php?t=' + Date.now());
if (!response.ok) {
throw new Error(`Error HTTP: ${response.status}`);
}
const connections = await response.json();
// Limpiar marcadores anteriores
markersLayer.clearLayers();
// Contadores
let totalConnections = 0;
let validLocations = 0;
// Agregar nuevos marcadores
connections.forEach(conn => {
totalConnections += conn.connections || 1;
// Validar coordenadas
if (typeof conn.lat === 'number' &&
typeof conn.lon === 'number' &&
!isNaN(conn.lat) &&
!isNaN(conn.lon)) {
validLocations++;
// Crear marcador con conteo de conexiones
const marker = L.marker([conn.lat, conn.lon], {
icon: createCustomIcon(conn.connections || 1)
}).bindPopup(createPopupContent(conn));
markersLayer.addLayer(marker);
}
});
// Actualizar estadísticas
document.getElementById('connection-count').textContent = totalConnections;
document.getElementById('location-count').textContent = validLocations;
document.getElementById('last-updated').textContent = new Date().toLocaleTimeString();
// Actualizar vista si hay marcadores
if (validLocations > 0) {
map.fitBounds(markersLayer.getBounds(), {
padding: [50, 50],
maxZoom: 15
});
}
} catch (error) {
console.error('Error al obtener datos:', error);
}
}
// Iniciar temporizador de actualización
function startRefreshTimer() {
refreshTimer = 5;
clearInterval(updateTimer);
updateTimer = setInterval(() => {
refreshTimer--;
document.getElementById('refresh-counter').textContent = refreshTimer;
if (refreshTimer <= 0) {
clearInterval(updateTimer);
updateMapData();
startRefreshTimer();
}
}, 1000);
}
// Estilos dinámicos para marcadores
const style = document.createElement('style');
style.textContent = `
.connection-marker {
position: relative;
width: 30px;
height: 42px;
}
.marker-pin {
position: absolute;
top: 0;
left: 0;
width: 30px;
height: 30px;
background: #e74c3c;
border-radius: 50% 50% 50% 0;
transform: rotate(-45deg);
box-shadow: 0 2px 5px rgba(0,0,0,0.3);
}
.marker-count {
position: absolute;
top: 4px;
left: 0;
width: 100%;
text-align: center;
color: white;
font-weight: bold;
font-size: 14px;
transform: rotate(45deg);
}
.leaflet-popup-content {
margin: 12px;
font-size: 14px;
}
`;
document.head.appendChild(style);
// Inicializar
updateMapData();
startRefreshTimer();
</script>
</body>
</html>