// rust/01-spatial-data-structures.rs/*Spatial data structures and best practices*/usestd::collections::HashMap;useserde::{Deserialize,Serialize};/// Point in 2D space.#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]pubstructPoint{pubx:f64,puby:f64,}implPoint{pubfnnew(x:f64,y:f64)->Self{Self{x,y}}/// Calculate distance to another point.pubfndistance_to(&self,other:&Point)->f64{letdx=self.x-other.x;letdy=self.y-other.y;(dx*dx+dy*dy).sqrt()}/// Calculate bearing to another point.pubfnbearing_to(&self,other:&Point)->f64{letdx=other.x-self.x;letdy=other.y-self.y;dy.atan2(dx).to_degrees()}}/// Bounding box for spatial queries.#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]pubstructBoundingBox{pubmin_x:f64,pubmin_y:f64,pubmax_x:f64,pubmax_y:f64,}implBoundingBox{pubfnnew(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.pubfncontains(&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.pubfnintersects(&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.pubfnexpand_to_include(&mutself,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.pubfnarea(&self)->f64{(self.max_x-self.min_x)*(self.max_y-self.min_y)}}/// Polygon with holes.#[derive(Debug, Clone, Serialize, Deserialize)]pubstructPolygon{pubexterior:Vec<Point>,pubholes:Vec<Vec<Point>>,}implPolygon{pubfnnew(exterior:Vec<Point>)->Self{Self{exterior,holes:Vec::new(),}}/// Add a hole to the polygon.pubfnadd_hole(&mutself,hole:Vec<Point>){self.holes.push(hole);}/// Check if point is inside polygon.pubfncontains(&self,point:&Point)->bool{if!self.point_in_ring(point,&self.exterior){returnfalse;}// Check if point is in any holeforholein&self.holes{ifself.point_in_ring(point,hole){returnfalse;}}true}/// Check if point is inside a ring using ray casting.fnpoint_in_ring(&self,point:&Point,ring:&[Point])->bool{letmutinside=false;letmutj=ring.len()-1;foriin0..ring.len(){letpi=&ring[i];letpj=&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.pubfnarea(&self)->f64{letmutarea=self.ring_area(&self.exterior);// Subtract area of holesforholein&self.holes{area-=self.ring_area(hole);}area.abs()}/// Calculate area of a ring.fnring_area(&self,ring:&[Point])->f64{ifring.len()<3{return0.0;}letmutarea=0.0;letmutj=ring.len()-1;foriin0..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.pubfnbounding_box(&self)->BoundingBox{letmutbbox=BoundingBox::new(f64::INFINITY,f64::INFINITY,f64::NEG_INFINITY,f64::NEG_INFINITY,);forpointin&self.exterior{bbox.expand_to_include(point);}forholein&self.holes{forpointinhole{bbox.expand_to_include(point);}}bbox}}/// Line string (polyline).#[derive(Debug, Clone, Serialize, Deserialize)]pubstructLineString{pubpoints:Vec<Point>,}implLineString{pubfnnew(points:Vec<Point>)->Self{Self{points}}/// Calculate length of line string.pubfnlength(&self)->f64{ifself.points.len()<2{return0.0;}letmutlength=0.0;foriin1..self.points.len(){length+=self.points[i-1].distance_to(&self.points[i]);}length}/// Get bounding box of line string.pubfnbounding_box(&self)->BoundingBox{ifself.points.is_empty(){returnBoundingBox::new(0.0,0.0,0.0,0.0);}letmutbbox=BoundingBox::new(f64::INFINITY,f64::INFINITY,f64::NEG_INFINITY,f64::NEG_INFINITY,);forpointin&self.points{bbox.expand_to_include(point);}bbox}}/// Spatial feature with geometry and properties.#[derive(Debug, Clone, Serialize, Deserialize)]pubstructSpatialFeature{pubid:String,pubgeometry:Geometry,pubproperties:HashMap<String,serde_json::Value>,}#[derive(Debug, Clone, Serialize, Deserialize)]pubenumGeometry{Point(Point),LineString(LineString),Polygon(Polygon),MultiPoint(Vec<Point>),MultiLineString(Vec<LineString>),MultiPolygon(Vec<Polygon>),}implGeometry{/// Get bounding box of geometry.pubfnbounding_box(&self)->BoundingBox{matchself{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)=>{ifpoints.is_empty(){returnBoundingBox::new(0.0,0.0,0.0,0.0);}letmutbbox=BoundingBox::new(f64::INFINITY,f64::INFINITY,f64::NEG_INFINITY,f64::NEG_INFINITY,);forpointinpoints{bbox.expand_to_include(point);}bbox}Geometry::MultiLineString(linestrings)=>{iflinestrings.is_empty(){returnBoundingBox::new(0.0,0.0,0.0,0.0);}letmutbbox=linestrings[0].bounding_box();forlinestringin&linestrings[1..]{letls_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)=>{ifpolygons.is_empty(){returnBoundingBox::new(0.0,0.0,0.0,0.0);}letmutbbox=polygons[0].bounding_box();forpolygonin&polygons[1..]{letpoly_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)]modtests{usesuper::*;#[test]fntest_point_distance(){letp1=Point::new(0.0,0.0);letp2=Point::new(3.0,4.0);assert_eq!(p1.distance_to(&p2),5.0);}#[test]fntest_bounding_box_contains(){letbbox=BoundingBox::new(0.0,0.0,10.0,10.0);letpoint=Point::new(5.0,5.0);assert!(bbox.contains(&point));letpoint_outside=Point::new(15.0,15.0);assert!(!bbox.contains(&point_outside));}#[test]fntest_polygon_area(){letexterior=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),];letpolygon=Polygon::new(exterior);assert_eq!(polygon.area(),100.0);}#[test]fntest_polygon_contains(){letexterior=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),];letpolygon=Polygon::new(exterior);letinside_point=Point::new(5.0,5.0);assert!(polygon.contains(&inside_point));letoutside_point=Point::new(15.0,15.0);assert!(!polygon.contains(&outside_point));}}
// rust/02-spatial-indexing.rs/*Spatial indexing patterns and best practices*/usestd::collections::HashMap;useserde::{Deserialize,Serialize};/// R-tree for spatial indexing.pubstructRTree{root:Option<Box<RTreeNode>>,max_entries:usize,min_entries:usize,}#[derive(Debug, Clone)]pubstructRTreeNode{pubbounding_box:BoundingBox,pubentries:Vec<RTreeEntry>,pubis_leaf:bool,}#[derive(Debug, Clone)]pubenumRTreeEntry{Leaf{id:String,bounding_box:BoundingBox,data:SpatialFeature,},Node{node:Box<RTreeNode>,},}implRTree{pubfnnew(max_entries:usize)->Self{Self{root:None,max_entries,min_entries:max_entries/2,}}/// Insert a spatial feature into the R-tree.pubfninsert(&mutself,feature:SpatialFeature)->Result<(),String>{letbounding_box=feature.geometry.bounding_box();letentry=RTreeEntry::Leaf{id:feature.id.clone(),bounding_box,data:feature,};ifletSome(root)=&mutself.root{self.insert_entry(entry,root)?;}else{// Create root nodeletmutroot=RTreeNode{bounding_box:bounding_box,entries:vec![entry],is_leaf:true,};self.root=Some(Box::new(root));}Ok(())}/// Insert entry into node.fninsert_entry(&mutself,entry:RTreeEntry,node:&mutRTreeNode)->Result<(),String>{ifnode.is_leaf{node.entries.push(entry);ifnode.entries.len()>self.max_entries{self.split_node(node)?;}}else{// Find best child nodeletmutbest_child=0;letmutmin_expansion=f64::INFINITY;for(i,child_entry)innode.entries.iter().enumerate(){ifletRTreeEntry::Node{node:child_node}=child_entry{letexpansion=self.calculate_expansion(&entry,&child_node.bounding_box);ifexpansion<min_expansion{min_expansion=expansion;best_child=i;}}}// Insert into best childifletRTreeEntry::Node{node:child_node}=&mutnode.entries[best_child]{self.insert_entry(entry,child_node)?;}}// Update bounding boxself.update_bounding_box(node);Ok(())}/// Split node when it exceeds maximum entries.fnsplit_node(&mutself,node:&mutRTreeNode)->Result<(),String>{ifnode.entries.len()<=self.max_entries{returnOk(());}// Simple split: divide entries into two groupsletmid=node.entries.len()/2;letmutentries1=node.entries.drain(..mid).collect::<Vec<_>>();letentries2=node.entries;// Create new nodesletmutnode1=RTreeNode{bounding_box:BoundingBox::new(0.0,0.0,0.0,0.0),entries:entries1,is_leaf:node.is_leaf,};letmutnode2=RTreeNode{bounding_box:BoundingBox::new(0.0,0.0,0.0,0.0),entries:entries2,is_leaf:node.is_leaf,};// Update bounding boxesself.update_bounding_box(&mutnode1);self.update_bounding_box(&mutnode2);// Replace current node with two child nodesnode.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.fncalculate_expansion(&self,entry:&RTreeEntry,bbox:&BoundingBox)->f64{letentry_bbox=matchentry{RTreeEntry::Leaf{bounding_box,..}=>bounding_box,RTreeEntry::Node{node}=>&node.bounding_box,};letnew_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.fnupdate_bounding_box(&self,node:&mutRTreeNode){ifnode.entries.is_empty(){return;}letmutbbox=match&node.entries[0]{RTreeEntry::Leaf{bounding_box,..}=>*bounding_box,RTreeEntry::Node{node:child_node}=>child_node.bounding_box,};forentryin&node.entries[1..]{letentry_bbox=matchentry{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.pubfnsearch(&self,bbox:&BoundingBox)->Vec<&SpatialFeature>{letmutresults=Vec::new();ifletSome(root)=&self.root{self.search_node(bbox,root,&mutresults);}results}/// Search within a node.fnsearch_node(&self,bbox:&BoundingBox,node:&RTreeNode,results:&mutVec<&SpatialFeature>){if!node.bounding_box.intersects(bbox){return;}forentryin&node.entries{matchentry{RTreeEntry::Leaf{bounding_box,data,..}=>{ifbounding_box.intersects(bbox){results.push(data);}}RTreeEntry::Node{node:child_node}=>{self.search_node(bbox,child_node,results);}}}}/// Find nearest neighbors to a point.pubfnnearest_neighbors(&self,point:&Point,k:usize)->Vec<&SpatialFeature>{letmutresults=Vec::new();ifletSome(root)=&self.root{self.nearest_neighbors_node(point,k,root,&mutresults);}// Sort by distanceresults.sort_by(|a,b|{letdist_a=point.distance_to(&a.geometry.bounding_box().center());letdist_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.fnnearest_neighbors_node(&self,point:&Point,k:usize,node:&RTreeNode,results:&mutVec<&SpatialFeature>){forentryin&node.entries{matchentry{RTreeEntry::Leaf{data,..}=>{results.push(data);}RTreeEntry::Node{node:child_node}=>{self.nearest_neighbors_node(point,k,child_node,results);}}}}}implBoundingBox{/// Get center point of bounding box.pubfncenter(&self)->Point{Point::new((self.min_x+self.max_x)/2.0,(self.min_y+self.max_y)/2.0,)}}#[cfg(test)]modtests{usesuper::*;#[test]fntest_rtree_insert(){letmutrtree=RTree::new(4);letfeature=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]fntest_rtree_search(){letmutrtree=RTree::new(4);letfeature=SpatialFeature{id:"test".to_string(),geometry:Geometry::Point(Point::new(1.0,2.0)),properties:HashMap::new(),};rtree.insert(feature).unwrap();letbbox=BoundingBox::new(0.0,0.0,10.0,10.0);letresults=rtree.search(&bbox);assert_eq!(results.len(),1);}}
// rust/03-coordinate-reference-systems.rs/*Coordinate reference system patterns and best practices*/usestd::collections::HashMap;useserde::{Deserialize,Serialize};/// Coordinate reference system.#[derive(Debug, Clone, Serialize, Deserialize)]pubstructCoordinateReferenceSystem{pubcode:String,pubname:String,pubproj4_string:String,pubwkt:String,pubbounds:BoundingBox,}/// Coordinate transformation service.pubstructCoordinateTransformService{crs_registry:HashMap<String,CoordinateReferenceSystem>,transform_cache:HashMap<(String,String),TransformFunction>,}typeTransformFunction=Box<dynFn(Point)->Point+Send+Sync>;implCoordinateTransformService{pubfnnew()->Self{letmutservice=Self{crs_registry:HashMap::new(),transform_cache:HashMap::new(),};// Register common CRSservice.register_common_crs();service}/// Register a coordinate reference system.pubfnregister_crs(&mutself,crs:CoordinateReferenceSystem){self.crs_registry.insert(crs.code.clone(),crs);}/// Register common coordinate reference systems.fnregister_common_crs(&mutself){// WGS84 (EPSG:4326)letwgs84=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)letweb_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.pubfntransform_point(&mutself,point:Point,source_crs:&str,target_crs:&str)->Result<Point,String>{ifsource_crs==target_crs{returnOk(point);}// Check if transformation is cachedletcache_key=(source_crs.to_string(),target_crs.to_string());ifletSome(transform_fn)=self.transform_cache.get(&cache_key){returnOk(transform_fn(point));}// Create transformation functionlettransform_fn=self.create_transform_function(source_crs,target_crs)?;letresult=transform_fn(point);// Cache the transformation functionself.transform_cache.insert(cache_key,transform_fn);Ok(result)}/// Create transformation function between two CRS.fncreate_transform_function(&self,source_crs:&str,target_crs:&str)->Result<TransformFunction,String>{letsource=self.crs_registry.get(source_crs).ok_or_else(||format!("Source CRS not found: {}",source_crs))?;lettarget=self.crs_registry.get(target_crs).ok_or_else(||format!("Target CRS not found: {}",target_crs))?;// Simple transformation implementationsmatch(source_crs,target_crs){("EPSG:4326","EPSG:3857")=>{Ok(Box::new(move|point|{letx=point.x*20037508.34/180.0;lety=(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|{letx=point.x*180.0/20037508.34;lety=(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.pubfntransform_geometry(&mutself,geometry:&Geometry,source_crs:&str,target_crs:&str)->Result<Geometry,String>{matchgeometry{Geometry::Point(point)=>{lettransformed_point=self.transform_point(*point,source_crs,target_crs)?;Ok(Geometry::Point(transformed_point))}Geometry::LineString(linestring)=>{lettransformed_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)=>{lettransformed_exterior:Vec<Point>=polygon.exterior.iter().map(|point|self.transform_point(*point,source_crs,target_crs)).collect::<Result<Vec<_>,_>>()?;letmuttransformed_holes=Vec::new();forholein&polygon.holes{lettransformed_hole:Vec<Point>=hole.iter().map(|point|self.transform_point(*point,source_crs,target_crs)).collect::<Result<Vec<_>,_>>()?;transformed_holes.push(transformed_hole);}letmuttransformed_polygon=Polygon::new(transformed_exterior);forholeintransformed_holes{transformed_polygon.add_hole(hole);}Ok(Geometry::Polygon(transformed_polygon))}_=>Err("Complex geometry transformation not implemented".to_string()),}}/// Get CRS information.pubfnget_crs(&self,code:&str)->Option<&CoordinateReferenceSystem>{self.crs_registry.get(code)}/// List all registered CRS.pubfnlist_crs(&self)->Vec<&CoordinateReferenceSystem>{self.crs_registry.values().collect()}}/// Spatial reference system utilities.pubstructSpatialReferenceUtils;implSpatialReferenceUtils{/// Calculate distance between two points in meters.pubfndistance_meters(point1:&Point,point2:&Point)->f64{constEARTH_RADIUS:f64=6371000.0;// Earth radius in metersletlat1_rad=point1.y.to_radians();letlat2_rad=point2.y.to_radians();letdelta_lat=(point2.y-point1.y).to_radians();letdelta_lon=(point2.x-point1.x).to_radians();leta=(delta_lat/2.0).sin().powi(2)+lat1_rad.cos()*lat2_rad.cos()*(delta_lon/2.0).sin().powi(2);letc=2.0*a.sqrt().atan2((1.0-a).sqrt());EARTH_RADIUS*c}/// Calculate bearing between two points.pubfnbearing(point1:&Point,point2:&Point)->f64{letlat1_rad=point1.y.to_radians();letlat2_rad=point2.y.to_radians();letdelta_lon=(point2.x-point1.x).to_radians();lety=delta_lon.sin()*lat2_rad.cos();letx=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.pubfndestination(point:&Point,distance:f64,bearing:f64)->Point{constEARTH_RADIUS:f64=6371000.0;// Earth radius in metersletlat1_rad=point.y.to_radians();letlon1_rad=point.x.to_radians();letbearing_rad=bearing.to_radians();letlat2_rad=(lat1_rad.sin()*(distance/EARTH_RADIUS).cos()+lat1_rad.cos()*(distance/EARTH_RADIUS).sin()*bearing_rad.cos()).asin();letlon2_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)]modtests{usesuper::*;#[test]fntest_coordinate_transform(){letmutservice=CoordinateTransformService::new();letpoint=Point::new(0.0,0.0);// Equator, Prime Meridianlettransformed=service.transform_point(point,"EPSG:4326","EPSG:3857").unwrap();// Should be close to (0, 0) in Web Mercatorassert!((transformed.x-0.0).abs()<1.0);assert!((transformed.y-0.0).abs()<1.0);}#[test]fntest_distance_calculation(){letpoint1=Point::new(0.0,0.0);letpoint2=Point::new(1.0,1.0);letdistance=SpatialReferenceUtils::distance_meters(&point1,&point2);assert!(distance>0.0);assert!(distance<200000.0);// Should be less than 200km}#[test]fntest_bearing_calculation(){letpoint1=Point::new(0.0,0.0);letpoint2=Point::new(1.0,0.0);letbearing=SpatialReferenceUtils::bearing(&point1,&point2);assert!((bearing-90.0).abs()<1.0);// Should be approximately 90 degrees}}