2013-01-18 15 views
7

Veritabanım var ve içinde gps koordinatlarına sahip bir sınıf otel var. Seçtiğim koordinatlara en yakın yer almak istiyorum. Ben bu gibi görünmelidir düşünüyorumC# - LINQ - GPS enlemesine ve boylamına göre en kısa mesafe

(Burada birçok örnek kodları buldum ve bunun gibi):

: Ben bir şey aramak çalıştığımızda

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); 

sorun bu hatayı olması

Sadece parametresiz kurucular ve ilklendiriciler

bunu bir google çalıştı LıNQ varlıkları desteklenir d Ben bu linq'u ikiye bölmenin bana yardımcı olabileceğini buldum ama nasıl emin değilim. Yardım için teşekkürler. O yöntemin uygulanmasını sağlayabilir,

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); 

Ama büyük olasılıkla GetDistanceTo yöntemiyle kaynaklanan sorunları olacaktır:

+0

* Bu uygulama kullanarak başarı hakkında makul oldu. Çok fazla iş olmadan işe yaramayacağından şüpheleniyorum. Muhtemelen depolanmış bir proc veya bir UDF istersiniz. –

cevap

6

yerine parametreli yapıcı nesne başlatıcı kullanabilir?

+0

Evet, haklısın. Problemimi parametresiz kurucuyla çözdünüz ama bu yöntemle ilgili bir sorunum var. Bunu itham etmiyorum. GeoCoordinate sınıfı tarafından sağlanan yöntemdir. Linq'de sadece birkaç aritmetik işlemin olduğu yerde bulduğum için onları kullanmalıyım ve bunun için bir algoritma buldum, doğru muyum? –

+0

@Bibo Oh, GeoCoordinate'in bir çerçeve sınıfı olduğunu bilmiyordum. Linq to Entities, daha sonra bir IQueryable 'da ToList() öğesini çağırdıktan sonra veritabanına karşı çalıştırılacak bir SQL betiği oluşturur, bu nedenle SQL veri manipülasyon dilinin izin verdiği birkaç işlemle sınırlısınız demektir. – TKharaishvili

+0

@Bibo aşağıdaki iki sınıfı yararlı bulabilirsiniz: http://msdn.microsoft.com/en-us/library/system.data.objects.entityfunctions.aspx http://msdn.microsoft.com/ tr-us/library/system.data.objects.sqlclient.sqlfunctions.aspx – TKharaishvili

4

Burada şimdilik kullandığım çözümümüzü gönderin. Ama ben GwynnBliedd cevabını seçiyorum çünkü söz konusu problemi çözüyor.

Sınıfıma DbGeography türü ekledim ve veritabanında enlem ve boylamı kaydetmek yerine kullanıyorum.

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

Sonra linq kullanımı çok kolaydır: Şimdi bu çalışıyor çözüm için

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; 

ve iyidir. Bir süredir bunu kullanıyorum ama az sayıda kullanıcı burada dediğim gibi Haversine formülü (this answer gibi) uygulayarak UDF'yi deneyebilirim.

+0

+1 - DbGeography sınıfının işaretçisi için teşekkürler! Ancak, FromText yönteminde tersine çevrilmiş enlem ve boylam argümanlarının sırasına sahip olduğunuzu düşünüyorum; bkz: http://stackoverflow.com/questions/16546598/dbgeography-pointfromtext-throws-latitude-values-must-be-between-90-and-90-d –

3

ben * veritabanında şu anda aritmetik yapmaya çalışıyor Haversine Distance formül

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);