我可以使用Postgres函数在固定大小的旋转矩形内找到点吗?

时间:2016-09-23 15:11:49

标签: sql postgresql machine-learning geometry postgis

我正在使用Postgres 9.5,我刚刚为一些扩展功能安装了PostGIS。我有一个带(x,y)点的表,我想找到适合最大点数的矩形。约束是矩形边长度是固定的。到目前为止,我正在计算没有旋转的盒子中有多少个点。我的观点以原点为中心,(0,0)

SELECT Sum(CASE
             WHEN x > -5
                  AND x < 5
                  AND y > -10
                  AND y < 10 THEN 1
             ELSE 0
           END) AS inside_points,
       Count(1) AS total_points
FROM   track_t;  

此查询为我提供了一个矩形内的点数,其中原点为(0,0),长度 x = 10 y = 20

从这里我将创建一个旋转矩形角点(角度,x1,y1,x2,y2)的辅助表,然后交叉连接到我的数据,并计算每个角度的点,而GROUP BY角度。然后我可以选择哪个角度给我矩形内的最多点。

但这似乎有点老式,也许不具备性能。此外,计算旋转矩形内的点不是trivial calculation

是否有更有效和更优雅的方式,可能使用Postgres Geometric Datatypes或PostGIS Box2D旋转固定边长的矩形,然后计数内部的点数?几何函数看起来不错,但它们似乎提供了最小的边界框而不是相反的方式。

除了Postgresql之外,我正在使用一个Python框架,可以在SQL无法使用的情况下使用它。

更新:我尝试的一件事是使用Geometric Types,特别是BOX

  SELECT deg, Box(Point(-5, -10), Point(5, 10)) * Point(1, Radians(deg)) 
        FROM   Generate_series(0, 360, 90) AS deg

不幸的是,Rotate functiondoesn't work for Polygons

1 个答案:

答案 0 :(得分:1)

我最终生成矩形顶点,旋转这些顶点,然后将矩形区域(常量)与包含测试点所形成的4个三角形的面积进行比较。

此技术基于parsimonious answer

  

制作三角形。假设,abcd是矩形,x是点,那么如果area(abx)+area(bcx)+area(cdx)+area(dax) equals area(abcd)则点在其中。

矩形由

定义
  • A 左下角(-x / 2,-y / 2)

  • B 左上角(-x / 2,+ y / 2)

  • C 右上角(+ x / 2,+ y / 2)

  • D 右下角(+ x / 2,-y / 2)

此代码然后检查点(qx,qy)是否在宽度x=10和高度y=20的矩形内,该矩形围绕原点(0,0)旋转一个角度,范围为0到180度,10度。

这是代码。它花了9分钟检查750k点,所以有明确的改进空间。此外,一旦我升级到9.6

,它就可以并行化
with t as (select 10*0.5 as x, 20*0.5 as y, 17.0 as qx, -3.0 as qy)

select 
    z.angle
    -- ABC area
    --,abs(0.5*(z.ax*(z.by-z.cy)+z.bx*(z.cy-z.ay)+z.cx*(z.ay-z.by)))

    -- CDA area
    --,abs(0.5*(z.cx*(z.dy-z.ay)+z.dx*(z.ay-z.cy)+z.ax*(z.cy-z.dy)))

    -- ABCD area
    ,abs(0.5*(z.ax*(z.by-z.cy)+z.bx*(z.cy-z.ay)+z.cx*(z.ay-z.by))) + abs(0.5*(z.cx*(z.dy-z.ay)+z.dx*(z.ay-z.cy)+z.ax*(z.cy-z.dy))) as abcd_area

    -- ABQ area
    --,abs(0.5*(z.ax*(z.by-z.qx)+z.bx*(z.qy-z.ay)+z.qx*(z.ay-z.by)))

    -- BCQ area
    --,abs(0.5*(z.bx*(z.cy-z.qx)+z.cx*(z.qy-z.by)+z.qx*(z.by-z.cy)))

    -- CDQ area
    --,abs(0.5*(z.cx*(z.dy-z.qx)+z.dx*(z.qy-z.cy)+z.qx*(z.cy-z.dy)))

    -- DAQ area
    --,abs(0.5*(z.dx*(z.ay-z.qx)+z.ax*(z.qy-z.dy)+z.qx*(z.dy-z.ay)))

    -- total area of triangles with question point (ABQ + BCQ + CDQ + DAQ)
    ,abs(0.5*(z.ax*(z.by-z.qx)+z.bx*(z.qy-z.ay)+z.qx*(z.ay-z.by)))
        + abs(0.5*(z.bx*(z.cy-z.qx)+z.cx*(z.qy-z.by)+z.qx*(z.by-z.cy)))
        + abs(0.5*(z.cx*(z.dy-z.qx)+z.dx*(z.qy-z.cy)+z.qx*(z.cy-z.dy)))
        + abs(0.5*(z.dx*(z.ay-z.qx)+z.ax*(z.qy-z.dy)+z.qx*(z.dy-z.ay))) as point_area

from
(
SELECT 
    a.id as angle
    -- bottom left (A)
    ,(-t.x) * cos(radians(a.id)) - (-t.y) * sin(radians(a.id)) as ax
    ,(-t.x) * sin(radians(a.id)) + (-t.y) * cos(radians(a.id)) as ay
    --top left (B)
    ,(-t.x) * cos(radians(a.id)) - (t.y) * sin(radians(a.id)) as bx
    ,(-t.x) * sin(radians(a.id)) + (t.y) * cos(radians(a.id)) as by
    --top right (C)
    ,(t.x) * cos(radians(a.id)) - (t.y) * sin(radians(a.id)) as cx
    ,(t.x) * sin(radians(a.id)) + (t.y) * cos(radians(a.id)) as cy
    --bottom right (D)
    ,(t.x) * cos(radians(a.id)) - (-t.y) * sin(radians(a.id)) as dx
    ,(t.x) * sin(radians(a.id)) + (-t.y) * cos(radians(a.id)) as dy

    -- point to check (Q)
    ,t.qx as qx
    ,t.qy as qy
FROM generate_series(0,180,10) AS a(id), t
) z
;

结果是

angle;abcd_area;point_area
0;200;340
10;200;360.6646055963
20;200;373.409049054212
30;200;377.846096908265
40;200;373.84093170467
50;200;361.515248361426
60;200;341.243556529821
70;200;313.641801308188
80;200;279.548648061772
90;200;240
*100;200;200*
*110;200;200*
*120;200;200*
*130;200;200*
*140;200;200*
150;200;237.846096908265
160;200;277.643408923024
170;200;312.04311584956
180;200;340

当角度100,110,120,130和140度的旋转包括测试点(用*表示)