latlng

latlng vs PostGIS: live geofencing vs spatial SQL

PostGIS is the spatial database of record. latlng is an in-memory engine for objects that move and the events they trigger. They solve different problems, and they often work best side by side.

Reviewed against latlng v0.2.0 and PostGIS 3 on PostgreSQL · Updated September 27, 2026 · postgis.net · PostGIS docs

Summary

  • PostGIS wins on depth: SQL, transactions, disk-backed scale, hundreds of spatial functions, and the whole GIS ecosystem.
  • latlng wins on liveness: in-memory position updates, built-in enter/exit/cross/roam geofences, and durable webhook delivery.
  • They complement each other: latlng for the current state and events, PostGIS for history, analytics, and authoring fence polygons.

Feature comparison

What it is In-memory geospatial object engine and geofencing server Spatial extension for the PostgreSQL relational database
License MIT GPL-2.0-or-later (PostgreSQL itself uses the PostgreSQL License)
Data model Collections of objects keyed by ID, with fields and JSON documents SQL tables with any schema, foreign keys, and joins
Geometry Points, bounding boxes, geohashes, and GeoJSON geometries in WGS84 Full OGC Simple Features, geography type, curves, 3D/4D, raster, and topology in any coordinate system
Spatial operations Search predicates: nearby, within, intersects, with optional clipping Hundreds of ST_ functions: buffers, unions, area, length, simplification, reprojection, and more
Query language HTTP/JSON commands SQL
Indexing In-memory R-tree GiST, SP-GiST, and BRIN indexes on disk; KNN ordering with the <-> operator
Dataset size Working set must fit in memory Disk-backed; scales well beyond RAM
Write profile Built for continuous position updates to the same objects Each update writes a new row version, WAL, and index entries; high-rate updates need vacuum and table tuning
Transactions Each command is atomic; no multi-statement transactions Full ACID multi-statement transactions
Geofence events Built in: inside, outside, enter, exit, cross, and roam detection None built in; implement with triggers plus NOTIFY, or change data capture
Event delivery Durable HTTP webhooks with retries and dead-letter; WebSocket and Cap'n Proto streams LISTEN/NOTIFY reaches only currently connected sessions; durable delivery needs an outbox table or CDC pipeline
Authentication Bearer tokens and JWT/JWKS with per-collection permissions PostgreSQL roles, GRANTs, and row-level security
Replication and backup Append-only file with verify, backup, restore; single-leader follower replication WAL archiving, point-in-time recovery, streaming and logical replication, mature HA tooling
Ecosystem Young project; TypeScript SDK, Rust crates, WebAssembly build for the browser QGIS, GeoServer, GDAL/ogr2ogr, vector tile servers, every ORM, managed on all major clouds

Different jobs, not rivals

PostGIS is the spatial database of record: durable, transactional, queryable with SQL, and capable of almost any geometric computation. If you need to store parcels, road networks, or years of trip history and run analytics over them, PostGIS is the right tool and latlng is not trying to replace it.

latlng is built for the other half of many location systems: the live state of things that move, and reacting the moment one of them crosses a boundary. It keeps current positions in memory, evaluates geofences on every update, and pushes events out as webhooks or streams.

PostGIS (SQL)
CREATE TABLE fleet (
  id   text PRIMARY KEY,
  geom geography(Point, 4326)
);
CREATE INDEX ON fleet USING gist (geom);

INSERT INTO fleet VALUES
  ('truck1', ST_MakePoint(9.99, 53.55))
ON CONFLICT (id) DO UPDATE SET geom = EXCLUDED.geom;

SELECT id FROM fleet
WHERE ST_DWithin(geom, ST_MakePoint(9.99, 53.55)::geography, 1000);
latlng (HTTP/JSON)
curl -X POST localhost:7421/collections/fleet/objects/truck1 \
  -d '{"object":{"Point":{"lat":53.55,"lon":9.99,"z":null}}}'

curl -X POST localhost:7421/collections/fleet/search/nearby \
  -d '{"lat":53.55,"lon":9.99,"meters":1000,"options":{}}'

Geofencing on PostGIS

PostGIS can tell you whether a point is inside a polygon, but it won't tell you when that changes. A typical do-it-yourself setup adds a trigger on the positions table that runs ST_Contains against a fences table, compares the result with the previous membership stored per object, and calls pg_notify for each transition.

That works, with caveats: NOTIFY only reaches sessions that are listening at that moment, payloads are limited in size, and the fence check runs inside every position write. Reliable delivery usually means an outbox table plus a worker, or a change-data-capture pipeline such as logical decoding into a message broker.

latlng does this natively. Fences are stored queries with inside, outside, enter, exit, cross, and roam detection. Matching events are written to the durable log in the same batch as the mutation and delivered as webhooks with exponential backoff, retry budgets, and dead-lettering.

High-frequency position updates

PostgreSQL's MVCC design writes a new row version for every UPDATE. When the geometry column is indexed, updates can't use the heap-only-tuple optimization, so each position change also touches the spatial index and the WAL. For fleets reporting every few seconds, that means careful autovacuum tuning, fillfactor settings, or an append-only table partitioned by time.

latlng keeps the current position of each object in memory and updates its R-tree in place, with optional append-only-file persistence that group-commits writes. It is designed for exactly this "same object, new position, again and again" workload.

Using them together

A common architecture sends device updates to latlng for real-time indexing and geofencing, and writes the same stream (or latlng's webhook events) into PostGIS for history, reporting, and heavy spatial analysis. Fence polygons can be authored and versioned in PostGIS and pushed to latlng as GeoJSON hooks.

Choose latlng when

  • Positions change constantly and you care about the current state, not every historical row.
  • You need enter, exit, cross, or roam events pushed to other services reliably.
  • The live dataset fits in memory and you want low-latency nearby and within queries.
  • You want the same spatial engine in the browser for offline or local-first apps.

Choose PostGIS when

  • You need a durable system of record with SQL, joins, and multi-statement transactions.
  • Your data is larger than memory, or includes rasters, networks, or complex topology.
  • You need rich geometry processing: buffers, unions, reprojection, measurements.
  • You rely on GIS tooling such as QGIS, GeoServer, or GDAL, or want a managed database.

Frequently asked questions

Can PostGIS do real-time geofencing?

Not out of the box. PostGIS evaluates spatial predicates such as ST_Contains or ST_DWithin, but it has no geofence objects or enter/exit events. You can build them with triggers that track previous membership and call pg_notify, or with change data capture, but delivery, retries, and state tracking are up to you.

Is latlng a replacement for PostGIS?

No. latlng is an in-memory engine for live object positions and geofence events. It has no SQL, joins, or multi-statement transactions, and only a focused set of spatial predicates. Many systems use latlng for real-time state and PostGIS for history and analysis.

Why not just store live positions in PostgreSQL?

You can, and at moderate update rates it works well. At high rates, every position update creates a new row version, WAL traffic, and spatial index maintenance, which increases vacuum load and write amplification. latlng updates positions in memory and persists them to an append-only log instead.

Can latlng use PostGIS polygons as geofences?

Yes. Export fences from PostGIS with ST_AsGeoJSON and register them in latlng as hook or channel geofences with a GeoJSON area. The latlng CLI can also create hooks directly from GeoJSON files.