diff --git a/FdwService.java b/FdwService.java index fd5d30e..132d76e 100644 --- a/FdwService.java +++ b/FdwService.java @@ -25,7 +25,8 @@ public class FdwService { String sqlEntidad = "SELECT sigem_site, sigem_dbname, boundno, boundse, latlong, zoom, maxzoom, minzoom FROM public.entidades WHERE entidad = ?"; List> entidades = masterJdbcTemplate.queryForList(sqlEntidad, Integer.parseInt(entidadId)); - if (entidades.isEmpty()) throw new RuntimeException("Entidad " + entidadId + " no encontrada."); + if (entidades.isEmpty()) + throw new RuntimeException("Entidad " + entidadId + " no encontrada."); Map data = entidades.get(0); String sigemSite = (String) data.get("sigem_site"); @@ -44,17 +45,25 @@ public class FdwService { try { gisJdbcTemplate.execute("CREATE EXTENSION IF NOT EXISTS postgres_fdw"); gisJdbcTemplate.execute("DROP SERVER IF EXISTS " + serverName + " CASCADE"); - gisJdbcTemplate.execute(String.format("CREATE SERVER %s FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '%s', port '%s', dbname '%s')", serverName, host, port, sigemDbname)); - gisJdbcTemplate.execute(String.format("CREATE USER MAPPING FOR sigem_user SERVER %s OPTIONS (user '%s', password '%s')", serverName, user, pass)); + gisJdbcTemplate.execute(String.format( + "CREATE SERVER %s FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '%s', port '%s', dbname '%s')", + serverName, host, port, sigemDbname)); + gisJdbcTemplate.execute( + String.format("CREATE USER MAPPING FOR sigem_user SERVER %s OPTIONS (user '%s', password '%s')", + serverName, user, pass)); gisJdbcTemplate.execute("CREATE SCHEMA IF NOT EXISTS " + schemaName); - gisJdbcTemplate.execute(String.format("IMPORT FOREIGN SCHEMA public LIMIT TO (v_liq_entidad_totalxobjeto, v_liq_entidad_percentiles, usuarios, ventanas_usuario) FROM SERVER %s INTO %s", serverName, schemaName)); + gisJdbcTemplate.execute(String.format( + "IMPORT FOREIGN SCHEMA public LIMIT TO (v_liq_entidad_totalxobjeto, v_liq_entidad_percentiles, usuarios, ventanas_usuario) FROM SERVER %s INTO %s", + serverName, schemaName)); // REGLA 23 ACTUALIZADA: Join OBLIGATORIO por CCC = INM_CTACATASTRAL String tableLotes = "public.e" + entidadId + "_lotes_conccc"; String viewName = "vw_lotes_morosidad_" + entidadId; String viewWms = "vw_lotes_wms_" + entidadId; - String sqlJoin = String.format("CREATE OR REPLACE VIEW public.%s AS SELECT l.*, m.inm_ficha, m.inm_ctacatastral, m.trb_total_deuda, m.trb_total_pago, m.ultimo_pago FROM %s l LEFT JOIN %s.v_liq_entidad_totalxobjeto m ON l.ccc = m.inm_ctacatastral", viewName, tableLotes, schemaName); - + String sqlJoin = String.format( + "CREATE OR REPLACE VIEW public.%s AS SELECT l.*, m.inm_ficha, m.inm_ctacatastral, m.trb_total_deuda, m.trb_total_pago, m.ultimo_pago FROM %s l LEFT JOIN %s.v_liq_entidad_totalxobjeto m ON l.ccc = m.inm_ctacatastral", + viewName, tableLotes, schemaName); + gisJdbcTemplate.execute(sqlJoin); gisJdbcTemplate.execute(sqlJoin.replace(viewName, viewWms)); @@ -64,15 +73,20 @@ public class FdwService { geoServerService.truncateCache(viewName); geoServerService.publishLayer(viewWms, viewWms, "morosidad_style_wms", boundNo, boundSe); geoServerService.truncateCache(viewWms); - } catch (Exception e) { System.err.println("Advertencia GS: " + e.getMessage()); } + } catch (Exception e) { + System.err.println("Advertencia GS: " + e.getMessage()); + } - } catch (Exception e) { throw new RuntimeException("Error FDW: " + e.getMessage(), e); } + } catch (Exception e) { + throw new RuntimeException("Error FDW: " + e.getMessage(), e); + } } private String extractParam(String siteParam, String key, String defaultValue) { if (siteParam != null && siteParam.contains(key + "=")) { String[] parts = siteParam.split(key + "="); - if (parts.length > 1) return parts[1].split(" ")[0].trim(); + if (parts.length > 1) + return parts[1].split(" ")[0].trim(); } return defaultValue; } @@ -91,8 +105,12 @@ public class FdwService { conn.setRequestProperty("Content-Type", "text/xml"); conn.setDoOutput(true); conn.setConnectTimeout(2000); - try (java.io.OutputStream os = conn.getOutputStream()) { os.write(xmlBody.getBytes()); } + try (java.io.OutputStream os = conn.getOutputStream()) { + os.write(xmlBody.getBytes()); + } conn.getResponseCode(); - } catch (Exception e) { System.err.println("Error MVT: " + e.getMessage()); } + } catch (Exception e) { + System.err.println("Error MVT: " + e.getMessage()); + } } } diff --git a/GIS-GEOSERVER-REGLAS.md b/GIS-GEOSERVER-REGLAS.md index 70d3080..a87cfb2 100644 --- a/GIS-GEOSERVER-REGLAS.md +++ b/GIS-GEOSERVER-REGLAS.md @@ -4,12 +4,20 @@ Regla 0. Eres un senior fullstack developer con experiencia reconocida en Base d El ecosistema principal es Java 21 con Spring Boot y deberá usarse de forma preferencial todas las veces que se pueda. El frontend usa el framework corporativo AdminLTE/Bootstrap. +Este proyecto GIS-GEOSERVER debe utilizar un patrón arquitectónico llamado Monolito Spring Boot con Recursos Embebidos (Server-Side Monolith). + +El Backend (El Cerebro): Está escrito en Java 21 con Spring Boot (ubicado en la carpeta src/main/java/). Se encarga de la lógica pesada: seguridad, conexión a la base de datos PostgreSQL/PostGIS, consultas FDW, y expone los servicios REST (ej. /api/gis/entidad/505). + +El Frontend (La Cara): Utiliza tecnologías de navegador clásicas: HTML, Vanilla JavaScript, jQuery y AdminLTE/Bootstrap. Pero en lugar de vivir en un servidor web aparte (como un Nginx puro o un Node.js), todos estos archivos viven dentro de la carpeta del propio proyecto Java: en src/main/resources/static/ y src/main/resources/templates/. + +No hay dos servidores distintos. Cuando Spring Boot arranca su servidor interno (Tomcat), hace el trabajo doble: Si el navegador le pide datos, el código Java responde con JSON (API REST). Si el navegador le pide /gis-geoserver/mapas, el mismo servidor Java va a su propia carpeta interna (static o templates), lee el archivo HTML, y se lo envía al navegador del usuario. Cuando se ejecute el comando de Maven (mvnw clean package) se empaquetará tanto el código Java como los archivos HTML dentro de un único archivo .jar. Todo se levanta en un solo contenedor Docker y el proxy de Apache enruta todo bajo el mismo prefijo (/gis-geoserver/). + Regla 1. El ambiente de desarrollo y compilación se encuentra en el 192.168.1.123. El JENKINS a usar está en este servidor. Los comandos del jenkins se ejecutan en el servidor 192.168.1.123. El MAVEN a usar está en este servidor. Los comandos maven se ejecutan en el servidor 192.168.1.123. El DOCKER a usar está en el servidor 192.168.1.123. Los comandos docker se ejecutan en el servidor192.168.1.123. Todas las compilaciones se ejecutarán en el servidor 192.168.1.123. -La base de datos georreferenciada SIGEM del Postgis está en este servidor y el superuser es el usuario registrado en el motor de base de datos como: +La base de datos georreferenciada física es SIGEM del Postgis está en este servidor y el superuser es el usuario registrado en el motor de base de datos como: Usuario: sigem_user Contraseña: sigem_pass @@ -18,6 +26,7 @@ Regla 2. Las bases de datos alfanuméricas de los municipios a usar vinculadas a Regla 3. El proxypass principal de redireccionamiento reside en el servidor 192.168.1.10 El proxypass maestro de redireccionamiento reside en el servidor 192.168.1.20 Estos proxypass no deben ser modificados por ningún motivo. +El proxy de Nginx de la Regla 3 tiene GeoServer mapeado a la ruta /geoserver/. Las peticiones se deberán mantener en el mismo dominio para evitar bloqueos CORS y para que sean enrutadas correctamente al contenedor de GeoServer. Regla 4. Para el LOGIN, para el campo desplegable de las ENTIDADES (Municipios) los datos deben ser obtenidos de la tabla ENTIDADES del SIGEMWEB del servidor 192.168.1.254. Para el LOGIN, el usuario debe utilizar su usuario y contraseña del SIGEM del municipio. @@ -54,11 +63,18 @@ Regla 11. Jenkins (.123): admin / x25yvaga2024. Regla 12. Jenkins SSH Credential ID: sigem-server-123 (root). -Regla 13. Tomcat Manager: manager / x25yvaga2023. GeoServer Web UI: admin / geoserver. +Regla 13. +Tomcat Manager: manager / x25yvaga2023. +GeoServer Web UI: admin / geoserver. + +Regla 14. +Endpoints Geoserver (.123:8080): /geoserver/wms, /geoserver/wfs, /geoserver/rest. -Regla 14. Endpoints Geoserver (.123:8080): /geoserver/wms, /geoserver/wfs, /geoserver/rest. +User: admin +Password: x25yvaga2023 -Regla 15. La aplicación se desplegará en el servidor 192.168.1.123 +Regla 15. +La aplicación se desplegará en el servidor 192.168.1.123 La carpeta de trabajo es: /yvyape/proyectos/sigem-gis Regla 16. Conexión FDW y Sincronización de Vistas. @@ -66,7 +82,10 @@ Debe verificarse la existencia del FDW del municipio en cada LOGIN. Si no existe, crearlo. Refrescar obligatoriamente las vistas `vw_lotes_morosidad_X`. -Regla 17. Git (.100): cbareiro@yvaga.com.py / carlos57. Repo: git@git.yvaga.com.py:geo/gis-geoserver.git. +Regla 17. GIT +El repositorio de Git se encuentra en git.yvaga.com.py en el servidor 192.168.1.100 +Credenciales de Git (.100): cbareiro@yvaga.com.py / carlos57. Repo: git@git.yvaga.com.py:geo/gis-geoserver.git. +Si la carpeta a usar ya tiene archivos, el comando puede fallar, y debe limpiarse antes de clonar. Regla 18. SSH Local: Usar Bitvise con usuario cbareiro. @@ -78,10 +97,17 @@ Regla 20. Prefijo FrontEnd: /gis-geoserver/. Regla 21. ContextPath Backend: /gis-geoserver. Regla 22: Integridad de Comandos Remotos: -Se utilizarán comandos disponibles en Bitvise. -Los accesos a otros servidores se realizarán mediante SSH y sftp. +Desde el ambiente de desarrollo (Windows 11) se utilizará SSH de Bitvise para los accesos a otros servidores, usando la Regla 9. +Debe usarse -cmdQuoted para ejecutar comandos complejos. + Queda prohibido el uso de comandos printf, echo o concatenaciones multilínea complejas para crear archivos. Usar sftpc para subir archivos íntegros. -Para los comandos SQL complejos debido a la interpretación del shell de Windowsy para asegurar la integridad total (Regla 22), se deben subir los archivos de estructura y población por SFTP ejecutándolos desde el respectivo servidor. El paso intermedio para no generar errores es preparar los archivos de comandos en el servidor local y luego copiar el archivo de comandos al interior del contenedor antes de su ejecución. +Para los comandos SQL complejos debido a la interpretación del shell de Windows y para asegurar la integridad total (Regla 22), se deben subir los archivos de estructura y población por SFTPC ejecutándolos desde el respectivo servidor. El paso intermedio para no generar errores es preparar los archivos de comandos en el servidor local y luego copiar el archivo de comandos al interior del contenedor antes de su ejecución, usando sftpc para subir archivos íntegros. + +Por ejemplo: +…\GIS-GEOSERVER > sexec cbareiro@192.168.1.123 -pw=x25yvaga2023 -cmd="cd /yvyape/proyectos/sigem-gis" +…\GIS-GEOSERVER > sexec cbareiro@192.168.1.123 -pw=x25yvaga2023 -cmd="./mvnw clean package -DskipTests" +…\GIS-GEOSERVER > sftpc cbareiro@192.168.1.123 -pw=x25yvaga2023 -cmd="put -o docker-compose.yml.remote.tmp /yvyape/proyectos/sigem-gis/docker-compose.yml" +…\GIS-GEOSERVER > sexec cbareiro@192.168.1.123 -pw=x25yvaga2023 -cmdQuoted -cmd="curl -s -u admin:geoserver -X PUT -H 'Content-Type: application/xml' -d @/yvyape/proyectos/sigem-gis/geoserver_config.xml http://proyecto-geoserver-1:8080/geoserver/rest/workspaces/sigem/datastores/sigem/featuretypes/vw_lotes_morosidad_505.xml" Regla 23. Columnas de Unión (Joins). Standard SNC. Para toda vista de unión con el FDW, se debe utilizar la columna `snc_cuenta` (limpia) contra `REPLACE(liq.inm_ctacatastral, '-', '')`. @@ -144,13 +170,13 @@ La inserción en la base de datos se realizará mediante el uso directo de ST_Ge Implementar un TRUNCATE automático al inicio del proceso de importación. Si al importar los números son UTM, se deben transformar a 4326 antes de guardarlos. Las columnas de las tablas eXXX_lotes_activos deberá tener todas las columnas del SNC. - Regla 29. -Para la construcción en la compilación, se usa JAVA21 del 192.168.1.123, en sincronía con la Regla 1. +Para la construcción en la compilación, se usa JAVA21 del 192.168.1.123, en sincronía con la Regla 1. +El uso de rutas relativas en Docker ya ha estado causando problemas desde antes. Debes usar rutas absolutas Regla 30. Arquitectura de Visualización en Dos Niveles. El visor GIS utiliza una estrategia de superposición para optimizar el rendimiento y la claridad visual. Utiliza dos tipos de capas para mostrar los lotes: Una Capa Base (Lotes Grises) y múltiples Capas Temáticas (Lotes Coloreados). 1. Capa Base (Esqueleto): Utiliza la tabla física soberana `eXXX_lotes_activos`. Se publica en GeoServer con un estilo neutro (gris/transparente). Representa la totalidad del territorio y siempre es local y persistente. 2. Capa Temática (Inteligencia): Utiliza la vista lógica `vw_lotes_morosidad_XXX`. Es la capa dinámica que aplica el JOIN con el FDW municipal. El Efecto Visual: La capa temática se superpone a la base. Aquellos lotes con coincidencia tributaria se "pintan" con el estilo de morosidad (semáforo), mientras que los lotes sin vínculo o sin deuda permanecen en gris (dejando ver la capa base), garantizando que la ciudad nunca se vea "incompleta". - +El contenedor de GeoServer (sigem-gis-geoserver-1) y el de PostgreSQL (proyecto-postgres-1) tienen sus propias redes internas de Docker y no tienen acceso directo a otras redes Docker por seguridad y estabilidad. Por lo tanto, debe configurarse y ejecutarse dentro de la red privada interna de Docker. \ No newline at end of file diff --git a/apply_config.sh b/apply_config.sh new file mode 100644 index 0000000..17beed7 --- /dev/null +++ b/apply_config.sh @@ -0,0 +1,2 @@ +#!/bin/bash +curl -s -u admin:geoserver -X PUT -H "Content-Type: application/xml" -d @/yvyape/proyectos/sigem-gis/geoserver_config.xml http://proyecto-geoserver-1:8080/geoserver/rest/workspaces/sigem/datastores/sigem/featuretypes/vw_lotes_morosidad_505.xml diff --git a/apply_config_v2.sh b/apply_config_v2.sh new file mode 100644 index 0000000..5d69102 --- /dev/null +++ b/apply_config_v2.sh @@ -0,0 +1,2 @@ +#!/bin/bash +curl -v -u admin:x25yvaga2023 -X PUT -H "Content-Type: application/xml" -d @/yvyape/proyectos/sigem-gis/geoserver_config.xml http://localhost:8080/geoserver/rest/workspaces/sigem/datastores/sigem_datastore/featuretypes/vw_lotes_morosidad_505.xml diff --git a/apply_config_v3.sh b/apply_config_v3.sh new file mode 100644 index 0000000..7e2c8ac --- /dev/null +++ b/apply_config_v3.sh @@ -0,0 +1,2 @@ +#!/bin/bash +curl -v -u admin:geoserver -X PUT -H "Content-Type: application/xml" -d @/yvyape/proyectos/sigem-gis/geoserver_config.xml http://localhost:8080/geoserver/rest/workspaces/sigem/datastores/sigem_datastore/featuretypes/vw_lotes_morosidad_505.xml diff --git a/apply_config_v4.sh b/apply_config_v4.sh new file mode 100644 index 0000000..08a217e --- /dev/null +++ b/apply_config_v4.sh @@ -0,0 +1,2 @@ +#!/bin/bash +curl -v -u admin:geoserver -X PUT -H "Content-Type: text/xml" -d @/yvyape/proyectos/sigem-gis/geoserver_config.xml http://localhost:8080/geoserver/rest/workspaces/sigem/datastores/sigem_db/featuretypes/vw_lotes_morosidad_505.xml diff --git a/geoserver_config.xml b/geoserver_config.xml new file mode 100644 index 0000000..4e48921 --- /dev/null +++ b/geoserver_config.xml @@ -0,0 +1,4 @@ + + vw_lotes_morosidad_505 + trb_tributo = 'INM' + diff --git a/mapas.html b/mapas.html index ed90aea..d967e97 100644 --- a/mapas.html +++ b/mapas.html @@ -1,5 +1,6 @@ + @@ -9,57 +10,209 @@ - + +
@@ -99,7 +252,8 @@ Por Total (Percentiles)
- Vista PNG (Full) [M] + Vista PNG (Full) [M]
Administración
@@ -114,116 +268,128 @@
📊 MOROSIDAD - ÚLTIMO ADEUDADO
- +
Último Adeudado
🟢 <= 2026
-
🎾 <= 2025
-
🟡 <= 2024
-
🟠 <= 2023
-
🔴 <= 2022
-
📢 <= 2021
-
🔘 SIN DEUDA / PRESCRIPTAS
-
Fuente: Sistema SIGEM
-
-
- - - - - - - - +
🎾 <= + 2025
+
+ 🟡 <= 2024
+
🟠 <= 2023
+
🔴 <= 2022
+
📢 <= 2021
+
🔘 SIN DEUDA + / PRESCRIPTAS
+
Fuente: Sistema SIGEM
+ + + + + + + + + + - + + \ No newline at end of file diff --git a/src/main/resources/static/mapas.html b/src/main/resources/static/mapas.html index b0037f6..0d02275 100644 --- a/src/main/resources/static/mapas.html +++ b/src/main/resources/static/mapas.html @@ -437,7 +437,7 @@ map.addSource('lotes-mvt', { type: 'vector', tiles: [ - `${window.location.origin}/gis-geoserver/gwc/service/tms/1.0.0/sigem:vw_lotes_morosidad_${entidad}@XYZ-900913@pbf/{z}/{x}/{y}.pbf` + `${window.location.origin}/geoserver/gwc/service/tms/1.0.0/sigem:e${entidad}_lotes_activos@XYZ-900913@pbf/{z}/{x}/{y}.pbf` ], scheme: 'tms' }); @@ -449,7 +449,7 @@ id: 'lotes-layer', type: 'fill', source: 'lotes-mvt', - 'source-layer': `vw_lotes_morosidad_${entidad}`, + 'source-layer': `e${entidad}_lotes_activos`, paint: { 'fill-color': 'rgba(59, 130, 246, 0.1)', // Azul tenue inicial 'fill-outline-color': 'rgba(255, 255, 255, 0.3)' // Borde blanco visible @@ -457,23 +457,13 @@ }); } - // Fuente de Mejoras (MVT) - TMS Nativo de GeoServer - if (!map.getSource('mejoras-mvt')) { - map.addSource('mejoras-mvt', { - type: 'vector', - tiles: [ - `${window.location.origin}/gis-geoserver/gwc/service/tms/1.0.0/sigem:e${entidad}_mejoras@XYZ-900913@pbf/{z}/{x}/{y}.pbf` - ], - scheme: 'tms' - }); - } // [PRUEBA CONTROLADA] Fuente Raster WMS (Renderizado en Servidor) 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}` + `${window.location.origin}/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 }); @@ -489,19 +479,7 @@ }); // Agregamos al final } - // Capa de Mejoras (Inicialmente oculta o 2D) - if (!map.getLayer('mejoras-layer')) { - map.addLayer({ - id: 'mejoras-layer', - type: 'fill', - source: 'mejoras-mvt', - 'source-layer': `e${entidad}_mejoras`, - paint: { - 'fill-color': 'rgba(59, 130, 246, 0.2)', - 'fill-outline-color': 'rgba(59, 130, 246, 0.5)' - } - }); - } + } // --- Lógica de Capas Base (Estricto Fondo Nativo) ---