Як я ефективно шукати всі орієнтири в межах певного орієнтиру?


14

Я намагаюся почати з гео пошукового проекту, який знайде всі орієнтири в 10 км / милях (не важливо для цієї історії) певного орієнтиру.

Так, наприклад, скажімо, що у мене є база даних на 1 000 000 орієнтирів. Для того, щоб знайти всі орієнтири в межах 10 миль орієнтиру з певними координатами, я повинен був би обчислити відстань між орієнтиром від мого пошуку та 1 000 000 орієнтирів.

Чи є кращий спосіб зробити це?

Альтернатива, про яку я думав, - класифікувати такі визначні пам'ятки, як країна, регіон, місто, околиці, бізнес, історичні тощо, таким чином, що бізнес може бути частиною мікрорайону чи міста. Місто є частиною регіону, країни тощо. Це може звузити перелік розрахунків, але все ще виглядає як багато роботи для того, щоб пошук був швидким і точним.

Чи може допомогти API Карт Google?


5
Можливо, ви могли б усунути хорошу кількість, просто виконавши швидкий підрахунок відстані на Манхеттені, а потім виконавши другий фільтр, щоб виключити орієнтири, які знаходяться в межах 10 км, але поза радіусом 10 км.
Ніл

3
Яку технологію баз даних ви використовуєте? Відповідь не є агностичною базою даних.
jpmc26

1
@Neil В якості другого проходу ви можете включити будь-який орієнтир, у якому обидва х і у падають на 7 км від початку, без обчислення фактичної відстані.
JimmyJames

Відповіді:


10

Починаючи з SQL Server 2008, існує географічний тип даних, який зберігає місця розташування (lat / lon пар) і полегшує вам написання запитів, пов’язаних з розташуванням.

Існує відповідь StackOverflow, яка детально обговорює це питання.

Основний запит для пошуку найближчих 7 елементів :

USE AdventureWorks2012  
GO  
DECLARE @g geography = 'POINT(-121.626 47.8315)';  
SELECT TOP(7) SpatialLocation.ToString(), City FROM Person.Address  
WHERE SpatialLocation.STDistance(@g) IS NOT NULL  
ORDER BY SpatialLocation.STDistance(@g);  

Основний запит, щоб знайти все в межах 100 м (друга відповідь на питання)

-- Get the center point
DECLARE @g geography
SELECT @g = geo FROM yourTable WHERE PointId = something

-- Get the results, radius 100m
SELECT * FROM yourTable WHERE @g.STDistance(geo) <= 100

11
@KonradRudolph: Як це стосується будь-якого стовпця SQL, який використовується для запитів на таблиці з масовим числом рядків. Ви правильні, але цей коментар стосується практично будь-якого запиту SQL, розміщеного як відповідь.
Flater

2
Де ви читали "MS SQL Server" у запитанні?
Док Браун

3
@Flater Я погоджуюся, що це, як правило, очевидно і надмірно, але формулювання ОП, схоже, говорить про те, що вони не знають про такі механізми.
Конрад Рудольф

2
@ jpmc26: Ви здивовані тим, що я вказав дійсний варіант і не включив якийсь інший варіант? Що? Якщо ви вважаєте, що доречно додати PostGIS, додайте відповідь власноруч (що ви зробили) і не вдайтеся критикувати інших за те, що вони не мають такої ж ідеї, як ви.
Flater

3
Ваша відповідь здається мені, як правило, лише продажем MS SQL. Ваші коментарі свідчать про те, що вони переходять на базу даних на щось, що коштуватиме 10 тисяч тисяч доларів, не запитуючи насправді про те, яка їх ситуація лише робить це ще більше. Він навіть не описує, як ОП може реально реалізувати свій запит або обговорити той факт, що виконання цього та використання просторового індексу не так просто в MS SQL, як в інших БД. Не обговорюється також жодна з основних концепцій. Це погана відповідь, незалежно від того, чи є "дійсною". Ось чому це мене турбує.
jpmc26

29

Використовуйте базу даних із підтримкою GIS (географічних інформаційних систем) запитів. Більшість баз даних підтримують це прямо або мають розширення, але деталі будуть специфічними для бази даних (у своїй відповіді Флатер показує синтаксис для SQL-сервера).

Якщо вам потрібно реалізувати такі запити у вашій програмі, ви можете реалізувати структуру даних, яка дозволяє просторові запити, наприклад, дерево kd . Це як бинарне дерево пошуку, за винятком того, що кожен рівень дерева розділяє на різні розміри координат. Це дозволяє обмежити пошук меншим набором можливих кандидатів. Ефективно ви переводите пошук «радіус 10 км» в межі для кожного розміру координат і затягуєте межі, коли ви повторно входите в дерево.



8
PostGIS - це головний безкоштовний варіант. Він підтримує набагато більше, ніж основні типи та функції GIS-сервера. Але це основна функціональність.
jpmc26

@amon Я вважаю, що коментар jpmc26 є гарним доповненням, але не стільки, скільки критикувати ваш приклад. "Якщо ви хочете почати з нуля, вам не потрібно платити за ліцензовану БД - ця безкоштовна, з відкритим кодом також дуже добре виконає трюк".
mgarciaisaia

11

Так, є кращий спосіб. Потрібно використовувати просторовий індекс . Ці індекси організовують метадані про геометрії, щоб дуже швидко фільтрувати далекі геометрії, економлячи багато циклів процесора, уникаючи описаних вами обчислень. Вам не слід заважати впроваджувати один, оскільки всі основні реляційні бази даних надають тип просторової геометрії та індекси для їх використання.

