Skip to content

Rust Geospatial Development Best Practices

Objective: Master senior-level Rust geospatial development patterns for production systems. When you need to build high-performance geospatial applications, when you want to leverage Rust's speed for spatial data processing, when you need enterprise-grade geospatial patternsโ€”these best practices become your weapon of choice.

Core Principles

  • Spatial Data Types: Use appropriate spatial data structures
  • Coordinate Systems: Handle different coordinate reference systems
  • Spatial Indexing: Implement efficient spatial indexing
  • Spatial Operations: Use optimized spatial algorithms
  • Performance: Leverage Rust's speed for spatial computations

Geospatial Development Patterns

Spatial Data Structures

// rust/01-spatial-data-structures.rs

/*
Spatial data structures and best practices
*/

use std::collections::HashMap;
use serde::{Deserialize, Serialize};

/// Point in 2D space.
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct Point {
    pub x: f64,
    pub y: f64,
}

impl Point {
    pub fn new(x: f64, y: f64) -> Self {
        Self { x, y }
    }

    /// Calculate distance to another point.
    pub fn distance_to(&self, other: &Point) -> f64 {
        let dx = self.x - other.x;
        let dy = self.y - other.y;
        (dx * dx + dy * dy).sqrt()
    }

    /// Calculate bearing to another point.
    pub fn bearing_to(&self, other: &Point) -> f64 {
        let dx = other.x - self.x;
        let dy = other.y - self.y;
        dy.atan2(dx).to_degrees()
    }
}

/// Bounding box for spatial queries.
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct BoundingBox {
    pub min_x: f64,
    pub min_y: f64,
    pub max_x: f64,
    pub max_y: f64,
}

impl BoundingBox {
    pub fn new(min_x: f64, min_y: f64, max_x: f64, max_y: f64) -> Self {
        Self { min_x, min_y, max_x, max_y }
    }

    /// Check if point is within bounding box.
    pub fn contains(&self, point: &Point) -> bool {
        point.x >= self.min_x && point.x <= self.max_x &&
        point.y >= self.min_y && point.y <= self.max_y
    }

    /// Check if bounding box intersects with another.
    pub fn intersects(&self, other: &BoundingBox) -> bool {
        !(self.max_x < other.min_x || self.min_x > other.max_x ||
          self.max_y < other.min_y || self.min_y > other.max_y)
    }

    /// Expand bounding box to include point.
    pub fn expand_to_include(&mut self, point: &Point) {
        self.min_x = self.min_x.min(point.x);
        self.min_y = self.min_y.min(point.y);
        self.max_x = self.max_x.max(point.x);
        self.max_y = self.max_y.max(point.y);
    }

    /// Calculate area of bounding box.
    pub fn area(&self) -> f64 {
        (self.max_x - self.min_x) * (self.max_y - self.min_y)
    }
}

/// Polygon with holes.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Polygon {
    pub exterior: Vec<Point>,
    pub holes: Vec<Vec<Point>>,
}

impl Polygon {
    pub fn new(exterior: Vec<Point>) -> Self {
        Self {
            exterior,
            holes: Vec::new(),
        }
    }

    /// Add a hole to the polygon.
    pub fn add_hole(&mut self, hole: Vec<Point>) {
        self.holes.push(hole);
    }

    /// Check if point is inside polygon.
    pub fn contains(&self, point: &Point) -> bool {
        if !self.point_in_ring(point, &self.exterior) {
            return false;
        }

        // Check if point is in any hole
        for hole in &self.holes {
            if self.point_in_ring(point, hole) {
                return false;
            }
        }

        true
    }

    /// Check if point is inside a ring using ray casting.
    fn point_in_ring(&self, point: &Point, ring: &[Point]) -> bool {
        let mut inside = false;
        let mut j = ring.len() - 1;

        for i in 0..ring.len() {
            let pi = &ring[i];
            let pj = &ring[j];

            if ((pi.y > point.y) != (pj.y > point.y)) &&
               (point.x < (pj.x - pi.x) * (point.y - pi.y) / (pj.y - pi.y) + pi.x) {
                inside = !inside;
            }
            j = i;
        }

        inside
    }

