【问题标题】:How can I return a substring of a LINESTRING in SQL Server 2008 Spatial?如何在 SQL Server 2008 Spatial 中返回 LINESTRING 的子字符串?
【发布时间】:2010-03-28 17:52:45
【问题描述】:

假设我有一个 LINESTRING 定义为

LINESTRING(-122.360 47.656, -122.343 47.656, -122.310 47.690, -122.310 47.670, -122.300 47.630)

我想从中获取一个子字符串

POINT(-122.360 47.656) to POINT(-122.310 47.690)

如何在 SQL Server 2008 Spatial 中返回 LINESTRING 的子字符串?

【问题讨论】:

    标签: sql-server spatial


    【解决方案1】:

    好吧,我设法在 CLR 用户定义函数中做到了。

    它本身并不是一个“子字符串”方法,但我需要将一个 LINESTRING 细分为多个 LINESTRING,每个长度为 X 个单位,或者与形成 LINESTRING 段的子点一样短(不是很清楚,我知道!)

    下面是我做的一个sn-p,希望对其他人有用:

    using System;
    using System.Data;
    using System.Data.SqlClient;
    using System.Data.SqlTypes;
    using Microsoft.SqlServer.Server;
    using Microsoft.SqlServer.Types;
    using System.Collections;
    
    public partial class UserDefinedFunctions
    {
        /// <summary>
        /// Take a LINESTRING and return a sub LINESTRING from it given the
        /// starting point and the distance to move
        /// </summary>
        /// <param name="inputLine"></param>
        /// <param name="divideEveryDistance"></param>
        /// <returns></returns>
        #region DivideLineString
        [Microsoft.SqlServer.Server.SqlFunction(FillRowMethodName = "FillDivideLineStringRow", TableDefinition = "segment geography")]
        public static IEnumerable DivideLineString(Microsoft.SqlServer.Types.SqlGeography inputLine, double divideEveryDistance)
        {
            // ArrayList to hold the resulting rows
            ArrayList resultCollection = new ArrayList();
    
            // Check that the input geography is a LINESTRING
            if (!inputLine.InstanceOf("LINESTRING"))
                throw new ArgumentException("This operation may only be executed on LineString instances.");
    
            // If the input distance is less than or equal zero
            // just return the original linestring
            if (divideEveryDistance <= 0)
            {
                resultCollection.Add(inputLine);
                return resultCollection;
            }
    
            // Builder to hold the aggregated LINESTRING
            SqlGeographyBuilder subLinestringBuilder;
    
            // Initialize the starting point to the start point of the input LINESTRING
            SqlGeography startPoint = inputLine.STStartPoint();
            SqlGeography currentPoint = null;
    
            // Initialize the starting index to the first point on the input LINESTRING
            int currentPointIndex = 1;
    
            // Loop on all the points on the input LINESTRING
            while (currentPointIndex < inputLine.STNumPoints())
            {
                // Initialize the builder
                subLinestringBuilder = new SqlGeographyBuilder();
                subLinestringBuilder.SetSrid(4326);
                subLinestringBuilder.BeginGeography(OpenGisGeographyType.LineString);
    
                // Start with the starting point of the line
                subLinestringBuilder.BeginFigure((double)startPoint.Lat, (double)startPoint.Long);
    
                // Distance traversed accumulator
                double currentDistance = 0;
    
                // While we didn't cover the required divide distance and we're still within the boundaries of the input LINESTRING
                while (currentDistance < divideEveryDistance && currentPointIndex < inputLine.STNumPoints())
                {
                    // Calculate the distance between the startPoint and the nth point
                    currentPoint = inputLine.STPointN(currentPointIndex);
                    currentDistance = (double)startPoint.STDistance(currentPoint);
    
                    // Add the currentPoint to the subLineString
                    subLinestringBuilder.AddLine((double)currentPoint.Lat, (double)currentPoint.Long);
    
                    // Visit the next point
                    currentPointIndex++;
                }
    
                // We covered the required divide distance,
                // Move on to the next segment of the line
                if (currentPoint != null)
                    // Set the startpoint of the next segment to be the last point we visited
                    startPoint = SqlGeography.Point((double)currentPoint.Lat, (double)currentPoint.Long, 4326);
    
                // If we reached the end of the LINESTRING, create a segment between the last point
                // we visited and the end point of the LINESTRING
                if (currentPointIndex >= inputLine.STNumPoints())
                {
                    // Add the endpoint of the original linestring
                    subLinestringBuilder.AddLine((double)inputLine.STEndPoint().Lat, (double)inputLine.STEndPoint().Long);
                }
    
                // End the current line segment
                subLinestringBuilder.EndFigure();
                subLinestringBuilder.EndGeography();
    
                // Add the row to the result collection
                resultCollection.Add(subLinestringBuilder.ConstructedGeography);
            }
    
            // We're done, return the table
            return resultCollection;
        } 
        #endregion
    
        /// <summary>
        /// Method required to fill the table-valued function
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="geography"></param>
        #region FillDivideLineStringRow
        private static void FillDivideLineStringRow(Object obj, out SqlGeography geography)
        {
            geography = (SqlGeography)obj;
        } 
        #endregion
    };
    

    【讨论】:

    • 嗯,如果有 SQL 的内置函数就好了……(如果我们导入程序集,我们仍然可以这样做)
    • 该功能已经可用,因为代码被编译成程序集并被导入。
    【解决方案2】:

    怎么样

    DECLARE @g geometry;
    SET @g = geometry::STGeomFromText('LINESTRING(-122.360 47.656, -122.343 47.656, -122.310 47.690, -122.310 47.670, -122.300 47.630)', 0);
    SELECT @g.STPointN(1).ToString();
    SELECT @g.STPointN(3).ToString();
    
    DECLARE @h geometry;
    SET @h = geometry::STGeomFromText('POINT(-122.360 47.656)', 0);
    SELECT @h.STTouches(@g);
    

    看看

    STPointN (geometry Data Type)

    STStartPoint (geometry Data Type)

    也可以试试

    STTouches (geometry Data Type)

    【讨论】:

    • 问题是,我不会知道线上点的索引,我会得到点本身
    • 我使用的是地理数据类型,所以 STTouches 不可用 :)
    【解决方案3】:

    declare @linestring geography = geography::STGeomFromText('LINESTRING(-122.360 47.656, -122.343 47.656, -122.310 47.690, -122.310 47.670, -122.300 47.630)',4326) declare @point_n geography declare @iterator int = 1 while @iterator <= @linestring.STNumPoints() begin print @linestring.STPointN(@iterator).STAsText() set @iterator = @iterator + 1 end

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-05-28
      • 1970-01-01
      • 1970-01-01
      • 2012-07-23
      • 1970-01-01
      • 1970-01-01
      • 2018-02-18
      相关资源
      最近更新 更多