Procesamiento de Divisiones Administrativas Chinas con GeoTools y PostGIS

Contexto y Motivación

En el desarrollo de aplicaciones meteorológicas geoespaciales, surge frecuentemente la necesidad de vincular coordenadas geográficas con entidades administrativas concretas. Este escenario presenta tres requerimientos fundamentales: facilitar la búsqueda de localidades, obtener información climática mediante coordenadas, y permitir la selección interactiva de puntos sobre mapas con resolución de jurisdicción.

Aunque existen servicios comerciales que proporcionan este tipo de datos, contar con un repositorio propio ofrece ventajas significativas en cuanto a control, personalización y comprensión profunda de la estructura territorial. El enfoque descrito a continuación emplea datos cartográficos propios para construir una base de referencia geoespacial completa.

Arquitectura de Datos Territoriales

El sistema de divisiones administrativas de la República Popular China se organiza jerárquicamente en cuatro niveles principales, cada uno con características específicas de codificación y delimitación:

Primer Nivel: Provincias y Equivalentes

Comprende las entidades de mayor rango: provincias, regiones autónomas, municipalidades directamente controladas por el gobierno central, y regiones administrativas especiales. Estas unidades dependen directamente del poder ejecutivo nacional.

Segundo Nivel: Prefecturas y Equivalentes

Incluye ciudades-prefectura, prefecturas, leagues y prefecturas autónomas. Normalmente subordinadas a una entidad provincial, aunque existen excepciones notables como las ciudades de nivel provincial que administran directamente subdivisiones de nivel inferior sin intermediarios.

Tercer Nivel: Condados y Equivalentes

Engloba condados, distritos, ciudades-condado y banderas. Constituyen el nivel donde tradicionalmente se concentran los servicios públicos esenciales y la planificación regional detallada.

Cuarto Nivel: Township y Equivalentes

El nivel más granular, compuesto por townships, towns, subdistritos y comunidades. Representa la extensión territorial más próxima a la población y la gestión comunitaria directa.

Diseño del Esquema Geoespacial

La solución implementa una única tabla relacional con soporte espacial mediante PostGIS, optimizada para consultas por ubicación y por jerarquía administrativa:

-- Habilitar extensión espacial
CREATE EXTENSION IF NOT EXISTS postgis WITH SCHEMA public;

-- Secuencia para identificadores
CREATE SEQUENCE public.territorio_id_seq
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;

-- Tabla principal de divisiones territoriales
CREATE TABLE IF NOT EXISTS public.territorio_administrativo (
    id bigint NOT NULL DEFAULT nextval('territorio_id_seq'::regclass),
    nombre character varying COLLATE pg_catalog."default",
    nivel integer,
    codigo character varying COLLATE pg_catalog."default",
    punto_central geometry(Point,4326),
    perimetro geometry(MultiPolygon,4326),
    fecha_registro timestamp without time zone DEFAULT CURRENT_TIMESTAMP,
    identificador_unico character varying COLLATE pg_catalog."default",
    CONSTRAINT territorio_pkey PRIMARY KEY (id)
) TABLESPACE pg_default;

ALTER SEQUENCE public.territorio_id_seq OWNED BY public.territorio_administrativo.id;

-- Documentación de campos
COMMENT ON TABLE public.territorio_administrativo IS 'Catálogo de divisiones administrativas con geometría';
COMMENT ON COLUMN public.territorio_administrativo.nombre IS 'Denominación oficial de la división';
COMMENT ON COLUMN public.territorio_administrativo.nivel IS 'Jerarquía: 1=provincial, 2=prefectural, 3=condal, 4=local';
COMMENT ON COLUMN public.territorio_administrativo.codigo IS 'Código administrativo normalizado (6 dígitos niveles 1-3, 9 dígitos nivel 4)';
COMMENT ON COLUMN public.territorio_administrativo.punto_central IS 'Centroide calculado del polígono, SRC 4326';
COMMENT ON COLUMN public.territorio_administrativo.perimetro IS 'Límite geográfico de la jurisdicción, SRC 4326';
COMMENT ON COLUMN public.territorio_administrativo.identificador_unico IS 'UUID para trazabilidad y sincronización';

Estrategia de Normalización de Códigos

La calidad de los datos administrativos presenta desafíos significativos. Se implementan las siguientes reglas de saneamiento:

  • Para niveles 1 a 3: truncamiento a 6 dígitos, descartando registros incompletos
  • Para nivel 4: truncamiento a 9 dígitos con el mismo criterio de exclusión
  • Verificación de unicidad por código normalizado antes de la carga definitiva

Implementación con GeoTools

El flujo de trabajo de procesamiento utiliza la biblioteca GeoTools para la lectura de archivos shape y su transformación a entidades persistentes:

public class ProcesadorDivisiones {
    
    private static final String RUTA_BASE = "/datos/cartografia/china";
    private static final Map<String, Integer> MAPEO_NIVELES = Map.of(
        "provincia", 1,
        "prefectura", 2,
        "condado", 3,
        "localidad", 4
    );
    
