postgis

PostGIS geometry and geography spatial types and functions

Overview

PackageVersionCategoryLicenseLanguage
postgis3.6.2GISGPL-2.0C
IDExtensionBinLibLoadCreateTrustRelocSchema
1500postgisNoYesNoYesNoNo-
1501postgis_topologyNoYesNoYesNoNotopology
1502postgis_rasterNoYesNoYesNoNo-
1503postgis_sfcgalNoYesNoYesNoYes-
1504postgis_tiger_geocoderNoYesNoYesYesNotiger
1505address_standardizerNoYesNoYesNoYes-
1506address_standardizer_data_usNoYesNoYesNoYes-
Relatedpointcloud h3 pg_geohash geoip pg_polyline earthdistance ogr_fdw tzf
Depended Bydocumentdb h3_postgis mobilitydb pgrouting pointcloud_postgis postgis_raster postgis_sfcgal postgis_tiger_geocoder postgis_topology

Version

TypeRepoVersionPG VerPackageDeps
EXTPGDG3.6.21817161514postgis-
RPMPGDG3.6.21817161514postgis36_$v-
DEBPGDG3.6.21817161514postgresql-$v-postgis-3-
OS / PGPG18PG17PG16PG15PG14
el8.x86_64
el8.aarch64
el9.x86_64
el9.aarch64
el10.x86_64
el10.aarch64
d12.x86_64
d12.aarch64
PGDG 3.6.2
PGDG 3.6.2
PGDG 3.6.2
PGDG 3.6.2
PGDG 3.6.2
d13.x86_64
d13.aarch64
PGDG 3.6.2
PGDG 3.6.2
PGDG 3.6.2
PGDG 3.6.2
PGDG 3.6.2
u22.x86_64
u22.aarch64
u24.x86_64
u24.aarch64

Install

You can install postgis directly. First, make sure the PGDG repository is added and enabled:

pig repo add pgdg -u          # Add PGDG repo and update cache

Install the extension using pig or apt/yum/dnf:

pig install postgis;          # Install for current active PG version
pig ext install -y postgis -v 18  # PG 18
pig ext install -y postgis -v 17  # PG 17
pig ext install -y postgis -v 16  # PG 16
pig ext install -y postgis -v 15  # PG 15
pig ext install -y postgis -v 14  # PG 14
dnf install -y postgis36_18       # PG 18
dnf install -y postgis36_17       # PG 17
dnf install -y postgis36_16       # PG 16
dnf install -y postgis36_15       # PG 15
dnf install -y postgis36_14       # PG 14
apt install -y postgresql-18-postgis-3   # PG 18
apt install -y postgresql-17-postgis-3   # PG 17
apt install -y postgresql-16-postgis-3   # PG 16
apt install -y postgresql-15-postgis-3   # PG 15
apt install -y postgresql-14-postgis-3   # PG 14

Create Extension:

CREATE EXTENSION postgis;

Usage

PostGIS: Spatial and Geographic objects for PostgreSQL

PostGIS adds support for geographic objects to PostgreSQL, turning it into a spatial database. It implements the OGC Simple Features specification and provides spatial indexing, spatial functions, and coordinate transformation capabilities.

Documentation

Setup

CREATE EXTENSION postgis;

Verify the installation:

SELECT PostGIS_Full_Version();

Core Data Types

PostGIS provides two primary spatial types:

TypeDescriptionCoordinate System
geometryPlanar (flat-earth) spatial typeCartesian, uses SRID for projection
geographySpheroidal (round-earth) spatial typeAlways lon/lat on WGS 84 (SRID 4326)

Geometry

The geometry type works in a projected coordinate system. It is fast and supports the full range of PostGIS functions. Best suited when working within a single projected coordinate system (e.g., UTM zones, state planes).

Geography

The geography type performs calculations on a sphere/spheroid. Distances and areas are returned in meters. It has a smaller set of supported functions but gives accurate results over large areas without needing to choose a projection.

-- Geography column: distances are in meters, always WGS 84
CREATE TABLE cities (
    name text PRIMARY KEY,
    location geography(Point, 4326)
);

INSERT INTO cities VALUES
    ('New York',  ST_GeogFromText('POINT(-74.006 40.7128)')),
    ('London',    ST_GeogFromText('POINT(-0.1278 51.5074)')),
    ('Tokyo',     ST_GeogFromText('POINT(139.6917 35.6895)'));

