PostGIS
розширення, що додає PostgreSQL геометрію
Що це таке
PostGIS — розширення з відкритим кодом для сервера баз даних PostgreSQL, яке дає змогу зберігати в стовпці форму, наприклад адресу доставки, річку чи межу земельної ділянки, і звичайним SQL питати, як ці форми розташовані одна щодо одної. Воно починалося як дослідницький проєкт у Refractions Research, консалтинговій компанії з ГІС і баз даних, а сьогодні розвивається й випускається як окремий проєкт з відкритим кодом.
Розширення встановлюють у кожну базу окремо однією командою, CREATE EXTENSION postgis;, і разом із ним з’являються три речі.
- Просторові типи.
geometryописує форми на пласкій площині в тій системі координат, у якій дані зібрали.geographyописує їх на вигнутій поверхні Землі й вимірює в метрах. - Просторові функції. Велика родина функцій, назви яких починаються з
ST_:ST_IntersectsіST_Containsдля взаємного розташування,ST_AreaіST_Lengthдля вимірювань,ST_Transformдля переходу між системами координат,ST_AsGeoJSONдля передачі результату вебкарті. - Просторові індекси. PostGIS будує R-дерево поверх механізму GiST у PostgreSQL, а для окремих видів даних підтримує ще SP-GiST та індекси діапазонів блоків.
Форми відповідають моделі Simple Features від Open Geospatial Consortium, яку
також ухвалено як ISO 19125: точки, лінії й полігони, їхні складені варіанти та
колекції змішаних типів. Кожне значення несе SRID — число, яке вказує на рядок у
таблиці spatial_ref_sys і пояснює, що означають координати. SRID 4326 — це
довгота й широта в градусах на датумі WGS 84, у якому рахує GPS. Проєкційна
система натомість вимірює в метрах на схід і на північ від фіксованого початку
координат і точна лише в межах регіону, для якого її створили.
Растрові дані, топологія та частина 3D-операцій винесені в окремі розширення,
postgis_raster, postgis_topology і postgis_sfcgal, тож база несе лише ті
частини, якими справді користується.
Чому це важливо
PostGIS змушує вирішити, де житимуть дані про місця. Без нього координати зазвичай лежать у двох числових стовпцях, а справжня просторова робота відбувається деінде: у коді застосунку, який перебирає рядки й рахує відстані, у настільній ГІС, куди хтось щопонеділка робить експорт, або в окремому геосервісі з власною копією даних. Кожна з цих копій поступово розходиться з рештою. З PostGIS питання «які клієнти живуть за два кілометри від складу, де товар є в наявності» стає одним запитом, що об’єднує залишки, дані клієнтів і форми в одній транзакції, під тими самими резервними копіями й тими самими правами, що й решта бази.
Так просторові дані отримують гарантії, які решта схеми вже має. Обмеження CHECK може відхилити недійсний полігон ще під час вставки. Row-level security може обмежити регіональну команду ділянками її власного регіону. Імпорт, що зламався на півдорозі, відкочується, а не лишає по собі половину шару.
Друге рішення — geometry чи geography, і посібник PostGIS говорить про це прямо.
Дані в межах міста чи країни зазвичай найкраще зберігати як geometry у
проєкції, підібраній саме для цієї території, бо тоді доступний увесь набір
функцій, а обчислення швидкі. Дані, що охоплюють континенти, або випадки, коли
користувачі хочуть відстань у метрах між будь-якими точками планети, простіше
зробити правильно з geography: цей тип сам рахує на вигнутій поверхні, хоч і
має менше функцій.
А іноді відповідь така: PostGIS — більше, ніж потрібно задачі. Застосунку, який лише зберігає широту й довготу кожного магазину та ставить шпильки на карті, цілком досить двох числових стовпців. PostGIS виправдовує своє місце, коли питання стосуються відстані, вкладеності чи перетину або коли в даних з’являються лінії й полігони, а не лише окремі точки.
Як це виглядає на практиці
Заради PostGIS нерідко міняють базу даних. Картографічний застосунок, що обслуговував просторові запити в реальному часі для багатьох користувачів, працював на Microsoft SQL Server без вбудованої геопросторової індексації, і затримки шкодили і карті, і сервісам за нею. Розв’язанням став перехід на PostgreSQL із PostGIS: схема, спроєктована для просторових даних, індекси GiST і SP-GiST на стовпцях geometry та geography і обмеження на просторові координати. Понад 50 мільйонів просторових записів пройшли через ETL-конвеєри, які дорогою перетворили їх у EPSG:4326. На PostGIS просторовий запит у середньому відповідав менш ніж за 250 мілісекунд, тоді як на старій базі на це йшло 2,5 секунди, тож виграш склав 10 разів, а навантаження на CPU бекенду водночас знизилося на 65%.
Інша робота почалася з даних, а не зі швидкості. Масштабний геопросторовий аналіз виявив понад 1 000 невідповідностей у географічних наборах даних і часових рядах. Для пошуку й виправлення просторових помилок використовували GDAL, QGIS і ArcGIS, а PostGIS виконував просторові запити й індексацію; процедури на PL/pgSQL перетворювали дані всередині PostgreSQL, а очищений результат показували в Mapbox. Час виконання просторових запитів скоротився на 35%.
Де виникають проблеми
Відстані повертаються крихітними дробами. Запит питає, наскільки далеко одне
від одного два міста, і отримує 0.08. Стовпець має тип geometry у SRID 4326,
тож відповідь у градусах, а градус довготи стискається приблизно від 111
кілометрів на екваторі до нуля на полюсах. Приведіть значення до geography,
щоб отримати метри, або перенесіть дані через ST_Transform у проєкційну
систему, що підходить для території, і вже тоді вимірюйте.
Пошук у радіусі читає кожен рядок. Таблиця має просторовий індекс, а план
показує послідовне сканування. Звична причина — фільтр, записаний як
ST_Distance(a, b) < 500. У списку посібника з функціями, які самі
використовують індекс, є ST_DWithin, а ST_Distance там немає. Обгортання
індексованого стовпця в приведення типу чи трансформацію дає той самий ефект, бо
індекс на geom нічого не знає про geom::geography.
-- стовпець geography, індексований як зберігається, відстань у метрах
CREATE INDEX depots_geog_idx ON depots USING GIST (geog);
SELECT id FROM depots
WHERE ST_DWithin(geog, ST_MakePoint(12.57, 55.68)::geography, 500);
Якщо дані мають лишитися в стовпці geometry з SRID 4326, додайте стовпець
geography, як у прикладі, й проіндексуйте його або створіть індекс на тому
самому виразі, який використовує запит. Після великого завантаження запустіть
ANALYZE, щоб планувальник знав, що індекс вартий уваги.
Точки опиняються в морі. Кожна форма зберігає свій малюнок, але стоїть не
там, часто на іншому континенті. Таку картину дають дві помилки. ST_SetSRID
лише змінює мітку геометрії й не чіпає чисел, тож якщо викликати його там, де
малося на увазі ST_Transform, метри отримають штамп градусів. Або десь раніше
в ланцюжку переплутали довготу й широту: PostGIS, як і GeoJSON, чекає x перед y,
тобто довготу перед широтою. Використовуйте ST_SetSRID лише для того, щоб
оголосити, чим числа вже є, ST_Transform — щоб їх перерахувати, і перевіряйте
кожен імпорт на одній точці, розташування якої відоме.
Обчислення площ і перетинів падають з помилкою. Імпорт із shapefile
проходить, а згодом ST_Intersection падає з топологічним винятком. PostGIS
навмисно зберігає недійсну геометрію, щоб її можна було знайти й виправити, а
полігон, межа якого перетинає сама себе, недійсний. Перевіряйте через
ST_IsValid, виправляйте через ST_MakeValid, який зберігає всі вхідні
вершини, і додайте обмеження CHECK, щоб погану форму відхиляли на вході, а не
виявляли у звіті.
Що почитати далі
- PostGIS Documentation — власний покажчик проєкту до посібника, FAQ і навчальних матеріалів.
- Data Management — geometry і geography, SRID, правила дійсності та три типи індексів так, як їх визначає сам посібник.
- Spatial Indexing — розділ воркшопу про обмежувальні прямокутники й функції, які користуються індексом без підказки.
- ST_DWithin — довідкова сторінка перевірки відстані з одиницями для кожного типу.
- Simple Feature Access – Part 1 — стандарт OGC, на якому стоять типи геометрії, що їх зберігає PostGIS.