mapas.html 18.2 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: [`http://192.168.1.123:8083/geoserver/gwc/service/wmts?REQUEST=GetTile&SERVICE=WMTS&VERSION=1.0.0&LAYER=sigem:e${entidad}_lotes_activos&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': `e${entidad}_lotes_activos`,
                                                            paint: { 'line-color': '#475569', 'line-width': 0.8 }
                                                        });

                                                        // Fuente PNG (WMS) - Alineada a Regla 5 (EPSG:4326)
                                                        map.addSource('lotes-wms', {
                                                            type: 'raster',
                                                            tiles: [ `http://192.168.1.123:8083/geoserver/sigem/wms?service=WMS&version=1.1.1&request=GetMap&layers=sigem:vw_lotes_morosidad_${entidad}&bbox={bbox-epsg-3857}&width=256&height=256&srs=EPSG:3857&styles=&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