    /// Calculate area of polygon.
    pub fn area(&self) -> f64 {
        let mut area = self.ring_area(&self.exterior);

        // Subtract area of holes
        for hole in &self.holes {
            area -= self.ring_area(hole);
        }

        area.abs()
    }

    /// Calculate area of a ring.
    fn ring_area(&self, ring: &[Point]) -> f64 {
        if ring.len() < 3 {
            return 0.0;
        }

        let mut area = 0.0;
        let mut j = ring.len() - 1;

        for i in 0..ring.len() {
            area += (ring[j].x + ring[i].x) * (ring[j].y - ring[i].y);
            j = i;
        }

        area / 2.0
    }

    /// Get bounding box of polygon.
    pub fn bounding_box(&self) -> BoundingBox {
        let mut bbox = BoundingBox::new(
            f64::INFINITY, f64::INFINITY,
            f64::NEG_INFINITY, f64::NEG_INFINITY,
        );

        for point in &self.exterior {
            bbox.expand_to_include(point);
        }

        for hole in &self.holes {
            for point in hole {
                bbox.expand_to_include(point);
            }
        }

        bbox
    }
}

/// Line string (polyline).
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LineString {
    pub points: Vec<Point>,
}

impl LineString {
    pub fn new(points: Vec<Point>) -> Self {
        Self { points }
    }

    /// Calculate length of line string.
    pub fn length(&self) -> f64 {
        if self.points.len() < 2 {
            return 0.0;
        }

        let mut length = 0.0;
        for i in 1..self.points.len() {
            length += self.points[i-1].distance_to(&self.points[i]);
        }
        length
    }

    /// Get bounding box of line string.
    pub fn bounding_box(&self) -> BoundingBox {
        if self.points.is_empty() {
            return BoundingBox::new(0.0, 0.0, 0.0, 0.0);
        }

        let mut bbox = BoundingBox::new(
            f64::INFINITY, f64::INFINITY,
            f64::NEG_INFINITY, f64::NEG_INFINITY,
        );

        for point in &self.points {
            bbox.expand_to_include(point);
        }

        bbox
    }
}

/// Spatial feature with geometry and properties.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SpatialFeature {
    pub id: String,
    pub geometry: Geometry,
    pub properties: HashMap<String, serde_json::Value>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Geometry {
    Point(Point),
    LineString(LineString),
    Polygon(Polygon),
    MultiPoint(Vec<Point>),
    MultiLineString(Vec<LineString>),
    MultiPolygon(Vec<Polygon>),
}

