【问题标题】:Linq to sql Haversine formulaLinq to sql Haversine 公式
【发布时间】:2012-06-10 17:51:05
【问题描述】:

我在 c# 和 TSQL 中都有一个 Harversine 公式的实现。 我不确定如何最好地在服务器端实现公式,以便我能够在 Linq 查询中使用它。

理想情况下,我只需将本地公式链接到服务器上的函数即可。从而避免了“没有转换为 sql”的错误,并且一切都很好而且无缝。

显然,对这个问题的任何看法都是有帮助的。

我知道 SQL2008 中的地理类型。但是,我正在处理的代码库已经对 Linq to SQL 如此依赖,我希望它付出的努力多于其价值!

谢谢

【问题讨论】:

    标签: c# sql-server tsql linq-to-sql


    【解决方案1】:

    为什么不使用 100% SQL,因为它是进行计算的最佳方式,并且只需获取一个已经填满距离的表格?

    来自existing answer

    CREATE FUNCTION dbo.udf_Haversine(@lat1 float, @long1 float, @lat2 float, @long2 float) RETURNS float 
      BEGIN
        DECLARE @dlon float, @dlat float, @rlat1 float, @rlat2 float, @rlong1 float, @rlong2 float, @a float, @c float, @R float, @d float, @DtoR float
    
        SELECT @DtoR = 0.017453293
        SELECT @R = 3937 --3976
    
        SELECT 
            @rlat1 = @lat1 * @DtoR,
            @rlong1 = @long1 * @DtoR,
            @rlat2 = @lat2 * @DtoR,
            @rlong2 = @long2 * @DtoR
    
        SELECT 
            @dlon = @rlong1 - @rlong2,
            @dlat = @rlat1 - @rlat2
    
        SELECT @a = power(sin(@dlat/2), 2) + cos(@rlat1) * cos(@rlat2) * power(sin(@dlon/2), 2)
        SELECT @c = 2 * atn2(sqrt(@a), sqrt(1-@a))
        SELECT @d = @R * @c
    
        RETURN @d 
      END
    

    并像这样使用:

    var table = from r in db.VenuePostCodes 
                select new {
                    lat = r.Latitude,
                    lng = r.Longitude,
                    name = r.Name,
                    distance = db.udf_Haversine(
                                      r.Latitude,r.Longitude,
                                      r.Latitude,r.Longitude2)
                };
    

    但最好的办法总是将所有内容都放在 SQL 上,这样您的托管服务器就不用做太多事情了,只需在您的 SQL 中添加一个 VIEW 并调用该视图,让我们想象一下:

    SELECT 
       latitude, longitude, name, latitude1, longitude2, postcode, 
       udf_Haversine(latitude, longitude, latitude2, longitude2) AS distance 
    FROM 
       venuepostcodes
    ORDER BY 
       distance
    

    并使用 LINQ 直接调用该视图。

    【讨论】:

    • 我同意这是最好的方法。可以在 LINQ 表达式中定义 Haversine 公式,但它会很丑陋,并且包含很多冗余计算(例如,由于您不能使用表达式块,因此您将无法存储a,因此在计算 c 时必须计算两次)。免得自己头疼,并按照@balexandre 的例子。
    • 干杯,我已经创建了一个函数来做到这一点。但是我一生无法弄清楚如何从 Linq 中调用它!谢谢。将研究为地理功能使用专用视图
    • 您需要先将该 SQL 函数添加到您的实体中。如果您使用edmx 文件,只需右键单击并Function Import...
    • 我只是在使用 Linq2SQL。理想情况下只是希望能够访问 L2S 部分实体类中的 udf。所以我可以创建 myFoo.NearestBars() 并找到所有按距离排序的 Bars。
    • 纯 c# 使用 this example,但它需要处理,如果不是异步的,它可能会阻塞 UI……最好是 SQL Server 来进行计算。跨度>
    【解决方案2】:

    @balexandre 的回答很好,但我对提供的 SQL 函数不满意(它缺少 cmets,有趣的注释掉常量,是英里吗?KM?等等...)

    CREATE FUNCTION [dbo].[udf_Haversine](@lat1 float, @long1 float, @lat2 float, @long2 float) RETURNS float 
    BEGIN
        DECLARE @dlon float, @dlat float,
                @rlat1 float, @rlat2 float, @rlong1 float, @rlong2 float,
                @a float, @c float, @R float, @d float, @DtoR float
    
        SELECT
            @DtoR = PI() / 180, -- Degrees to radians const
            @R = 6371 -- Radius of Earth in KM
    
        SELECT 
            @rlat1 = @lat1 * @DtoR,
            @rlong1 = @long1 * @DtoR,
            @rlat2 = @lat2 * @DtoR,
            @rlong2 = @long2 * @DtoR
    
        SELECT 
            @dlat = @rlat1 - @rlat2,
            @dlon = @rlong1 - @rlong2
    
        SELECT @a = SIN(@dlat / 2) * SIN(@dlat / 2) +
                    SIN(@dlon / 2) * SIN(@dlon / 2) * COS(@rlat2) * COS(@rlat1)
    
        SELECT @c = 2 * atn2(sqrt(@a), sqrt(1 - @a))
        SELECT @d = @R * @c -- Final distance in KM
    
        SELECT @d = @d * 0.621371192 -- Final distance in miles
    
    RETURN @d 
    END
    

    它是从我们从这里抓取的 JavaScript 实现转换而来的,最后还转换为英里:

    // Converted from JavaScript implementation:
    // http://www.movable-type.co.uk/scripts/latlong.html
    
    var R = 6371; // km
    var dLat = (lat2-lat1).toRad();
    var dLon = (lon2-lon1).toRad();
    var lat1 = lat1.toRad();
    var lat2 = lat2.toRad();
    
    var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
            Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2); 
    var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 
    var d = R * c;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-02-26
      • 2012-10-26
      • 2017-01-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-22
      相关资源
      最近更新 更多