Quindi, usando il suggerimento di Phil, ho riscritto molto di questo usando spaziali. Ha funzionato alla grande, hai solo bisogno di .NET4.5, EF5+ e sql server (credo dal 2008 in poi). Sto usando EF6 + SQL Server 2012.
Impostazione
Il primo passo è stato aggiungere una Geography
colonna al mio database EventListings
tabella (fai clic destro su di essa -> Design). Ho chiamato la mia Località:
Successivamente, poiché sto utilizzando l'EDM con il database prima, ho dovuto aggiornare il mio modello per utilizzare il nuovo campo che ho creato. Quindi ho ricevuto un errore relativo all'impossibilità di convertire Geografia in double, quindi quello che ho fatto per risolverlo è stato selezionare la proprietà Posizione nell'entità, andare alle sue proprietà e cambiarne il tipo da double a Geography
:
Interrogazione entro un raggio e aggiunta di eventi con posizioni
Quindi tutto ciò che devi fare è interrogare la tua raccolta di entità in questo modo:
var events = EventRepository.EventListings
.Where(x => x.Location.Distance(originCoordinates) * 0.00062 <= radiusParam);
Il metodo di estensione della distanza ottiene la distanza dall'oggetto corrente, all'"altro" oggetto in cui si passa. Quest'altro oggetto è di tipo DbGeography
. Chiama semplicemente un metodo statico e crea uno di questi cuccioli, quindi inserisci la tua longitudine e latitudine come punto:
DBGeography originCoordinates = DBGeography.fromText("Point(" + originLongitude + " " + originLatitude + ")");
Non è così che ho creato le mie OriginCoordinates. Ho scaricato un database separato che conteneva un elenco di tutti i codici postali e le loro latitudini e longitudini. Ho aggiunto una colonna di tipo Geography
anche a quello. Mostrerò come alla fine di questa risposta. Ho quindi interrogato il contesto del codice postale per ottenere un oggetto DbGeography dal campo Posizione nella tabella Codice postale.
Se l'utente desidera un'origine più specifica rispetto a un semplice codice postale, faccio una chiamata all'API di Google Maps (GeoCode per essere più specifici) e ottengo la latitudine e la longitudine per l'indirizzo specifico dell'utente tramite un servizio web e creo un oggetto DBGeography da i valori di latitudine e longitudine nella risposta.
Uso anche le API di Google quando creo un evento. Ho appena impostato la variabile di posizione in questo modo prima di aggiungere la mia entità a EF:
someEventEntity.Location = DBGeography.fromText("Point(" + longitudeFromGoogle+ " " + latitudeFromGoogle + ")");
Altri suggerimenti, trucchi e risoluzione dei problemi
Come ho ottenuto il metodo di estensione della distanza?
Per ottenere il metodo di estensione Distance
devi aggiungere un riferimento al tuo progetto:System.Data.Entity
Dopo averlo fatto, devi aggiungere using:using System.Data.Entity.Spatial;
alla tua classe
La Distance
metodo di estensione restituisce la distanza con un'unità di misura dei metri (Puoi cambiarlo credo, ma questo è predefinito). Qui, il mio parametro del raggio era in miglia, quindi ho fatto dei calcoli per convertire.
Nota :C'è un DBGeography
classe in System.Data.Spatial
. Questo è quello sbagliato e non funzionerebbe per me. Molti esempi che ho trovato su Internet hanno usato questo.
Come convertire Lat/Long in Colonna Geografia
Quindi, se tu fossi come me e scaricassi un database di codici postali con tutto il Latitude
&Longitude
colonne, e poi ho capito che non aveva una colonna Geografia... questo potrebbe aiutare:
1) Aggiungi una colonna Posizione alla tua tabella. Imposta il tipo su Geografia.
2) Esegui il seguente sql
UPDATE [ZipCodes]
SET Location = geography::STPointFromText('POINT(' + CAST([Longitude] AS VARCHAR(20)) + ' ' + CAST([Latitude] AS VARCHAR(20)) + ')', 4326)
Come visualizzare i dettagli di un elemento Geografia
Quindi, quando esegui una query sulla tabella EventListings in Sql Server Management Studio dopo aver inserito alcuni elementi di DbGeography, vedrai il Location
la colonna contiene un valore esadecimale come:0x1234513462346. Questo non è molto utile quando vuoi assicurarti che siano stati inseriti i valori corretti.
Per visualizzare effettivamente la latitudine e la longitudine fuori da questo campo, devi interrogarlo in questo modo:
SELECT Location.Lat Latitude, Location.Long Longitude
FROM [EventListings]