【问题标题】:Postgis - How do i check the geometry type before i do an insertPostgis - 我如何在插入之前检查几何类型
【发布时间】:2011-01-02 16:31:35
【问题描述】:

我有一个包含数百万行的 postgres 数据库,它有一个名为 geom 的列,其中包含属性的边界。

我使用 python 脚本从该表中提取信息并将其重新插入到新表中。

当我在新表中插入时,脚本会出现以下错误:

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"

新表有一个检查约束 enforce_geotype_geom = ((geometrytype(geom) = 'POLYGON'::text) OR (geom IS NULL)) 而旧表没有,所以我猜那里有无用数据或非多边形(也许是多面数据?)在旧表中。我想将新数据保留为多边形,所以不想插入其他任何东西。

最初,我尝试使用标准 python 错误处理包装查询,希望哑几何行会失败但脚本会继续运行,但脚本已被编写为在最后提交而不是每一行,因此它不起作用。

我认为我需要做的是遍历旧表的几何行并检查它们是什么类型的几何图形,以便在插入新表之前确定是要保留还是丢弃它

解决这个问题的最佳方法是什么?

【问题讨论】:

    标签: python postgresql postgis


    【解决方案1】:

    我认为你可以使用 ST_CollectionExtract — 给定一个(多)几何,返回一个仅由指定类型的元素组成的(多)几何。

    我在插入 ST_Intersection 的结果时使用它,ST_Dump 将任何多多边形集合分解为单个几何图形。然后ST_CollectionExtract (theGeom, 3) 丢弃除了多边形之外的任何东西:

    ST_CollectionExtract((st_dump(ST_Intersection(data.polygon, grid.polygon))).geom, )::geometry(polygon, 4326)

    3上面的第二个参数可以是:1 == POINT, 2 == LINESTRING, 3 == POLYGON

    【讨论】:

      【解决方案2】:

      这个 PostGIS SQL 非常有用的部分应该可以帮助您弄清楚...这里有很多几何类型测试:

      -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      -- 
      -- $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;
      

      【讨论】:

      • 谢谢,本来可以选择混合 python/postgres 的答案,但是能够在 postgres 中完成这一切真是太棒了。谢谢你的回答
      【解决方案3】:

      选项 1 是在每次插入之前创建一个保存点,并在 INSERT 失败时回滚到该安全点。

      选项 2 是将检查约束表达式作为 WHERE 条件附加到生成数据的原始查询上,以避免选择它。

      最佳答案取决于表的大小、错误行的相对数量以及应该运行的速度和频率。

      【讨论】:

      • 感谢您的回答。我喜欢选项 2,但即使没有插入 geom,我仍然需要插入其他数据。你知道我怎么能做一个选择语句来打印每一行的几何类型吗?
      • 并澄清数据库有大约 500 万行,每月只运行 1 次来重新生成数据,不需要很快。我还不知道错误行的数量
      • 您可以执行原始查询(根据选项 2),例如 SELECT par_id, street_add, title_no, proprietors, au_name, ua_name, CASE WHEN ((geometrytype(geom) = 'POLYGON'::text) OR (geom IS NULL)) THEN geom ELSE null END AS geom FROM oldtable;用 null 代替不“适合”的几何值。
      • 感谢彼得的回答,我使用了 geometrytype(geom) 但类似于上面的回答
      猜你喜欢
      • 2021-05-04
      • 1970-01-01
      • 1970-01-01
      • 2020-03-21
      • 2019-08-23
      • 2011-10-14
      • 2023-01-11
      • 2012-11-18
      • 2012-06-05
      相关资源
      最近更新 更多