Как использовать пространственные данные для поиска радиуса почтовых индексов?

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

Вопрос

Задний план

Я пишу приложение, которое находит события в определенном радиусе почтового индекса. Вы можете думать об этом как ticketmaster, где вы вводите свой почтовый индекс, и все концерты в радиусе x отображаются.

У меня есть таблица базы данных, в которой есть почтовый индекс, и каждый почтовый индекс «Широта и долгота». У меня также есть таблица EventListings, где каждое «событие» имеет поле ZipCode.

проблема

В настоящее время я использую формулу Haversine в запросе Linq-to-Entities на моем уровне сервиса, чтобы определить, какие события находятся в радиусе. Прямо сейчас, я использую его как фильтр в предложении where. Я также хотел бы разместить его в предложении select, поэтому на веб-сайте, который я могу показать «это на расстоянии в 4,6 мили» и т. Д.

Я не могу переместить этот код в отдельный метод C #, потому что Linq-to-Entities будет жаловаться, что он не может преобразовать его в sql, так что это оставляет меня с дублированием всей формулы в инструкции select. Это очень уродливо. Я попытался это исправить.

Что я пробовал

Я редактировал Entity и добавил специальное скалярное свойство «DistanceFromOrigin». Затем я создал хранимую процедуру, которая вернула все данные сущности, а также твердое кодированное значение (для целей тестирования) для нового поля «DistanceFromOrigin».

Затем я понял, что я не могу сказать сущностной структуре использовать мой sproc для своего оператора select в объекте EventListings ... Фил предложил пространственные данные, так что с этим я пошел.

Вопрос

Как я могу использовать Spatials для поиска событий в радиусе почтовых индексов?

Принятый ответ

Итак, используя предложение Фила, я много раз переписывал это с использованием пространств. Это получилось отлично, вам просто нужны .NET4.5, EF5 + и sql-сервер (2008 и выше, я считаю). Я использую EF6 + Sql Server 2012.

Настроить

Первым шагом было добавление столбца Geography в мою базу данных Таблица EventListings (щелкните правой кнопкой мыши по ней -> Дизайн). Я назвал мое Место:

введите описание изображения здесь

Затем, поскольку я использую EDM с базой данных - сначала мне пришлось обновить мою модель, чтобы использовать новое поле, которое я создал. Затем я получил ошибку в том, что я не смог преобразовать географию в двойную, поэтому я решил исправить ее, выбрав свойство Location в объекте, перейдите к его свойствам и измените его тип с двойным на Geography :

введите описание изображения здесь

Запрос в радиус и добавление событий с местоположениями

Тогда все, что вам нужно сделать, - это запросить свою коллекцию объектов следующим образом:

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

Метод расширения расстояния получает расстояние от текущего объекта до объекта «другого», который вы передаете. Этот другой объект имеет тип DbGeography . Вы просто вызываете статический метод, и он создает одного из этих щенков, а затем просто бросайте в него свою долготу и широту как точку:

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

Это не то, как я создал свое происхождение. Я загрузил отдельную базу данных, в которой был список всех почтовых индексов и их широты и долготы. Я добавил к этому столбцу типа Geography . Я покажу, как в конце этого ответа. Затем я запросил контекст zipcode, чтобы получить объект DbGeography из поля Location в таблице ZipCode.

Если пользователю требуется более конкретное происхождение, а не только почтовый индекс, я обращаюсь к API Карт Google (более конкретно к GeoCode) и получаю широту и долготу для конкретного адреса пользователей через веб-службу и создаю объект DBGeography из значения широты и долготы в ответе.

Я также использую API Google при создании события. Я просто установил переменную местоположения, как это, прежде чем добавить мою сущность в EF:

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

Другие советы и рекомендации и устранение неполадок

Как я получил метод расширения расстояния?

Чтобы получить метод расширения Distance вы должны добавить ссылку на свой проект: System.Data.Entity После этого вы должны добавить использование: using System.Data.Entity.Spatial; к вашему классу.

Метод расширения Distance возвращает расстояние с единицей измерения метров (вы можете изменить его, я думаю, но это значение по умолчанию). Здесь мой параметр радиуса находился в милях, поэтому я сделал некоторую математику для преобразования.

Примечание . В System.Data.Spatial DBGeography класс DBGeography . Это неправильно, и это не сработает для меня. Множество примеров, которые я нашел в Интернете, использовали этот.

Как преобразовать столбцы Lat / Long в столбцы географии

Итак, если вы были похожи на меня и загрузили базу данных zipcode со всеми столбцами Latitude & Longitude , а затем поняли, что у нее нет столбца География ... это может помочь:

1) Добавьте столбец местоположения в таблицу. Установите тип в Географию.

2) Выполните следующий sql

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

Как просмотреть детали объекта География

Поэтому, когда вы запрашиваете таблицу EventListings в Sql Server Management Studio после того, как вы вставили некоторые элементы DbGeography, вы увидите, что столбец Location содержит шестнадцатеричное значение, подобное: 0x1234513462346. Это не очень полезно, если вы хотите убедиться, что правильные значения вставлены.

Чтобы фактически просмотреть широту и долготу этого поля, вы должны запросить его так:

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



Лицензировано согласно: CC-BY-SA with attribution
Не связан с Stack Overflow
Является ли этот КБ законным? Да, узнайте, почему
Лицензировано согласно: CC-BY-SA with attribution
Не связан с Stack Overflow
Является ли этот КБ законным? Да, узнайте, почему