SQL Server空间查找剩余距离

3

不确定如何解决这个问题。我有两个地理位置列。

CREATE TABLE #Trip
...
LegRoute geography NULL,
GPSPoint geography NULL,
GPSPointToLegRouteDistance Float NULL,
...
  • LegRoute 包含折线
  • GPSPoint 包含点

我可以获取点到线的距离(以英里为单位)。这是 GPS 位置与路径的关系。

UPDATE T
SET GPSPointToLegRouteDistance = LegRoute.STDistance(GPSPoint) / 1609.344            
FROM #Trip T

我需要找到的是计算出来的距离所指向的。然后我需要一种方法来计算从那个点到折线的结束点的距离。
现实世界的描述:
车辆可能会偏离路线(总会)。但我需要找到最近的路线点,以及剩余的行程距离。
1个回答

2

好的,我解决了这个问题。欢迎评论并提出更好/更快的方法。经过一些研究,似乎没有内置的SQL Server功能可以完成此操作。因此,我采用了CLR集成。

有一堆“假设”,从数学角度来看解决方案并不完全准确。但我的目标是速度。所以,我做了我该做的事情。此外,在我们的数据库中,我们将GPS和路线存储为纬度/经度和字符串(这些字符串类似于多个Web服务返回的内容,只是一系列点)

仅通过查看我们拥有的数据-我们从未拥有单独的“腿”超过1/2-1英里。我估计误差最多为0.2英里。对于我们所做的事情(估计公路卡车的剩余驾驶距离/时间),这无关紧要。与地理类型相比,性能非常不错。但如果您对改进C#代码速度有建议,请提出..

public class Functions
{
    /// <summary>
    /// Function receives path and current location. We find remaining distance after
    /// matching position. Function will return null in case of error.
    /// If everything goes well but last point on route is closest to GPS - we return 0
    /// </summary>
    /// <param name="lat">
    /// Latitude of current location
    /// </param>
    /// <param name="lon">
    /// Longitude of current location
    /// </param>
    /// <param name="path">
    /// Path as a series of points just like we have it in RouteCache
    /// </param>
    /// <param name="outOfRouteMetersThreshhold">
    /// Out of route distance we can tolerate. If reached - return NULL
    /// </param>
    /// <returns>
    /// Meters to end of path.
    /// </returns>
    [SqlFunction]
    public static double? RemainingDistance(double lat, double lon, string path, double outOfRouteThreshhold)
    {
        var gpsPoint = new Point { Lat = lat, Lon = lon };

        // Parse path into array of points
        // Loop and find point in sequence closest to our input GPS lat/lon
        // IMPORTANT!!! There is some simplification of issue here.
        //  We assume that linestring is pretty granular with small-ish segments
        //  This way we don't care if distance is to segment. We just check distance to each point.
        //  This will give better performance but will not be too precise. For what we do - it's OK
        var closestPointIndex = 0;
        double distance = 10000;
        var pointArrayStr = path.Split(',');
        if (pointArrayStr.Length < 2) return null;

        for (var i = 0; i < pointArrayStr.Length; i++)
        {
            var latLonStr = pointArrayStr[i].Split(' ');
            var currentDistance = DistanceSqrt(
                gpsPoint,
                new Point { Lat = double.Parse(latLonStr[1]), Lon = double.Parse(latLonStr[0]) });
            if (currentDistance >= distance) continue;

            distance = currentDistance;
            closestPointIndex = i;
        }

        // Closest point known. Let's see what this distance in meters and handle out of route
        var closestPointStr = pointArrayStr[closestPointIndex].Split(' ');
        var closestPoint = new Point { Lat = double.Parse(closestPointStr[1]), Lon = double.Parse(closestPointStr[0]) };
        var distanceInMeters = DistanceMeters(gpsPoint, closestPoint);
        if (distanceInMeters > outOfRouteThreshhold) return null;

        // Last point closest, this is "complete" route or something wrong with passed data  
        if (closestPointIndex == pointArrayStr.Length - 1) return 0;

        // Reconstruct path string, but only for remaining points in line
        var strBuilder = new StringBuilder();
        for (var i = closestPointIndex; i < pointArrayStr.Length; i++) strBuilder.Append(pointArrayStr[i] + ",");
        strBuilder.Remove(strBuilder.Length - 1, 1);

        // Create geography linestring and calculate lenght. This will be our remaining driving distance
        try
        {
            var geoPath = SqlGeography.STGeomFromText(new SqlChars($"LINESTRING({strBuilder})"), 4326);
            var dist = geoPath.STLength().Value;
            return dist;
        }
        catch (Exception)
        {
            return -1;
        }
    }

    // Compute the distance from A to B
    private static double DistanceSqrt(Point pointA, Point pointB)
    {
        var d1 = pointA.Lat - pointB.Lat;
        var d2 = pointA.Lon - pointB.Lon;

        return Math.Sqrt(d1 * d1 + d2 * d2);
    }

    private static double DistanceMeters(Point pointA, Point pointB)
    {
        var e = Math.PI * pointA.Lat / 180;
        var f = Math.PI * pointA.Lon / 180;
        var g = Math.PI * pointB.Lat / 180;
        var h = Math.PI * pointB.Lon / 180;

        var i = Math.Cos(e) * Math.Cos(g) * Math.Cos(f) * Math.Cos(h)
            + Math.Cos(e) * Math.Sin(f) * Math.Cos(g) * Math.Sin(h)
            + Math.Sin(e) * Math.Sin(g);

        var j = Math.Acos(i);
        var k = 6371 * j; // 6371 earth radius

        return k * 1000;
    }

    private struct Point
    {
        public double Lat { get; set; }

        public double Lon { get; set; }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接