cratestack_sql/filter/spatial.rs
1/// PostGIS spatial filter primitives. v1 ships two ops that cover the
2/// "is point inside this zone" / "is this point within radius of that
3/// zone" cases — the rest of the ST_* surface can land on demand.
4///
5/// All current variants treat the column as `geography(Point, 4326)`
6/// — the WGS-84 lat/lon CRS that PostGIS ships with extensions
7/// enabled. Casting happens at render time via `::geography` so
8/// schemas storing `geometry` or `geometry(...)` columns work without
9/// extra annotations, at the cost of an in-flight cast each call.
10#[derive(Debug, Clone, PartialEq)]
11pub enum SpatialFilter {
12 /// `ST_Covers(col::geography, ST_MakePoint($lng, $lat)::geography)`.
13 /// Matches when the column's geography fully covers (contains
14 /// including the boundary) the given point. Use for "is this
15 /// caller-supplied point inside the row's service area" lookups.
16 CoversGeographyPoint {
17 column: &'static str,
18 lng: f64,
19 lat: f64,
20 },
21 /// `ST_DWithin(col::geography, ST_MakePoint($lng, $lat)::geography,
22 /// $radius_meters)`. Matches when the column's geography is
23 /// within `radius_meters` of the given point (great-circle
24 /// distance on WGS-84, since `::geography` triggers the spheroid
25 /// path).
26 DWithinGeographyPoint {
27 column: &'static str,
28 lng: f64,
29 lat: f64,
30 radius_meters: f64,
31 },
32}
33
34/// Builder returned by [`crate::point`] for assembling a spatial
35/// filter. Holds nothing but the lat/lng pair until a comparator is
36/// chained.
37#[derive(Debug, Clone, Copy, PartialEq)]
38pub struct SpatialPoint {
39 pub lng: f64,
40 pub lat: f64,
41}
42
43/// Geographic point (WGS-84 lng/lat). The naming follows the PostGIS
44/// `ST_MakePoint(x, y)` convention — `lng` is the X axis (longitude),
45/// `lat` is the Y axis (latitude). Don't accidentally swap them;
46/// the engine has no way to detect it and your filter will silently
47/// match points across the world.
48pub const fn point(lng: f64, lat: f64) -> SpatialPoint {
49 SpatialPoint { lng, lat }
50}