【问题标题】:PostGIS: intersections of set of collinear line segments, with countsPostGIS:一组共线线段的交点,有计数
【发布时间】:2016-06-24 15:23:18
【问题描述】:

我有一组共线线段(可能相互不相交、包含或重叠)。

我想制作一组新的线段,其中线段不相交或接触(不重叠),并且每个线段都有覆盖它的原始线段的计数。

例如,假设原始集合是(为了说明而绘制非共线):

A----------------------B
        C---------------------------D
            E-----F
                                           G-------------H
                                                         I-------J

所需的新集合是:

A-------C---E-----F-----B-----------D      G-------------H-------J
    1     2    3     2        1                    1         1

(只有点坐标重要,新集合不与旧集合共享点对象)

如何使用 PostGIS 实现这一目标?

相关问题:假设我从一个线段表开始,而不是全部共线,我如何编写将共线段组合在一起的整个查询,然后将解决方案应用于我的第一个问题?

感谢您的帮助!

【问题讨论】:

  • 请一次提出一个问题,并请发布您的表格和一些示例数据

标签: postgresql gis postgis


【解决方案1】:

设置(用于以后的查询):

create table lines (
  id        serial               primary key,
  label     text                 not null,
  line_data geometry(linestring) not null
);

insert into lines(label, line_data)
values ('A-B', ST_MakeLine(ST_MakePoint(-3, -6), ST_MakePoint( 1,  2))),
       ('D-C', ST_MakeLine(ST_MakePoint( 2,  4), ST_MakePoint(-2, -4))),
       ('E-F', ST_MakeLine(ST_MakePoint(-1, -2), ST_MakePoint( 0,  0))),
       ('G-H', ST_MakeLine(ST_MakePoint( 3,  6), ST_MakePoint( 4,  8))),
       ('I-J', ST_MakeLine(ST_MakePoint( 4,  8), ST_MakePoint( 5, 10))),
       ('P-L', ST_MakeLine(ST_MakePoint( 1,  0), ST_MakePoint( 2,  2))),
       ('X-Y', ST_MakeLine(ST_MakePoint( 2,  2), ST_MakePoint( 0,  4)));

注意事项

  • 我特意调换了你的 D 点和 C 点来证明需要向量求反
  • P-L 线与您的示例线平行(但不共线)
  • X-Y 行与其他行无关
  • 以下解决方案显然不起作用,当您的 linestrings 有超过 2 个点并且它们不在同一条线上时(所以当单个 linestring 不直)。

ST_Union 聚合函数可以拆分您的共线 linestring。您只需要计算包含这些内容的行数。

但是,按共线性分组并不是那么简单。我没有找到任何开箱即用的解决方案,但你可以计算它(这不会计算计数):

select   string_agg(label, ','), ST_AsText(ST_Multi(ST_Union(line_data)))
from     lines
group by (
  select case
           when ST_SRID(s) <> ST_SRID(e) then row(ST_SRID(s), s, null)
           when ST_X(s) = ST_X(e) then row(ST_SRID(s), ST_SetSRID(ST_MakePoint(ST_X(s), 1.0), ST_SRID(s)), null)
           when ST_Y(s) = ST_Y(e) then row(ST_SRID(s), ST_SetSRID(ST_MakePoint(1.0, ST_Y(e)), ST_SRID(s)), null)
           else (
             select  row(
                       ST_SRID(s),
                       (select case
                          when ST_Y(rv) < 0
                          then ST_SetSRID(ST_MakePoint(-ST_X(rv), -ST_Y(rv)), ST_SRID(s))
                          else rv
                        end), -- normalized vector (negated when necessary, but same for all parallel lines)
                       (ST_X(e) * ST_Y(s) - ST_X(s) * ST_Y(e)) / (ST_X(e) - ST_X(s)) -- solution of the linear equaltion, where x=0
                     )
             from    coalesce(1.0 / nullif(ST_Distance(s, e), 0), 0) dmi, -- distance's multiplicative inverse
                     ST_TransScale(e, -ST_X(s), -ST_Y(s), dmi, dmi) rv -- raw vector (translated and scaled)
           )
         end
  from   ST_StartPoint(line_data) s,
         ST_EndPoint(line_data) e
)

