Postgis - ¿Cómo reviso el tipo de geometría antes de hacer un inserto?
-
19-09-2019 - |
Pregunta
Tengo una base de datos Postgres con millones de filas, tiene una columna llamada Geom que contiene el límite de una propiedad.
Usando un script de Python, estoy extrayendo la información de esta tabla y vuelvo a insertarla en una nueva tabla.
Cuando me inserto en la nueva tabla, el script se excita con lo siguiente:
Traceback (most recent call last):
File "build_parcels.py", line 258, in <module>
main()
File "build_parcels.py", line 166, in main
update_cursor.executemany("insert into parcels (par_id, street_add, title_no, proprietors, au_name, ua_name, geom) VALUES (%s, %s, %s, %s, %s, %s, %s)", inserts)
psycopg2.IntegrityError: new row for relation "parcels" violates check constraint "enforce_geotype_geom"
La nueva tabla tiene una restricción de verificación aplicada_geotype_geom = ((geometryType (geom) = 'polygon' :: text) o (geom es nula)) mientras que la tabla anterior no, por lo que supongo que hay datos de DUD o no polígono (quizás los datos multipoligón ?) En la vieja mesa. Quiero mantener los nuevos datos como polígono, así que no quiero insertar nada más.
Inicialmente, intenté envolver la consulta con el manejo estándar de errores de Python con la esperanza de que las filas DUD Geom fallaran, pero el script seguiría ejecutando, pero el script se ha escrito para comprometerse al final, no cada fila, por lo que no funciona.
Creo que lo que necesito hacer es iterar a través de las viejas filas de geom de la mesa y verificar qué tipo de geometría son para que pueda establecer si quiero mantenerlo o tirarlo antes de insertar en la nueva mesa.
¿Cuál es la mejor manera de ir sobre esto?
Solución
Este poco asombrosamente útil de Postgis SQL debería ayudarlo a resolverlo ... Hay muchas pruebas de tipo de geometría aquí:
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
--
-- $Id: cleanGeometry.sql 2008-04-24 10:30Z Dr. Horst Duester $
--
-- cleanGeometry - remove self- and ring-selfintersections from
-- input Polygon geometries
-- http://www.sogis.ch
-- Copyright 2008 SO!GIS Koordination, Kanton Solothurn, Switzerland
-- Version 1.0
-- contact: horst dot duester at bd dot so dot ch
--
-- This is free software; you can redistribute and/or modify it under
-- the terms of the GNU General Public Licence. See the COPYING file.
-- This software is without any warrenty and you use it at your own risk
--
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CREATE OR REPLACE FUNCTION cleanGeometry(geometry)
RETURNS geometry AS
$BODY$DECLARE
inGeom ALIAS for $1;
outGeom geometry;
tmpLinestring geometry;
Begin
outGeom := NULL;
-- Clean Process for Polygon
IF (GeometryType(inGeom) = 'POLYGON' OR GeometryType(inGeom) = 'MULTIPOLYGON') THEN
-- Only process if geometry is not valid,
-- otherwise put out without change
if not isValid(inGeom) THEN
-- create nodes at all self-intersecting lines by union the polygon boundaries
-- with the startingpoint of the boundary.
tmpLinestring := st_union(st_multi(st_boundary(inGeom)),st_pointn(boundary(inGeom),1));
outGeom = buildarea(tmpLinestring);
IF (GeometryType(inGeom) = 'MULTIPOLYGON') THEN
RETURN st_multi(outGeom);
ELSE
RETURN outGeom;
END IF;
else
RETURN inGeom;
END IF;
------------------------------------------------------------------------------
-- Clean Process for LINESTRINGS, self-intersecting parts of linestrings
-- will be divided into multiparts of the mentioned linestring
------------------------------------------------------------------------------
ELSIF (GeometryType(inGeom) = 'LINESTRING') THEN
-- create nodes at all self-intersecting lines by union the linestrings
-- with the startingpoint of the linestring.
outGeom := st_union(st_multi(inGeom),st_pointn(inGeom,1));
RETURN outGeom;
ELSIF (GeometryType(inGeom) = 'MULTILINESTRING') THEN
outGeom := multi(st_union(st_multi(inGeom),st_pointn(inGeom,1)));
RETURN outGeom;
ELSIF (GeometryType(inGeom) = '<NULL>' OR GeometryType(inGeom) = 'GEOMETRYCOLLECTION') THEN
RETURN NULL;
ELSE
RAISE NOTICE 'The input type % is not supported %',GeometryType(inGeom),st_summary(inGeom);
RETURN inGeom;
END IF;
End;$BODY$
LANGUAGE 'plpgsql' VOLATILE;
Otros consejos
La opción 1 es crear un punto de guardado antes de cada inserción y volver a ese safePoint si un INSERT
falla.
La opción 2 es adjuntar la expresión de restricción de verificación como un WHERE
condición en la consulta original que produjo los datos para evitar seleccionarlos en absoluto.
La mejor respuesta depende del tamaño de las tablas, el número relativo de filas defectuosas y qué tan rápido y a menudo se supone que se debe funcionar.
Creo que puedes usarSt_collectionExtract - Dada una (multi) geometría, devuelve una geometría (multi) que consiste solo en elementos del tipo especificado.
Lo uso al insertar los resultados de una ST_INTERSECTION, ST_DUMP rompe cualquier colección multi-polifonal, en geometría individual. Después ST_CollectionExtract (theGeom, 3)
descarta cualquier cosa menos polígonos:
ST_CollectionExtract((st_dump(ST_Intersection(data.polygon, grid.polygon))).geom, )::geometry(polygon, 4326)
El segundo parámetro anterior 3
puede ser: 1 == POINT, 2 == LINESTRING, 3 == POLYGON