【问题标题】:STIntersect says every point is intersecting every zone - need to confirmSTIntersect 表示每个点都与每个区域相交 - 需要确认
【发布时间】:2020-08-27 03:42:44
【问题描述】:

我正在为一个行业团体实施车辆安全计划。该组提供了 3k+ 个地理区域,涵盖了该组成员的站点。提供的文件是一个 shapefile。我的同事把它提取出来并转换为4326并上传到一个新的数据库中。我使用IsValidDetailed() 检查了这些区域。 7 是无效的,但在运行 MakeValid() 时自行纠正。地理表 SaferTogether 已创建空间索引,仅使用 auto。

我最初使用以下方法进行测试。这将针对每个区域运行每个点,一个多小时后我得到了结果。

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, @Message, @SessionName, @LogDebug, @Section

SET @Message = 'Save intersects with ' + CONVERT(VARCHAR(10), @GeographyId) + ':' + @GeographyName
EXEC usp_Log @ProcessName, @Message, @SessionName, @LogInfo, @Section

INSERT INTO @Intersects (LogId, NodeId, ogr_fid)
SELECT 
    LogId,
    NodeId,
    @GeographyId
FROM #Logs
WHERE IsIntersect = 1

我在这里寻找一种可视化区域与点的方法,以便我可以判断它们是否损坏。我不知道如何比较内置空间结果选项卡中的字段。

【问题讨论】:

    标签: sql-server data-visualization sql-server-2014 geospatial


    【解决方案1】:

    问题可能在于将 shapefile(平面地图)数据转换为 SQL Server 地理(球形)模型。这些很容易出错并得到倒置(互补)多边形,即描述所有地球区域除了预期形状的多边形。

    还看到了几个类似的案例,他们使用的是 Google BigQuery,它使用了类似的球形多边形语义:

    您可以通过计算多边形的面积来检查多边形是否被正确摄取到数据库中 - 对于倒置多边形来说,它会很大(大约 5e14 m^2),而对于正确摄取的多边形,您应该得到合理的结果。

    SQL Server 具有反转多边形方向的ReorientObject() 方法。不过要小心,因为方向与有效性相互作用——所以最好在MakeValid()之前使用ReorientObject()。最好的当然是修复摄取方法。

    或者,您可以使用使用平面地图语义的 SQL Server Geometry(而不是 Geography)类型。使用平面数据存在一些问题,但假设您没有越过反子午线,并且对哪些投影适合哪种计算有基本的了解,您通常应该没问题。

    或者切换到使用球面模式的数据库,但可以正确解释和转换平面数据。例如。 BigQuery 了解 GeoJson 几何图形是平面的,并且在将它们转换为内部球形 Geography 模型时会忽略它们的方向,因此如果您将 Geometry 作为 GeoJson 字段加载,则不会出现此问题。

    【讨论】:

    • 不会检查 MSSQL 捕获大于地球一半的多边形的有效性吗?我相信地理是一个要求。矿场很大。有什么可视化工具推荐吗?
    • 检查区域后,我不得不使用 ReorientObject() 翻转大部分区域。这具有将所有 MULTIPOLGON 定义变成空集合的副作用。他们的面积都接近51e14的限制。娱乐的想法,所以他们会是正确的方式?把它们拆开单独处理?
    • 大于地球一半的多边形是有效的,SQL Server 有办法处理它们(从某些版本开始)。不幸的是,要允许它们,它必须遵循方向规则 - shapefile 中不存在该规则,因此它们可能会被错误地加载。我不知道如何可靠地将 shapefile 加载到 SQL Server。我很了解 BigQuery,它使用相同的方向规则,但 BigQuery 可以加载 GeoJson,而我认为 SQL Server 不支持这一点。
    • 任何其他问题我会问一个新问题。 TYVM 为您提供帮助。
    【解决方案2】:

    以下是我使用的最终脚本,以防有人需要帮助。

    @Shapes 是一个表变量。它被一个插入填充,然后被游标循环。

    每个游标循环都会测试@Shape 的有效性。如果无效,则运行 MakeValid()。然后对其进行测试以查看它是否为 MULTIPOLYGON 。如果是,则将其分解为@SubShapes。如果是单个形状,则将该形状分配给第一个 @SubShape。

    每个@SubShape 都经过验证和面积测试。如果需要,运行 MakeValid()ReorientObject()

    在运行@Shape 的所有@SubShape 之后,如果@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,
            0,
            '',
            ''
        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.', 0, 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.', 0, 1, @Id, @Name) WITH NOWAIT
                    SET @IsMulti = 1
                    INSERT INTO @SubShapes(SubId, SubShape) 
                    VALUES (1, @Shape)
                END
                ELSE
                BEGIN 
                    RAISERROR('%i:%s: is NONE.', 0, 1, @Id, @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', 0, 1, @Id, @Name, @i, @SubValidationDetail) WITH NOWAIT
    
                    IF @SubValidationDetail <> '24400: Valid'
                    BEGIN
                        RAISERROR('%i:%s:%i: .MakeValid()', 0, 1, @Id, @Name, @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', 0, 1, @Id, @Name, @i, @AreaStr) WITH NOWAIT
                    IF @Area > 510000000000000
                    BEGIN
                        RAISERROR('%i:%s:%i: .ReorientObject()', 0, 1, @Id, @Name, @i) WITH NOWAIT
                        SET @SubShape = @SubShape.ReorientObject()
                        SET @Area = @SubShape.STArea()
                        SET @AreaStr = LTRIM(STR(@Area, 20, 5))
                        RAISERROR('%i:%s:%i: .STArea(): Post: %s', 0, 1, @Id, @Name, @i, @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', 0, 1, @Id, @Name, @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, @SessionName, @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.', 0, 1, @Id, @Name, @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, @SessionName, @Message
                    END CATCH
                END
                ELSE IF @IsMulti = 1
                BEGIN
                    RAISERROR('%i:%s: Is SINGLE.  Set Shape to subshape', 0, 1, @Id, @Name, @i) WITH NOWAIT
                    SET @Shape = @SubShape
                END
                ELSE
                BEGIN
                    RAISERROR('%i:%s: Is NONE.  Set Shape to none', 0, 1, @Id, @Name, @i) WITH NOWAIT
                END
    
                RAISERROR('%i:%s: Update temp table', 0, 1, @Id, @Name) WITH NOWAIT
                UPDATE @Shapes
                SET Shape = @Shape
                WHERE Id = @Id
    
                FETCH NEXT FROM _c INTO @Id, @Name, @Shape, @IsMulti, @MultiWKT, @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', 0, 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', 0, 1, @Id, @Name) WITH NOWAIT
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-10-16
      • 2019-01-28
      • 2014-08-14
      • 2019-11-13
      • 2018-05-07
      • 2015-02-24
      • 1970-01-01
      • 2021-05-30
      相关资源
      最近更新 更多