mapas_repatriado.html 14.7 KB
<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>SISTEMA GIS | Dashboard Institucional</title>

    <!-- Google Font: Source Sans Pro -->
    <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,400i,700&display=fallback">
    <!-- Font Awesome -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
    <!-- AdminLTE 3.2 -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/admin-lte@3.2/dist/css/adminlte.min.css">
    <!-- MapLibre GL JS -->
    <link href="https://unpkg.com/maplibre-gl@3.6.2/dist/maplibre-gl.css" rel="stylesheet" />
    
    <style>
        .content-wrapper { position: relative; height: calc(100vh - 57px); overflow: hidden; background: #fff; }
        #map { width: 100%; height: 100%; }
        
        .map-overlay {
            position: absolute;
            bottom: 20px;
            left: 20px;
            z-index: 1000;
            background: rgba(255, 255, 255, 0.9);
            border-radius: 4px;
            padding: 10px;
            box-shadow: 0 0 10px rgba(0,0,0,0.1);
            display: none;
        }

        .maplibre-popup { z-index: 2000; }
        .popup-header { background: #007bff; color: #fff; padding: 5px 10px; font-weight: bold; font-size: 11px; }
        .popup-body { padding: 10px; font-family: 'Source Sans Pro', sans-serif; font-size: 13px; }

        .rotating { animation: rotation 2s infinite linear; }
        @keyframes rotation { from { transform: rotate(0deg); } to { transform: rotate(359deg); } }
        
        .main-sidebar { border-right: 1px solid #4b545c !important; }
        .brand-link { border-bottom: 1px solid #4b545c !important; }
    </style>
</head>
<body class="hold-transition sidebar-mini layout-fixed">
<div class="wrapper">

    <!-- Navbar -->
    <nav class="main-header navbar navbar-expand navbar-white navbar-light">
        <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 font-weight-bold text-primary" id="muni-title">MUNICIPALIDAD - VISOR GIS</span>
            </li>
        </ul>

        <ul class="navbar-nav ml-auto">
            <li class="nav-item">
                <a class="nav-link text-danger" href="/gis-geoserver/login.html" role="button">
                    <i class="fas fa-sign-out-alt"></i> Cerrar Sesión
                </a>
            </li>
        </ul>
    </nav>

    <!-- Main Sidebar Container -->
    <aside class="main-sidebar sidebar-dark-primary elevation-4">
        <a href="#" class="brand-link">
            <img src="https://adminlte.io/themes/v3/dist/img/AdminLTELogo.png" alt="AdminLTE Logo" class="brand-image img-circle elevation-3" style="opacity: .8">
            <span class="brand-text font-weight-light">SIGEM <b>GIS</b></span>
        </a>

        <div class="sidebar">
            <div class="user-panel mt-3 pb-3 mb-3 d-flex">
                <div class="image">
                    <i class="fas fa-user-circle text-light fa-2x"></i>
                </div>
                <div class="info">
                    <a href="#" class="d-block" id="user-display">Operador Municipal</a>
                </div>
            </div>

            <nav class="mt-2">
                <ul class="nav nav-pills nav-sidebar flex-column" data-widget="treeview" role="menu">
                    <li class="nav-header">RESUMEN MUNICIPAL</li>
                    <li class="nav-item">
                        <div class="info-box bg-info m-2">
                            <span class="info-box-icon"><i class="fas fa-map-marked-alt"></i></span>
                            <div class="info-box-content">
                                <span class="info-box-text">Lotes Totales</span>
                                <span class="info-box-number" id="total-lotes">0</span>
                            </div>
                        </div>
                    </li>
                    <li class="nav-item">
                        <div class="info-box bg-danger m-2">
                            <span class="info-box-icon"><i class="fas fa-exclamation-triangle"></i></span>
                            <div class="info-box-content">
                                <span class="info-box-text">Lotes Morosos</span>
                                <span class="info-box-number" id="lotes-morosos">0</span>
                            </div>
                        </div>
                    </li>

                    <li class="nav-header">CONTROL DE MAPA</li>
                    <li class="nav-item">
                        <a href="javascript:void(0)" onclick="applyMorosidadTheme()" class="nav-link">
                            <i class="nav-icon fas fa-dollar-sign text-warning"></i>
                            <p>Mapa Tributario</p>
                        </a>
                    </li>
                    <li class="nav-item">
                        <a href="javascript:void(0)" onclick="resetMap()" class="nav-link">
                            <i class="nav-icon fas fa-undo-alt"></i>
                            <p>Reiniciar Vista</p>
                        </a>
                    </li>
                    <li class="nav-item">
                        <a href="javascript:void(0)" onclick="toggleWmsLayer()" class="nav-link">
                            <i class="nav-icon fas fa-layer-group text-success"></i>
                            <p>WMS Dinámico <span class="badge badge-info right" id="wms-status">OFF</span></p>
                        </a>
                    </li>

                    <li class="nav-header">ADMINISTRACIÓN</li>
                    <li class="nav-item">
                        <a href="javascript:void(0)" onclick="updateMunicipalData()" class="nav-link" id="btn-update">
                            <i class="nav-icon fas fa-sync-alt" id="update-icon"></i>
                            <p id="update-text">Sincronizar FDW</p>
                        </a>
                    </li>
                </ul>
            </nav>
        </div>
    </aside>

    <!-- Content Wrapper -->
    <div class="content-wrapper">
        <div id="map"></div>
        <div id="legend" class="map-overlay card card-outline card-primary p-2">
            <h6 class="text-xs font-weight-bold text-uppercase mb-2">Escala de Morosidad (Gs)</h6>
            <div id="legend-content"></div>
        </div>
    </div>
</div>

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

<script>
    // Configuración desde LocalStorage (ADN SIGEM-GIS)
    const token = localStorage.getItem('jwt');
    const entidad = localStorage.getItem('entidad') || '505';
    const userName = localStorage.getItem('user_name') || 'Operador';
    
    // Si no hay token, redirigir al login
    if (!token) window.location.href = '/gis-geoserver/login.html';

    document.getElementById('user-display').innerText = userName;
    document.getElementById('muni-title').innerText = `MUNICIPALIDAD ENTIDAD ${entidad}`;

    // Inicialización del Mapa LibRe 3D Elite
    const map = new maplibregl.Map({
        container: 'map',
        style: {
            'version': 8,
            'sources': {
                'osm': {
                    'type': 'raster',
                    'tiles': ['https://a.tile.openstreetmap.org/{z}/{x}/{y}.png'],
                    'tileSize': 256,
                    'attribution': '&copy; OpenStreetMap'
                },
                'lotes-png': {
                    'type': 'raster',
                    'tiles': [
                        `${window.location.origin}/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&format=image/png&transparent=true`
                    ],
                    'tileSize': 256
                },
                'lotes-mvt': {
                    'type': 'vector',
                    'tiles': [`${window.location.origin}/gis-geoserver/sigem/gwc/service/tms/1.0.0/sigem:vw_lotes_morosidad_${entidad}@EPSG:900913@pbf/{z}/{x}/{y}.pbf`],
                    'minzoom': 13,
                    'maxzoom': 22
                }
            },
            'layers': [
                { 'id': 'background-osm', 'type': 'raster', 'source': 'osm', 'minzoom': 0, 'maxzoom': 22 },
                { 'id': 'lotes-raster', 'type': 'raster', 'source': 'lotes-png', 'minzoom': 13, 'maxzoom': 22, 'paint': { 'raster-opacity': 0.8 } },
                {
                    'id': 'lotes-layer',
                    'type': 'line',
                    'source': 'lotes-mvt',
                    'source-layer': `vw_lotes_morosidad_${entidad}`,
                    'paint': {
                        'line-color': '#444444',
                        'line-width': 0.5
                    }
                }
            ]
        },
        center: [
            parseFloat(localStorage.getItem('map_lng') || -56.446949),
            parseFloat(localStorage.getItem('map_lat') || -25.456443)
        ],
        zoom: parseInt(localStorage.getItem('map_zoom') || 14),
        pitch: 0,
        bearing: 0,
        antialias: true
    });

    map.addControl(new maplibregl.NavigationControl(), 'top-right');

    map.on('load', () => {
        console.log("Mapa AdminLTE 3 Cargado - Entidad " + entidad);
        loadMunicipalStats();
        // Carga automática del tema de morosidad (Funcionalidad de Ayer)
        applyMorosidadTheme();
    });

    // --- Funciones de Negocio GIS ---
    function applyMorosidadTheme() {
        const legend = document.getElementById('legend');
        const legendContent = document.getElementById('legend-content');
        legend.style.display = 'block';
        
        const scale = [
            { limit: 0, color: '#6b9070', label: 'Sin Deuda 🟢' },
            { limit: 351585, color: '#b5c47a', label: 'Baja 🟡' },
            { limit: 714969, color: '#ffd966', label: 'Media 🟠' },
            { limit: 1226946, color: '#f08060', label: 'Alta 🔴' },
            { limit: 2099322, color: '#a91d1d', label: 'Crítica 💀' }
        ];

        legendContent.innerHTML = scale.map(i => `
            <div class="d-flex align-items-center mb-1">
                <div style="width: 15px; height: 15px; background: ${i.color}; margin-right: 8px; border-radius: 2px;"></div>
                <span class="text-xs">${i.label}</span>
            </div>
        `).join('');

        map.setPaintProperty('lotes-layer', 'fill-color', [
            'step',
            ['get', 'trb_total_deuda'],
            '#6b9070',
            351585, '#b5c47a',
            714969, '#ffd966',
            1226946, '#f08060',
            2099322, '#a91d1d'
        ]);
        map.setPaintProperty('lotes-layer', 'fill-outline-color', 'rgba(255, 255, 255, 0.4)');
    }

    function resetMap() {
        document.getElementById('legend').style.display = 'none';
        map.setPaintProperty('lotes-layer', 'fill-color', 'rgba(255, 255, 255, 0.05)');
        if(map.getLayer('lotes-wms-layer')) {
            map.setLayoutProperty('lotes-wms-layer', 'visibility', 'none');
            document.getElementById('wms-status').innerText = 'OFF';
        }
    }

    function toggleWmsLayer() {
        if (!map.getSource('lotes-wms')) {
            map.addSource('lotes-wms', {
                'type': 'raster',
                'tiles': [`${window.location.origin}/gis-geoserver/sigem/wms?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&LAYERS=sigem:vw_lotes_morosidad_${entidad}&STYLES=morosidad_style_wms&FORMAT=image/png&TRANSPARENT=TRUE&WIDTH=256&HEIGHT=256&SRS=EPSG:3857&BBOX={bbox-epsg-3857}`],
                'tileSize': 256
            });
            map.addLayer({ 'id': 'lotes-wms-layer', 'type': 'raster', 'source': 'lotes-wms', 'layout': { 'visibility': 'none' }, 'paint': { 'raster-opacity': 0.8 } }, 'lotes-layer');
        }
        const isVisible = map.getLayoutProperty('lotes-wms-layer', 'visibility') === 'visible';
        const next = isVisible ? 'none' : 'visible';
        map.setLayoutProperty('lotes-wms-layer', next);
        document.getElementById('wms-status').innerText = next === 'visible' ? 'ON' : 'OFF';
    }

    async function loadMunicipalStats() {
        try {
            const res = await fetch(`/gis-geoserver/api/gis/entidad/${entidad}/resumen`, {
                headers: { 'Authorization': `Bearer ${token}` }
            });
            const data = await res.json();
            document.getElementById('total-lotes').innerText = data.total_lotes.toLocaleString();
            document.getElementById('lotes-morosos').innerText = data.lotes_con_deuda.toLocaleString();
        } catch (err) { console.error("Error stats:", err); }
    }

    async function updateMunicipalData() {
        const icon = document.getElementById('update-icon');
        const text = document.getElementById('update-text');
        icon.classList.add('rotating');
        text.innerText = 'Sincronizando...';

        try {
            const res = await fetch(`/gis-geoserver/api/admin/fdw/update/${entidad}`, {
                method: 'POST',
                headers: { 'Authorization': `Bearer ${token}` }
            });
            const data = await res.json();
            alert(data.success ? "✅ Sincronización Exitosa" : "❌ Error: " + data.message);
            if (data.success) location.reload();
        } catch (err) { alert("❌ Error de servidor"); }
        finally {
            icon.classList.remove('rotating');
            text.innerText = 'Sincronizar FDW';
        }
    }

    // Popups de Datos (MVT)
    map.on('click', 'lotes-layer', (e) => {
        const props = e.features[0].properties;
        new maplibregl.Popup()
            .setLngLat(e.lngLat)
            .setHTML(`
                <div class="popup-header">ESTADO DE CUENTA: ${props.ccc || 'N/A'}</div>
                <div class="popup-body">
                    <b>Ficha:</b> ${props.inm_ficha || '0'}<br>
                    <b style="color:red">Adeudado:</b> Gs. ${props.trb_total_deuda ? parseFloat(props.trb_total_deuda).toLocaleString('es-PY') : '0'}<br>
                    <b>Pagado:</b> Gs. ${props.trb_total_pago ? parseFloat(props.trb_total_pago).toLocaleString('es-PY') : '0'}<br>
                    <b>Último Pago:</b> ${props.ultimo_pago || 'Nunca'}
                </div>
            `).addTo(map);
    });
</script>
</body>
</html>
GitLab Appliance - Powered by TurnKey Linux