在Postgis中获得类似于ArcGIS的速度


62

我已经使用3/4年的Postgis 2.0了,虽然我真的很喜欢使用它,但是查询处理时间过长,使得它基本上无法用于我的用例。

我倾向于对通常具有成千上万个多面体的市政数据集进行繁重的地理处理。这些多面体的形状有时会非常不规则,每个多面体的范围从4点到78,000点不等。

例如,当我将一个包含329,152个多边形的宗地数据集与包含525个多边形的管辖权数据集相交时,我得到了以下消耗总时间的统计信息:

ArcGIS 10.0 (on same host with windows 7 OS): 3 minutes
Postgis:56 minutes (not including geometry pre-processing queries)

换句话说,在Postgis中进行此相交所需的时间比在ArcGIS中多1500%,这是我更简单的查询之一!

据说ArcGIS运行速度更快的原因之一是由于索引更好。一些程序员最近弄清楚了这些索引是如何工作的,我想知道是否有人知道如何在Postgis中构建这些索引(或构建可以模拟索引的表)。也许这可以解决Postgis中的大多数速度问题。我只能希望一定有办法,尤其是因为ArcGIS只能使用4 GB的RAM,而我的postgis服务器最多可以使用4倍的内存!

当然,postgis运行缓慢的原因有很多,因此,我将提供系统规格的详细版本:

Machine: Dell XPS 8300 
Processor: i7-2600 CPU @ 3.40 GHz 3.40 GHz 
Memory: Total Memory 16.0 GB (10.0 GB on virtual machine)

Platform: Ubuntu Server 12.04 Virtual Box VM

Potgres Version: 9.1.4
Postgis Version: POSTGIS="2.0.1 r9979" GEOS="3.3.5-CAPI-1.7.5" PROJ="Rel. 4.8.0, 6 March 2012" GDAL="GDAL 1.9.1, released 2012/05/15" LIBXML="2.7.8" LIBJSON="UNKNOWN" TOPOLOGY RASTER

我还将详细介绍用于设置postgis整个安装过程,包括创建VM本身

我还将conf文件中的共享内存从默认的24MB增加到6 GB,并运行以下命令以允许postgres运行:

sudo sysctl -w kernel.shmmax=7516192768 (I know this setting is deleted every time you restart the OS)
sudo /etc/init.d/postgresql restart

据我所知,这在性能方面绝对没有任何显着之处。

这是我用于此测试的数据的链接:

  1. 包裹:tcad_parcels_06142012.shp.zip,来自德克萨斯州奥斯丁市
  2. 管辖区域:管辖边界市德克萨斯州奥斯汀的

这是我处理数据的步骤:

的ArcGIS

  1. 将数据集添加到ArcMap
  2. 将坐标系设置为中心得克萨斯英尺(srid 2277)
  3. 使用下拉菜单中的交集工具

Postgis

使用以下方式导入包裹:

shp2pgsql -c -s 2277 -D -i -I -W UTF-8 "tcad_parcels_06142012.shp" "public"."tcad_parcels_06142012" |psql -d postgis_testing -U postgres -h local_ip -p 5432

导入管辖区使用:

shp2pgsql -c -s 2277 -D -i -I -W UTF-8 "jurisdictions.shp" "public"."jurisdictions" |psql -d postgis_testing -U postgres -h local_ip -p 5432

清理包裹中的无效几何:

DROP TABLE IF EXISTS valid_parcels;
CREATE TABLE valid_parcels(
  gid serial PRIMARY KEY,
  orig_gid integer,
  geom geometry(multipolygon,2277)
);
CREATE INDEX ON valid_parcels USING gist (geom);
INSERT INTO valid_parcels(orig_gid,geom)
  SELECT 
    gid 
    orig_gid,
    st_multi(st_makevalid(geom)) 
  FROM 
    tcad_parcels_06142012;
CLUSTER valid_parcels USING valid_parcels_geom_idx;

清除辖区中的无效几何:

DROP TABLE IF EXISTS valid_jurisdictions;
CREATE TABLE valid_jurisdictions(
  gid serial PRIMARY KEY,
  orig_gid integer,
  geom geometry(multipolygon,2277)
);
CREATE INDEX ON valid_jurisdictions USING gist (geom);
INSERT INTO valid_jurisdictions(orig_gid,geom)
  SELECT 
    gid 
    orig_gid,
    st_multi(st_makevalid(geom)) 
  FROM 
    jurisdictions;
CLUSTER valid_jurisdictions USING valid_jurisdictions_geom_idx;

运行集群:

cluster;

运行真空分析:

vacuum analyze;

在已清理表上执行交集:

CREATE TABLE parcel_jurisdictions(
  gid serial primary key,
  parcel_gid integer,
  jurisdiction_gid integer,
  isect_geom geometry(multipolygon,2277)
);
CREATE INDEX ON parcel_jurisdictions using gist (isect_geom);

INSERT INTO parcel_jurisdictions(parcel_gid,jurisdiction_gid,isect_geom)
  SELECT
    a.orig_gid parcel_gid,
    b.orig_gid jurisdiction_gid,
    st_multi(st_intersection(a.geom,b.geom))
  FROM
    valid_parcels a, valid_jurisdictions b
  WHERE
    st_intersects(a.geom,b.geom);

解释分析路口查询:

Total runtime: 3446860.731 ms
        Index Cond: (geom && b.geom)
  ->  Index Scan using valid_parcels_geom_idx on valid_parcels a  (cost=0.00..11.66 rows=2 width=1592) (actual time=0.030..4.596 rows=1366 loops=525)
  ->  Seq Scan on valid_jurisdictions b  (cost=0.00..113.25 rows=525 width=22621) (actual time=0.009..0.755 rows=525 loops=1)