impl Geometry {
    /// Get bounding box of geometry.
    pub fn bounding_box(&self) -> BoundingBox {
        match self {
            Geometry::Point(point) => {
                BoundingBox::new(point.x, point.y, point.x, point.y)
            }
            Geometry::LineString(linestring) => linestring.bounding_box(),
            Geometry::Polygon(polygon) => polygon.bounding_box(),
            Geometry::MultiPoint(points) => {
                if points.is_empty() {
                    return BoundingBox::new(0.0, 0.0, 0.0, 0.0);
                }
                let mut bbox = BoundingBox::new(
                    f64::INFINITY, f64::INFINITY,
                    f64::NEG_INFINITY, f64::NEG_INFINITY,
                );
                for point in points {
                    bbox.expand_to_include(point);
                }
                bbox
            }
            Geometry::MultiLineString(linestrings) => {
                if linestrings.is_empty() {
                    return BoundingBox::new(0.0, 0.0, 0.0, 0.0);
                }
                let mut bbox = linestrings[0].bounding_box();
                for linestring in &linestrings[1..] {
                    let ls_bbox = linestring.bounding_box();
                    bbox.min_x = bbox.min_x.min(ls_bbox.min_x);
                    bbox.min_y = bbox.min_y.min(ls_bbox.min_y);
                    bbox.max_x = bbox.max_x.max(ls_bbox.max_x);
                    bbox.max_y = bbox.max_y.max(ls_bbox.max_y);
                }
                bbox
            }
            Geometry::MultiPolygon(polygons) => {
                if polygons.is_empty() {
                    return BoundingBox::new(0.0, 0.0, 0.0, 0.0);
                }
                let mut bbox = polygons[0].bounding_box();
                for polygon in &polygons[1..] {
                    let poly_bbox = polygon.bounding_box();
                    bbox.min_x = bbox.min_x.min(poly_bbox.min_x);
                    bbox.min_y = bbox.min_y.min(poly_bbox.min_y);
                    bbox.max_x = bbox.max_x.max(poly_bbox.max_x);
                    bbox.max_y = bbox.max_y.max(poly_bbox.max_y);
                }
                bbox
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_point_distance() {
        let p1 = Point::new(0.0, 0.0);
        let p2 = Point::new(3.0, 4.0);
        assert_eq!(p1.distance_to(&p2), 5.0);
    }

    #[test]
    fn test_bounding_box_contains() {
        let bbox = BoundingBox::new(0.0, 0.0, 10.0, 10.0);
        let point = Point::new(5.0, 5.0);
        assert!(bbox.contains(&point));

        let point_outside = Point::new(15.0, 15.0);
        assert!(!bbox.contains(&point_outside));
    }

    #[test]
    fn test_polygon_area() {
        let exterior = vec![
            Point::new(0.0, 0.0),
            Point::new(10.0, 0.0),
            Point::new(10.0, 10.0),
            Point::new(0.0, 10.0),
        ];
        let polygon = Polygon::new(exterior);
        assert_eq!(polygon.area(), 100.0);
    }

    #[test]
    fn test_polygon_contains() {
        let exterior = vec![
            Point::new(0.0, 0.0),
            Point::new(10.0, 0.0),
            Point::new(10.0, 10.0),
            Point::new(0.0, 10.0),
        ];
        let polygon = Polygon::new(exterior);

        let inside_point = Point::new(5.0, 5.0);
        assert!(polygon.contains(&inside_point));

        let outside_point = Point::new(15.0, 15.0);
        assert!(!polygon.contains(&outside_point));
    }
}

Spatial Indexing

// rust/02-spatial-indexing.rs

/*
Spatial indexing patterns and best practices
*/

use std::collections::HashMap;
use serde::{Deserialize, Serialize};

/// R-tree for spatial indexing.
pub struct RTree {
    root: Option<Box<RTreeNode>>,
    max_entries: usize,
    min_entries: usize,
}

#[derive(Debug, Clone)]
pub struct RTreeNode {
    pub bounding_box: BoundingBox,
    pub entries: Vec<RTreeEntry>,
    pub is_leaf: bool,
}

#[derive(Debug, Clone)]
pub enum RTreeEntry {
    Leaf {
        id: String,
        bounding_box: BoundingBox,
        data: SpatialFeature,
    },
    Node {
        node: Box<RTreeNode>,
    },
}

impl RTree {
    pub fn new(max_entries: usize) -> Self {
        Self {
            root: None,
            max_entries,
            min_entries: max_entries / 2,
        }
    }

    /// Insert a spatial feature into the R-tree.
    pub fn insert(&mut self, feature: SpatialFeature) -> Result<(), String> {
        let bounding_box = feature.geometry.bounding_box();
        let entry = RTreeEntry::Leaf {
            id: feature.id.clone(),
            bounding_box,
            data: feature,
        };

        if let Some(root) = &mut self.root {
            self.insert_entry(entry, root)?;
        } else {
            // Create root node
            let mut root = RTreeNode {
                bounding_box: bounding_box,
                entries: vec![entry],
                is_leaf: true,
            };
            self.root = Some(Box::new(root));
        }

        Ok(())
    }

    /// Insert entry into node.
    fn insert_entry(&mut self, entry: RTreeEntry, node: &mut RTreeNode) -> Result<(), String> {
        if node.is_leaf {
            node.entries.push(entry);
            if node.entries.len() > self.max_entries {
                self.split_node(node)?;
            }
        } else {
            // Find best child node
            let mut best_child = 0;
            let mut min_expansion = f64::INFINITY;

            for (i, child_entry) in node.entries.iter().enumerate() {
                if let RTreeEntry::Node { node: child_node } = child_entry {
                    let expansion = self.calculate_expansion(&entry, &child_node.bounding_box);
                    if expansion < min_expansion {
                        min_expansion = expansion;
                        best_child = i;
                    }
                }
            }

            // Insert into best child
            if let RTreeEntry::Node { node: child_node } = &mut node.entries[best_child] {
                self.insert_entry(entry, child_node)?;
            }
        }

        // Update bounding box
        self.update_bounding_box(node);
        Ok(())
    }

    /// Split node when it exceeds maximum entries.
    fn split_node(&mut self, node: &mut RTreeNode) -> Result<(), String> {
        if node.entries.len() <= self.max_entries {
            return Ok(());
        }

        // Simple split: divide entries into two groups
        let mid = node.entries.len() / 2;
        let mut entries1 = node.entries.drain(..mid).collect::<Vec<_>>();
        let entries2 = node.entries;

        // Create new nodes
        let mut node1 = RTreeNode {
            bounding_box: BoundingBox::new(0.0, 0.0, 0.0, 0.0),
            entries: entries1,
            is_leaf: node.is_leaf,
        };
        let mut node2 = RTreeNode {
            bounding_box: BoundingBox::new(0.0, 0.0, 0.0, 0.0),
            entries: entries2,
            is_leaf: node.is_leaf,
        };

        // Update bounding boxes
        self.update_bounding_box(&mut node1);
        self.update_bounding_box(&mut node2);

        // Replace current node with two child nodes
        node.entries = vec![
            RTreeEntry::Node { node: Box::new(node1) },
            RTreeEntry::Node { node: Box::new(node2) },
        ];
        node.is_leaf = false;

        Ok(())
    }

    /// Calculate expansion needed to include entry.
    fn calculate_expansion(&self, entry: &RTreeEntry, bbox: &BoundingBox) -> f64 {
        let entry_bbox = match entry {
            RTreeEntry::Leaf { bounding_box, .. } => bounding_box,
            RTreeEntry::Node { node } => &node.bounding_box,
        };

        let new_bbox = BoundingBox::new(
            bbox.min_x.min(entry_bbox.min_x),
            bbox.min_y.min(entry_bbox.min_y),
            bbox.max_x.max(entry_bbox.max_x),
            bbox.max_y.max(entry_bbox.max_y),
        );

        new_bbox.area() - bbox.area()
    }

    /// Update bounding box of node.
    fn update_bounding_box(&self, node: &mut RTreeNode) {
        if node.entries.is_empty() {
            return;
        }

        let mut bbox = match &node.entries[0] {
            RTreeEntry::Leaf { bounding_box, .. } => *bounding_box,
            RTreeEntry::Node { node: child_node } => child_node.bounding_box,
        };

        for entry in &node.entries[1..] {
            let entry_bbox = match entry {
                RTreeEntry::Leaf { bounding_box, .. } => bounding_box,
                RTreeEntry::Node { node: child_node } => &child_node.bounding_box,
            };

            bbox.min_x = bbox.min_x.min(entry_bbox.min_x);
            bbox.min_y = bbox.min_y.min(entry_bbox.min_y);
            bbox.max_x = bbox.max_x.max(entry_bbox.max_x);
            bbox.max_y = bbox.max_y.max(entry_bbox.max_y);
        }

        node.bounding_box = bbox;
    }

    /// Search for features within bounding box.
    pub fn search(&self, bbox: &BoundingBox) -> Vec<&SpatialFeature> {
        let mut results = Vec::new();

        if let Some(root) = &self.root {
            self.search_node(bbox, root, &mut results);
        }

        results
    }

    /// Search within a node.
    fn search_node(&self, bbox: &BoundingBox, node: &RTreeNode, results: &mut Vec<&SpatialFeature>) {
        if !node.bounding_box.intersects(bbox) {
            return;
        }

        for entry in &node.entries {
            match entry {
                RTreeEntry::Leaf { bounding_box, data, .. } => {
                    if bounding_box.intersects(bbox) {
                        results.push(data);
                    }
                }
                RTreeEntry::Node { node: child_node } => {
                    self.search_node(bbox, child_node, results);
                }
            }
        }
    }

    /// Find nearest neighbors to a point.
    pub fn nearest_neighbors(&self, point: &Point, k: usize) -> Vec<&SpatialFeature> {
        let mut results = Vec::new();

        if let Some(root) = &self.root {
            self.nearest_neighbors_node(point, k, root, &mut results);
        }

        // Sort by distance
        results.sort_by(|a, b| {
            let dist_a = point.distance_to(&a.geometry.bounding_box().center());
            let dist_b = point.distance_to(&b.geometry.bounding_box().center());
            dist_a.partial_cmp(&dist_b).unwrap()
        });

        results.truncate(k);
        results
    }

    /// Find nearest neighbors within a node.
    fn nearest_neighbors_node(&self, point: &Point, k: usize, node: &RTreeNode, results: &mut Vec<&SpatialFeature>) {
        for entry in &node.entries {
            match entry {
                RTreeEntry::Leaf { data, .. } => {
                    results.push(data);
                }
                RTreeEntry::Node { node: child_node } => {
                    self.nearest_neighbors_node(point, k, child_node, results);
                }
            }
        }
    }
}

impl BoundingBox {
    /// Get center point of bounding box.
    pub fn center(&self) -> Point {
        Point::new(
            (self.min_x + self.max_x) / 2.0,
            (self.min_y + self.max_y) / 2.0,
        )
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_rtree_insert() {
        let mut rtree = RTree::new(4);

        let feature = SpatialFeature {
            id: "test".to_string(),
            geometry: Geometry::Point(Point::new(1.0, 2.0)),
            properties: HashMap::new(),
        };

        rtree.insert(feature).unwrap();
        assert!(rtree.root.is_some());
    }

    #[test]
    fn test_rtree_search() {
        let mut rtree = RTree::new(4);

        let feature = SpatialFeature {
            id: "test".to_string(),
            geometry: Geometry::Point(Point::new(1.0, 2.0)),
            properties: HashMap::new(),
        };

        rtree.insert(feature).unwrap();

        let bbox = BoundingBox::new(0.0, 0.0, 10.0, 10.0);
        let results = rtree.search(&bbox);
        assert_eq!(results.len(), 1);
    }
}

Coordinate Reference Systems

// rust/03-coordinate-reference-systems.rs

/*
Coordinate reference system patterns and best practices
*/

use std::collections::HashMap;
use serde::{Deserialize, Serialize};

/// Coordinate reference system.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CoordinateReferenceSystem {
    pub code: String,
    pub name: String,
    pub proj4_string: String,
    pub wkt: String,
    pub bounds: BoundingBox,
}

/// Coordinate transformation service.
pub struct CoordinateTransformService {
    crs_registry: HashMap<String, CoordinateReferenceSystem>,
    transform_cache: HashMap<(String, String), TransformFunction>,
}

type TransformFunction = Box<dyn Fn(Point) -> Point + Send + Sync>;

impl CoordinateTransformService {
    pub fn new() -> Self {
        let mut service = Self {
            crs_registry: HashMap::new(),
            transform_cache: HashMap::new(),
        };

        // Register common CRS
        service.register_common_crs();
        service
    }

    /// Register a coordinate reference system.
    pub fn register_crs(&mut self, crs: CoordinateReferenceSystem) {
        self.crs_registry.insert(crs.code.clone(), crs);
    }

    /// Register common coordinate reference systems.
    fn register_common_crs(&mut self) {
        // WGS84 (EPSG:4326)
        let wgs84 = CoordinateReferenceSystem {
            code: "EPSG:4326".to_string(),
            name: "WGS 84".to_string(),
            proj4_string: "+proj=longlat +datum=WGS84 +no_defs".to_string(),
            wkt: "GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]]".to_string(),
            bounds: BoundingBox::new(-180.0, -90.0, 180.0, 90.0),
        };
        self.crs_registry.insert(wgs84.code.clone(), wgs84);

        // Web Mercator (EPSG:3857)
        let web_mercator = CoordinateReferenceSystem {
            code: "EPSG:3857".to_string(),
            name: "Web Mercator".to_string(),
            proj4_string: "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs".to_string(),
            wkt: "PROJCS[\"WGS 84 / Pseudo-Mercator\",GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"Mercator_1SP\"],PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1]]".to_string(),
            bounds: BoundingBox::new(-20037508.34, -20037508.34, 20037508.34, 20037508.34),
        };
        self.crs_registry.insert(web_mercator.code.clone(), web_mercator);
    }

    /// Transform point from source CRS to target CRS.
    pub fn transform_point(&mut self, point: Point, source_crs: &str, target_crs: &str) -> Result<Point, String> {
        if source_crs == target_crs {
            return Ok(point);
        }

        // Check if transformation is cached
        let cache_key = (source_crs.to_string(), target_crs.to_string());
        if let Some(transform_fn) = self.transform_cache.get(&cache_key) {
            return Ok(transform_fn(point));
        }

        // Create transformation function
        let transform_fn = self.create_transform_function(source_crs, target_crs)?;
        let result = transform_fn(point);

        // Cache the transformation function
        self.transform_cache.insert(cache_key, transform_fn);

        Ok(result)
    }

    /// Create transformation function between two CRS.
    fn create_transform_function(&self, source_crs: &str, target_crs: &str) -> Result<TransformFunction, String> {
        let source = self.crs_registry.get(source_crs)
            .ok_or_else(|| format!("Source CRS not found: {}", source_crs))?;
        let target = self.crs_registry.get(target_crs)
            .ok_or_else(|| format!("Target CRS not found: {}", target_crs))?;

        // Simple transformation implementations
        match (source_crs, target_crs) {
            ("EPSG:4326", "EPSG:3857") => {
                Ok(Box::new(move |point| {
                    let x = point.x * 20037508.34 / 180.0;
                    let y = (point.y * std::f64::consts::PI / 180.0).tan().ln() * 20037508.34 / std::f64::consts::PI;
                    Point::new(x, y)
                }))
            }
            ("EPSG:3857", "EPSG:4326") => {
                Ok(Box::new(move |point| {
                    let x = point.x * 180.0 / 20037508.34;
                    let y = (point.y * std::f64::consts::PI / 20037508.34).exp().atan() * 180.0 / std::f64::consts::PI;
                    Point::new(x, y)
                }))
            }
            _ => Err(format!("Transformation not supported: {} -> {}", source_crs, target_crs)),
        }
    }

    /// Transform geometry from source CRS to target CRS.
    pub fn transform_geometry(&mut self, geometry: &Geometry, source_crs: &str, target_crs: &str) -> Result<Geometry, String> {
        match geometry {
            Geometry::Point(point) => {
                let transformed_point = self.transform_point(*point, source_crs, target_crs)?;
                Ok(Geometry::Point(transformed_point))
            }
            Geometry::LineString(linestring) => {
                let transformed_points: Vec<Point> = linestring.points.iter()
                    .map(|point| self.transform_point(*point, source_crs, target_crs))
                    .collect::<Result<Vec<_>, _>>()?;
                Ok(Geometry::LineString(LineString::new(transformed_points)))
            }
            Geometry::Polygon(polygon) => {
                let transformed_exterior: Vec<Point> = polygon.exterior.iter()
                    .map(|point| self.transform_point(*point, source_crs, target_crs))
                    .collect::<Result<Vec<_>, _>>()?;

                let mut transformed_holes = Vec::new();
                for hole in &polygon.holes {
                    let transformed_hole: Vec<Point> = hole.iter()
                        .map(|point| self.transform_point(*point, source_crs, target_crs))
                        .collect::<Result<Vec<_>, _>>()?;
                    transformed_holes.push(transformed_hole);
                }

                let mut transformed_polygon = Polygon::new(transformed_exterior);
                for hole in transformed_holes {
                    transformed_polygon.add_hole(hole);
                }

                Ok(Geometry::Polygon(transformed_polygon))
            }
            _ => Err("Complex geometry transformation not implemented".to_string()),
        }
    }

    /// Get CRS information.
    pub fn get_crs(&self, code: &str) -> Option<&CoordinateReferenceSystem> {
        self.crs_registry.get(code)
    }

    /// List all registered CRS.
    pub fn list_crs(&self) -> Vec<&CoordinateReferenceSystem> {
        self.crs_registry.values().collect()
    }
}

/// Spatial reference system utilities.
pub struct SpatialReferenceUtils;

impl SpatialReferenceUtils {
    /// Calculate distance between two points in meters.
    pub fn distance_meters(point1: &Point, point2: &Point) -> f64 {
        const EARTH_RADIUS: f64 = 6371000.0; // Earth radius in meters

        let lat1_rad = point1.y.to_radians();
        let lat2_rad = point2.y.to_radians();
        let delta_lat = (point2.y - point1.y).to_radians();
        let delta_lon = (point2.x - point1.x).to_radians();

        let a = (delta_lat / 2.0).sin().powi(2) +
                lat1_rad.cos() * lat2_rad.cos() * (delta_lon / 2.0).sin().powi(2);
        let c = 2.0 * a.sqrt().atan2((1.0 - a).sqrt());

        EARTH_RADIUS * c
    }

    /// Calculate bearing between two points.
    pub fn bearing(point1: &Point, point2: &Point) -> f64 {
        let lat1_rad = point1.y.to_radians();
        let lat2_rad = point2.y.to_radians();
        let delta_lon = (point2.x - point1.x).to_radians();

        let y = delta_lon.sin() * lat2_rad.cos();
        let x = lat1_rad.cos() * lat2_rad.sin() - lat1_rad.sin() * lat2_rad.cos() * delta_lon.cos();

        y.atan2(x).to_degrees()
    }

    /// Calculate point at given distance and bearing.
    pub fn destination(point: &Point, distance: f64, bearing: f64) -> Point {
        const EARTH_RADIUS: f64 = 6371000.0; // Earth radius in meters

        let lat1_rad = point.y.to_radians();
        let lon1_rad = point.x.to_radians();
        let bearing_rad = bearing.to_radians();

        let lat2_rad = (lat1_rad.sin() * (distance / EARTH_RADIUS).cos() +
                       lat1_rad.cos() * (distance / EARTH_RADIUS).sin() * bearing_rad.cos()).asin();

        let lon2_rad = lon1_rad + (bearing_rad.sin() * (distance / EARTH_RADIUS).sin() * lat1_rad.cos()).atan2(
            (distance / EARTH_RADIUS).cos() - lat1_rad.sin() * lat2_rad.sin()
        );

        Point::new(lon2_rad.to_degrees(), lat2_rad.to_degrees())
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_coordinate_transform() {
        let mut service = CoordinateTransformService::new();

        let point = Point::new(0.0, 0.0); // Equator, Prime Meridian
        let transformed = service.transform_point(point, "EPSG:4326", "EPSG:3857").unwrap();

        // Should be close to (0, 0) in Web Mercator
        assert!((transformed.x - 0.0).abs() < 1.0);
        assert!((transformed.y - 0.0).abs() < 1.0);
    }

    #[test]
    fn test_distance_calculation() {
        let point1 = Point::new(0.0, 0.0);
        let point2 = Point::new(1.0, 1.0);

        let distance = SpatialReferenceUtils::distance_meters(&point1, &point2);
        assert!(distance > 0.0);
        assert!(distance < 200000.0); // Should be less than 200km
    }

    #[test]
    fn test_bearing_calculation() {
        let point1 = Point::new(0.0, 0.0);
        let point2 = Point::new(1.0, 0.0);

        let bearing = SpatialReferenceUtils::bearing(&point1, &point2);
        assert!((bearing - 90.0).abs() < 1.0); // Should be approximately 90 degrees
    }
}

TL;DR Runbook

Quick Start

// 1. Spatial data structures
let point = Point::new(1.0, 2.0);
let bbox = BoundingBox::new(0.0, 0.0, 10.0, 10.0);

// 2. Spatial indexing
let mut rtree = RTree::new(4);
rtree.insert(feature)?;
let results = rtree.search(&bbox);

// 3. Coordinate transformation
let mut service = CoordinateTransformService::new();
let transformed = service.transform_point(point, "EPSG:4326", "EPSG:3857")?;

Essential Patterns

// Complete geospatial setup
pub fn setup_rust_geospatial() {
    // 1. Spatial data structures
    // 2. Spatial indexing
    // 3. Coordinate reference systems
    // 4. Spatial operations
    // 5. Performance optimization
    // 6. Memory management
    // 7. Error handling
    // 8. Testing

    println!("Rust geospatial development setup complete!");
}

This guide provides the complete machinery for Rust geospatial development. Each pattern includes implementation examples, spatial strategies, and real-world usage patterns for enterprise geospatial systems.