将产生:

X-Y                 | MULTILINESTRING((2 2,0 4))
P-L                 | MULTILINESTRING((1 0,2 2))
E-F,A-B,I-J,G-H,D-C | MULTILINESTRING((-3 -6,-2 -4),(-2 -4,-1 -2),(-1 -2,0 0),(0 0,1 2),(2 4,1 2),(3 6,4 8),(4 8,5 10))

要计算计数,请再次JOIN 您的原始数据,其中拆分的行包含在 (ST_Contains) 您的原始行中:

select    ST_AsText(splitted_line), count(line_data)
from      (select   ST_Multi(ST_Union(line_data)) ml
           from     lines
           group by (
             select case
               when ST_SRID(s) <> ST_SRID(e) then row(ST_SRID(s), s, null)
               when ST_X(s) = ST_X(e) then row(ST_SRID(s), ST_SetSRID(ST_MakePoint(ST_X(s), 1.0), ST_SRID(s)), null)
               when ST_Y(s) = ST_Y(e) then row(ST_SRID(s), ST_SetSRID(ST_MakePoint(1.0, ST_Y(e)), ST_SRID(s)), null)
               else (
                 select  row(
                           ST_SRID(s),
                           (select case
                              when ST_Y(rv) < 0
                              then ST_SetSRID(ST_MakePoint(-ST_X(rv), -ST_Y(rv)), ST_SRID(s))
                              else rv
                            end), -- normalized vector (negated when necessary, but same for all parallel lines)
                           (ST_X(e) * ST_Y(s) - ST_X(s) * ST_Y(e)) / (ST_X(e) - ST_X(s)) -- solution of the linear equaltion, where x=0
                         )
                 from    coalesce(1.0 / nullif(ST_Distance(s, e), 0), 0) dmi, -- distance's multiplicative inverse
                         ST_TransScale(e, -ST_X(s), -ST_Y(s), dmi, dmi) rv -- raw vector (translated and scaled)
               )
             end
             from   ST_StartPoint(line_data) s,
                    ST_EndPoint(line_data) e)) al,
          generate_series(1, ST_NumGeometries(ml)) i,
          ST_GeometryN(ml, i) splitted_line
left join lines on ST_Contains(line_data, splitted_line)
group by  splitted_line

将返回:

LINESTRING(-3 -6,-2 -4) | 1
LINESTRING(-2 -4,-1 -2) | 2
LINESTRING(-1 -2,0 0)   | 3
LINESTRING(0 0,1 2)     | 2
LINESTRING(2 2,0 4)     | 1
LINESTRING(1 0,2 2)     | 1
LINESTRING(2 4,1 2)     | 1
LINESTRING(3 6,4 8)     | 1
LINESTRING(4 8,5 10)    | 1

【讨论】:

  • 感谢您的解决方案。现在我尝试使用真实数据(来自 OpenStreetMap 的路线)运行第一个 SQL 查询,但收到错误:“错误:对混合 SRID 几何 SQL 状态的操作:XX000”。在 EPSG:4326 坐标系中的数据库线性几何中。我该如何解决这个错误? GeoJSON 格式的示例数据link。谢谢!
  • @HasT 您不能混合 SRID,因此如果您有数据,请使用 f.ex。 SRID=4326,您应该将 ST_MakePoint 调用包装在 ST_SetSRID(ST_MakePoint(...), 4326) call 中。
  • @HasT 我已经更新了我的解决方案,也可以携带 SRID。但我不得不说:你的样本数据看起来不像只有直线。解决方案仅适用于这些。
猜你喜欢
  • 1970-01-01
  • 2015-07-03
  • 2013-04-25
  • 1970-01-01
  • 2012-04-19
  • 2013-11-26
  • 2015-05-17
  • 2022-11-02
  • 1970-01-01
相关资源
最近更新 更多