【问题标题】:PostGIS: optimised way to find intersection between polygon and circlePostGIS:找到多边形和圆之间交点的优化方法
【发布时间】:2018-09-14 05:23:00
【问题描述】:

我正在尝试使用 PostGIS 查找事件(多边形)和观察区(圆 - 点和半径)之间的交集。基线数据将超过 10 000 个多边形和 500 000 个圆。另外,我对 PostGIS 还是很陌生。

我已经尝试了一些东西,但执行需要很长时间。有人可以建议任何优化或仅使用 PostGIS 的更好方法。这是我尝试过的-

1.使用几何数据类型: 我已将事件和观察区存储在类型几何中。 在它们上创建 GIST 索引,使用 ST_DWITHIN 查找交集。

1 个事件和 500 000 个观察区的输出花费了大约 6.750 秒。在这里,花费的时间是最佳的,但问题是我的半径以米为单位,几何类型 ST_DWithin 要求它采用 SRID 单位。我无法弄清楚这种转换。

CREATE TABLE incident (
 incident_id SERIAL NOT NULL, 
 incident_name VARCHAR(20), 
 incident_span GEOMETRY(POLYGON, 4326), 
 CONSTRAINT incident_id PRIMARY KEY (incident_id)
);
CREATE TABLE watchzones (
 id SERIAL NOT NULL, 
 date_created timestamp with time zone DEFAULT now(), 
 latitude NUMERIC(10, 7) DEFAULT NULL, 
 Longitude NUMERIC(10, 7) DEFAULT NULL, 
 radius integer, 
 position GEOMETRY(POINT, 4326), 
 CONSTRAINT id PRIMARY KEY (id)
);

CREATE INDEX ix_spatial_geom on watchzones using gist(position);
CREATE INDEX ix_spatial_geom_1 on incident using gist(incident_span);


Insert into incident values (
   1, 
   'test', 
   ST_GeomFromText('POLYGON((152.945470916 -29.212227933,152.942130026 -29.213431145,152.939345911 -29.2125423759999,152.935144791 -29.21454003,152.933185494 -29.2135838469999,152.929481762 -29.216065516,152.929698621 -29.217402937,152.927245999 
-29.219576,152.921539 -29.217676,152.918487996 -29.2113786959999,152.919254355 -29.206029929,152.919692387 -29.2027824419999,152.936020197 -29.207567346,152.944901258 -29.207729953,152.945470916 
-29.212227933))', 
     4326
     )
     );

insert into watchzones  
  SELECT generate_series(1, 500000) AS id, 
         now(), 
         -29.21073, 
         152.93322, 
         '50', 
         ST_GeomFromText('POINT( 152.93322 -29.21073)', 4326);


explain analyze SELECT wz.id, 
       i.incident_id 
FROM watchzones wz, 
     incident i 
WHERE ST_DWithin(incident_span,position,wz.radius);

    "Nested Loop  (cost=0.14..227467.00 rows=42 width=8) (actual time=0.142..1506.476 rows=500000 loops=1)"
"  ->  Seq Scan on watchzones wz  (cost=0.00..11173.00 rows=500000 width=40) (actual time=0.109..47.822 rows=500000 loops=1)"
"  ->  Index Scan using ix_spatial_geom_1 on incident i  (cost=0.14..0.42 rows=1 width=284) (actual time=0.002..0.002 rows=1 loops=500000)"
"        Index Cond: (incident_span && st_expand(wz."position", (wz.radius)::double precision))"
"        Filter: ((wz."position" && st_expand(incident_span, (wz.radius)::double precision)) AND _st_dwithin(incident_span, wz."position", (wz.radius)::double precision))"
"Planning time: 0.150 ms"
"Execution time: 1523.312 ms"

2。使用地理数据类型:

这里有 1 个事件和 500 000 个观察区的输出,花费了大约 29.987 秒,这非常慢。请注意,我已经对 GIST 和 BRIN 索引进行了尝试,并且还在表上运行了 VACUUM ANALYZE。

CREATE TABLE watchzones_geog 
         ( 
            id SERIAL PRIMARY KEY, 
            date_created TIMESTAMP with time zone DEFAULT now(), 
            latitude NUMERIC(10, 7) DEFAULT NULL, 
            longitude NUMERIC(10, 7) DEFAULT NULL, 
            radius INTEGER, 
            position geography(point) 
         );


CREATE INDEX watchzones_geog_gix ON watchzones_geog USING GIST (position);

insert into watchzones_geog
SELECT generate_series(1,500000) AS id, now(),-29.21073,152.93322,'50',ST_GeogFromText('POINT(152.93322 -29.21073)');

 CREATE TABLE incident_geog (
    incident_id    SERIAL PRIMARY KEY,
    incident_name   VARCHAR(20),
    incident_span      GEOGRAPHY(POLYGON)
);

    CREATE INDEX incident_geog_gix ON incident_geog USING GIST (incident_span);

Insert into incident_geog values (1,'test', ST_GeogFromText
('POLYGON((152.945470916 -29.212227933,152.942130026 -29.213431145,152.939345911 -29.2125423759999,152.935144791 -29.21454003,152.933185494 -29.2135838469999,152.929481762 -29.216065516,152.929698621 -29.217402937,152.927245999 
-29.219576,152.921539 -29.217676,152.918487996 -29.2113786959999,152.919254355 -29.206029929,152.919692387 -29.2027824419999,152.936020197 -29.207567346,152.944901258 -29.207729953,152.945470916 
-29.212227933))'));

