【问题标题】:Find closest location with longitude and latitude查找具有经度和纬度的最近位置
【发布时间】:2012-10-11 08:58:34
【问题描述】:

我正在开发一个需要获取附近位置的应用程序, 我的网络服务将接收 2 个参数(十进制经度,十进制纬度)

我有一个表格,其中位置保存在带有经度和纬度字段的数据库中,

我想检索最近的位置。

谁能帮忙?

这是我的代码:

 var locations = from l in locations

     select l

以下是有关此的更多详细信息: 我在数据库表中有 2 个字段(十进制(18, 2)空)1 个纬度,2 个经度,

我有一个方法

public List<Locations>  GetLocation(decimal? Long, decimal? lat) 
{
var Loc = from l in Locations
  //// now here is how to get nearest location ? how to query?
  //// i have also tried Math.Abs(l.Lat - lat) its giving error about nullable decimal always hence i have seted decimal to nullable or converted to nullable
 //// also i have tried where (l.lat - Lat) * (l.lon - Long)  this is also giving error about can not convert decimal to bool
return Loc.ToList();
}

【问题讨论】:

  • 代码?非常简短...到目前为止,您尝试过什么?

标签: c# linq


【解决方案1】:

您可以先将数据库中的位置数据转换为System.Device.Location.GeoCoordinate,然后使用LINQ查找最近的。

var coord = new GeoCoordinate(latitude, longitude);
var nearest = locations.Select(x => new GeoCoordinate(x.Latitude, x.Longitude))
                       .OrderBy(x => x.GetDistanceTo(coord))
                       .First();

【讨论】:

  • +1 不错的发现!我不得不添加对System.Device 的引用,但效果很好!很好的代码谢谢!
  • 位置是否需要在内存中?换句话说:是否有可能在纯 Linq-To-Entities(翻译成 SQL)中做到这一点?
  • @sports 不,我认为 LINQ to Entities 提供程序不支持GeoCoordinate 类型和GetDistanceTo 方法。我读到 EF6 现在支持空间类型,DbGeography.Distance 可能就是您要找的。​​span>
  • 根据this answer,应该在OrderBy之前调用AsEnuemrable()方法,它确实有效:D
  • 在哪里可以找到要在 VS 2017 中安装的 System.Device 库 [dll]?
【解决方案2】:

为了详细说明@Fung 的评论,如果您使用实体框架/LINQ to Entities,如果您尝试在 LINQ 查询中使用GeoCoordinate.GetDistanceTo 方法,您将收到运行时 NotSupportedException 消息:

LINQ to Entities 无法识别方法 'Double GetDistanceTo(System.Device.Location.GeoCoordinate)' 方法,并且该方法无法转换为存储表达式。

对于 Entity Framework 版本 5 或 6,另一种方法是使用 System.Data.Spatial.DbGeography 类。例如:

DbGeography searchLocation = DbGeography.FromText(String.Format("POINT({0} {1})", longitude, latitude));

var nearbyLocations = 
    (from location in _context.Locations
     where  // (Additional filtering criteria here...)
     select new 
     {
         LocationID = location.ID,
         Address1 = location.Address1,
         City = location.City,
         State = location.State,
         Zip = location.Zip,
         Latitude = location.Latitude,
         Longitude = location.Longitude,
         Distance = searchLocation.Distance(
             DbGeography.FromText("POINT(" + location.Longitude + " " + location.Latitude + ")"))
     })
    .OrderBy(location => location.Distance)
    .ToList();

_context 在此示例中是您之前实例化的 DbContext 实例。

虽然当前是undocumented in MSDN,但DbGeography.Distance 方法返回的单位似乎是米。见:System.Data.Spatial DbGeography.Distance units?

【讨论】:

  • 关于距离类型以米而不是英里为单位的注释似乎是一个非常好的信息。感谢您的关注。
【解决方案3】:

解决办法

var constValue = 57.2957795130823D

var constValue2 = 3958.75586574D;

