ST_Buffer(geom, distance_meters) → Polygon. Expands geometry by distance using equirectangular approximation. - Point → 32-sided circle polygon - LineString → buffered corridor (parallel offset + end caps) - Polygon → expanded polygon (vertex offset outward) Meter-to-degree conversion: `Δlng = meters / (111320 * cos(lat))`, `Δlat = meters / 110540`. Accurate to <0.5% for ±70° latitude.
(geom: &Geometry, distance_meters: f64, segments: usize)
| 17 | /// Meter-to-degree conversion: `Δlng = meters / (111320 * cos(lat))`, |
| 18 | /// `Δlat = meters / 110540`. Accurate to <0.5% for ±70° latitude. |
| 19 | pub fn st_buffer(geom: &Geometry, distance_meters: f64, segments: usize) -> Geometry { |
| 20 | let segments = segments.max(4); |
| 21 | |
| 22 | match geom { |
| 23 | Geometry::Point { coordinates } => { |
| 24 | buffer_point(coordinates[0], coordinates[1], distance_meters, segments) |
| 25 | } |
| 26 | Geometry::LineString { coordinates } => { |
| 27 | buffer_linestring(coordinates, distance_meters, segments) |
| 28 | } |
| 29 | Geometry::Polygon { coordinates } => buffer_polygon(coordinates, distance_meters, segments), |
| 30 | // Multi* types: buffer each component and collect. |
| 31 | Geometry::MultiPoint { coordinates } => { |
| 32 | let polys: Vec<Vec<Vec<[f64; 2]>>> = coordinates |
| 33 | .iter() |
| 34 | .map(|pt| { |
| 35 | if let Geometry::Polygon { coordinates: rings } = |
| 36 | buffer_point(pt[0], pt[1], distance_meters, segments) |
| 37 | { |
| 38 | rings |
| 39 | } else { |
| 40 | vec![] |
| 41 | } |
| 42 | }) |
| 43 | .collect(); |
| 44 | Geometry::MultiPolygon { coordinates: polys } |
| 45 | } |
| 46 | _ => { |
| 47 | // For unsupported types, fall back to buffering the bbox. |
| 48 | let bb = geometry_bbox(geom).expand_meters(distance_meters); |
| 49 | bbox_to_polygon(&bb) |
| 50 | } |
| 51 | } |
| 52 | } |
| 53 | |
| 54 | /// ST_Envelope(geom) → Polygon — bounding box as a polygon. |
| 55 | pub fn st_envelope(geom: &Geometry) -> Geometry { |