Donc, en utilisant la suggestion de Phil, j'ai réécrit une grande partie de cela en utilisant des éléments spatiaux. Cela a très bien fonctionné, vous avez juste besoin de .NET4.5, EF5 + et serveur sql (2008 et supérieur, je crois). J'utilise EF6 + SQL Server 2012.
Configuration
La première étape consistait à ajouter une Geography
colonne à ma base de données EventListings
tableau (Clic droit dessus -> Design). J'ai nommé le mien Emplacement :
Ensuite, puisque j'utilise l'EDM avec la base de données d'abord, j'ai dû mettre à jour mon modèle pour utiliser le nouveau champ que j'ai créé. Ensuite, j'ai reçu une erreur concernant l'impossibilité de convertir la géographie en double, donc ce que j'ai fait pour y remédier a été de sélectionner la propriété Location dans l'entité, d'accéder à ses propriétés et de changer son type de double à Geography
:
Requête dans un rayon et ajout d'événements avec des emplacements
Ensuite, tout ce que vous avez à faire est d'interroger votre collection d'entités comme ceci :
var events = EventRepository.EventListings
.Where(x => x.Location.Distance(originCoordinates) * 0.00062 <= radiusParam);
La méthode d'extension Distance obtient la distance entre l'objet actuel et "l'autre" objet que vous transmettez. Cet autre objet est de type DbGeography
. Vous appelez simplement une méthode statique et elle crée l'un de ces chiots, puis jetez-y simplement votre longitude et votre latitude en tant que point :
DBGeography originCoordinates = DBGeography.fromText("Point(" + originLongitude + " " + originLatitude + ")");
Ce n'est pas ainsi que j'ai créé mon originCoordinates. J'ai téléchargé une base de données séparée contenant une liste de tous les codes postaux et leurs latitudes et longitudes. J'ai ajouté une colonne de type Geography
à cela aussi. Je montrerai comment à la fin de cette réponse. J'ai ensuite interrogé le contexte du code postal pour obtenir un objet DbGeography à partir du champ Emplacement de la table Code postal.
Si l'utilisateur souhaite une origine plus spécifique qu'un simple code postal, j'appelle l'API Google Maps (GeoCode pour être plus précis) et j'obtiens la latitude et la longitude de l'adresse spécifique de l'utilisateur via un service Web, et je crée un objet DBGeography à partir de les valeurs de latitude et de longitude dans la réponse.
J'utilise également les API Google lorsque je crée un événement. Je viens de définir la variable d'emplacement comme ceci avant d'ajouter mon entité à EF :
someEventEntity.Location = DBGeography.fromText("Point(" + longitudeFromGoogle+ " " + latitudeFromGoogle + ")");
Autres trucs et astuces et dépannage
Comment ai-je obtenu la méthode d'extension de distance ?
Pour obtenir la méthode d'extension Distance
vous devez ajouter une référence à votre projet :System.Data.Entity
Après cela, vous devez ajouter l'utilisation :using System.Data.Entity.Spatial;
à votre classe.
La Distance
la méthode d'extension renvoie la distance avec une unité de mesure de mètres (Vous pouvez le changer je pense, mais c'est par défaut). Ici, mon paramètre de rayon était en miles, j'ai donc fait quelques calculs pour convertir.
Remarque :Il existe une DBGeography
classe dans System.Data.Spatial
. C'est le mauvais et cela ne fonctionnerait pas pour moi. Beaucoup d'exemples que j'ai trouvés sur Internet utilisent celui-ci.
Comment convertir lat/long en colonne géographique
Donc, si vous étiez comme moi et que vous téléchargiez une base de données de codes postaux avec tous les Latitude
&Longitude
colonnes, puis j'ai réalisé qu'il n'y avait pas de colonne Géographie... cela pourrait aider :
1) Ajoutez une colonne Emplacement à votre tableau. Définissez le type sur Géographie.
2) Exécutez le sql suivant
UPDATE [ZipCodes]
SET Location = geography::STPointFromText('POINT(' + CAST([Longitude] AS VARCHAR(20)) + ' ' + CAST([Latitude] AS VARCHAR(20)) + ')', 4326)
Comment afficher les détails d'un élément géographique
Ainsi, lorsque vous interrogez votre table EventListings dans Sql Server Management Studio après avoir inséré des éléments DbGeography, vous verrez le Location
colonne contient une valeur hexadécimale comme :0x1234513462346. Ce n'est pas très utile lorsque vous voulez vous assurer que les bonnes valeurs ont été insérées.
Pour réellement afficher la latitude et la longitude de ce champ, vous devez l'interroger comme suit :
SELECT Location.Lat Latitude, Location.Long Longitude
FROM [EventListings]