Що ви хочете розглянути, це запити "в межах відстані" (запити щодо геометрії на певній відстані від якоїсь іншої геометрії). Це дуже стандартні та дуже вирішені проблеми, які можливі у всіх вищезазначених базах даних (та вбудованих у декілька):

  • PostGIS: ST_DWithin
  • SQL Server: STDistance(Не ясно, що підтримується використання індексу у версії 3D-географії цієї функції)
  • Oracle: SDO_WITHIN_DISTANCE(Це прямо не говорить про те, що це призведе до використання індексу. Я повторно перевіряю план запитів. Можливо, вам потрібно буде застосувати, SDO_FILTERщоб отримати його для використання індексу.)
  • MySQL: Я все ще з'ясовував це.

Обхід для запуску використання індексу

У гіршому випадку, коли у вас виникають проблеми з отриманням системою використання просторового індексу з цими запитами, ви можете додати додатковий фільтр. Ви повинні створити квадратне обмежувальне поле зі сторонами довжиною 2 * (відстань пошуку), орієнтованим у точці пошуку, і порівняти обмежувальні поля геометрії таблиці з цим, перш ніж перевірити фактичну відстань. Ось що робить PostGIS ST_DWithinвище всередині все одно.


Відстань у ГІС

Хоча просторові індекси є фантастичним і абсолютно правильним рішенням вашої проблеми, обчислення відстані може логічно ускладнитися. Зокрема, вам потрібно потурбуватися про те, в якому проекції (в основному всі параметри системи координат) зберігаються ваші дані. Більшість двовимірних проекцій (такі речі, як кутові системи координат, як різні лати / довгі проекції) значно спотворюють довжину. Наприклад, проекція Web Mercator (використовувана Google, Bing та всіма іншими основними постачальниками базових карт) все більше розширює області та відстані, оскільки місце розташування стає далі від екватора . Можливо, я помиляюся, оскільки офіційно не навчаюся в ГІС, але найкраще, що я бачив для 2D-проекцій, - це деякі конкретні, які обіцяють правильні відстані відєдина, постійна точка у всьому світі. (Ні, для кожного запиту не практично використовувати різну проекцію; це зробить ваші індекси марними.)

Суть полягає в тому, що вам потрібно переконатися, що ваша математика є точною. Найпростіший спосіб зробити це з точки зору розвитку - використовувати кутові проекції (їх часто називають «географічними») і функції, які підтримують виконання математики за допомогою сфероїдної моделі, але ці обчислення трохи дорожчі, ніж 2D-аналоги а деякі БД можуть не підтримувати їх індексацію. Якщо ви можете отримати прийнятну ефективність, використовуючи їх, мабуть, це шлях. Іншим поширеним варіантом є регіональні прогнози (наприклад, зони UTM), які дають відстані та області досить близькі до виправлення, якщо ваші дані обмежені певною частиною світу. Що найкраще для вашої програми залежатиме від ваших конкретних вимог,

Це застосовується, навіть якщо ви не використовуєте вбудовані в просторові індекси. Ваші дані мають певні прогнози незалежно від того, яку технологію чи техніку ви використовуєте або використовуєте в майбутньому, і це вже впливає на будь-які запити та обчислення, які ви робите.


3

Я погодився б, якщо можливо, використання конкретної підтримки в базі даних було б найрозумнішим способом зробити це.

Однак якби мені довелося це робити в базі даних без конкретної підтримки, я б почав із запиту квадрата, що закриває циркуляр, наприклад (y> (y1 - rad)) AND (y <(y1 + rad)) AND (x> ( x1 - rad)) AND (x <(x1 + rad)). Якщо припустити, що ваші очки мають приблизно рівний запит на розподіл для квадрата, ви отримаєте справжні збіги плюс близько 30% додаткових помилкових збігів. Потім ви можете викреслити помилкові сірники.


Але без відповідного просторового індексу такий запит сканує в гіршому випадку всю базу даних, в кращому випадку всі елементи в межах заданої широти АБО довготи в залежності від вашого індексу, тобто "смуги", а не квадрата. Якщо ви не хочете знищувати продуктивність, використовуйте базу даних, яка підтримує просторові індекси!
jcaron

@jcaron Я вважаю, що цей запит можна оптимізувати за допомогою звичайного індексу B-дерева на xта y. (Можливо, комбінований, можливо, окремий. Я б трохи
профайлював,

@ jpmc26 Ні, не може. Подумайте, ви побачите.
jcaron

@jcaron Можливо, було б краще, якби ви не гадали про щось, що явно не є простим. B-дерева можуть використовуватися для BETWEENзапитів. Я не бачу, чому в гіршому випадку у вас не було двох індексів, а потім відфільтровані результати з кожного індексу об'єднуються разом. (Це те, що RDBMS роблять всередині, коли вони вважають, що варто використовувати декілька індексів.) Якщо комбінований індекс працює, він повинен повністю відфільтрувати один вимір на першому рівні, а потім відносно швидко звузитися на другому рівні.
jpmc26

2
@jcaron насправді ви можете використовувати індекс для чогось подібного, y between -68 and -69 and x between 10 and 11але, звичайно, просторовий індекс зробить кращу роботу для цього завдання
Хуан Карлос Оропеза
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.