Learning Platform
Глоссарий Troubleshooting
Урок 11.05 · 22 мин
Средний
spatialgeometrygisextensions

spatial: пространственная аналитика

Геоданные — точки, линии, полигоны, координаты — встречаются в аналитике постоянно: магазины на карте, маршруты доставки, зоны покрытия, границы регионов. Расширение spatial превращает DuckDB в полноценный инструмент геоаналитики: оно добавляет геометрические функции, чтение и запись десятков ГИС-форматов и пространственные операции. Этот урок про то, что умеет spatial, как устроена пространственная геометрия в DuckDB — и про важную особенность: spatial единственное core-расширение, которое нельзя загрузить автоматически.

spatial требует явной загрузки

Начнём с того, о чём легко забыть. В уроке про систему расширений мы видели: большинство core-расширений autoloadable — первое обращение к их функциям подтягивает расширение само. spatial — исключение. Хотя это полноценное core-расширение, оно не autoloadable. Первый же вызов пространственной функции без предварительной загрузки даст ошибку «функция не найдена».

Поэтому работа с геоданными всегда начинается с двух явных команд:

INSTALL spatial;
LOAD spatial;

-- Только теперь пространственные функции доступны
SELECT ST_Point(13.405, 52.520) AS berlin;

Причина исключения — практическая: spatial велик и тянет внешние ГИС-библиотеки (GDAL для форматов, GEOS для геометрических операций, PROJ для проекций). Загружать такой объём кода молча, при случайном упоминании функции, было бы неожиданно для пользователя. Поэтому spatial требует осознанного LOAD.

DANGER

Если запрос с пространственной функцией падает с ошибкой вида «Function ST_Point does not exist», первое, что надо проверить, — выполнен ли LOAD spatial в этой сессии. LOAD действует только в пределах сессии: каждое новое подключение к DuckDB стартует без загруженного spatial, и команду нужно повторять. Это самая частая ошибка новичков с геоданными в DuckDB.

Тип GEOMETRY

В основе геоаналитики лежит тип данных для пространственных объектов. Исторически тип GEOMETRY приходил вместе с расширением spatial. В DuckDB 1.5 GEOMETRY повышен до core-типа — он стал частью системы типов ядра, как INTEGER или VARCHAR. Но сами пространственные функции и форматы по-прежнему живут в расширении spatial; в core переехало только определение типа.

GEOMETRY — это контейнер для геометрических объектов разных видов: точка (POINT), линия (LINESTRING), полигон (POLYGON), а также их множественные варианты (MULTIPOINT, MULTIPOLYGON и так далее). Внутри значение хранится в формате WKB (Well-Known Binary) — компактном бинарном представлении геометрии, стандартном в ГИС-мире.

LOAD spatial;

-- Разные виды геометрии в одной колонке GEOMETRY
SELECT ST_GeomFromText('POINT(13.4 52.5)')              AS a_point,
       ST_GeomFromText('LINESTRING(0 0, 1 1, 2 0)')     AS a_line,
       ST_GeomFromText('POLYGON((0 0, 4 0, 4 4, 0 4, 0 0))') AS a_polygon;
Тип GEOMETRY: контейнер для пространственных объектов
POINTОдна точка с координатами; например положение магазина или адреса
LINESTRINGЛоманая линия из последовательности точек; например маршрут или участок дороги
POLYGONЗамкнутая область, заданная контуром; например граница района или зона доставки

Текстовое представление геометрии называется WKT (Well-Known Text) — человекочитаемая запись POINT(13.4 52.5). WKT удобен для ввода и отладки, WKB — для хранения. Функции ST_GeomFromText и ST_AsText переводят между ними.

Пространственные функции

spatial добавляет большое семейство функций с префиксом ST_ (это стандартное соглашение из спецификации Simple Features, общее для всех ГИС-систем). Их можно разбить на группы.

Конструкторы создают геометрию: ST_Point(x, y), ST_GeomFromText(wkt), ST_GeomFromWKB(wkb).

