这有点棘手,但是肯定是可能的。
让我们开始计算从一个点到另一个点的方位。给定起点,方位角和距离,以下函数将返回目标点:
CREATE FUNCTION [dbo].[func_MoveTowardsPoint](@start_point geography, @end_point geography, @distance int) RETURNS geographyASBEGIN DECLARE @ang_dist float = @distance / 6371000.0; DECLARE @bearing decimal(18,15); DECLARE @lat_1 decimal(18,15) = Radians(@start_point.Lat); DECLARE @lon_1 decimal(18,15) = Radians(@start_point.Long); DECLARE @lat_2 decimal(18,15) = Radians(@end_point.Lat); DECLARE @lon_diff decimal(18,15) = Radians(@end_point.Long - @start_point.Long); DECLARE @new_lat decimal(18,15); DECLARE @new_lon decimal(18,15); DECLARE @result geography; SET @bearing = ATN2(sin(@lon_diff) * cos(@lat_2), (cos(@lat_1) * sin(@lat_2)) - (sin(@lat_1) * cos(@lat_2) * cos(@lon_diff))); SET @new_lat = asin(sin(@lat_1) * cos(@ang_dist) + cos(@lat_1) * sin(@ang_dist) * cos(@bearing)); SET @new_lon = @lon_1 + atn2( sin(@bearing) * sin(@ang_dist) * cos(@lat_1), cos(@ang_dist) - sin(@lat_1) * sin(@lat_2)); SET @new_lat = Degrees(@new_lat); SET @new_lon = Degrees(@new_lon); SET @result = geography::STPointFromText('POINT(' + ConVERT(varchar(64), @new_lon) + ' ' + ConVERT(varchar(64), @new_lat) + ')', 4326); RETURN @result;END我了解您需要一个以线串作为输入,而不仅仅是起点和终点的函数。该点必须沿着连接的线段的路径移动,并且必须继续沿路径的“角”移动。起初这似乎很复杂,但我认为可以解决以下问题:
STPointN()
从x = 1到x =遍历线串的每个点STNumPoints()
。- 找到
STDistance()
迭代中当前点到下一个点之间的距离:@linestring.STPointN(x).STDistance(@linestring.STPointN(x+1))
- 如果上述距离>您的输入距离’n’:
…然后,目的地点在此点和下一个点之间。只需将
func_MoveTowardsPoint传递点x用作起点,将点x +
1用作终点,并应用距离n。返回结果并中断迭代。
别的:
…目标点在迭代中距下一个点更远的路径中。从距离’n’中减去点x和点x + 1之间的距离。以修改后的距离继续进行迭代。
您可能已经注意到,我们可以轻松地以递归方式(而不是迭代地)实现上述功能。
我们开始做吧:
CREATE FUNCTION [dbo].[func_MoveAlongPath](@path geography,@distance int,@index int = 1) RETURNS geographyASBEGIN DECLARE @result geography = null; DECLARE @num_points int = @path.STNumPoints(); DECLARE @dist_to_next float; IF @index < @num_points BEGIN SET @dist_to_next = @path.STPointN(@index).STDistance(@path.STPointN(@index + 1)); IF @distance <= @dist_to_next BEGIN SET @result = [dbo].[func_MoveTowardsPoint](@path.STPointN(@index), @path.STPointN(@index + 1), @distance); END ELSE BEGIN SET @result = [dbo].[func_MoveAlongPath](@path, @distance - @dist_to_next, @index + 1); END END ELSE BEGIN SET @result = @path.STPointN(@index); END RETURN @result;END
有了适当的设置,是时候进行一些测试了。让我们使用问题中提供的原始线串,然后在350m,3500m和7000m处请求目标点:
DECLARE @g geography;SET @g = geography::STGeomFromText('LINESTRING(-122.360 47.656, -122.343 47.656, -122.310 47.690)', 4326);SELECT [dbo].[func_MoveAlongPath](@g, 350, DEFAULT).ToString();SELECT [dbo].[func_MoveAlongPath](@g, 3500, DEFAULT).ToString();SELECT [dbo].[func_MoveAlongPath](@g, 7000, DEFAULT).ToString();我们的测试返回以下结果:
POINT (-122.3553270591861 47.6560002502638)POINT (-122.32676470116748 47.672728464582583)POINT (-122.31 47.69)
请注意,我们要求的最后距离(7000m)超出了线串的长度,因此我们返回了最后一点。在这种情况下,您可以根据需要轻松地修改该函数以返回NULL。



