PostGIS
What it is
PostGIS turns PostgreSQL into a spatial database. It adds geometry and geography types, spatial indexes, coordinate-system transforms, and hundreds of functions for measuring and relating locations and shapes.
What it provides
- Points, lines, polygons, rasters, and geographic coordinates
- Distance, containment, intersection, nearest-neighbor, and transform operations
- GiST and SP-GiST spatial indexes for large location datasets
Use it for store locators, delivery zones, geofencing, route analysis, property boundaries, map tiles, or "find everything within five kilometers" searches.
Use it with Rent
extension postgis {
name = "postgis"
}
model Post {
id BigInt @id
title String
location postgis.Point? @db.Geometry(Point, 4326)
}Add rent-ext-postgis to your dependencies, enable Rent's postgres and extensions features, then run
rent generate and rent migrate dev --name add_locations. The generated field is Option<Point>.
use rent_ext_postgis::Point;
let office = Point::try_new(-87.6298, 41.8781, 4326)?;
let nearby = client
.post()
.order_by_location_distance(office)?
.limit(25)
.all()
.await?;
The generated create/update setters accept .location(office). .clear_location() stores SQL NULL.
Coordinates and SRIDs are validated before binding, and native EWKB preserves the point's SRID through
reads, projections, eager loading, and transactions. The generated spatial field currently represents a
two-dimensional geometry point. Other PostGIS types remain available through explicit SQL expressions.
Units matter: geometry calculations use the coordinate system's units. EPSG:4326 coordinates are degrees, so this ordering does not return meters or a geodesic distance. For meter-based radius searches, choose an appropriate projected coordinate system or an explicit geography calculation. Do not mix SRIDs.
Run the application
Add the extension declaration above to your application's schema. Confirm that your PostgreSQL provider offers the extension and that your migration role can install it, then apply the migration and run your application:
rent migrate dev --name enable_postgis
cargo runComplete example
Download this project, or copy the files below into an empty directory.
Replace RENT_REPOSITORY_URL in Cargo.toml with the URL supplied with your preview access. Use the matching Rent CLI release.
Use an empty PostgreSQL database with the extension binaries available on its server. Set DATABASE_URL
to that database and follow any server setup requirements described above.
rent extension check --provider self-hosted
rent generate
rent migrate dev --name initial
cargo runIf your host preinstalls other extensions, declare those in RSL as well. For a database with existing application tables, baseline it first.
Cargo.toml
[package]
name = "rent-extension-postgis"
version = "0.1.0"
edition = "2024"
[workspace]
[[bin]]
name = "rent-extension-postgis"
path = "main.rs"
[dependencies]
rent = { git = "RENT_REPOSITORY_URL", tag = "v0.8.6", default-features = false, features = ["postgres", "extensions", "migrate"] }
rent-ext-postgis = { git = "RENT_REPOSITORY_URL", tag = "v0.8.6" }
anyhow = "1.0.104"
tokio = { version = "1.53.1", features = ["full"] }rent.toml
schema = "schema.rsl"
generated = "generated"
migrations = "migrations"
[database]
dialect = "postgres"
backends = ["postgres"]schema.rsl
datasource db {
provider = "postgresql"
}
extension postgis {
name = "postgis"
}
model Post {
id BigInt @id
title String
location postgis.Point? @db.Geometry(Point, 4326)
}main.rs
use anyhow::Result;
use rent::driver::DatabasePool;
#[allow(dead_code, unused_imports)]
#[rustfmt::skip]
#[path = "generated/mod.rs"]
mod generated;
#[tokio::main]
async fn main() -> Result<()> {
let pool = DatabasePool::connect_url(&std::env::var("DATABASE_URL")?).await?;
let client = generated::client::Client::new(pool.clone());
use rent_ext_postgis::Point;
let office = Point::try_new(-87.6298, 41.8781, 4326)?;
client
.upsert_post(generated::post::unique::id)
.id(1)
.title("Acme office")
.location(office)
.save()
.await?;
let nearby = client.post().order_by_location_distance(office)?.limit(10).all().await?;
println!("Nearby posts: {}", nearby.len());
Ok(())
}