    public void ejecutarImportacion() throws IOException {
        File directorioRaiz = new File(RUTA_BASE);
        Queue<File> archivosPendientes = new LinkedList<>();
        
        localizarArchivosShape(directorioRaiz, archivosPendientes);
        
        while (!archivosPendientes.isEmpty()) {
            File archivoActual = archivosPendientes.poll();
            procesarCapaGeoespacial(archivoActual);
        }
    }
    
    private void localizarArchivosShape(File directorio, Queue<File> destino) {
        if (!directorio.exists() || !directorio.isDirectory()) {
            return;
        }
        
        for (File elemento : directorio.listFiles()) {
            if (elemento.isDirectory()) {
                localizarArchivosShape(elemento, destino);
            } else if (elemento.getName().toLowerCase().endsWith(".shp")) {
                destino.offer(elemento);
            }
        }
    }
    
    private void procesarCapaGeoespacial(File archivoShape) throws IOException {
        Map<String, Object> parametrosConexion = new HashMap<>();
        parametrosConexion.put("url", archivoShape.toURI().toURL());
        
        DataStore almacenDatos = DataStoreFinder.getDataStore(parametrosConexion);
        SimpleFeatureSource fuente = almacenDatos.getFeatureSource();
        SimpleFeatureCollection coleccion = fuente.getFeatures();
        
        System.out.printf("Procesando: %s%n", archivoShape.getAbsolutePath());
        System.out.printf("Entidades detectadas: %d%n", coleccion.size());
        
        try (SimpleFeatureIterator iterador = coleccion.features()) {
            while (iterador.hasNext()) {
                SimpleFeature entidad = iterador.next();
                persistirEntidadTerritorial(entidad);
            }
        }
        
        almacenDatos.dispose();
    }
    
    private void persistirEntidadTerritorial(SimpleFeature entidad) {
        String denominacion = (String) entidad.getAttribute("NAME");
        Geometry geometria = (Geometry) entidad.getDefaultGeometry();
        String codigoRaw = (String) entidad.getAttribute("ADCODE");
        
        // Cálculo de centroide y normalización
        Point centroide = geometria.getCentroid();
        String codigoNormalizado = normalizarCodigoAdministrativo(codigoRaw);
        int jerarquia = determinarNivelPorPrefijo(codigoNormalizado);
        
        // Inserción vía JDBC preparado...
    }
    
    private String normalizarCodigoAdministrativo(String codigoOriginal) {
        if (codigoOriginal == null || codigoOriginal.length() < 6) {
            throw new IllegalArgumentException("Código administrativo inválido");
        }
        
        int longitudRequerida = codigoOriginal.length() >= 9 ? 9 : 6;
        return codigoOriginal.substring(0, longitudRequerida);
    }
    
    private int determinarNivelPorPrefijo(String codigo) {
        return codigo.length() <= 6 ? 
            (codigo.endsWith("0000") ? 1 : 
             codigo.endsWith("00") ? 2 : 3) : 4;
    }
}

Resultados de la Carga

El procesamiento completo de la cartografía nacional genera aproximadamente 46,652 registros espacializados. La distribución por nivel refleja la complejidad administrativa del territorio:

Región Ejemplo Nivel Rgeistros
Chongqing Provincial 4
Chongqing Prefectural 12
Chongqing Condal 75
Chongqing Local 1,218
Sichuan Local 4,933
Hebei Local 2,574

Consultas de Verificación Espacial

Una vez poblada la base, resultan útiles las siguientes operaciones de validación y aálisis:

-- Verificar proyección correcta de geometrías
SELECT ST_AsText(punto_central), nombre, nivel 
FROM territorio_administrativo 
WHERE nivel = 1 
ORDER BY id 
LIMIT 100;

-- Calcular superficies provinciales
SELECT nombre, ST_Area(perimetro) AS superficie_m2
FROM territorio_administrativo
WHERE nivel = 1 AND nombre LIKE '%Hainan%';

-- Detectar posibles duplicidades de código
SELECT codigo, COUNT(*) AS frecuencia
FROM territorio_administrativo
GROUP BY codigo
HAVING COUNT(*) > 1
ORDER BY frecuencia DESC;

-- Conversión explícita de SRID si fuera necesario
-- ALTER TABLE territorio_administrativo 
-- ALTER COLUMN punto_central TYPE Geometry(Point, 4326) 
-- USING ST_SetSRID(punto_central, 4326);

Consideraciones Técnicas Finales

La elección del centroide del polígono como representación puntual de cada entidad, aunque imperfecta para ciertos análisis de proximidad, resulta operativamente suficiente para la mayoría de los casos de uso meteorológicos. La arquitectura permite su sustitución posterior por ubicaciones oficiales cuando estén disponibles.

El índice espacial implícito de PostGIS sobre las columnas geometry garantiza tiempos de respuesta aceptables incluso para consultas de contención punto-polígono en tiempo real.

Etiquetas: geotools PostGIS Shapefile gis java

Publicado el 8-21 19:30