Wie verwende ich Spatials, um einen Radius von Postleitzahlen zu suchen?

c# entity-framework entity-framework-6 sql-server-2012

Frage

Hintergrund

Ich schreibe eine Anwendung, die Ereignisse innerhalb eines bestimmten Radius einer Postleitzahl findet. Sie können sich das wie Ticketmaster vorstellen, wo Sie Ihre Postleitzahl eingeben und alle Konzerte im Umkreis von x erscheinen.

Ich habe eine Datenbanktabelle, die die Postleitzahl hat, und jede Postleitzahl Latitude und Longitude. Ich habe auch eine "EventListings" -Tabelle, in der jedes "Event" ein ZipCode-Feld hat.

Problem

Derzeit verwende ich die Haversine-Formel in einer Linq-to-Entities-Abfrage in meiner Serviceebene, um zu ermitteln, welche Ereignisse innerhalb des Radius liegen. Im Moment verwende ich es als Filter in der Where-Klausel. Ich möchte es auch in die Auswahlklausel setzen, also auf der Website, die ich zeigen kann "das ist 7,4 Meilen entfernt", usw.

Ich kann diesen Code nicht in eine separate C # -Methode verschieben, da sich Linq-to-Entities darüber beschweren, dass er sie nicht in sql konvertieren kann, sodass ich die gesamte Formel auch in der select-Anweisung duplizieren kann. Das ist sehr hässlich. Ich habe versucht, es zu beheben.

Was ich versucht habe

Ich habe die Entität bearbeitet und eine spezielle skalare Eigenschaft von "DistanceFromOrigin" hinzugefügt. Ich habe dann eine gespeicherte Prozedur erstellt, die alle Entitätsdaten plus einen hartcodierten Wert (zu Testzwecken) für das neue Feld "DistanceFromOrigin" zurückgebracht hat.

Ich kam dann zu der Erkenntnis, dass ich Entity Framework nicht sagen kann, dass ich meinen Sproc für seine Select-Anweisung in der EventListings-Entity verwenden soll ... Phil schlug Spatials vor, also das, womit ich gegangen bin.

Frage

Wie kann ich mithilfe von Spatials nach Ereignissen in einem Umkreis von Postleitzahlen suchen?

Akzeptierte Antwort

Also, unter Verwendung von Phils Vorschlag, habe ich viel davon mit Spatials neu geschrieben. Das hat super geklappt, man braucht nur .NET4.5, EF5 + und SQL Server (2008 und darüber glaube ich). Ich benutze EF6 + Sql Server 2012.

Konfiguration

Der erste Schritt bestand darin, meiner Datenbank EventListings eine Geography Spalte EventListings (Rechtsklick darauf -> Design). Ich nannte meinen Standort:

Bildbeschreibung hier eingeben

Als nächstes, da ich den EDM mit Datenbank zuerst verwende, musste ich mein Modell aktualisieren, um das neue Feld zu verwenden, das ich erstellt habe. Dann habe ich einen Fehler erhalten, dass ich Geografie nicht in Double konvertieren konnte. Also habe ich die Location-Eigenschaft in der Entity ausgewählt, gehe zu den Eigenschaften und ändere den Typ von Double in Geography :

Bildbeschreibung hier eingeben

Abfragen innerhalb eines Radius und Hinzufügen von Ereignissen mit Standorten

Dann müssen Sie nur Ihre Entitätssammlung wie folgt abfragen:

 var events = EventRepository.EventListings
                             .Where(x => x.Location.Distance(originCoordinates) * 0.00062 <= radiusParam); 

Die Entfernungsverlängerungsmethode erhält den Abstand vom aktuellen Objekt zum anderen Objekt, das Sie übergeben. Dieses andere Objekt ist vom Typ DbGeography . Sie rufen einfach eine statische Methode auf und erstellen einen dieser Welpen, dann werfen Sie einfach Ihren Längen- und Breitengrad hinein:

DBGeography originCoordinates = DBGeography.fromText("Point(" + originLongitude + " " + originLatitude + ")");

So habe ich meine originCoordinates nicht erstellt. Ich lud eine separate Datenbank herunter, die eine Liste aller Postleitzahlen und ihrer Breiten- und Längengrade enthielt. Ich fügte auch eine Spalte vom Typ Geography hinzu. Ich werde zeigen, wie am Ende dieser Antwort. Ich habe dann den Postleitzahlkontext abgefragt, um ein DbGeography-Objekt aus dem Feld Ort in der ZipCode-Tabelle zu erhalten.

Wenn der Benutzer eine spezifischere Herkunft als nur eine Postleitzahl möchte, rufe ich das Google Maps-API (GeoCode, um genauer zu sein) an und erhalte den Breiten- und Längengrad für die benutzerspezifische Adresse über einen Webservice und erstelle ein DBGeography-Objekt die Breiten- und Längengradwerte in der Antwort.

Ich verwende Google APIs, wenn ich auch ein Ereignis erstelle. Ich setze einfach die Standortvariable so, bevor ich meine Entität zu EF hinzufüge:

someEventEntity.Location = DBGeography.fromText("Point(" + longitudeFromGoogle+ " " + latitudeFromGoogle + ")");

Weitere Tipps & Tricks & Fehlerbehebung

Wie habe ich die Entfernungsmethode erhalten?

Um die Erweiterungsmethode Distance , müssen Sie einen Verweis auf Ihr Projekt hinzufügen: System.Data.Entity Nachdem Sie das getan haben, müssen Sie Folgendes hinzufügen: using System.Data.Entity.Spatial; zu deiner Klasse.

Die Distance gibt die Entfernung mit einer Maßeinheit von Metern zurück (Sie können es ändern, denke ich, aber das ist Standard). Hier war mein Radius-Parameter in Meilen, also habe ich etwas Mathe zum Umrechnen verwendet.

Hinweis : Es gibt eine DBGeography Klasse in System.Data.Spatial . Das ist falsch und es würde nicht für mich funktionieren. Viele Beispiele, die ich im Internet gefunden habe, nutzten diese.

Wie man Lat / Long in eine Geografie-Spalte konvertiert

Also, wenn Sie wie ich waren und eine Postleitzahlen-Datenbank mit allen Latitude & Longitude Spalten heruntergeladen haben und dann festgestellt haben, dass es keine Geography-Spalte hat, könnte das helfen:

1) Fügen Sie Ihrer Tabelle eine Standortspalte hinzu. Legen Sie den Typ auf Geografie fest.

2) Führen Sie die folgenden SQL-Anweisungen aus

UPDATE [ZipCodes]
SET Location = geography::STPointFromText('POINT(' + CAST([Longitude] AS VARCHAR(20)) + ' ' + CAST([Latitude] AS VARCHAR(20)) + ')', 4326)

So zeigen Sie die Details eines Geografieelements an

Wenn Sie Ihre EventListings-Tabelle in Sql Server Management Studio abfragen, nachdem Sie einige DbGeography-Elemente eingefügt haben, sehen Sie, dass die Spalte Location einen hexadezimalen Wert wie folgt enthält: 0x1234513462346. Dies ist nicht sehr hilfreich, wenn Sie sicherstellen möchten, dass die richtigen Werte eingefügt wurden.

Um den Breiten- und Längengrad dieses Feldes tatsächlich anzuzeigen, müssen Sie es wie folgt abfragen:

SELECT Location.Lat Latitude, Location.Long Longitude
FROM [EventListings]


Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum
Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum