Обработка геопространственных данных в 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. Начните с простых точечных объектов, постепенно переходя к сложным полигонам и многослойным картам, чтобы ваше приложение могло эффективно работать с любыми пространственными данными.