mapas.html 13 KB
<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>SIGEM GIS | Dashboard Institucional</title>
    <!-- AdminLTE 3 & Bootstrap 4 -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/admin-lte/3.2.0/css/adminlte.min.css">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
    <link rel="stylesheet" href="https://unpkg.com/maplibre-gl@3.6.2/dist/maplibre-gl.css">
    <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;700&display=swap" rel="stylesheet">
    
    <style>
        body { font-family: 'Roboto', sans-serif; height: 100vh; background: #0b0e14; overflow: hidden; }
        #map { position: absolute; top: 0; bottom: 0; left: 0; right: 0; }
        
        /* Tema Oscuro Personalizado */
        .main-sidebar { background-color: #0f172a !important; width: 280px !important; }
        .content-wrapper { background: transparent !important; }
        .main-header { background-color: #0f172a !important; border-bottom: 1px solid #1e293b !important; }
        
        .nav-link p { color: #94a3b8; font-weight: 500; }
        .nav-link.active { background-color: #3b82f6 !important; color: white !important; }
        .nav-link.active p { color: white !important; }
        
        .info-box { background: #1e293b; color: white; border-radius: 8px; margin-bottom: 15px; border: 1px solid #334155; }
        .info-box-text { color: #94a3b8; text-transform: uppercase; font-size: 0.7rem; font-weight: 700; }
        .info-box-number { font-size: 1.2rem; color: #fff; }

        .map-section-title { color: #475569; font-size: 0.65rem; font-weight: 700; text-transform: uppercase; padding: 10px 15px; letter-spacing: 1px; }
        
        /* Widget Superior */
        .top-widget { position: absolute; top: 15px; left: 50%; transform: translateX(-50%); background: rgba(15, 23, 42, 0.9); padding: 8px 25px; border-radius: 50px; border: 1px solid #334155; color: white; font-weight: 700; font-size: 0.85rem; z-index: 10; box-shadow: 0 4px 20px rgba(0,0,0,0.5); backdrop-filter: blur(5px); }

        /* Leyenda Inferior Derecha */
        .legend-card { position: absolute; bottom: 30px; right: 20px; width: 220px; background: rgba(15, 23, 42, 0.9); border-radius: 12px; border: 1px solid #334155; padding: 15px; color: white; z-index: 10; box-shadow: 0 4px 20px rgba(0,0,0,0.5); display: none; }
        .legend-title { font-size: 0.7rem; font-weight: 800; color: #3b82f6; text-transform: uppercase; margin-bottom: 10px; border-bottom: 1px solid #334155; padding-bottom: 5px; }
        .legend-item { display: flex; align-items: center; margin-bottom: 4px; font-size: 0.75rem; color: #cbd5e1; }
        .legend-color { width: 15px; height: 10px; border-radius: 2px; margin-right: 12px; }

        /* Estilo Botones Menú MVT/PNG */
        .btn-map-mode { background: #1e293b; border: 1px solid #334155; border-radius: 6px; margin: 5px 15px; padding: 10px; cursor: pointer; transition: 0.3s; display: flex; align-items: center; color: #cbd5e1; font-size: 0.85rem; }
        .btn-map-mode:hover { background: #334155; }
        .btn-map-mode.active { background: #3b82f6; color: white; border-color: #60a5fa; box-shadow: 0 0 15px rgba(59, 130, 246, 0.4); }
        .btn-map-mode i { margin-right: 12px; font-size: 1rem; }

        .btn-sync { background: rgba(34, 197, 94, 0.1); border: 1px dashed #22c55e; color: #22c55e; padding: 8px; margin: 15px; border-radius: 6px; cursor: pointer; font-size: 0.8rem; font-weight: 700; display: flex; justify-items: center; align-items: center; justify-content: center; }
        .btn-sync:hover { background: #22c55e; color: white; }
    </style>
</head>
<body class="hold-transition sidebar-mini layout-fixed">
    <div class="wrapper">
        <!-- Header -->
        <nav class="main-header navbar navbar-expand navbar-dark">
            <ul class="navbar-nav">
                <li class="nav-item"><a class="nav-link" data-widget="pushmenu" href="#" role="button"><i class="fas fa-bars"></i></a></li>
                <li class="nav-item d-none d-sm-inline-block">
                    <span class="nav-link text-white font-weight-bold">SIGEM GIS | <small class="text-gray">Bienvenido, OPERADOR SISTEMA</small></span>
                </li>
            </ul>
            <ul class="navbar-nav ml-auto">
                <li class="nav-item"><a href="login.html" class="btn btn-sm btn-outline-danger mr-3 font-weight-bold">Cerrar Sesión</a></li>
            </ul>
        </nav>

        <!-- Sidebar -->
        <aside class="main-sidebar sidebar-dark-primary elevation-4">
            <div class="sidebar">
                <div class="user-panel mt-3 pb-3 mb-3 d-flex">
                    <div class="image"><i class="fas fa-globe-americas text-primary p-2"></i></div>
                    <div class="info"><a href="#" class="d-block font-weight-bold">SIGEM GIS</a></div>
                </div>

                <div class="container-fluid px-3">
                    <div class="map-section-title">Resumen Municipal</div>
                    <div class="info-box shadow-none">
                        <div class="info-box-content">
                            <span class="info-box-text">Total Lotes</span>
                            <span class="info-box-number" id="val-total">-</span>
                        </div>
                    </div>
                    <div class="info-box shadow-none">
                        <div class="info-box-content">
                            <span class="info-box-text">Morosidad</span>
                            <span class="info-box-number text-danger" id="val-morosos">-</span>
                        </div>
                    </div>

                    <div class="map-section-title">Control de Gestión</div>
                    <div class="btn-map-mode active" id="btn-base" onclick="setMapMode('base')">
                        <i class="fas fa-satellite"></i> Capas Base
                    </div>

                    <div class="map-section-title">Mapas Tributarios</div>
                    <div class="btn-map-mode" id="btn-pago" onclick="setMapMode('pago')">
                        <i class="fas fa-calendar-alt"></i> Por Último Pago
                    </div>
                    <div class="btn-map-mode" id="btn-total" onclick="setMapMode('total')">
                        <i class="fas fa-chart-pie"></i> Por Total (Percentiles)
                    </div>
                    <div class="btn-map-mode" id="btn-png" onclick="setMapMode('png')">
                        <i class="fas fa-image text-warning"></i> Vista PNG (Full) <small class="ml-1 text-muted">[M]</small>
                    </div>

                    <div class="map-section-title">Administración</div>
                    <div class="btn-sync" onclick="location.reload()">
                        <i class="fas fa-sync-alt mr-2"></i> Actualizar Datos MUNI
                    </div>
                </div>
            </div>
        </aside>

        <!-- Main Content (Map) -->
        <div class="content-wrapper">
            <div id="map"></div>
            <div class="top-widget" id="top-widget">📊 MOROSIDAD - ÚLTIMO ADEUDADO</div>
            
            <div class="legend-card" id="legend">
                <div class="legend-title">Último Adeudado</div>
                <div class="legend-item"><span class="legend-color" style="background:#71de75;"></span> 🟢 <= 2026</div>
                <div class="legend-item"><span class="legend-color" style="background:#bef264;"></span> 🎾 <= 2025</div>
                <div class="legend-item"><span class="legend-color" style="background:#ffaa00;"></span> 🟡 <= 2024</div>
                <div class="legend-item"><span class="legend-color" style="background:#fb923c;"></span> 🟠 <= 2023</div>
                <div class="legend-item"><span class="legend-color" style="background:#f87171;"></span> 🔴 <= 2022</div>
                <div class="legend-item"><span class="legend-color" style="background:#e11d48;"></span> 📢 <= 2021</div>
                <div class="legend-item"><span class="legend-color" style="background:#475569;"></span> 🔘 SIN DEUDA / PRESCRIPTAS</div>
                <div class="mt-2 pt-2 border-top border-secondary text-muted" style="font-size:0.6rem">Fuente: Sistema SIGEM</div>
            </div>
        </div>
    </div>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/4.6.1/js/bootstrap.bundle.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/admin-lte/3.2.0/js/adminlte.min.js"></script>
    <script src="https://unpkg.com/maplibre-gl@3.6.2/dist/maplibre-gl.js"></script>

    <script>
        const entidad = localStorage.getItem('entidad') || '505';
        const contextPath = '/gis-geoserver';
        let map;

        async function init() {
            try {
                const r = await fetch(`${contextPath}/api/gis/entidad/${entidad}`);
                const data = await r.json();
                const res = await fetch(`${contextPath}/api/gis/entidad/${entidad}/resumen`);
                const stats = await res.json();
                
                document.getElementById('val-total').textContent = stats.total_lotes.toLocaleString();
                document.getElementById('val-morosos').textContent = stats.lotes_con_deuda.toLocaleString();

                const center = data.latlong.split(',').map(Number);
                
                map = new maplibregl.Map({
                    container: 'map',
                    style: 'https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json',
                    center: [center[1], center[0]],
                    zoom: parseInt(data.zoom) || 15,
                    pitch: 0,
                    bearing: 0,
                    dragRotate: false
                });

                map.on('load', () => {
                    // Fuente Vectorial (MVT) compatible con Regla 23
                    map.addSource('lotes-mvt', {
                        type: 'vector',
                        tiles: [ `${window.location.origin}${contextPath}/geoserver/gwc/service/wmts?REQUEST=GetTile&SERVICE=WMTS&VERSION=1.0.0&LAYER=sigem:vw_lotes_morosidad_${entidad}&STYLE=&TILEMATRIXSET=GoogleMapsCompatible&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&FORMAT=application/x-protobuf` ],
                        scheme: 'xyz'
                    });

                    map.addLayer({
                        id: 'lotes-layer',
                        type: 'line',
                        source: 'lotes-mvt',
                        'source-layer': `vw_lotes_morosidad_${entidad}`,
                        paint: { 'line-color': '#475569', 'line-width': 0.8 }
                    });

                    // Fuente PNG (WMS) - Alineada a Regla 5 (EPSG:4326)
                    map.addSource('lotes-wms', {
                        type: 'raster',
                        tiles: [ `${window.location.origin}${contextPath}/geoserver/wms?service=WMS&version=1.1.1&request=GetMap&layers=sigem:vw_lotes_wms_${entidad}&bbox={bbox-epsg-4326}&width=256&height=256&srs=EPSG:4326&styles=morosidad_style_wms&format=image/png&transparent=true` ],
                        tileSize: 256
                    });

                    map.addLayer({
                        id: 'lotes-color-wms',
                        type: 'raster',
                        source: 'lotes-wms',
                        paint: { 'raster-opacity': 0 }
                    }, 'lotes-layer');
                });

                map.on('click', 'lotes-layer', (e) => {
                    const p = e.features[0].properties;
                    const content = `<div class="p-2"><b class="text-primary">CCC: ${p.ccc || 'N/A'}</b><hr class="my-1">Deuda: Gs. ${Number(p.trb_total_deuda || 0).toLocaleString()}<br>Último Pago: ${p.ultimo_pago || 'Nunca'}</div>`;
                    new maplibregl.Popup().setLngLat(e.lngLat).setHTML(content).addTo(map);
                });

            } catch (e) {
                console.error("Error cargando dashboard:", e);
                // Si hay 503, re-intentar en 5s
                setTimeout(init, 5000);
            }
        }

        function setMapMode(mode) {
            $('.btn-map-mode').removeClass('active');
            const legend = document.getElementById('legend');
            const topWidget = document.getElementById('top-widget');

            if (mode === 'base' || mode === 'general') {
                $('#btn-base').addClass('active');
                map.setPaintProperty('lotes-color-wms', 'raster-opacity', 0);
                legend.style.display = 'none';
                topWidget.style.display = 'none';
            } else {
                $(`#btn-${mode}`).addClass('active');
                map.setPaintProperty('lotes-color-wms', 'raster-opacity', 0.8);
                legend.style.display = 'block';
                topWidget.style.display = 'block';
            }
        }

        init();
    </script>
</body>
</html>
GitLab Appliance - Powered by TurnKey Linux