2012-10-11 23 views
20

Sto lavorando su un'applicazione in cui ho bisogno di ottenere zona, il mio servizio web riceverà 2 parametri (longitudine decimali, latitudine decimale)Trova closest posizione con la longitudine e la latitudine

Ho una tabella in cui le posizioni vengono salvati nel database con i campi di longitudine e latitudine,

Desidero recuperare le posizioni più vicine.

Qualcuno può aiutare?

Qui è il mio codice:

var locations = from l in locations 

    select l 

Qui ci sono ulteriori dettagli su questo: ho un 2 campi (decimale (18, 2) null) 1 latitudine, 2 longitudine all'interno di una tabella di database,

e ho un metodo di

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(); 
} 
+0

Codice? Molto breve ... Che cosa hai provato con Sofar? – lboshuizen

risposta

4

Ecco Soluzione

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(); 
+0

troppo codice – cyclical

+1

Quali sono i valori costanti? –

+0

Il nome 'ConversionHelper' non esiste nel contesto corrente. ,Come risolvere? –

2

avete un intervallo valido, al di fuori della quale il "colpo" non è davvero rilevante? In tal caso, utilizzare

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 

poi trovare il successo con il più piccolo valore della distanza al quadrato all'interno di un ciclo di tali risultati.

+0

Non penso che il confronto della distanza euclidea si applichi alla longitudine/latitudine. – Fung

+0

Per intervalli piccoli (<3 in lat/lng coords), dovrebbe andare bene ... –

+0

lat e long non sono quadrati, vicino all'equatore forse, ma non da nessun'altra parte ... – sasjaq

44

È possibile prima convertire i dati di posizione nel database in System.Device.Location.GeoCoordinate, quindi utilizzare LINQ per trovare quello più vicino.

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

+1 Bella scoperta! Ho dovuto aggiungere comunque un riferimento a 'System.Device', ma funziona benissimo! bel codice, grazie! –

+0

Risposta brillante, questo. +1. –

+1

Le posizioni devono essere in memoria? in altre parole: è possibile farlo in puro Linq-To-Entities (tradotto in SQL)? – sports

2

Per elaborare il commento di @Fung, se si utilizza Entity Framework/LINQ to Entities, se si tenta di utilizzare il metodo GeoCoordinate.GetDistanceTo in una query LINQ, si otterrà un NotSupportedException runtime con il messaggio:

LINQ to Entities non riconosce il metodo di 'Double GetDistanceTo (System.Device.Location.GeoCoordinate)' metodo, e questo metodo non può essere tradotto in un'espressione negozio.

Con Entity Framework versione 5 o 6, un'alternativa è utilizzare la classe System.Data.Spatial.DbGeography. Per esempio:

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 in questo esempio è l'istanza DbContext precedentemente creata un'istanza.

Sebbene sia attualmente undocumented in MSDN, le unità restituite dal metodo DbGeography.Distance sembrano essere metri. Vedi: System.Data.Spatial DbGeography.Distance units?

1
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(); 
Problemi correlati