2010-01-07 17 views
9

tengo una base de datos postgres con millones de filas en ella tiene una columna llamada geom que contiene el límite de una propiedad.Postgis - Cómo verifico el tipo de geometría antes de hacer una inserción

usando una secuencia de comandos python Estoy extrayendo la información de esta tabla y volviéndola a insertar en una nueva tabla.

cuando inserto en la nueva tabla de los errores de script con el 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 comprobación enforce_geotype_geom = ((GeometryType (geom) = 'POLÍGONO' :: texto) o (geom IS NULL)) mientras que la tabla anterior no, así que supongo que hay datos falsos o no polígono (¿quizás datos multipolígonos?) en la tabla anterior. Quiero mantener los nuevos datos como un polígono, así que no quiero insertar nada más.

Inicialmente traté de envolver la consulta con el manejo de errores de python estándar con la esperanza de que las filas fuddrom fallarían pero la secuencia de comandos seguiría ejecutándose, pero la secuencia de comandos se escribió para comprometer al final no cada fila por lo que no funciona .

Creo que lo que tengo que hacer es recorrer las filas de geom de la tabla anterior y comprobar qué tipo de geometría son para poder establecer si quiero conservarlo o tirarlo antes de insertarlo en la nueva tabla

¿Cuál es la mejor manera de solucionar esto?

Respuesta

7

Este bit sorprendentemente útil de PostGIS SQL debería ayudar a averiguarlo ... hay muchas pruebas de tipo 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; 
+0

gracias, me habría conformado con una respuesta híbrida python/postgres pero es increíble poder hacerlo todo dentro de postgres. gracias por tu respuesta – ADAM

2

La opción 1 es crear un punto de guardado antes de cada inserción y retroceder a ese punto de seguridad si falla un INSERT.

La opción 2 es adjuntar la expresión de restricción de comprobación como una condición WHERE en la consulta original que produjo los datos para evitar seleccionarlos.

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 ejecutar.

+0

Gracias por la respuesta. Me gusta la opción 2 pero aún necesito que se inserten los otros datos, incluso si el geom no se inserta. ¿Sabes cómo puedo hacer una declaración de selección que imprime el tipo de geom para cada fila? – ADAM

+0

y para aclarar que la base de datos tiene alrededor de 5 millones de filas y esto solo se ejecuta 1 vez al mes para regenerar los datos y no necesita ser rápido.todavía no sé la cantidad de filas defectuosas – ADAM

+1

Puede hacer la consulta original (según la opción 2) como SELECCIONAR par_id, street_add, title_no, owners, au_name, ua_name, CASE WHEN ((geometrytype (geom) = 'POLYGON' :: texto) O (geom IS NULL)) ENTONCES geom ELSE null END COMO geom FROM oldtable; para sustituir los valores de geom que no se "ajustan". –

0

Creo que se puede utilizar ST_CollectionExtract - Dado un (varios) la geometría , devuelve una (multi) geometría que consiste únicamente en elementos del tipo especificado.

Lo uso cuando inserto los resultados de una ST_Intersection, ST_Dump rompe cualquier polígono múltiple, colecciones en geometría individual. Entonces 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

Cuestiones relacionadas