Измерения считают величины: ST_Area(geom) — площадь полигона, ST_Length(geom) — длина линии, ST_Distance(a, b) — расстояние между геометриями.

Предикаты отношений возвращают логическое значение: ST_Contains(a, b) — содержит ли A геометрию B, ST_Intersects(a, b) — пересекаются ли, ST_Within(a, b) — лежит ли A внутри B, ST_DWithin(a, b, d) — находятся ли A и B в пределах расстояния d.

Преобразования строят новую геометрию из старой: ST_Buffer(geom, d) — зона на расстоянии d вокруг геометрии, ST_Centroid(geom) — центр тяжести, ST_Intersection(a, b) — общая часть двух геометрий.

Типичный аналитический запрос — «сколько магазинов попадает в зону 2 км вокруг точки»:

LOAD spatial;

-- Магазины в радиусе 2000 метров от центра города
SELECT name, ST_Distance(location, ST_Point(13.405, 52.520)) AS dist_m
FROM stores
WHERE ST_DWithin(location, ST_Point(13.405, 52.520), 2000)
ORDER BY dist_m;
┌──────────────────┬──────────┐
│       name       │  dist_m  │
│     varchar      │  double  │
├──────────────────┼──────────┤
│ Mitte Flagship   │   340.18 │
│ Alexanderplatz   │   910.55 │
│ Hackescher Markt │  1620.07 │
└──────────────────┴──────────┘

И пространственный джойн — «к каждой точке доставки найти район, в котором она лежит»:

-- Пространственный JOIN: точка внутри полигона района
SELECT d.order_id, r.district_name
FROM deliveries d
JOIN districts r ON ST_Contains(r.boundary, d.point);

Шейринг геометрии и статистика

GEOMETRY — это не просто blob, который DuckDB хранит как непрозрачные байты. Движок понимает структуру геометрии и применяет к ней оптимизации хранения, как и к обычным колонкам.

Для колонок с однородной геометрией (например все значения — точки) DuckDB применяет приём, который в документации называют шейрингом (shredding) геометрии: вместо того чтобы хранить каждое значение как самостоятельный WKB-блоб, повторяющаяся структура раскладывается покомпонентно, и колонка сжимается заметно лучше — для однородных колонок выигрыш порядка трёхкратного.

Кроме того, для каждого row group DuckDB хранит bounding box — прямоугольник, охватывающий все геометрии этого row group. Это пространственный аналог zonemap из обычного Parquet: по bounding box можно отсечь целые row groups при пространственном фильтре. Если запрос ищет геометрии в некоторой области, а bounding box row group с ней не пересекается — row group пропускается без чтения данных.

Bounding box: пространственный аналог zonemap
Пространственный фильтр областиЗапрос ищет геометрии внутри заданного прямоугольника или полигона
сверка с bounding box
Row group: bbox не пересекает областьПрямоугольник, охватывающий геометрии row group, не пересекается с областью запроса — row group пропускается
Row group: bbox пересекает областьBounding box пересекается с областью запроса — этот row group читается

Форматы геоданных

ГИС-мир накопил множество форматов файлов, и spatial умеет читать большинство из них через встроенную библиотеку GDAL. Функция ST_Read открывает геопространственный файл:

LOAD spatial;

-- Чтение GeoJSON
SELECT * FROM ST_Read('regions.geojson') LIMIT 5;

-- Чтение Shapefile (классический ГИС-формат)
SELECT * FROM ST_Read('boundaries.shp');

Через ST_Read доступны GeoJSON, Shapefile, GeoPackage, KML и десятки других форматов. Запись геоданных делается через COPY ... TO с указанием драйвера GDAL — так можно выгрузить результат пространственного запроса обратно в GeoJSON или GeoPackage.

Отдельно стоит GeoParquet — соглашение о хранении геометрии в обычных Parquet-файлах с пространственными метаданными. spatial читает и пишет GeoParquet, и это удобный мост между геоаналитикой и остальным Parquet-конвейером курса: геоданные лежат в том же формате, что и всё прочее, но несут пространственную семантику.

