Geolocation and Maps in Rust: Overview of Libraries and Practical Examples
Rust is a systems programming language gaining popularity in areas requiring high performance and security. Working with geodata is no exception. Whether you are developing a vehicle tracking service, a geographic information system, or simply want to add an interactive map to a website, the Rust ecosystem offers several mature solutions. In this article, we will review key libraries for geolocation and map rendering, and provide practical code examples.
1. The geo Library — The Foundation of Geometric Computations
The geo library (crates geo-types and geo) provides basic data types: points, lines, polygons, as well as algorithms for working with them. It is the foundation for many other geo-crates.
// cargo add geo-types geouse geo_types::{Point, LineString, Polygon};use geo::algorithm::haversine_distance;
fn main() { let moscow = Point::new(37.6173, 55.7558); let spb = Point::new(30.3351, 60.0);
// Haversine distance (in meters) let dist = haversine_distance(&moscow, &spb); println!("Distance Moscow — St. Petersburg: {:.0} km", dist / 1000.0);
// Creating a polygon let coords = vec![ (37.0, 55.0), (38.0, 55.0), (38.0, 56.0), (37.0, 56.0), (37.0, 55.0), ]; let polygon = Polygon::new( LineString::from(coords), vec![], ); println!("Polygon area: {:?}", polygon.unsigned_area());}Features:
- Types: Point, Line, LineString, Polygon, MultiPoint, GeometryCollection
- Algorithms: Haversine distance, Vincenty distance, area, centroid, buffering, intersections
- Serialization via serde (GeoJSON compatibility)
2. geoutils — Working with GPS and Data Formats
The geoutils crate specializes in parsing and validating GPS data. It can read NMEA strings (the standard GPS receiver protocol) and convert coordinates.
// cargo add geoutilsuse geoutils::{Location, Distance};
fn main() { // Creating a point from latitude/longitude let point_a = Location::new(55.7558, 37.6173).unwrap(); let point_b = Location::new(59.9343, 30.3351).unwrap();
// Haversine distance let dist = point_a.distance_to(&point_b).unwrap(); println!("Distance: {} km", dist.as_kilometers());
// Parsing an NMEA string (example) let nmea = "$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47"; if let Ok(loc) = Location::from_nmea(nmea) { println!("NMEA parsing: {:?}", loc); }}Features:
- Parsing NMEA-0183 (GPGGA, GPRMC)
- Support for formats: DMS (degrees/minutes/seconds), decimal degrees
- Conversion between coordinate systems (WGS84, UTM)
3. mapbox-gl-native-rs — Map Rendering in Rust
For displaying maps in desktop or embedded applications, you can use the Rust wrapper over Mapbox GL Native. The library allows rendering vector tiles, overlaying markers and polygons, and managing styles.
// Simplified example — environment setup required// cargo add mapbox-gl-native-rsuse mapbox_gl_native_rs::{Map, MapOptions, Style};
fn main() { let opts = MapOptions::new() .size(800, 600) .center(37.6173, 55.7558) .zoom(10.0);
let mut map = Map::new(opts); map.load_style(Style::from_url("https://demotiles.mapbox.com/style.json"));
// Adding a marker map.add_marker(37.6173, 55.7558, "Moscow");
// Rendering to a buffer (e.g., to PNG) let img = map.render_to_image(); img.save("map.png").unwrap();}Note: Requires installation