Обработка геопространственных данных в PHP: от PostGIS до карт

В современном вебе всё чаще требуется работа с географической информацией: от поиска ближайших объектов до построения сложных геозон. PHP в сочетании с PostGIS — мощный стек для решения таких задач, который обходит ограничения простых координатных полей и открывает доступ к профессиональным GIS-операциям прямо из вашего приложения.

Почему PostGIS вместо простых координат?

Хранение широты и долготы в отдельных полях типа DECIMAL — устаревший подход. Он не позволяет эффективно выполнять пространственные запросы, проверять попадание точки в полигон или рассчитывать расстояния с учётом кривизны Земли. PostGIS — это расширение PostgreSQL, добавляющее поддержку географических объектов и более 300 пространственных функций.

Настройка окружения и базовые типы данных

Убедитесь, что в вашей PostgreSQL установлено расширение PostGIS: CREATE EXTENSION postgis;. Основные типы данных, которые вы будете использовать:

  • GEOMETRY — работа в декартовой системе координат (плоскость)
  • GEOGRAPHY — учёт сферичности Земли, более точные расчёты расстояний
  • POINT, LINESTRING, POLYGON — конкретные геометрические типы

Создадим таблицу для объектов с геоданными:

CREATE TABLE places (
id SERIAL PRIMARY KEY,
name VARCHAR(100),
geom GEOGRAPHY(Point, 4326)
);

Взаимодействие PHP с PostGIS: практические примеры

Вставка геоданных

Для работы используем PDO. Важно правильно формировать WKT (Well-Known Text) — текстовый формат представления геообъектов.

$pdo = new PDO('pgsql:host=localhost;dbname=geodb', 'user', 'pass');
$name = 'Центр города';
$latitude = 55.751244;
$longitude = 37.618423;

// Формируем WKT для точки
$wktPoint = "POINT($longitude $latitude)";

$stmt = $pdo->prepare(
"INSERT INTO places (name, geom) VALUES (?, ST_GeogFromText(?))"
);
$stmt->execute([$name, $wktPoint]);

Поиск объектов в радиусе

Одна из частых задач — найти все места в радиусе N километров от заданной точки. Используем функцию ST_DWithin для geography типа, которая учитывает сферичность.

$userLon = 37.617698;
$userLat = 55.755864;
$radiusMeters = 5000; // 5 км

$query = "
SELECT name,
ST_X(geom::geometry) AS lon,
ST_Y(geom::geometry) AS lat,
ST_Distance(geom, ST_MakePoint(?, ?)::geography) AS distance_m
FROM places
WHERE ST_DWithin(
geom,
ST_MakePoint(?, ?)::geography,
?
)
ORDER BY distance_m ASC
";

$stmt = $pdo->prepare($query);
$stmt->execute([$userLon, $userLat, $userLon, $userLat, $radiusMeters]);
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);

Сложные пространственные операции

Работа с полигонами и геозонами

Допустим, нам нужно определить, попадают ли точки доставки в определённую зону (например, зону покрытия службы). Создадим полигон и проверим вхождение точек.

// Создаём полигон (координаты вершин по порядку)
$polygonWKT = 'POLYGON((37.6 55.7, 37.8 55.7, 37.8 55.8, 37.6 55.8, 37.6 55.7))';

// Проверяем, какие точки находятся внутри полигона
$query = "
SELECT name
FROM places
WHERE ST_Within(
geom::geometry,
ST_GeomFromText(?, 4326)
)
";

$stmt = $pdo->prepare($query);
$stmt->execute([$polygonWKT]);

Оптимизация производительности пространственных запросов

Геопространственные запросы могут быть ресурсоёмкими. Ключевые методы оптимизации:

  • Использование GiST-индексов: CREATE INDEX idx_places_geom ON places USING GIST (geom);
  • Предварительная фильтрация по bounding box: сначала отсечь точки по прямоугольной области с помощью && оператора, затем выполнить точный расчёт.
  • Кэширование результатов для статических или редко меняющихся геоданных.

Пример комбинированного запроса с предварительной фильтрацией:

$query = "
SELECT name, ST_Distance(...) AS dist
FROM places
WHERE geom::geometry && ST_MakeEnvelope(?, ?, ?, ?, 4326) -- быстрая фильтрация
AND ST_DWithin(geom, ST_MakePoint(?, ?)::geography, ?) -- точный расчёт
";

Интеграция с картографическими сервисами

Полученные геоданные часто нужно визуализировать. Можно генерировать GeoJSON — стандартный формат для веб-карт.

$query = "
SELECT json_build_object(
'type', 'FeatureCollection',
'features', json_agg(
json_build_object(
'type', 'Feature',
'geometry', ST_AsGeoJSON(geom::geometry)::json,
'properties', json_build_object('name', name)
)
)
) AS geojson
FROM places
LIMIT 100
";

$stmt = $pdo->query($query);
$geojson = $stmt->fetchColumn();
// Теперь $geojson можно передать в Leaflet, Mapbox или другой JS-библиотеку

Заключение и дальнейшие шаги

Интеграция PHP с PostGIS открывает возможности для создания сложных геоинформационных систем прямо в веб-приложении. Для глубокого погружения изучите дополнительные функции: пространственные соединения (ST_Intersects, ST_Crosses), расчёт площадей (ST_Area), работу с растровыми данными и маршрутизацию через pgRouting. Начните с простых точечных объектов, постепенно переходя к сложным полигонам и многослойным картам, чтобы ваше приложение могло эффективно работать с любыми пространственными данными.

Автор: Разработчик