PostGIS расширяет PostgreSQL типами для геометрии, географических координат, растров и топологии. С ним база умеет хранить точки, линии и полигоны, искать пересечения, считать расстояния и отдавать GeoJSON для карт. Это не отдельная СУБД, а набор типов, функций и индексов внутри PostgreSQL.
Разберём установку, выбор между geometry и geography, импорт данных, пространственные запросы, индексы и типовые ошибки. Примеры предполагают PostgreSQL 16+ и PostGIS 3.x; точные имена пакетов зависят от дистрибутива.
Установка и включение расширения
Пакет ставится в систему, а расширение включается в конкретной базе: одна установка обслуживает кластер, но CREATE EXTENSION нужен для каждой базы.
sudo apt install postgresql-16-postgis-3
sudo -u postgres psql -c "CREATE DATABASE geo;"
sudo -u postgres psql -d geo -c "CREATE EXTENSION postgis;"
sudo -u postgres psql -d geo -c "SELECT postgis_full_version();"
Для растров добавьте postgis_raster, для топологии — postgis_topology. В облачных базах расширение включает администратор через консоль. Если появляется ошибка «type geometry does not exist», расширение не создано или выбрана не та база.
Приложение должно видеть функции PostGIS через search_path. Не создавайте расширение в произвольной схеме — это усложняет восстановление дампа.
Geometry или geography
geometry хранит координаты в выбранной системе (SRID) и работает быстро, но расстояния считает в единицах этой системы. geography интерпретирует координаты как долготу и широту на сфере и возвращает метры, зато требует больше вычислений.
- Храните данные в
geometry(..., 4326)— это WGS 84, привычные широта и долгота. - Для метрических запросов либо приводите к
geography, либо проецируйте в локальную систему. - Для веб-карт часто используют 3857 (Web Mercator), но хранить исходные 4326 удобнее.
Порядок координат в PostGIS — X Y, то есть долгота, затем широта. Это частая причина «перевёрнутых» точек. ST_MakePoint(37.62, 55.75) даёт точку в Москве, а не наоборот.
Таблица, вставка и индекс
Колонку объявляют с типом и SRID, чтобы база проверяла данные. Индекс GiST нужен почти всегда: без него поиск по геометрии превращается в полный перебор.
CREATE TABLE places (
id bigserial PRIMARY KEY,
name text NOT NULL,
geom geometry(Point, 4326) NOT NULL
);
INSERT INTO places (name, geom)
VALUES ('Точка', ST_SetSRID(ST_MakePoint(37.62, 55.75), 4326));
CREATE INDEX places_geom_gix ON places USING GIST (geom);
CREATE INDEX places_geog_gix ON places USING GIST ((geom::geography));
ANALYZE places;
Для линий и полигонов используйте LINESTRING и POLYGON либо общий GEOMETRY, если типы смешаны. Ограничение типа ловит часть ошибок на входе, но не проверяет валидность полигона — для этого есть ST_IsValid.
Импорт из Shapefile выполняют утилитой shp2pgsql, из GeoJSON — ogr2ogr. Обе умеют создавать таблицу и индекс.
shp2pgsql -I -s 4326 -W UTF-8 roads.shp public.roads | psql -d geo
ogr2ogr -f PostgreSQL PG:"dbname=geo" places.geojson -nln places_import
psql -d geo -c "SELECT COUNT(*) FROM roads;"
Пространственные запросы
Главные функции — ST_Intersects, ST_DWithin, ST_Contains, ST_Distance. ST_DWithin предпочтительнее сравнения расстояния: он использует индекс и не вычисляет точное значение для каждой пары.
SELECT name
FROM places
WHERE ST_DWithin(
geom::geography,
ST_SetSRID(ST_MakePoint(37.60, 55.74), 4326)::geography,
1000
);
SELECT ST_AsGeoJSON(geom) FROM places LIMIT 5;
Здесь 1000 — метры, потому что обе стороны приведены к geography. Если оставить geometry в 4326, число будет означать градусы, и результат окажется неверным. Приведение к geography стоит производительности, поэтому для больших таблиц нужен индекс по выражению или отдельная колонка в метрической проекции.
Пересечения и вложенность:
SELECT a.id, b.id
FROM districts a
JOIN roads b ON ST_Intersects(a.geom, b.geom)
WHERE a.name = 'Центральный';
SELECT ST_Area(geom::geography) / 1000000 AS km2 FROM districts;
SELECT ST_Length(geom::geography) / 1000 AS km FROM roads;
Для поиска ближайших объектов применяют оператор <-> с индексом GiST. Он возвращает расстояние в единицах SRID, поэтому для метров нужна проекция или geography-колонка.
Растры и топология
Расширение postgis_raster хранит тайлы и позволяет выбирать значения по координатам. Растр удобен для высот, температуры и снимков, но требует аккуратной нарезки: один огромный растр замедляет запросы.
CREATE EXTENSION postgis_raster;
raster2pgsql -s 4326 -I -C -M dem.tif public.dem | psql -d geo
SELECT ST_Value(rast, ST_SetSRID(ST_MakePoint(37.6, 55.7), 4326))
FROM dem
WHERE ST_Intersects(rast, ST_SetSRID(ST_MakePoint(37.6, 55.7), 4326));
Топология (postgis_topology) хранит узлы, рёбра и грани и следит за связностью. Она нужна для редактирования карт и проверки качества, но добавляет сложность. Для обычных выборок достаточно геометрии и индексов.
Производительность и диагностика
Смотрите план запроса. Если GiST-индекс не используется, проверьте тип колонки, приведение и селективность условия.
EXPLAIN (ANALYZE, BUFFERS)
SELECT id FROM places
WHERE ST_DWithin(geom::geography, ST_SetSRID(ST_MakePoint(37.6, 55.7), 4326)::geography, 500);
VACUUM ANALYZE places;
SELECT Find_SRID('public', 'places', 'geom');
Типичные проблемы: индекс не на той колонке, сравнение геометрии с текстом, смешанный SRID, невалидные полигоны. ST_IsValidReason объясняет причину, ST_MakeValid пытается исправить объект. Для очень больших полигонов помогает ST_Subdivide: он режет геометрию на части и ускоряет пересечения.
Резервное копирование выполняйте через pg_dump, но помните: дамп содержит CREATE EXTENSION, а файлы расширения должны быть установлены на целевом сервере. Проверяйте восстановление на отдельной базе и не забывайте про ANALYZE после массовой загрузки.
Чек-лист внедрения
- SRID зафиксирован в типе колонки, координаты в порядке «долгота, широта».
- Для метрических запросов используется geography или проекция.
- На геометрические колонки созданы GiST-индексы и выполнен
ANALYZE. - Полигоны проверены на валидность до загрузки.
- Импорт и экспорт воспроизводимы, формат и кодировка зафиксированы.
- Бэкап проверен восстановлением на тестовой базе.
Итог
PostGIS превращает PostgreSQL в полноценную геоинформационную базу: типы, функции, индексы и работа с растрами находятся в одном месте. Ключевые решения — выбор geometry или geography, корректный SRID и индекс GiST. Начните с одной таблицы точек, проверьте ST_DWithin и план запроса, а затем расширяйте схему. Такой путь даёт предсказуемую производительность и не требует отдельного ГИС-сервера.