Nested Loop  (cost=0.00..61428.74 rows=217501 width=24213) (actual time=2.625..3445946.889 rows=329152 loops=1)
  Join Filter: _st_intersects(a.geom, b.geom)

从我阅读的所有内容来看,我的交集查询是高效的,而且我完全不知道我在做错什么,因为查询花费56分钟处理干净的几何体!


2
在PostGIS中,添加边界框交叉检查以加快处理速度是常见的习惯。尝试在您的WHERE子句中添加“ AND a.geom && b.geom”,看看它有什么不同。
肖恩

2
在postgis 2.x中执行任何交集测试之前,st_intersects()包括一个边界框查询,因此很遗憾,这样做不会节省任何时间。
THX1138

1
您可以使用EXPLAIN ANALYZE运行查询并发布结果
Nathan W

1
您还应该知道,您在postgis和arcgis上运行的数据集不同,因为您说要让自己感到内ex,以使它们有效地被接受。
NicklasAvén2012年

2
是否有可能获取数据集以进行查看?
NicklasAvén2012年

Answers:


87

不同的方法。知道痛苦在ST_Intersection中,并且对/错测试很快,尝试最小化通过相交的几何体数量可能会加快速度。例如,不需要修剪完全包含在一个辖区中的宗地,但是ST_Intersection可能仍会麻烦一些,那就是在构建交集叠加层的一部分之前,它不必生成任何新的几何图形。所以这

INSERT INTO parcel_jurisdictions(parcel_gid,jurisdiction_gid,isect_geom)
SELECT
  a.orig_gid AS parcel_gid,
  b.orig_gid AS jurisdiction_gid,

  st_multi(st_intersection(a.geom,b.geom)) AS geom
FROM
  valid_parcels a, valid_jurisdictions b
WHERE
  st_intersects(a.geom, b.geom) and not st_within(a.geom, b.geom)
UNION ALL
SELECT
  a.orig_gid AS parcel_gid,
  b.orig_gid AS jurisdiction_gid,
  a.geom AS geom
FROM
  valid_parcels a, valid_jurisdictions b
WHERE
  st_within(a.geom, b.geom);

甚至更短

INSERT INTO parcel_jurisdictions(parcel_gid,jurisdiction_gid,isect_geom)
SELECT
  a.orig_gid AS parcel_gid,
  b.orig_gid AS jurisdiction_gid,
  CASE 
     WHEN ST_Within(a.geom,b.geom) 
     THEN a.geom
     ELSE ST_Multi(ST_Intersection(a.geom,b.geom)) 
  END AS geom
FROM valid_parcels a
JOIN valid_jurisdictions b
ON ST_Intersects(a.geom, b.geom)

不使用UNION甚至可能更快。


13
谢谢,我才花了3.63分钟!我从未想到过工会会更快。这个答案确实会让我重新思考从现在开始执行查询的方式。
THX1138

2
这很酷。我有一个工作案例,我的st_intersection查询需要30分钟以上的时间,现在我知道如何避免这种情况了:)
Nathan W

1
这个问题使我学习了Postgis!我今天会睡个好觉,看到Postgis与Arcgis
并肩作战

2
马丁·戴维斯(Martin Davis)的另一项改进,您可以内联“进出”?使用CASE语句向SELECT提问,并避免使用UNION。
保罗·拉姆西

2
UNION从两个查询中消除重复的行,但是这两个查询的结果集中不能有相同的行。因此UNION ALL,跳过重复检查的方法在这里比较合适。(我使用的次数UNION不多,但我通常会发现我使用的次数不多,因此我会更频繁地使用它UNION ALL。)
jpmc26

4

如果您省略该"st_multi(st_intersection(a.geom,b.geom))"零件会怎样?

如果没有它,下面的查询不是同一意思吗?我将其用于您提供的数据。

INSERT INTO parcel_jurisdictions(parcel_gid,jurisdiction_gid,isect_geom)
  SELECT
    a.orig_gid parcel_gid,
    b.orig_gid jurisdiction_gid,
    a.geom
  FROM
    valid_parcels a, valid_jurisdictions b
  WHERE
    st_intersects(a.geom,b.geom);

组态

Processor: AMD Athlon II X4 635 2.9 GHz 
Memory: 4 GB
Platform: Windows 7 Professional
Potgres Version: 8.4
Postgis Version: "POSTGIS="2.0.1 r9979" GEOS="3.3.5-CAPI-1.7.5" PROJ="Rel. 4.8.0, 6 March 2012" GDAL="GDAL 1.9.1, released 2012/05/15" LIBXML="2.7.8" LIBJSON="UNKNOWN" TOPOLOGY RASTER"

分析结果

"Nested Loop  (cost=0.00..7505.18 rows=217489 width=1580) (actual time=1.994..248405.616 rows=329150 loops=1)"
"  Join Filter: _st_intersects(a.geom, b.geom)"
"  ->  Seq Scan on valid_jurisdictions b  (cost=0.00..37.25 rows=525 width=22621) (actual time=0.054..1.732 rows=525 loops=1)"
"  ->  Index Scan using valid_parcels_index on valid_parcels a  (cost=0.00..11.63 rows=2 width=1576) (actual time=0.068..6.423 rows=1366 loops=525)"
"        Index Cond: (a.geom && b.geom)"
"Total runtime: 280087.497 ms"

不,他想要合成的相交多边形,但是您的查询很好地证明了所有的麻烦在于相交生成,而不是查询的二进制true / false测试部分。这是完全可以预期的,因为对真/假代码进行了高度优化,而对交集生成却没有进行优化。
保罗·拉姆西
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.