Не удается найти полигоны в базе данных, в которых находится точка

Я пытаюсь использовать метод, чтобы определить, находится ли координата Lat, Long в каком-либо из сохраненных полигонов в базе данных, и вернуть список тех, в которые она попадает (если есть).

У меня есть широта/долгота:

50.120578, -103.535156

Я создал геозону и сохранил ее в базе данных,

CREATE TABLE [dbo].[GeoFences] (
    [Id]           INT               IDENTITY (1, 1) NOT NULL,
    [GeoPoints]    [sys].[geography] NULL,
    CONSTRAINT [PK_dbo.GeoFences] PRIMARY KEY CLUSTERED ([Id] ASC)
);

Это значения для GeoPoints.

POLYGON ((-129.63729858398437 57.279042764977774, -92.899017333984375 56.8970039212726, -93.865814208984375 48.922499263758255, -122.86972045898437 48.806863461085172, -129.37362670898437 57.088515327886505, -129.63729858398437 57.279042764977774))

Используя этот метод, я ожидаю, что он вернет эту строку, поскольку многоугольник окружает точку.

public IEnumerable<GeoFence> SearchGeoPoint(DbGeography geoPoint)
        {
            try
            {
                return _geoLocationRepository.Get.Where(obj => obj.GeoPoints.Intersects(geoPoint));
            }
            catch (Exception)
            {
                return null;
            }
        }

Однако я продолжаю не возвращать строки. Я не получаю никаких исключений или ошибок, просто 0 строк.


person devfunkd    schedule 16.03.2014    source источник


Ответы (2)


Ваши координаты определены в неправильном порядке, и поэтому ваш многоугольник фактически представляет собой землю с «маленькой» дырой.

SqlGeography должен «ходить» по координатам, всегда сохраняя внутреннюю область слева. Это означает, что любые дыры, определенные в вашем многоугольнике, тем не менее, должны проходить «противоположным» путем (который по-прежнему сохраняет внутреннюю область многоугольника слева от вас). По сути, вы определяете эту дыру внутри Земли.

Короче говоря, вам нужно изменить порядок координат, чтобы получить правильный результат (и тот, который вы ищете). Если вы используете SQL Server 2012, попробуйте метод ReorientObject():

DECLARE @geog GEOGRAPHY = (SELECT [GeoPoints].ReorientObject() FROM [dbo].[GeoFences] WHERE [Id] = <an id>);
SELECT @geog;

Однако я сообщу, что ReorientObject() действительно является лишь временным решением в этом сценарии. Вам необходимо навсегда «переориентировать» все сохраненные полигоны, если они определены в правильном порядке.

person Jon Bellamy    schedule 17.03.2014

Узнал, что на самом деле это было несколько проблем, и, надеюсь, кто-то, кто столкнулся с этой проблемой, найдет это полезным.

Первая проблема заключалась в том, что я использовал .Get.Where вместе в своем запросе LINQ. Когда я удалил .Get и напрямую использовал контекст базы данных (я использовал общий репозиторий), он вернул строки, которые я ожидал.

я заменил

return _geoLocationRepository.Get.Where(obj => obj.GeoPoints.Intersects(geoPoint));

С

using(var context as DatabaseContext())
{
  var results = new List<GeoFence>();
  var query = context.GeoFences.Where(obj => obj.GeoPoints.Intersects(geoPoint));

  //Have to iterate through the results (Only way I've found to access the data)
  foreach(var fence in query)
{
   results.Add(fence);
}

return results;
}

Еще одна вещь, которую следует отметить, это то, что вам нужно использовать Long/Lat для создания вашей POINT, а не Lat/Long (вы получите исключение, если вы сделаете Lat/Long).

person devfunkd    schedule 17.03.2014
comment
Я полностью пропустил .Get()!! Однако я все еще сомневаюсь в вашем порядке полигонов, это должна была быть земля с дырой? Кроме того, если вам нужны все результаты, вы можете рассмотреть следующее, а не повторять один за другим: var query = context.GeoFences.Where(obj => obj.GeoPoints.Intersects(geoPoint)).ToList(); return query;. - person Jon Bellamy; 18.03.2014