如何解决STIntersect表示每个点都与每个区域相交-需要确认
我正在为一个工业集团实施车辆安全计划。该小组提供了3k +个地理区域,覆盖了属于该小组成员的站点。提供的文件是shapefile。我的同事将其提取并转换为4326,然后将其上传到新数据库中。我使用IsValidDetailed()
检查了区域。 7个无效,但在对它们运行MakeValid()
时会纠正。仅使用自动,地理表SaferTogether就创建了空间索引。
我最初使用以下工具进行测试。这将在每个区域的每个点上运行,一个多小时后,我得到了结果。
INSERT INTO @Intersects (LogId,ogr_fid,NodeId)
SELECT p.LogId,s.ogr_fid,p.NodeId
FROM LogPos p,[gis]..safertogether as s
WHERE p.MyPoint.STIntersects(s.geog4326) = 1
and AssembledTime between @PeriodStart and @PeriodEnd
我现在正在测试27个对数点的单程旅行,我注意到我有81162个结果,这大致匹配27个点x 3k +区域。我不认为每个区域都覆盖每个点。下面的代码是对一趟日志的测试。效率不高。
UPDATE #Logs
SET IsIntersect = 0
SET @Message = 'Detecting Log intersects with Zone ' + CONVERT(VARCHAR(10),@GeographyId) + ':' + @GeographyName
EXEC usp_Log @ProcessName,@Message,@SessionName,@LogInfo,@Section
UPDATE #Logs
SET IsIntersect = 1
WHERE @Geography.STIntersects(MyPoint) = 1
SELECT @rc = COUNT(1)
FROM #Logs WHERE IsIntersect = 1
SET @Message = 'Trip ' + CONVERT(VARCHAR(10),@TripLogId) + ' intersecting points with ' + CONVERT(VARCHAR(10),@GeographyId) + ':' + @GeographyName + ': ' + CONVERT(varchar(10),@rc)
EXEC usp_Log @ProcessName,@LogDebug,@Section
SET @Message = 'Save intersects with ' + CONVERT(VARCHAR(10),@Section
INSERT INTO @Intersects (LogId,NodeId,ogr_fid)
SELECT
LogId,@GeographyId
FROM #Logs
WHERE IsIntersect = 1
我在这里寻找的是一种可视化区域与点的方法,这样我就可以知道它们是否损坏。我不知道如何比较内置空间结果标签中的字段。
解决方法
问题可能出在shapefile(平面图)数据到SQL Server地理(球形)模型的转换中。这些很容易做错,并且会倒置(互补的)多边形,即描述所有地球区域的多边形除预期的形状
。也看到了一些类似的情况,例如Google BigQuery,它使用了类似球形定向多边形的语义:
- Query times out after 6 hours,how to optimize it?
- How to get the correct centroid of a bigquery polygon with st_centroid
您可以通过计算多边形的面积来检查多边形是否被正确地提取到数据库中-对于倒置的多边形,多边形将是巨大的(5e14 m ^ 2的量级),而正确提取的多边形应该得到合理的结果。
SQL Server具有ReorientObject()
方法,该方法可反转多边形的方向。不过要小心,因为方向与有效性会相互作用-因此最好在ReorientObject()
之前使用MakeValid()
。当然,最好的办法就是修复摄取方法。
或者,您可以使用使用平面地图语义的SQL Server Geometry
(而不是Geography
)类型。处理平面数据存在一些问题,但是假设您不交叉反子午线,并且对哪种投影适合哪种计算有基本的了解,通常应该没事。
或切换到使用球形模式但可以正确解释和转换平面数据的数据库。例如。 BigQuery理解GeoJson几何是平面的,并且在将其转换为内部球形Geography模型时会忽略它们的方向,因此,如果将Geometry加载为GeoJson字段,则永远不会出现此问题。
,下面是我使用的最终脚本,以防万一需要帮助的人。
@Shapes是一个表变量。它被插入物填充,然后被游标循环。
每个光标循环@Shape都经过有效性测试。如果无效,则运行MakeValid()
。然后测试它是否是MULTIPOLYGON。如果是这样,它将被分解为@SubShapes。如果是单个形状,则将该形状分配给第一个@SubShape。
每个@SubShape都经过验证和面积测试。如果需要,将运行MakeValid()
和ReorientObject()
。
运行完@Shape的所有@SubShapes之后,如果@Shape是MULTIPOLYGON,则将其放回原处。这是通过字符串连接和对WellKnownText的操作来完成的。这是我最不满意的一点。感觉就像是地理API的空白。
所有@Shapes处理完毕后,它们会写回到真实表中
usp_Log_Error是一个内部日志记录过程。随时将其注释掉
SET NOCOUNT ON
DECLARE @ProcessName varchar(50) = 'ST_Geography_Validation'
DECLARE @SessionName varchar(50) = 'ST_GV_' + CONVERT(VARCHAR(19),GETDATE(),126)
DECLARE @Message varchar(1000)
DECLARE @Id int
DECLARE @Name varchar(254)
DECLARE @Shape geography
DECLARE @ValidationDetail varchar(200)
DECLARE @ShapeCount int
DECLARE @SubValidationDetail varchar(200)
DECLARE @SubShape geography
Declare @Area float
DECLARE @AreaStr varchar(20)
DECLARE @i int
DECLARE @IsMulti int = 0
DECLARE @MultiWKT varchar(MAX)
DECLARE @SingleWKT varchar(MAX)
DECLARE @Shapes TABLE
(
Id int,Name varchar(254),Shape geography
)
INSERT INTO @Shapes (Id,Name,Shape)
SELECT
ogr_fid,name,geog4326
FROM safertogether2
DECLARE @SubShapes TABLE
(
SubId int PRIMARY KEY,SubShape geography,Processed bit default 0
)
DECLARE _c CURSOR FOR
SELECT
s.Id,s.name,s.Shape,'',''
FROM @Shapes s
OPEN _c
FETCH NEXT FROM _c INTO @Id,@Name,@Shape,@IsMulti,@MultiWKT,@SingleWKT
WHILE @@FETCH_STATUS = 0
BEGIN
DELETE FROM @SubShapes
SET @ValidationDetail = @Shape.IsValidDetailed() -- This is for the base shape
IF @ValidationDetail <> '24400: Valid'
BEGIN
SET @Shape = @Shape.MakeValid()
END
SET @ShapeCount = @Shape.STNumGeometries()
IF @ShapeCount > 1
BEGIN
RAISERROR('%i:%s: is MULTIPOLYGON. Break into subshapes.',1,@Id,@Name) WITH NOWAIT
SET @IsMulti = 2
INSERT INTO @SubShapes(SubId,SubShape)
SELECT
smg.Id,smg.Geog
FROM [dbo].uf_SplitMultiGeography(@Shape) smg
END
ELSE IF @ShapeCount = 1
BEGIN
RAISERROR('%i:%s: is SINGLE. Insert Shape whole as Id 1.',@Name) WITH NOWAIT
SET @IsMulti = 1
INSERT INTO @SubShapes(SubId,SubShape)
VALUES (1,@Shape)
END
ELSE
BEGIN
RAISERROR('%i:%s: is NONE.',@Name) WITH NOWAIT
SET @IsMulti = 0
END
SET @i = 1
WHILE @i <= @ShapeCount
BEGIN
SELECT @SubShape = s.SubShape
FROM @SubShapes s
WHERE SubId = @i
SET @SubValidationDetail = @SubShape.IsValidDetailed()
RAISERROR('%i:%s:%i: .IsValidDetailed(): %s',@i,@SubValidationDetail) WITH NOWAIT
IF @SubValidationDetail <> '24400: Valid'
BEGIN
RAISERROR('%i:%s:%i: .MakeValid()',@i) WITH NOWAIT
SET @SubShape = @SubShape.MakeValid() -- This is for the subshape
END
BEGIN TRY
SET @Area = @SubShape.STArea()
END TRY
BEGIN CATCH
SET @Message = 'While getting the Area of a subshape'
EXEC usp_Log_Error @ProcessName,@SessionName,@Message
END CATCH
SET @AreaStr = LTRIM(STR(@Area,20,5))
RAISERROR('%i:%s:%i: .STArea(): Pre: %s',@AreaStr) WITH NOWAIT
IF @Area > 510000000000000
BEGIN
RAISERROR('%i:%s:%i: .ReorientObject()',@i) WITH NOWAIT
SET @SubShape = @SubShape.ReorientObject()
SET @Area = @SubShape.STArea()
SET @AreaStr = LTRIM(STR(@Area,5))
RAISERROR('%i:%s:%i: .STArea(): Post: %s',@AreaStr) WITH NOWAIT
END
UPDATE @SubShapes
SET SubShape = @SubShape
WHERE SubId = @Id
IF @IsMulti = 2
BEGIN
RAISERROR('%i:%s:%i: MULTIPOLYGON member. Process WKT for recombination',@i) WITH NOWAIT
BEGIN TRY
SET @SingleWKT = @SubShape.STAsText()
END TRY
BEGIN CATCH
SET @Message = 'While getting the WKT of a subshape'
EXEC usp_Log_Error @ProcessName,@Message
END CATCH
SET @SingleWKT = REPLACE(@SingleWKT,'POLYGON','')
IF (@i <> @ShapeCount)
BEGIN
SET @SingleWKT = REPLACE(@SingleWKT,'))',')),')
END
SET @MultiWKT = @MultiWKT + @SingleWKT
END
SET @i = @i + 1
END
IF @IsMulti = 2
BEGIN
RAISERROR('%i:%s: Is a MULTIPOLYGON. Recreate from updated WKT.',@i) WITH NOWAIT
SET @MultiWKT = 'MULTIPOLYGON(' + @MultiWKT + ')'
BEGIN TRY
SET @Shape = geography::STGeomFromText(@MultiWKT,4326)
END TRY
BEGIN CATCH
SET @Message = 'While creating a multipolygon'
EXEC usp_Log_Error @ProcessName,@Message
END CATCH
END
ELSE IF @IsMulti = 1
BEGIN
RAISERROR('%i:%s: Is SINGLE. Set Shape to subshape',@i) WITH NOWAIT
SET @Shape = @SubShape
END
ELSE
BEGIN
RAISERROR('%i:%s: Is NONE. Set Shape to none',@i) WITH NOWAIT
END
RAISERROR('%i:%s: Update temp table',@Name) WITH NOWAIT
UPDATE @Shapes
SET Shape = @Shape
WHERE Id = @Id
FETCH NEXT FROM _c INTO @Id,@SingleWKT
END
CLOSE _c
DEALLOCATE _c
select
st.ogr_fid,st.name,st.geog4326 as ShapePre,s.Shape as ShapePost,CASE
WHEN st.geog4326.IsValidDetailed() = '24400: Valid' then st.geog4326.STArea()
ELSE st.geog4326.MakeValid().STArea()
END as AreaPre,CASE
WHEN s.Shape.IsValidDetailed() = '24400: Valid' then s.Shape.STArea()
ELSE s.Shape.MakeValid().STArea()
END as AreaPost
from safertogether2 st
join @Shapes s ON s.Id = st.ogr_fid
RAISERROR('Update back to safertogether table',1) WITH NOWAIT
UPDATE st
SET geog4326 = s.Shape
FROM @Shapes s
JOIN safertogether2 st ON s.Id = st.ogr_fid
RAISERROR('End of Script',@Name) WITH NOWAIT
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。