postgis
Overview
| Package | Version | Category | License | Language |
|---|---|---|---|---|
postgis | 3.6.2 | GIS | GPL-2.0 | C |
| ID | Extension | Bin | Lib | Load | Create | Trust | Reloc | Schema |
|---|---|---|---|---|---|---|---|---|
| 1500 | postgis | No | Yes | No | Yes | No | No | - |
| 1501 | postgis_topology | No | Yes | No | Yes | No | No | topology |
| 1502 | postgis_raster | No | Yes | No | Yes | No | No | - |
| 1503 | postgis_sfcgal | No | Yes | No | Yes | No | Yes | - |
| 1504 | postgis_tiger_geocoder | No | Yes | No | Yes | Yes | No | tiger |
| 1505 | address_standardizer | No | Yes | No | Yes | No | Yes | - |
| 1506 | address_standardizer_data_us | No | Yes | No | Yes | No | Yes | - |
Version
| Type | Repo | Version | PG Ver | Package | Deps |
|---|---|---|---|---|---|
| EXT | PGDG | 3.6.2 | 1817161514 | postgis | - |
| RPM | PGDG | 3.6.2 | 1817161514 | postgis36_$v | - |
| DEB | PGDG | 3.6.2 | 1817161514 | postgresql-$v-postgis-3 | - |
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 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
- PostGIS Reference – Full documentation
- Geometry Types – Point, LineString, Polygon, MultiPoint, etc.
- Spatial Relationships – ST_Contains, ST_Intersects, ST_Within, etc.
- Measurement Functions – ST_Distance, ST_Area, ST_Length, etc.
- Geometry Processing – ST_Buffer, ST_Union, ST_Intersection, etc.
- Geometry Input/Output – WKT, WKB, GeoJSON, KML, GML, SVG
- Coordinate Transformation – ST_Transform, ST_SetSRID
- Geography Functions – True spheroidal distance calculations
- Spatial Indexing – GiST and SP-GiST indexes
Setup
CREATE EXTENSION postgis;
Verify the installation:
SELECT PostGIS_Full_Version();
Core Data Types
PostGIS provides two primary spatial types:
| Type | Description | Coordinate System |
|---|---|---|
geometry | Planar (flat-earth) spatial type | Cartesian, uses SRID for projection |
geography | Spheroidal (round-earth) spatial type | Always 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
| Feature | geometry | geography |
|---|---|---|
| Coordinate system | Any projected CRS | WGS 84 only |
| Distance units | SRID units (degrees, meters, feet) | Meters |
| Accuracy over large areas | Requires correct projection | Accurate globally |
| Function support | Full (~300 functions) | Subset (~40 functions) |
| Index support | GiST, SP-GiST, BRIN | GiST |
| Performance | Faster | Slightly 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;
Feedback
Was this page helpful?
Thanks for the feedback! Please let us know how we can improve.
Sorry to hear that. Please let us know how we can improve.