1

我正在使用 Google 地图创建商店定位器。

用户将输入他们的邮政编码并从下拉菜单中选择距该位置的距离 - 5、10 15 20 英里。

这将返回距其位置指定距离的商店并在地图上放置标记。

我有一个包含以下列的邮政编码表:

 PostCode Latitude Longitude Easting Northing Grid Ref Country District Ward

和一个与该表有外键关系的存储表

我正在使用 MVC3 和 linq-sql,但我不知道如何编写查询以仅拉回距离已输入的邮政编码 5 英里的商店

编辑

我发现了以下内容:

select *,
    acos(cos(51.720663 * (PI()/180)) *
     cos(-0.299929 * (PI()/180)) *
     cos(latitude * (PI()/180)) *
     cos(longitude * (PI()/180))
     +
     cos(51.720663 * (PI()/180)) *
     sin(-0.299929 * (PI()/180)) *
     cos(latitude * (PI()/180)) *
     sin(longitude * (PI()/180))
     +
     sin(51.720663 * (PI()/180)) *
     sin(latitude * (PI()/180))
    ) * 3959 as Dist
from postcodes
having Dist < radius
order by Dist

但这部分不起作用:

having Dist < radius
    order by Dist

我错过了什么..?

如果可能的话,我真的想在 linq 中执行此操作....

任何帮助将不胜感激。

谢谢

4

2 回答 2

1

实际上,我最终使用了 GeoCodeCal 类来解决我需要的东西。

请看下面的课程:

using System;

namespace WidgetData
{
    /// <summary>
    /// Used when we need to calculate the distance between two geocodes (lat/long) points. 
    /// </summary>
    public static class GeoCodeCalc
    {

        ///<summary>
        ///</summary>
        public const double EarthRadiusInMiles = 3956.0;

        ///<summary>
        ///</summary>
        public const double EarthRadiusInKilometers = 6367.0;

        ///<summary>
        ///</summary>
        ///<param name="val"></param>
        ///<returns></returns>
        public static double ToRadian(double val)
        {
            return val * (Math.PI / 180);
        }

        ///<summary>
        ///</summary>
        ///<param name="val1"></param>
        ///<param name="val2"></param>
        ///<returns></returns>
        public static double DiffRadian(double val1, double val2)
        {
            return ToRadian(val2) - ToRadian(val1);
        }

        /// <summary> 
        /// Calculate the distance between two geocodes. Defaults to using Miles. 
        /// </summary> 
        public static double CalcDistance(double lat1, double lng1, double lat2, double lng2)
        {
            return CalcDistance(lat1, lng1, lat2, lng2, GeoCodeCalcMeasurement.Miles);
        }

        /// <summary> 
        /// Calculate the distance between two geocodes. 
        /// </summary> 
        public static double CalcDistance(double lat1, double lng1, double lat2, double lng2, GeoCodeCalcMeasurement m)
        {
            double radius = EarthRadiusInMiles;
            if (m == GeoCodeCalcMeasurement.Kilometers)
            {
                radius = EarthRadiusInKilometers;
            }
            return radius * 2 * Math.Asin(Math.Min(1, Math.Sqrt((Math.Pow(Math.Sin((DiffRadian(lat1, lat2)) / 2.0), 2.0) + Math.Cos(ToRadian(lat1)) * Math.Cos(ToRadian(lat2)) * Math.Pow(Math.Sin((DiffRadian(lng1, lng2)) / 2.0), 2.0)))));
        }