-- Distance in meters (great-circle)
SELECT a.name, b.name, ST_Distance(a.location, b.location) / 1000 AS distance_km
FROM cities a, cities b WHERE a.name < b.name;

Creating Spatial Tables

A spatial column has a geometry type, a dimensionality (2D, 3D, 4D), and a Spatial Reference System Identifier (SRID).

CREATE TABLE buildings (
    id serial PRIMARY KEY,
    name text,
    geom geometry(Polygon, 4326)
);

CREATE TABLE roads (
    id serial PRIMARY KEY,
    name text,
    geom geometry(LineString, 4326)
);

CREATE TABLE sensors (
    id serial PRIMARY KEY,
    label text,
    geom geometry(Point, 4326)
);

Inserting Spatial Data

From Well-Known Text (WKT):

INSERT INTO sensors (label, geom) VALUES
    ('S1', ST_GeomFromText('POINT(-73.985 40.748)', 4326)),
    ('S2', ST_GeomFromText('POINT(-73.979 40.754)', 4326));

Using constructor functions:

INSERT INTO sensors (label, geom) VALUES
    ('S3', ST_SetSRID(ST_MakePoint(-73.990, 40.735), 4326));

From GeoJSON:

INSERT INTO buildings (name, geom) VALUES
    ('Plaza', ST_GeomFromGeoJSON('{"type":"Polygon","coordinates":[[[-73.98,40.74],[-73.97,40.74],[-73.97,40.75],[-73.98,40.75],[-73.98,40.74]]]}'));

Spatial Indexing

GiST indexes are essential for spatial query performance. Always create one on spatial columns:

CREATE INDEX idx_sensors_geom ON sensors USING GIST (geom);
CREATE INDEX idx_buildings_geom ON buildings USING GIST (geom);
CREATE INDEX idx_roads_geom ON roads USING GIST (geom);

The spatial index enables the bounding-box operators (&&, @, ~) and is used automatically by spatial functions like ST_DWithin, ST_Intersects, and ST_Contains in WHERE clauses.

For very large datasets, consider BRIN indexes:

CREATE INDEX idx_sensors_geom_brin ON sensors USING BRIN (geom);

Core Spatial Functions

Creating Geometries

-- Point from coordinates
SELECT ST_MakePoint(-73.985, 40.748);

-- Point with SRID
SELECT ST_SetSRID(ST_MakePoint(-73.985, 40.748), 4326);

-- From WKT
SELECT ST_GeomFromText('LINESTRING(0 0, 1 1, 2 1)', 4326);

-- From GeoJSON
SELECT ST_GeomFromGeoJSON('{"type":"Point","coordinates":[-73.985,40.748]}');

-- Make a line from two points
SELECT ST_MakeLine(
    ST_MakePoint(0, 0),
    ST_MakePoint(1, 1)
);

-- Make a polygon from a closed linestring
SELECT ST_MakePolygon(
    ST_GeomFromText('LINESTRING(0 0, 1 0, 1 1, 0 1, 0 0)')
);

Measurement

-- Distance between two geometries (in SRID units)
SELECT ST_Distance(
    ST_GeomFromText('POINT(0 0)', 4326),
    ST_GeomFromText('POINT(1 1)', 4326)
);

-- Distance in meters using geography
SELECT ST_Distance(
    'SRID=4326;POINT(-73.985 40.748)'::geography,
    'SRID=4326;POINT(-0.1278 51.5074)'::geography
);

-- Area (in SRID units, or square meters for geography)
SELECT ST_Area(geom) FROM buildings;

-- Length of a linestring
SELECT ST_Length(geom) FROM roads;

-- Perimeter of a polygon
SELECT ST_Perimeter(geom) FROM buildings;

Spatial Relationships

-- Does A contain B?
SELECT ST_Contains(a.geom, b.geom) FROM buildings a, sensors b;

-- Does A intersect B?
SELECT ST_Intersects(a.geom, b.geom) FROM buildings a, roads b;

-- Is B within distance D of A? (index-friendly)
SELECT ST_DWithin(a.geom, b.geom, 0.01) FROM sensors a, sensors b;

-- Are A and B within distance D? (geography, meters)
SELECT ST_DWithin(a.location, b.location, 10000) FROM cities a, cities b;

