Точки запроса в пределах заданного радиуса в MySQL - PullRequest
9 голосов
/ 09 марта 2010

Я создал следующую таблицу MySQL для хранения координат широты / долготы вместе с именем для каждой точки:

CREATE TABLE `points` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(128) NOT NULL,
  `location` point NOT NULL,
  PRIMARY KEY (`id`),
  SPATIAL KEY `location` (`location`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1;

Я пытаюсь запросить:

  • все точки в радиусе n мили от данной точки;
  • расстояние каждой возвращаемой точки от данной точки

Все примеры, которые я нашел, относятся к использованию минимального ограничивающего прямоугольника (MBR), а не радиуса. Таблица содержит приблизительно 1 миллион точек, поэтому эта потребность должна быть максимально эффективной.

Ответы [ 4 ]

3 голосов
/ 13 сентября 2016

Для MySQL 5.7 +

Учитывая, что у нас есть следующая простая таблица,

create table example (
  id bigint not null auto_increment primary key,
  lnglat point not null
);

create spatial index example_lnglat 
    on example (lnglat);

Со следующими простыми данными,

insert into example (lnglat) 
values
(point(-2.990435, 53.409246)),
(point(-2.990037, 53.409471)),
(point(-2.989736, 53.409676)),
(point(-2.989554, 53.409797)),
(point(-2.989350, 53.409906)),
(point(-2.989178, 53.410085)),
(point(-2.988739, 53.410309)),
(point(-2.985874, 53.412656)),
(point(-2.758019, 53.635928));

Вы получите точки в заданном диапазоне другой точки (примечание: мы должны искать внутри многоугольника) со следующей комбинацией функций st:

set @px = -2.990497;
set @py = 53.410943;
set @range = 150; -- meters
set @rangeKm = @range / 1000;

set @search_area = st_makeEnvelope (
  point((@px + @rangeKm / 111), (@py + @rangeKm / 111)),
  point((@px - @rangeKm / 111), (@py - @rangeKm / 111))
);

select id, 
       st_x(lnglat) lng, 
       st_y(lnglat) lat,
       st_distance_sphere(point(@px, @py), lnglat) as distance
  from example
 where st_contains(@search_area, lnglat);

В результате вы должны увидеть что-то подобное:

3   -2.989736   53.409676   149.64084252776277
4   -2.989554   53.409797   141.93232714661812
5   -2.98935    53.409906   138.11516275402533
6   -2.989178   53.410085   129.40289289527473

Для справки по расстоянию, если мы удалим ограничение, результат для контрольной точки будет выглядеть следующим образом:

1   -2.990435   53.409246   188.7421181457556
2   -2.990037   53.409471   166.49406509160158
3   -2.989736   53.409676   149.64084252776277
4   -2.989554   53.409797   141.93232714661812
5   -2.98935    53.409906   138.11516275402533
6   -2.989178   53.410085   129.40289289527473
7   -2.988739   53.410309   136.1875540498202
8   -2.985874   53.412656   360.78532732013963
9   -2.758019   53.635928   29360.27797292756

Примечание 1 : поле называется lnglat, поскольку это правильный порядок, если вы рассматриваете точки как (x, y), а также порядок, в котором большинство функций (например, точка) принимают параметр

Примечание 2 : вы не можете использовать пространственные индексы, если используете круги; также обратите внимание, что поле точки может быть установлено для принятия значения NULL, но пространственные индексы не могут индексировать его, если оно имеет значение NULL (все поля в индексе должны быть ненулевыми).

Примечание 3 : st_buffer считается (по документации) плохим для этого варианта использования

Примечание 4 : указанные выше функции (в частности, st_distance_sphere) документированы как быстрые, но не обязательно сверхточные; если ваши данные сверхчувствительны к этому, добавьте немного простора для поиска и выполните точную настройку набора результатов

3 голосов
/ 10 марта 2010

Спасибо вам обоим за ответы.

В конце концов я нашел решение по адресу http://www.movable -type.co.uk / scripts / latlong-db.html .

2 голосов
/ 09 марта 2010

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

0 голосов
/ 23 апреля 2014

Я сделал это для одной точки внутри круга с радиусом

SELECT 
    *
FROM 
    `locator`
WHERE
    SQRT(POW(X(`center`) - 49.843317 , 2) + POW(Y(`center`) - 24.026642, 2)) * 100 < `radius`

подробности и еще один пример запроса здесь http://dexxtr.com/post/83498801191/how-to-determine-point-inside-circle-using-mysql, надеюсь, что это поможет

...