369 lines
11 KiB
PHP
Executable File
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: '© <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>
|