        /// <summary>
        /// Convert the OS Grid reference numbers which are easting and norhing into a latitude/longitude
        /// </summary>
        /// <param name="easting"></param>
        /// <param name="northing"></param>
        /// <param name="latitude"></param>
        /// <param name="londgitude"></param>
        public static void ConvertOsGridToLatLong(int easting, int northing, out double latitude, out double londgitude)
        {
            var e = easting;
            var N = northing;
            const double a = 6377563.396;
            const double b = 6356256.910; // Airy 1830 major & minor semi-axes  
            const double f0 = 0.9996012717; // NatGrid scale factor on central meridian  
            const double lat0 = 49*Math.PI/180;
            const double lon0 = -2*Math.PI/180; // NatGrid true origin  
            const int n0 = -100000;
            const int e0 = 400000; // northing & easting of true origin, metres  
            const double e2 = 1 - (b*b)/(a*a); // eccentricity squared  
            const double n = (a - b)/(a + b);
            const double n2 = n*n;
            const double n3 = n*n*n;
            var lat = lat0;
            var m=0.0;                                         
            do 
            {    
                lat = (N-n0-m)/(a*f0) + lat;    
                var ma = (1 + n + (5/4)*n2 + (5/4)*n3) * (lat-lat0);    
                var mb = (3*n + 3*n*n + (21/8)*n3) * Math.Sin(lat-lat0) * Math.Cos(lat+lat0);    
                var mc = ((15/8)*n2 + (15/8)*n3) * Math.Sin(2*(lat-lat0)) * Math.Cos(2*(lat+lat0));    
                var md = (35/24)*n3 * Math.Sin(3*(lat-lat0)) * Math.Cos(3*(lat+lat0));    
                m = b * f0 * (ma - mb + mc - md);                // meridional arc  
            } while (N-n0-m >= 0.00001);  // ie until < 0.01mm  
            var cosLat = Math.Cos(lat);
            var sinLat = Math.Sin(lat);  
            var nu = a*f0/Math.Sqrt(1-e2*sinLat*sinLat);              // transverse radius of curvature  
            var rho = a*f0*(1-e2)/Math.Pow(1-e2*sinLat*sinLat, 1.5);  // meridional radius of curvature  
            var eta2 = nu/rho-1;  
            var tanLat = Math.Tan(lat);
            var tan2Lat = tanLat*tanLat;
            var tan4Lat = tan2Lat*tan2Lat;
            var tan6Lat = tan4Lat*tan2Lat;  
            var secLat = 1/cosLat;
            var nu3 = nu*nu*nu;
            var nu5 = nu3*nu*nu;
            var nu7 = nu5*nu*nu;  
            var vii = tanLat/(2*rho*nu);  
            var viii = tanLat/(24*rho*nu3)*(5+3*tan2Lat+eta2-9*tan2Lat*eta2);  
            var ix = tanLat/(720*rho*nu5)*(61+90*tan2Lat+45*tan4Lat);  
            var x = secLat/nu;  
            var xi = secLat/(6*nu3)*(nu/rho+2*tan2Lat);  
            var xii = secLat/(120*nu5)*(5+28*tan2Lat+24*tan4Lat);  
            var xiia = secLat/(5040*nu7)*(61+662*tan2Lat+1320*tan4Lat+720*tan6Lat);
            var dE = (e - e0);
            var dE2 = dE*dE;
            var dE3 = dE2*dE;
            var dE4 = dE2*dE2;
            var dE5 = dE3*dE2;
            var dE6 = dE4*dE2;
            var dE7 = dE5*dE2;  
            lat = lat - vii*dE2 + viii*dE4 - ix*dE6;  
            var lon = lon0 + x*dE - xi*dE3 + xii*dE5 - xiia*dE7;
            latitude = ToDegree(lat);
            londgitude = ToDegree(lon);
        }

        /// <summary>
        /// Convert radians to degreens
        /// </summary>
        /// <param name="angle"></param>
        /// <returns></returns>    
        public static double ToDegree(double angle)
        {
            return Math.PI * angle / 180.0;
        }

    }


    ///<summary>
    ///</summary>
    public enum GeoCodeCalcMeasurement
    {
        ///<summary>
        /// Miles
        ///</summary>
        Miles = 0,
        ///<summary>
        /// Kilometers
        ///</summary>
        Kilometers = 1
    }
}

并使用从我的控制器调用的 CalcDistance(post 是初始邮政编码,store 是另一个邮政编码)

var calcDistance = GeoCodeCalc.CalcDistance(postLong, postLat, storeLong, storeLat);
于 2013-05-31T10:45:12.033 回答
0

这是我如何使用 LINQ over EF 按城市执行类似操作的一小段:

            var cityloc = _db.Cities.Find(t.CityId).location;
            var radius = Int32.Parse(t.CityRadius);
            results = results.Where(x => x.City.location.Distance(cityloc) < radius * metersPerMile || x.city_id == t.CityId);

t.CityId 是您所在的城市,它会查找距离您在 t.CityRadius 英里范围内的结果。Results 是具有 City 类的 city 属性的对象。City 类具有一个 DbGeography 类型的属性 location。

LINQ over SQL 中的空间支持不存在。但是,EF 5 支持它。您可能想查看这篇关于 LINQ over SQL 以及如何在空间类型之间进行转换的帖子:Is it possible to use SqlGeography with Linq to Sql?

好消息是,一旦您将邮政编码表放入一个类中,您就可以使用内置的 .NET 空间内容为您进行距离计算(或者您可以像以前一样手动进行)。

如果您的开发不太深入,您甚至可以考虑将 LINQ over SQL 切换到 LINQ over Entity Frameworks。它非常相似,并且完全支持空间数据类型。

于 2013-05-31T06:42:35.297 回答