PostGIS Basics
PostGIS adds geometry and geography types, spatial functions, and indexes to PostgreSQL. Keep transactional geospatial data in Postgres; push map rendering and heavy tile math to tile servers or client libraries.
Search across all documentation pages
PostGIS adds geometry and geography types, spatial functions, and indexes to PostgreSQL. Keep transactional geospatial data in Postgres; push map rendering and heavy tile math to tile servers or client libraries.
CREATE EXTENSION IF NOT EXISTS postgis WITH SCHEMA extensions VERSION '3.5.2';
CREATE TABLE stores (
id serial PRIMARY KEY,
name text NOT NULL,
geom extensions.geometry(Point, 4326) NOT NULL
);
INSERT INTO stores (name, geom) VALUES
('NYC Flagship', extensions.ST_SetSRID(extensions.ST_MakePoint(-73.9857, 40.7484), 4326)),
('LA Branch', extensions.ST_SetSRID(extensions.ST_MakePoint(-118.2437, 34.0522), 4326));
-- Nearby stores within 5 km of a point (geography for meter distances)
SELECT name,
extensions.ST_Distance(
geom::extensions.geography,
extensions.ST_SetSRID(extensions.ST_MakePoint(-73.99, 40.75), 4326)::extensions.geography
) AS meters
FROM stores
ORDER BY meters
LIMIT 10;When to reach for this: Delivery zones, asset tracking, geofencing, and spatial joins where data already lives in your OLTP database.
CREATE INDEX stores_geom_gist ON stores USING gist (geom);
EXPLAIN (ANALYZE, BUFFERS)
SELECT id, name
FROM stores
WHERE extensions.ST_DWithin(
geom::extensions.geography,
extensions.ST_SetSRID(extensions.ST_MakePoint(-73.99, 40.75), 4326)::extensions.geography,
5000
);
SELECT extensions.PostGIS_Version();
SELECT extensions.ST_AsGeoJSON(geom) FROM stores WHERE id = 1;What this demonstrates:
geometry(Point, 4326) stores WGS84 lat/longeography for geodesic meter distancesST_DWithin bounding-box prefilterST_AsGeoJSON feeds web maps without a separate export pipeline| Responsibility | Postgres + PostGIS | Tile server (Martin, pg_tileserv, Mapbox) |
|---|---|---|
| Store features | Yes | Reads from Postgres or cache |
| Spatial joins | Yes | No |
| Render map tiles | Possible but not ideal | Optimized MVT/WebP pipeline |
| Transactional updates | Yes | Eventually consistent cache |
| Heavy overlay analytics | SQL | Limited |
Pattern: PostGIS is the system of record. Tile server generates vector tiles for basemaps and overlays. App queries Postgres for search, routing inputs, and business logic.
geometry - Planar math in a projected coordinate system; fast for local areas with correct SRID.geography - Spheroid calculations; distances in meters on WGS84.CREATE EXTENSION postgis;
-- optional add-ons after core
CREATE EXTENSION postgis_topology;
CREATE EXTENSION postgis_raster; -- if raster workloads approvedPin versions in allowlist ADR. Upgrade with ALTER EXTENSION postgis UPDATE and postgis_extensions_upgrade() when release notes require.
-- GeoJSON for APIs
SELECT json_build_object(
'type', 'FeatureCollection',
'features', json_agg(extensions.ST_AsGeoJSON(s.*)::json)
) FROM stores s;ST_MakePoint(lon, lat) not (lat, lon). Fix: Code review convention; ST_Y/ST_X sanity tests.NOT NULL constraint with geometry(Point, 4326) and ST_SetSRID on insert.geography for meters or project to local UTM.ST_AsMVT on unindexed tables melts CPU. Fix: Dedicated tile layer with simplified geom and GiST.ST_IsValid check constraint or ST_MakeValid in ingest with audit.WITH SCHEMA extensions on greenfield installs.| Alternative | Use When | Don't Use When |
|---|---|---|
| Application geolib only | Few static points | Spatial joins across tables |
| Elasticsearch geo | Already on ES for search | Strong FK to relational core |
| Dedicated GIS server (GeoServer) | Complex WMS/WFS standards | Simple REST GeoJSON API |
| BigQuery GIS | Warehouse analytics | OLTP geofencing latency |
Stack versions: This page was written for PostgreSQL 18.4 (stable 18, maintenance 17), pgvector 0.8+, PostGIS 3.5+, pgbouncer 1.x, and Patroni 3.x.
Reviewed by Chris St. John·Last updated Jul 19, 2026