NOTE

Системы координат — отдельная важная тема геоаналитики. Координаты могут быть в градусах широты и долготы (система WGS84), а могут быть в метрах в проекции конкретного региона. Функция ST_Distance в градусах и в метрах даёт совершенно разные числа. spatial умеет преобразовывать координаты между системами через ST_Transform, опираясь на библиотеку PROJ. Если в запросе расстояния выглядят странно — почти всегда дело в том, что геометрии в разных системах координат или не в той системе.

Попробуй сам

Геоаналитику легко попробовать на небольших данных.

  1. Выполните INSTALL spatial; LOAD spatial;. Затем намеренно откройте новую сессию, не делайте LOAD и выполните SELECT ST_Point(0,0). Получите ошибку. Это подтверждает: spatial не autoloadable, LOAD нужен в каждой сессии.
  2. Создайте таблицу с колонкой GEOMETRY и несколькими точками городов (ST_Point(долгота, широта)). Посчитайте ST_Distance между парами и сравните с реальными расстояниями на карте.
  3. Постройте полигон-прямоугольник через ST_GeomFromText('POLYGON(...)') и проверьте функцией ST_Contains, какие из ваших точек лежат внутри него. Это основа пространственного джойна «точка в районе».
  4. Скачайте небольшой GeoJSON-файл с границами регионов, прочитайте его через ST_Read, и сделайте пространственный JOIN ваших точек с этими полигонами по ST_Contains. Объясните себе, чем такой JOIN отличается от обычного JOIN по равенству ключей.
GeoParquet: хранение геометрии в Parquet
Проверка знанийKnowledge check
Почему расширение spatial требует явных INSTALL и LOAD, в отличие от большинства core-расширений, и что представляет собой тип GEOMETRY?
ОтветAnswer
Большинство core-расширений DuckDB autoloadable — первое обращение к их функциям, типам или путям подтягивает расширение автоматически. Расширение spatial является исключением: хотя это полноценное core-расширение, оно не autoloadable, и первый же вызов пространственной функции без предварительной загрузки даст ошибку, что функция не найдена. Поэтому работа с геоданными всегда начинается с двух явных команд — INSTALL spatial (скачать на диск, один раз) и LOAD spatial (загрузить в сессию, в каждом новом подключении). Причина исключения практическая: spatial велик и тянет тяжёлые внешние ГИС-библиотеки — GDAL для чтения форматов, GEOS для геометрических операций, PROJ для преобразования систем координат. Загружать такой объём кода молча, при случайном упоминании функции, было бы неожиданно для пользователя, поэтому spatial требует осознанного LOAD. Самая частая ошибка новичков — забыть, что LOAD действует только в пределах сессии, и получить ошибку отсутствующей функции в новом подключении. Тип GEOMETRY — это тип данных для пространственных объектов. Исторически он приходил вместе с расширением spatial, но в DuckDB 1.5 был повышен до core-типа и стал частью системы типов ядра, как INTEGER или VARCHAR; при этом сами пространственные функции и форматы остались в расширении spatial — в ядро переехало только определение типа. GEOMETRY — это контейнер для геометрических объектов разных видов: точка (POINT), линия (LINESTRING), полигон (POLYGON) и их множественные варианты. Внутри значение хранится в стандартном бинарном формате WKB (Well-Known Binary), а человекочитаемое текстовое представление называется WKT. DuckDB понимает структуру геометрии: для однородных колонок применяет покомпонентное хранение с заметным выигрышем в сжатии, а для каждого row group хранит bounding box — пространственный аналог zonemap для отсечения row groups при пространственном фильтре.

Проверьте понимание

Результат: 0 из 0
Прикладной
Вопрос 1 из 4. Запрос SELECT ST_Point(0, 0) в новой сессии падает с ошибкой 'Function ST_Point does not exist'. В чём причина?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 7