Используйте пространственные расширения MySQL для выбора точек внутри круга
у меня есть таблица под названием flags
, которая содержит столбец с именем coordinates
Это полно "точек" MySQL. Мне нужно выполнить запрос, где я получаю все флаги в пределах круга на основе широты и долготы с радиусом 100m.
С точки зрения использования это основано на позиции пользователя. Например, мобильный телефон даст широту и долготу пользователя, а затем передаст его в эту часть API. Затем API должен создать невидимый круг вокруг пользователя с радиусом 100 метров, а затем вернуть флаги в этот круг.
Это эта часть API, которую я не уверен, как создать, поскольку я не уверен, как использовать SQL для создания этого невидимого круга и выбора точек только в пределах этого радиуса.
это возможно? Есть ли пространственная функция MySQL это поможет мне сделать это?
Я считаю
8 ответов
в MySQL нет геопространственных функций расширения, поддерживающих вычисления широты / долготы. существует с MySQL 5.7.
вы просите о близости кругов на поверхности Земли. Вы упоминаете в своем вопросе, что у вас есть значения lat/long для каждой строки в вашем flags
таблица, а также универсальной поперечной проекции Меркатора (UTM) прогнозируемые значения в одном из нескольких различных зоны UTM. Если я правильно помню мои карты артиллерийской разведки Великобритании, UTM полезен для определения местоположения предметов на этих картах.
это простой вопрос, чтобы вычислить расстояние между двумя точками в той же зоне в UTM: декартово расстояние делает трюк. Но когда точки находятся в разных зонах, это вычисление не работает.
соответственно, для приложения, описанного в вашем вопросе, необходимо использовать Большой Круг Расстояние, который вычисляется с использованием haversine или другой подходящей формулы.
MySQL, дополненный геопространственными расширениями, поддерживает способ представления различных плоских форм (точек, полилиний, полигонов и т. д.) В качестве геометрических примитивов. MySQL 5.6 реализует недокументированную функцию расстояния st_distance(p1, p2)
. Однако, эта функция возвращает декартово расстояние. Так это совершенно не подходит для вычислений на основе широты и долготы. В умеренном градус широты почти в два раза меньше расстояния от поверхности (Север-Юг), чем градус долготы(Восток-Запад), потому что линии широты сближаются ближе к полюсам.
таким образом, круговая формула близости должна использовать подлинную широту и долготу.
в вашем приложении, вы можете найти все flags
очков в десяти милях от заданного latpoint,longpoint
С таким запросом:
SELECT id, coordinates, name, r,
units * DEGREES( ACOS(
COS(RADIANS(latpoint))
* COS(RADIANS(X(coordinates)))
* COS(RADIANS(longpoint) - RADIANS(Y(coordinates)))
+ SIN(RADIANS(latpoint))
* SIN(RADIANS(X(coordinates))))) AS distance
FROM flags
JOIN (
SELECT 42.81 AS latpoint, -70.81 AS longpoint,
10.0 AS r, 69.0 AS units
) AS p ON (1=1)
WHERE MbrContains(GeomFromText (
CONCAT('LINESTRING(',
latpoint-(r/units),' ',
longpoint-(r /(units* COS(RADIANS(latpoint)))),
',',
latpoint+(r/units) ,' ',
longpoint+(r /(units * COS(RADIANS(latpoint)))),
')')), coordinates)
если вы хотите искать пункты в пределах 20 км, измените эту строку запроса
20.0 AS r, 69.0 AS units
к этому, например
20.0 AS r, 111.045 AS units
r
- это радиус, в котором вы хотите осуществить поиск. units
являются единицы расстояния (мили, км, фарлонги, все, что вы хотите) на градус широты на поверхности Земли.
этот запрос использует ограничивающий lat / long вместе с MbrContains
чтобы исключить точки, которые определенно слишком далеки от начальной точки, затем использует большое расстояние круга формула для создания расстояний для оставшихся точек. Ан объяснение всего этого можно найти здесь. Если ваша таблица использует метод доступа MyISAM и имеет пространственный индекс,MbrContains
будет использовать этот индекс, чтобы получить быстрый поиск.
наконец, запрос выше выбирает все точки внутри прямоугольника. Чтобы сузить это до Только точек в круге и упорядочить их по близости, оберните запрос следующим образом:
SELECT id, coordinates, name
FROM (
/* the query above, paste it in here */
) AS d
WHERE d.distance <= d.r
ORDER BY d.distance ASC
предполагается, что координаты в таблице хранятся как тип данных POINT () в столбце с надписью "point". Функция X(точка) и Y (точка) извлекают значения широты и долготы из значения точки соответственно.
SET @lat = the latitude of the point
SET @lon = the longitude of the point
SET @rad = radius in Kilometers to search from the point
SET @table = name of your table
SELECT
X(point),Y(point),*, (
6373 * acos (
cos ( radians( @lat ) )
* cos( radians( X(point) ) )
* cos( radians( Y(point) ) - radians( @lon ) )
+ sin ( radians( @lat ) )
* sin( radians( X(point) ) )
)
) AS distance
FROM @table
HAVING distance < @rad
Если вы хотите сделать это в милях, замените константу 6373 на 3959
для тех, кто хочет уменьшить синтаксис запроса, вот общая реализация пользовательской функции MySQL для реализации функции расстояния на основе Формула гаверсинуса.
CREATE FUNCTION HAVERSINE ( coord1 POINT, coord2 POINT )
RETURNS DOUBLE
DETERMINISTIC
BEGIN
DECLARE dist DOUBLE;
SET rlat1 = radians( X( coord1 ) );
SET rlat2 = radians( X( coord2 ) );
SET rlon1 = radians( Y( coord1 ) );
SET rlon2 = radians( Y( coord2 ) );
SET dist = ACOS( COS( rlat1 ) * COS( rlon1 ) * COS( rlat2 ) * COS( rlon2 ) + COS( rlat1 ) * SIN( rlon1 ) * COS( rlat2 ) * SIN( rlon2 ) + SIN( rlat1 ) * SIN( rlat2 ) ) * 6372.8;
RETURN dist;
END
буферы не очень помогут вам в MySQL
начиная с MySQL 5.6,полный non-MBR st_*
операции были выполнены. Но лучшим решением для вас, в случае круга, является использование без документов функции st_distance
:
select *
from waypoints
where st_distance(point(@center_lon, @center_lat), coordinates) <= radius;
Это было трудно найти, так как он без документов :-) но это упоминается на этот блог, автор которого также заполнил упоминается багрепорт. Однако есть предостережения (со ссылкой на блог):
плохие новости:
1) Все функции по-прежнему используют только планарной системе координат. Различные SRIDs не поддерживаются.
2) пространственные индексы (RTREE) поддерживаются только для таблиц MyISAM. Один можно использовать функции для таблиц InnoDB, но не использовать пространственные ключи.
пункт 1) означает, что единица расстояния будет такой же, как единица координат (в градусах в случае WGS84). Если вам нужно расстояние в метрах, вы должны использовать проецируемую систему координации (например, UTM или аналогичный), которая имеет единицы, соответствующие метрам.
Итак, если вы не хотите идти с этими предостережениями,или в случае MySQL
Надеюсь, моя версия поможет
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
от: https://gis.stackexchange.com/questions/31628/find-points-within-a-distance-using-mysql
SELECT
id, (
6371 * acos (
cos ( radians(78.3232) )
* cos( radians( lat ) )
* cos( radians( lng ) - radians(65.3234) )
+ sin ( radians(78.3232) )
* sin( radians( lat ) )
)
) AS distance
FROM markers
HAVING distance < 30
ORDER BY distance
LIMIT 0 , 20;
(Не забудьте заменить все константы, этот пример для километров)
вы можете использовать:
SELECT name, lat, lng
FROM vw_mytable
WHERE ST_Contains(ST_Buffer(
ST_GeomFromText('POINT(12.3456 34.5678)'), (0.00001*1000)) , mypoint) = 1
выражение: 0.00001 * 1000 внутри statment выше дают вам круг с 1000 метрами диаметра, его применение в представлении здесь, столбец name - это просто метка для точки , mypoint-это имя моего столбца точки, lat был рассчитан внутри представления с помощью ST_X (mytable.mypoint) и СПГ с ST_Y(mytable.mypoint), и они просто показывают мне буквальные значения lat и lng. Он даст вам все координаты, которые принадлежат кругу.
для полноты, начиная с MySQL 5.7.6. вы можете использовать ST_Distance_Sphere функция, которая достигает того же результата:
SET @pt1 = ST_GeomFromText('POINT(12.3456 34.5678)');
SELECT * from
(SELECT * ,(ST_Distance_Sphere(@pt1, location, 6373)) AS distance FROM mydb.Event ORDER BY distance) x WHERE x.distance <= 30;
в этом случае мы предоставляем приблизительный радиус Земли в километрах (6373) и точке (@pt1). Этот код вычислит расстояние (в километрах) между этой точкой (длиной 12.3456, lat 34.5678) и всеми точками, содержащимися в базе данных, где расстояние составляет 30 км или менее.