SQL Server GEOGRAPHY STIntersection 更改点顺序

Posted

技术标签:

【中文标题】SQL Server GEOGRAPHY STIntersection 更改点顺序【英文标题】:SQL Server GEOGRAPHY STIntersection changes points order 【发布时间】:2021-08-07 20:52:22 【问题描述】:

是否可以得到STIntersection()的结果与原始对象相同的点顺序?

DECLARE @g geography;  
DECLARE @h geography;  

SET @g = geography::STGeomFromText('POLYGON((25.84568061650872 49.69890972163677,25.844941083373904 49.69189373653131,25.900405504271674 49.69173426681988,25.900898532630585 49.69667747509544,25.84568061650872 49.69890972163677))', 4326);  

SET @h = geography::STGeomFromText('LINESTRING(25.84863871143376 49.68886378946257,25.85578746277756 49.702736200117556,25.874275603076814 49.69013957742882,25.88117784964471 49.696518021079896,25.890791686361744 49.693647826244444)', 4326);  

SELECT @g.STIntersection(@h)   

-- output is MULTILINESTRING ((25.890791686361744 49.693647826244444, 25.881177849644711 49.696518021079896, 25.876080243405344 49.691807467094165), (25.871810347453074 49.691819790540073, 25.862392696595393 49.698236908740775), (25.85364996544109 49.698589185269235, 25.8501925460631 49.691879772894147)) 

如果你检查输出例如here ,你会发现它就像颠倒了一样。

我不知道它是否真的颠倒了,它是点和线段的随机顺序,但我需要它们以“原始”顺序保存线方向。

有什么解决办法吗?

谢谢。

【问题讨论】:

您是否正在尝试创建一个有序的经纬度坐标的线串来逃避锯齿形影响?如果是这样,我遇到了同样的挑战,必须将分数转换为度数,然后按度数排序。效果很好,但有点冒险。 感谢您的回复。线串是车辆的轨迹。我想使用 Leaflet 在地图上显示它。我还添加了箭头来显示移动方向。当线串未拆分为多线串时 - 它可以正常工作。但是当我想在区域中显示轨道时 - 多线串的段是随机定向的。 【参考方案1】:

... 将@h 线串拆分为单条线的有序列表(每条线的起点是该线的参考点),将每条单条线与多边形@g 相交,并将交点拆分为单独的线行(如果不止一个)。如果交叉点的起点更接近参考点,则保持交叉点不变,否则将其反转(交叉点的终点成为起点,反之亦然)。最后按顺序(参考点/线序数,相交线序数)聚合几何集合中的交点

DECLARE @g geography;  
DECLARE @h geography;  

SET @g = geography::STGeomFromText('POLYGON((25.84568061650872 49.69890972163677,25.844941083373904 49.69189373653131,25.900405504271674 49.69173426681988,25.900898532630585 49.69667747509544,25.84568061650872 49.69890972163677))', 4326);  
SET @h = geography::STGeomFromText('LINESTRING(25.84863871143376 49.68886378946257,25.85578746277756 49.702736200117556,25.874275603076814 49.69013957742882,25.88117784964471 49.696518021079896,25.890791686361744 49.693647826244444)', 4326);  

--reverse linestring..test
--SET @h = geography::STGeomFromText('LINESTRING(25.890791686361744 49.693647826244444, 25.88117784964471 49.696518021079896, 25.874275603076814 49.69013957742882, 25.85578746277756 49.702736200117556, 25.84863871143376 49.68886378946257)', 4326);  

select 
concat(
'GEOMETRYCOLLECTION(', 
string_agg(directionline.ToString(), ',') within group (order by pnum, ilnum), 
')'
) as geometrycollection_string

 --geography::CollectionAggregate(directionline) -- no option for order...
from
(
select 
p.pnum,
io.ilnum,

case when
--reverse the interection when starting point is farther to the reference point
io.iline.STStartPoint().STDistance(p.spoint) > io.iline.STEndPoint().STDistance(p.spoint)
then 
--...assume intersection is linestring since @h is linestring 
cast(
  replace(
    concat( --lazy...string manipulation
      cast('linestring(' as varchar(4000)), 
      translate(io.iline.STEndPoint().ToString(),'()','  '),',', translate(io.iline.STStartPoint().ToString(),'()','  '), ')'
    )
  ,'point','') 
as geography)
--if the starting point of the intesection is closer to the reference point then keep the intersection as is
--this also covers if intersection is point (when @h is tangent to @g)
else io.iline end  as directionline

from
(

select 
    @h.STPointN(n.pnum) as spoint, --starting point of each line/reference
    n.pnum, --starting point ordinal
    --linestring starting from spoint till the next point
    cast(
      replace(
        concat(
          cast('linestring(' as varchar(4000)), --lazy...
          translate(@h.STPointN(n.pnum).ToString(),'()','  '),',', translate(@h.STPointN(n.pnum+1).ToString(),'()','  '), ')'
        )
      ,'point','') 
    as geography) as thelines --each line of the @h linestring
from
    (
        --points (excluding the last) of the @h linestring
        select top (@h.STNumPoints()-1) row_number() over(order by @@spid) as pnum --point number
        from (values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) as a(n)
        cross join (values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) as b(n)
    ) as n

) as p   

--get each intersection "object" of each line (in the @h linestring) with @g
cross apply
(
    --intersection of each line (of the @h linestring) with the polygon @g
    --the intersection could be multiple "objects", get each object
    select p.thelines.STIntersection(@g).STGeometryN(n.ilnum) as iline, ilnum
    from
    (
        --split any multi-object intersection into individual objects
        select top (p.thelines.STIntersection(@g).STNumGeometries()) row_number() over(order by @@spid) as ilnum
        from (values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) as a(n)
        cross join (values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) as b(n)
    ) as n
) as io
) as src
--order by pnum, ilnum

【讨论】:

以上是关于SQL Server GEOGRAPHY STIntersection 更改点顺序的主要内容,如果未能解决你的问题,请参考以下文章

确定一个 POINT 是不是位于 LINESTRING 上的两个其他 POINT 之间(SQL Server 2008 Geography)

为啥 SQL Server GEOGRAPHY 允许 -15069° 和 +15069° 之间的经度?为啥是±15069°?

如何测试 SQL Server Geography 列的值是不是为 POINT(0 0)?

在 F# 中,如何使用 Type Provider 访问 Sql Server Geography 数据类型?

SQL Server - 地理

SQL Server 地理