¿Cómo uso spatials para buscar un radio de códigos postales?

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

Pregunta

Fondo

Estoy escribiendo una aplicación que encuentra eventos dentro de un radio determinado de un código postal. Puedes pensar en esto como ticketmaster, donde ingresas tu código postal y todos los conciertos en el radio de x aparecen.

Tengo una tabla de base de datos que tiene el código postal, y la latitud y longitud de cada código postal. También tengo una tabla de 'EventListings' donde cada 'Evento' tiene un campo ZipCode.

Problema

Actualmente, estoy usando la fórmula Haversine en una consulta de Linq-to-Entities en mi capa de servicio para encontrar qué eventos están dentro del radio. En este momento, lo estoy usando como un filtro en la cláusula where. También me gustaría ubicarlo en la cláusula de selección para que en el sitio web pueda mostrar "esto está a 4.6 millas de distancia", etc.

No puedo mover este código a un método de C # separado porque Linq-to-Entities se quejará de que no puede convertirlo a SQL, por lo que me deja con la duplicación de toda la fórmula en la declaración de selección. Esto es muy feo. Traté de arreglarlo.

Lo que he intentado

Edité la Entidad y agregué una propiedad escalar especial de "DistanceFromOrigin". Luego creé un procedimiento almacenado que devolvió todos los datos de la entidad, más un valor codificado (para propósitos de prueba) para el nuevo campo "DistanceFromOrigin".

Entonces me di cuenta de que no puedo decirle al marco de la entidad que use mi sproc para su declaración selecta en la entidad EventListings ... Phil sugirió spatials, así que eso fue lo que hice.

Pregunta

¿Cómo puedo usar Spatials para buscar eventos dentro de un radio de códigos postales?

Respuesta aceptada

Entonces, usando la sugerencia de Phil, reescribí mucho de esto usando espaciales. Esto funcionó muy bien, solo necesitas .NET4.5, EF5 + y servidor SQL (creo que en 2008 y más). Estoy usando EF6 + Sql Server 2012.

Preparar

El primer paso fue agregar una columna de Geography a mi tabla de base de datos EventListings (haga clic con el botón derecho en ella -> Diseño). Nombro el mio Ubicación

introduzca la descripción de la imagen aquí

Luego, ya que estoy usando el EDM con la base de datos primero, tuve que actualizar mi modelo para usar el nuevo campo que creé. Luego recibí un error por no poder convertir la Geografía al doble, así que lo que hice para solucionarlo fue seleccionar la propiedad Ubicación en la entidad, ir a sus propiedades y cambiar su tipo de doble a Geography :

introduzca la descripción de la imagen aquí

Consultando dentro de un radio y agregando eventos con ubicaciones

Entonces todo lo que tiene que hacer es consultar su colección de entidades de esta manera:

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

El método de extensión de Distancia obtiene la distancia desde el objeto actual hasta el "otro" objeto que se pasa. Este otro objeto es de tipo DbGeography . Simplemente llama a un método estático y crea uno de estos cachorros, luego simplemente lance su longitud y latitud como un punto:

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

Así no es como creé mis coordinadores de origen. Descargué una base de datos separada que tenía una lista de todos los códigos postales y sus latitudes y longitudes. Agregué una columna de tipo Geography a eso también. Voy a mostrar cómo al final de esta respuesta. Luego consulté el contexto del código postal para obtener un objeto DbGeography del campo Ubicación en la tabla ZipCode.

Si el usuario desea un origen más específico que solo un código postal, hago una llamada a la API de Google Maps (GeoCode para ser más específico) y obtengo la latitud y longitud para la dirección específica de los usuarios a través de un servicio web, y creo un objeto DBGeography desde Los valores de latitud y longitud en la respuesta.

Yo uso las API de Google cuando creo un evento también. Acabo de configurar la variable de ubicación como esta antes de agregar mi entidad a EF:

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

Otros consejos y trucos y solución de problemas

¿Cómo obtuve el Método de Extensión de Distancia?

Para obtener el método de extensión Distance , debe agregar una referencia a su proyecto: System.Data.Entity Después de hacer eso, debe agregar el uso de: using System.Data.Entity.Spatial; a tu clase

El método de extensión de Distance devuelve la distancia con una unidad de medida de metros (puede cambiarlo, creo, pero esto es predeterminado). Aquí, mi parámetro de radio estaba en millas, así que hice algunos cálculos matemáticos para convertir.

Nota : Hay una clase DBGeography en System.Data.Spatial . Esta es la incorrecta y no funcionaría para mí. Muchos de los ejemplos que encontré en internet utilizaron este.

Cómo convertir Lat / Long a Geography Column

Entonces, si era como yo y descargó una base de datos de código postal con todas las columnas de Latitude y Longitude , y luego se dio cuenta de que no tenía una columna de Geografía ... esto podría ayudar:

1) Agregue una columna de ubicación a su tabla. Establece el tipo a Geografía.

2) Ejecutar el siguiente sql

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

Cómo ver los detalles de un elemento de Geografía

Entonces, cuando consulta su tabla de EventListings en Sql Server Management Studio después de haber insertado algunos elementos de DbGeography, verá que la columna Location contiene un valor hexadecimal como: 0x1234513462346. Esto no es muy útil cuando quiere asegurarse de que se insertaron los valores correctos.

Para ver realmente la latitud y longitud fuera de este campo, debe consultarlo así:

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


Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué