хранимая процедура для получения всех точек внутри ограничительной рамки с учетом отрицательной долготы - PullRequest
2 голосов
/ 29 ноября 2011

Я работаю над проектом, в котором в моей базе данных хранятся места с координатами широты и долготы.Я использую Google Maps, чтобы нанести эти места на карту в качестве маркеров.Я не хочу наносить на карту какие-либо «невидимые» маркеры (маркеры вне текущего окна / ограничивающей рамки).Поэтому я следую Googles советам относительно управления маркерами области просмотра .

У меня есть рабочее решение, в котором я использую AJAX для запроса веб-службы ASP.NET всякий раз, когда изменяется область просмотра моей карты.Этот веб-сервис вызывает хранимую процедуру в моей базе данных MSSQL, чтобы получить все места, которые имеют координаты, которые находятся внутри текущего видового экрана / ограничительной рамки.Хранимая процедура выглядит следующим образом:

ALTER PROCEDURE dbo.GetPlacesInsideBoundingBox

    (
    @swLat VarChar(11), /* south-west point latitude */
    @swLng VarChar(11), /* south-west point longitude */
    @neLat VarChar(11), /* north-east point latitude */
    @neLng VarChar(11) /* north-east point longitude */
    )

AS
    /* SET NOCOUNT ON */

    SELECT * FROM dbo.Place WHERE 
    place_lat >= CONVERT(Decimal(11,8), @swLat) 
    AND place_lat <= CONVERT(Decimal(11,8), @neLat) 
    AND place_lng >= CONVERT(Decimal(11,8), @swLng)  
    AND place_lng <= CONVERT(Decimal(11,8), @neLng)
    RETURN

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

Это отлично работает!Но я читал, что вы можете столкнуться с проблемами, если у вас есть отрицательное значение для долготы (к западу от простого меридиана) и что простое решение состояло в том, чтобы добавить 360 к долготе, если она была отрицательной.* У меня два вопроса:

  1. Как мне изменить мою хранимую процедуру (см. Выше), чтобы учесть отрицательные долготы?

  2. Есть ли какие-либо другие модификации, которые я должен рассмотреть, чтобы сделать эту хранимую процедуру надежной?

Если вам интересно узнать о преобразовании из VarChar в десятичное, я обнаружил, что с ним гораздо проще работатьпростые строки на стороне клиента (javascript), а затем конвертировать их в десятичные числа в моей хранимой процедуре, когда мне нужно было сделать вычисления.

Заранее спасибо!

Ответы [ 2 ]

1 голос
/ 05 декабря 2011

На самом деле я нашел лучшее решение. SQL Server имеет встроенные типы данных для работы с пространственными данными. Поэтому я добавил новое значение в свою таблицу и назвал его place_geo с типом данных geography. Затем я заполнил его географическими точками (созданными из широт и долгот, которые у меня уже были в базе данных). Когда в моей базе данных хранятся точки как географические точки, хранимая процедура становится намного проще по своему синтаксису, и я также думаю, что она будет выполняться намного быстрее.

Вот моя хранимая процедура:

ALTER PROCEDURE dbo.GetPlaceClosestToMe
    (
    @my_lat Decimal(11,8),
    @my_lng Decimal(11,8)
    )
AS
    DECLARE @my_point geography;
    SET @my_point = geography::Point(@my_lat, @my_lng , 4326); /* SRID 4326 */
    SET NOCOUNT ON

    SELECT TOP 1 
        place_id, 
        place_name,
        @my_point.STDistance(place_geo)/1000 [Distance in km]
FROM Places
ORDER BY @my_point.STDistance(place_geo)

Так, что это делает? Сначала я получаю долготу и широту в качестве входных параметров в десятичном формате. Я объявляю новую переменную (@my_point) с типом данных "география". Затем я назначаю новую географическую точку только что созданной переменной, используя входные параметры долготы и широты.

Затем я запрашиваю точку в моей таблице базы данных (place_geo), ближайшую к @my_point. (STDistance возвращает самую короткую линию между двумя типами географии.) Деление на 1000 - это расстояние, выраженное в километрах.

Если вы хотите, чтобы он возвращал несколько результатов, просто измените выбор: SELECT TOP 5 или SELECT TOP 10.

В настоящее время у меня недостаточно тестовых данных, чтобы проверить, быстрее ли это, чем при использовании старого метода, но, возможно, кто-то еще провел некоторые тесты? Я предполагаю, что использование типа данных geography намного быстрее, чем старый метод.

При моих ограниченных тестовых данных старая хранимая процедура и эта новая хранимая процедура возвращают одинаковые результаты.

Надеюсь, вы нашли это продолжение полезным!

1 голос
/ 29 ноября 2011

1) Долгота обычно должна составлять от -180 до 180, поэтому просто проверьте, соответствуют ли координаты из Google этому условию.Вероятно, да.Если нет, просто нормализуйте их, чтобы они были в пределах этого интервала, добавив или вычтя 360. Нормируйте их , прежде чем вызывать хранимую процедуру .

2) Обычно @swLng <= @neLng, но вы также должны обработатьдело @swLng > @neLng.Так что внутри вашей хранимой процедуры ваше состояние долготы будет выглядеть следующим образом:

AND 
(
    (CONVERT(Decimal(11,8), @swLng) <= CONVERT(Decimal(11,8), @neLng)
        AND place_lng >= CONVERT(Decimal(11,8), @swLng) 
        AND place_lng <= CONVERT(Decimal(11,8), @neLng)
    ) 
    OR 
    (CONVERT(Decimal(11,8), @swLng) > CONVERT(Decimal(11,8), @neLng)
        AND (place_lng >= CONVERT(Decimal(11,8), @swLng) 
             OR place_lng <= CONVERT(Decimal(11,8), @neLng))
    )
)

PS: обратите внимание, что вам, вероятно, следует использовать ГИС, подобную PostGIS, чтобы это было действительно эффективным :) Я был очтобы решить эту проблему, но я все еще откладывал это "до тех пор, пока у меня есть ГИС".Вы вдохновили меня, и, возможно, я тоже пойду на это решение.

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

...