explain analyze SELECT i.incident_id, 
       wz.id 
FROM   watchzones_geog wz, 
       incident_geog i 
WHERE  St_dwithin(position, incident_span, radius); 

"Nested Loop  (cost=0.27..348717.00 rows=17 width=8) (actual time=0.277..18551.844 rows=500000 loops=1)"
"  ->  Seq Scan on watchzones_geog wz  (cost=0.00..11173.00 rows=500000 width=40) (actual time=0.102..50.052 rows=500000 loops=1)"
"  ->  Index Scan using incident_geog_gix on incident_geog i  (cost=0.27..0.67 rows=1 width=711) (actual time=0.036..0.036 rows=1 loops=500000)"
"        Index Cond: (incident_span && _st_expand(wz."position", (wz.radius)::double precision))"
"        Filter: ((wz."position" && _st_expand(incident_span, (wz.radius)::double precision)) AND _st_dwithin(wz."position", incident_span, (wz.radius)::double precision, true))"
"Planning time: 0.155 ms"
"Execution time: 18587.041 ms"

3.我还尝试使用ST_Buffer(position, radius,'quad_segs=8') 创建一个圆,然后使用 ST_Intersects。这样一来,几何和地理数据类型的查询都需要一分钟多的时间。

如果有人可以提出更好的方法或一些可以加快执行速度的优化,那就太好了。

谢谢

【问题讨论】:

  • 在旁注中,您插入lat=-29.21073, long=152.93322,然后将点创建为ST_GeomFromText('POINT(-29.21073 152.93322)', 4326);,有效地交换纬度和经度(应该是POINT(long lat)
  • 在第一个查询中查询点周围 50 度,而在第二个查询中查询 50 米。两者的输出不同,所以比较时间是无效的。在这两种情况下,请尝试添加 EXPLAIN (ANALYZE, BUFFERS) 以找出速度慢的原因。
  • 正如您所指出的,一个是50度,另一个是50米,有没有办法将米转换为度数?我搜索了很多,但找不到任何相关内容。
  • 你可以检查这个answer关于度数到米的问题(简单地说,投射到地理是最简单的。为了帮助你提高查询效率,你仍然需要编辑问题以包含解释(分析,缓冲区)输出。
  • 嗨@JGH,我添加了解释分析的输出

标签: postgis


【解决方案1】:

查询没问题,但您的示例错误。首先,请注意,针对 1 个多边形优化的查询可能与针对数千个多边形优化的查询不同。

主要问题在于样本点。实际上,您在完全相同的位置有 500,000 个点,因此根据相交的多边形,查询将返回 0 或 500,000 个结果。 Postgis 首先使用索引使用方形框与点/多边形相交,然后通过计算真实距离来优化结果。使用您的样本,它必须计算 500,000 次距离,这很慢。

使用具有随机位置(1 度以内)的点层,查询只需不到 1 秒,因为它只需要计算 20 个位置的距离。

INSERT INTO watchzones_geog
SELECT generate_series(1,500000) AS id, now(),0,0,'50',
       ST_makePoint(152.93322+random(),-29.21073+random())::geography;


explain analyze SELECT i.incident_id, 
       wz.id 
FROM   watchzones_geog wz, 
       incident_geog i 
WHERE  St_dwithin(position, incident_span, radius); 
    Nested Loop  (cost=0.00..272424.01 rows=1 width=8) (actual time=25.956..921.846 rows=20 loops=1)
--------------------------------------------
   Join Filter: ((wz."position" && _st_expand(i.incident_span, (wz.radius)::double precision)) AND (i.incident_span && _st_expand(wz."position", (wz.radius)::double precision)) AND _st_dwithin(wz."position", i.incident_span, (wz.radius)::double precision, true))
   Rows Removed by Join Filter: 499980
   ->  Seq Scan on incident_geog i  (cost=0.00..1.01 rows=1 width=36) (actual time=0.009..0.009 rows=1 loops=1)
   ->  Seq Scan on watchzones_geog wz  (cost=0.00..11173.00 rows=500000 width=40) (actual time=0.006..65.625 rows=500000 loops=1)
 Planning time: 1.887 ms
 Execution time: 921.895 ms

【讨论】:

  • 感谢您的回复。我不认为样本数据是错误的,因为我的目的是对 500,000 个与多边形相交的点进行基准测试。在我的示例数据中,创建的点与多边形相交。我还通过创建随机点进行了检查。我的观察是执行时间与交叉点的数量成正比。例如 - 对于 500,000 个点,所有与多边形相交大约需要 13 秒。对于 100 万个点,全部相交,大约需要 25 秒。我对随机和相似的数据点进行了同样的尝试。没有 500000 个点的交叉点,大约需要 1 秒
猜你喜欢
  • 2013-03-15
  • 2010-09-23
  • 1970-01-01
  • 1970-01-01
  • 2014-08-24
  • 1970-01-01
  • 2015-03-06
  • 2019-11-07
  • 1970-01-01
相关资源
最近更新 更多