var searchWithin = 20;

double latitude = ConversionHelper.SafeConvertToDoubleCultureInd(Latitude, 0),
                    longitude = ConversionHelper.SafeConvertToDoubleCultureInd(Longitude, 0);
var loc = (from l in DB.locations
let temp = Math.Sin(Convert.ToDouble(l.Latitude) / constValue) *  Math.Sin(Convert.ToDouble(latitude) / constValue) +
                                 Math.Cos(Convert.ToDouble(l.Latitude) / constValue) *
                                 Math.Cos(Convert.ToDouble(latitude) / constValue) *
                                 Math.Cos((Convert.ToDouble(longitude) / constValue) - (Convert.ToDouble(l.Longitude) / constValue))
                             let calMiles = (constValue2 * Math.Acos(temp > 1 ? 1 : (temp < -1 ? -1 : temp)))
                             where (l.Latitude > 0 && l.Longitude > 0)
                             orderby calMiles

select new location
  {
     Name = l.name
  });
  return loc .ToList();

【讨论】:

  • 什么是常数值?
  • 名称ConversionHelper 在当前上下文中不存在。 ,如何解决?
  • l.Latitude > 0 && l.Longitude > 0 仅适用于北部位置和格林威治子午线以西 您可以从 >0 更改为 != 0
【解决方案4】:

您是否有一个有效范围,在该范围之外“命中”并不真正相关?如果是这样,请使用

from l in locations where ((l.lat - point.lat) * (l.lat - point.lat)) + ((l.lng - point.lng) * (l.lng - point.lng)) < (range * range) select l

然后在这些结果的循环中找到具有最小平方距离值的命中。

【讨论】:

  • 我认为欧几里得距离比较不适用于经度/纬度。
  • @sasjaq - 是的,但是如果我们谈论这个星球上的纬度和经度点,并且考虑的两点之间的距离很短,因此直线距离和环绕的距离地球的曲率有 1% 的差异,平坦的近似值足以对离查询点最近的已知点执行查询。
  • 如何获得500米的范围?
【解决方案5】:
var objAllListing = (from listing in _listingWithLanguageRepository.GetAll().Where(z => z.IsActive == true)
                                     let distance = 12742 * SqlFunctions.Asin(SqlFunctions.SquareRoot(SqlFunctions.Sin(((SqlFunctions.Pi() / 180) * (listing.Listings.Latitude - sourceLatitude)) / 2) * SqlFunctions.Sin(((SqlFunctions.Pi() / 180) * (listing.Listings.Latitude - sourceLatitude)) / 2) +
                                                        SqlFunctions.Cos((SqlFunctions.Pi() / 180) * sourceLatitude) * SqlFunctions.Cos((SqlFunctions.Pi() / 180) * (listing.Listings.Latitude)) *
                                                        SqlFunctions.Sin(((SqlFunctions.Pi() / 180) * (listing.Listings.Longitude - sourceLongitude)) / 2) * SqlFunctions.Sin(((SqlFunctions.Pi() / 180) * (listing.Listings.Longitude - sourceLongitude)) / 2)))
                                     where distance <= input.Distance

                                     select new ListingFinalResult { ListingDetail = listing, Distance = distance }).ToList();//.Take(5).OrderBy(x => x.distance).ToList();

【讨论】:

    【解决方案6】:

    一个对 netcore 友好的解决方案。根据需要进行重构。

    public static System.Drawing.PointF getClosestPoint(System.Drawing.PointF[] points, System.Drawing.PointF query) {
        return points.OrderBy(x => distance(query, x)).First();
    }
    
    public static double distance(System.Drawing.PointF pt1, System.Drawing.PointF pt2) {
        return Math.Sqrt((pt2.Y - pt1.Y) * (pt2.Y - pt1.Y) + (pt2.X - pt1.X) * (pt2.X - pt1.X));
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-08-14
      • 1970-01-01
      • 1970-01-01
      • 2017-11-10
      • 2016-10-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多