-- Does A touch B? (share boundary but not interior)
SELECT ST_Touches(a.geom, b.geom) FROM buildings a, roads b;

-- Does A cross B?
SELECT ST_Crosses(a.geom, b.geom) FROM roads a, roads b;

-- Does A overlap B? (same dimension, not identical)
SELECT ST_Overlaps(a.geom, b.geom) FROM buildings a, buildings b;

Geometry Processing

-- Buffer a geometry (expand by distance)
SELECT ST_Buffer(geom, 0.001) FROM sensors;

-- Intersection of two geometries
SELECT ST_Intersection(a.geom, b.geom) FROM buildings a, buildings b
WHERE ST_Intersects(a.geom, b.geom) AND a.id < b.id;

-- Union of geometries
SELECT ST_Union(geom) FROM buildings WHERE name LIKE 'Block%';

-- Convex hull
SELECT ST_ConvexHull(ST_Collect(geom)) FROM sensors;

-- Simplify a geometry (Douglas-Peucker)
SELECT ST_Simplify(geom, 0.0001) FROM roads;

-- Centroid
SELECT ST_Centroid(geom) FROM buildings;

-- Voronoi diagram
SELECT ST_VoronoiPolygons(ST_Collect(geom)) FROM sensors;

Coordinate Transformation

-- Transform from WGS 84 (4326) to Web Mercator (3857)
SELECT ST_Transform(geom, 3857) FROM sensors;

-- Transform to a UTM zone for meter-based calculations
SELECT ST_Area(ST_Transform(geom, 32618)) AS area_sqm FROM buildings;

-- Set the SRID on a geometry (does NOT reproject)
SELECT ST_SetSRID(geom, 4326) FROM sensors;

-- Get the current SRID
SELECT ST_SRID(geom) FROM sensors;

Output Formats

-- To GeoJSON
SELECT ST_AsGeoJSON(geom) FROM sensors;

-- To WKT
SELECT ST_AsText(geom) FROM sensors;

-- To KML
SELECT ST_AsKML(geom) FROM sensors;

-- To SVG path
SELECT ST_AsSVG(geom) FROM buildings;

-- To EWKT (includes SRID)
SELECT ST_AsEWKT(geom) FROM sensors;

Practical Examples

Find Nearby Points

Find all sensors within 500 meters of a given location:

SELECT label, ST_Distance(
    geom::geography,
    ST_SetSRID(ST_MakePoint(-73.985, 40.748), 4326)::geography
) AS distance_m
FROM sensors
WHERE ST_DWithin(
    geom::geography,
    ST_SetSRID(ST_MakePoint(-73.985, 40.748), 4326)::geography,
    500
)
ORDER BY distance_m;

Spatial Join

Find which building each sensor is inside:

SELECT s.label, b.name
FROM sensors s
JOIN buildings b ON ST_Contains(b.geom, s.geom);

Count Points in Polygons

SELECT b.name, COUNT(s.id) AS sensor_count
FROM buildings b
LEFT JOIN sensors s ON ST_Contains(b.geom, s.geom)
GROUP BY b.name;

K-Nearest Neighbors

Find the 5 closest sensors to a point using the index-accelerated <-> operator:

SELECT label, geom <-> ST_SetSRID(ST_MakePoint(-73.985, 40.748), 4326) AS dist
FROM sensors
ORDER BY geom <-> ST_SetSRID(ST_MakePoint(-73.985, 40.748), 4326)
LIMIT 5;

Aggregate into a Single Geometry

-- Collect all sensor points into a MultiPoint
SELECT ST_Collect(geom) FROM sensors;

-- Compute the minimum bounding circle
SELECT ST_MinimumBoundingCircle(ST_Collect(geom)) FROM sensors;

Geography vs Geometry

Featuregeometrygeography
Coordinate systemAny projected CRSWGS 84 only
Distance unitsSRID units (degrees, meters, feet)Meters
Accuracy over large areasRequires correct projectionAccurate globally
Function supportFull (~300 functions)Subset (~40 functions)
Index supportGiST, SP-GiST, BRINGiST
PerformanceFasterSlightly slower

A common pattern is to store data as geography for correctness, and cast to geometry when needed for functions not available on geography:

SELECT ST_Area(geom::geography) AS area_sqm FROM buildings;

Last Modified 2026-03-12: add pg extension catalog (95749bf)