2013-01-18 23 views
7

Mam bazę danych, aw niej mam hotel klasy ze współrzędnymi GPS. Chcę znaleźć najbliższe miejsca dla współrzędnych, które wybiorę.C# - LINQ - najkrótsza odległość według szerokości i długości geograficznej GPS

myślę, że powinno to wyglądać tak (Znalazłem wiele przykładowych kodów tutaj i jak ten):

var coord = new GeoCoordinate(latitude, longitude); 
var nearest = (from h in db.hotels 
       let geo = new GeoCoordinate(h.gps.lat, h.gps.lng) 
       orderby geo.GetDistanceTo(coord) 
       select h).Take(10); 

Problem polega na tym, że mam ten błąd, gdy starałem się szukać czegoś:

tylko konstruktorzy bez parametrów i inicjalizatory są obsługiwane w LINQ do podmiotów

próbowałem go to google d Zauważyłem, że podzielenie tego linq na dwoje może mi pomóc, ale nie jestem pewien jak. Dzięki za pomoc.

+0

Haversine Distance który jest aktualnie próbuje zrobić arytmetykę * w bazie danych *. Podejrzewam, że to nie zadziała bez ciężkiej pracy. Prawdopodobnie chcesz przechowywać proc lub UDF. –

Odpowiedz

6

Można użyć inicjator obiektu zamiast parametryzowanego konstruktora:

var nearest = (from h in db.hotels 
      let geo = new GeoCoordinate{ Latitude = h.gps.lat, Longitude = h.gps.lng} 
      orderby geo.GetDistanceTo(coord) 
      select h).Take(10); 

Ale prawdopodobnie będziesz mieć problemy spowodowane metodą GetDistanceTo, można zapewnić wdrożenie tej metody?

+0

Tak, masz rację. Rozwiązałeś mój problem z konstruktorem bez parametrów, ale mam problem z tą metodą. Nie jestem tego impelementem. Jest to metoda dostarczona przez klasę GeoCoordinate. Jak znalazłem w innym miejscu, że mam tylko kilka operacji arytmetycznych pozwalam w Linq, więc muszę ich użyć i znaleźć dla nich jakiś algorytm, czy mam rację? –

+0

@Bibo Oh, nie wiedziałem, że GeoCoordinate była klasą szkieletową. Linq to Entity konstruuje wewnętrznie skrypt SQL, który później zostanie wykonany przeciwko bazie danych, gdy na przykład wywołasz ToList() na IQueryable , więc masz rację, że jesteś ograniczony do kilku operacji, które pozwala język manipulacji danymi SQL. – TKharaishvili

+0

@Bibo Pomocne mogą okazać się następujące dwie klasy: http://msdn.microsoft.com/en-us/library/system.data.objects.entityfunctions.aspx http://msdn.microsoft.com/ pl-us/library/system.data.objects.sqlclient.sqlfunctions.aspx – TKharaishvili

4

Zamieszczam tutaj moje rozwiązanie, którego używam na razie. Ale wybieram odpowiedź GwynnBliedd, ponieważ rozwiązuje on mój problem.

Dodałem typ DbGeography do mojej klasy i używam go zamiast zapisywać szerokość i długość geograficzną w bazie danych.

locationField = DbGeography.FromText(String.Format("POINT({0} {1})", orig.gps.lat.ToString().Replace(",", "."), orig.gps.lng.ToString().Replace(",", "."))); 

Wtedy to bardzo łatwy w użyciu LINQ:

var coord = DbGeography.FromText(String.Format("POINT({0} {1})", latitude.ToString().Replace(",", "."), longitude.ToString().Replace(",", "."))); 
      var nearest = (from h in db.hotels 
          where h.location != null 
          orderby h.location.Distance(coord) 
          select h).Take(limit); 
      return nearest; 

Dla roztworu teraz to działa i to dobrze. Przez jakiś czas używałbym tego, ale jak powiedział niewielu użytkowników, może spróbuję UDF z implementacją formuły Haversine (jak w this answer).

+0

+1 - Dziękujemy za wskazówkę do klasy DbGeography! Jednakże: myślę, że masz odwróconą kolejność argumentów długości i szerokości geograficznej w metodzie FromText; zobacz: http://stackoverflow.com/questions/16546598/dbgeography-pointfromtext-throws-latitude-values-must-be-between-90-and-90-d –

3

miałem rozsądną o użyciu tego sukcesu wdrożenia wzoru

var startPoint = new { Latitude = 1.123, Longitude = 12.3 }; 

var closest = entities.Something.OrderBy(x => 12742 * SqlFunctions.Asin(SqlFunctions.SquareRoot(SqlFunctions.Sin(((SqlFunctions.Pi()/180) * (x.Latitude - startPoint.Latitude))/2) * SqlFunctions.Sin(((SqlFunctions.Pi()/180) * (x.Latitude - startPoint.Latitude))/2) + 
            SqlFunctions.Cos((SqlFunctions.Pi()/180) * startPoint.Latitude) * SqlFunctions.Cos((SqlFunctions.Pi()/180) * (x.Latitude)) * 
            SqlFunctions.Sin(((SqlFunctions.Pi()/180) * (x.Longitude - startPoint.Longitude))/2) * SqlFunctions.Sin(((SqlFunctions.Pi()/180) * (x.Longitude - startPoint.Longitude))/2)))).Take(5); 
Powiązane problemy