Rust is a relatively new programming language and many problems being tackled in Rust have existing solutions in C/C++. As a Rust developer in need of some non-trivial functionality, you often must choose between using a Rust wrapper of an existing C/C++ library and a pure Rust alternative. In this article, I compare geo, a crate that provides primitives and algorithms for two dimensional geometric operations, with the most prominent alternative, geos, a wrapper of the C++ libgeos library. You can use these crates together because they interoperate via the Rust ecosystem for Geospatial analysis.
As is frequently the case, libgeos is more mature than the Rust upstart. It has a longer history of development1, a more formalized project steering committee, and illustrious clients including PostGIS, QGIS, GDAL and Shapely. geo potentially has better memory safety because it is written in (safe) Rust.
libgeos is implemented in C++ and exports a C API. geos wraps (most of) this C API in two steps – The geos-sys crate provides raw FFI bindings for the C API, and geos wraps it in a more idiomatic Rust API. libgeos itself is a port of the Java JTS library, so you could add another conceptual step in the porting / wrapping chain.
In the rest of this article, I address a single point of comparison between these alternatives – feature parity. As a rust developer, the relevant API for you is the one exported finally by the geos crate so I mostly reference that in my comparison.
libgeos’ feature set is more extensive than geo’s and it uses more complex namespaces for organization of the functionality. For my comparison, I choose a different option for organizing the functionality – I classify geo’s functions using PostGIS reference documentation sections. PostGIS is a widely used and extensive implementation of the OGC Simple Feature Access-SQL standard so it is useful to map the features to PostGIS. I also think that PostGIS has the most intuitive organization of the feature set and the best in-depth documentation of each function. For each section, I map geo’s functionality to geos, and call out features that are missing in geo.
Finally, all of these projects are actively being developed. This comparison (and links below) apply best to geo 0.18.0, geos 8.0.3 and PostGIS 3.2.
Measurement Functions
geo provides 8 of 27 functions in this section of the PostGIS reference.
Missing features includes:
- 3D geometry operations. Not available in geos.
- Hausdorff distance between geometries. Available in geos.
- Maximum distance between geometries, converse of ST_ClosestPoint. Not available in geos.
- A measure of robustness (to loss of precision) of geometry specification. Not available in geos.
Beyond these, PostGIS provides some convenience functions that are easy to implement using those available in geo and geos.
Next, I map features available in geo to those from PostGIS and geos.
description | Compute area of a planar polygon. ST_Area |
---|---|
geo | algorithm::area::Area |
geos | Geom::area |
description | Compute the azimuthal angle between two geometries. ST_Azimuth |
---|---|
geo | algorithm::bearing::Bearing |
geos | Not available |
description | Find a point on a geometry closest to another geometry. ST_ClosestPoint |
---|---|
geo | algorithm::closest_point::ClosestPoint |
geos | Geom::nearest_points |
description | Find the Euclidean distance between geometries. All three packages have support for optimized computation of the
distance using an index. Additionally, PostGIS provides
an algebra based on this function. ST_Distance |
---|---|
geo | algorithm::euclidean_distance::EuclideanDistance, algorithm::euclidean_distance::nearest_neighbour_distance |
geos | Geom::distance, Geom::distance_indexed |
description | Compute the Cartesian length of a geometry. Only applicable to lines and multi-lines. ST_Length |
---|---|
geo | algorithm::euclidean_length::EuclideanLength |
geos | Geom::length |
description | Compute a measure of similarity of two geometries, due to Maurice Frechet.
PostGIS and geos provide a way to densify the geometries beforehand, which can improve the distance estimate. ST_FrechetDistance |
---|---|
geo | algorithm::frechet_distance::FrechetDistance |
geos | Geom::frechet_distance, Geom::frechet_distance_densify |
description | Compute the great-arc distance between two geometries on a geodetic model. geo provides two algorithms for this computation based on a spheroidal model of the earth. PostGIS provides one algorithm each based on spherical and spheroidal models. ST_DistanceSphere, ST_DistanceSpheroid (based on Vincenty's method) |
---|---|
geo | algorithm::vincenty_distance::VincentyDistance, algorithm::geodesic_distance::GeodesicDistance (improvement over Vincenty's method) |
geos | Not available |
description | Compute the great-arc length of a geometry. Implementations follow great-arc distance computations closely. ST_LengthSpheroid (based on Vincenty's method) |
---|---|
geo | algorithm::vincenty_length::VincentyLength, algorithm::geodesic_length::GeodesicLength (improvement over Vincenty's method) |
geos | Not available |
Geometry Accessors
geo effectively provides 15 of 42 functions in this section of the PostGIS reference . While there is a large gap in the feature set in this section, many of the missing functions have easy and idiomatic implementations using available accessors and iterators. Also, some of the functions are not applicable to geo because of limitations of the types of geometries supported.
Missing features includes:
- Functions related to 3D and 4D geometries. The extra coordinates are used to specify the height (z) and/or a measure (m) at the given point. These are not applicable to geo as geo only supports 2D geometries. geos supports 3D geometries (with a z-dimension) but has no notion of a measure.
- Functions related to curves (with non-linear interpolation between points) and surfaces (3D non-linear surfaces). These are not applicable to geo and geos as neither library supports non-linear geometries.
- A method to obtain the amount of memory consumed by a geometry. Not available in geo and geos.
Beyond these, PostGIS provides some convenience functions that are easy to implement using those available in geo and geos.
description | Compute the bounding rectangle for a geometry.
Additionally, PostGIS has
an algebra relating the bounding
rectangles of two geometries. ST_Envelope |
---|---|
geo | algorithm::bounding_rect::BoundingRect |
geos | Geom::envelope |
description | Compute the dimensionality of a geometry. Only geo properly handles collinear points. ST_Dimension |
---|---|
geo | algorithm::dimensions::HasDimensions |
geos | Geom::get_num_dimensions |
description | Access specific points on a line or multi-line. ST_NumPoints, ST_PointN, ST_StartPoint, ST_EndPoint |
---|---|
geo | LineString::points_iter, Line::start_point, Line::end_point etc. |
geos | Geom::get_num_points, Geom::get_point_n, Geom::get_start_point, Geom::get_end_point |
description | Destructure a point. geo only supports 2D geometries. geos supports the z dimension. PostGIS supports the z
and m dimensions. ST_X, ST_Y, ST_Z, ST_Zmflag |
---|---|
geo | Point::x, Point::y, Point::x_y |
geos | Geom::get_x, Geom::get_y, Geom::get_z |
description | Get the boundaries of a polygon. ST_ExteriorRing, ST_NumInteriorRings, ST_InteriorRingN |
---|---|
geo | Polygon::exterior (Polygon::exterior_mut), Polygon::interiors (Polygon::interiors_mut) |
geos | Geom::get_exterior_ring, Geom::get_num_interior_rings, Geom::get_interior_ring_n |
description | Get the winding order (clockwise / counter-clockwise) of the exterior ring of a polygon. geo provides a larger
variety of associated functions. ST_IsPolygonCW, ST_IsPolygonCCW |
---|---|
geo | algorithm::winding_order::Winding |
geos | CoordSeq::is_ccw |
Geometry Processing
Geo provides 6 of 26 functions in this section of the PostGIS reference. There are significant gaps in the feature set between geo and geos in this section.
Missing features include:
- Buffering (related: offset curves). Available in geos.
- Delaunay triangulation. Available in geos.
- Inscribed and bounding circle. Not available in geos.
- Rotated bounding box. Available in geos.
- Generation of points on a surface or at random. Available in geos.
- Generation of polygons or lines formed by intersections of a collection. Available in geos.
- Polygon simplification using Chaikin’s method. Not available in geos.
- Generation of Voronoi diagrams. Available in geos.
description | Compute the geometric center of mass of a geometry. ST_Centroid |
---|---|
geo | algorithm::centroid::Centroid |
geos | Geom::get_centroid |
description | Compute a
potentially concave polygon bounding a geometry.
There is no formal definition of a concave hull and the results from PostGIS and geo may disagree. ST_ConcaveHull |
---|---|
geo | algorithm::concave_hull::ConcaveHull |
geos | Not available in _geos_ |
description | Compute the convex hull of a geometry. geo allows an additional, slower, algorithm that properly handles
geometries with collinear points. ST_ConvexHull |
---|---|
geo | algorithm::convex_hull::quick_hull, algorithm::convex_hull::graham_hull |
geos | Geom::convex_hull |
description | Simplify a geometry using Ramer-Douglas-Peuker (RDP) algorithm. ST_Simplify |
---|---|
geo | algorithm::simplify::Simplify, algorithm::simplify::SimplifyIdx |
geos | Geom::simplify |
description | Simplify a geometry using the Visvalingam–Whyatt (VW) algorithm. ST_SimplifyVW |
---|---|
geo | algorithm::simplifyvw::SimplifyVW, algorithm::simplifyvw::SimplifyVwIdx |
geos | Not available in _geos_ |
description | Simplify a geometry preserving topological relationships. PostGIS and geos use the RDP algorithm described above.
geo uses the VW algorithm. ST_SimplifyPreserveTopology |
---|---|
geo | algorithm::simplifyvw::SimplifyVWPreserve |
geos | Geom::topology_preserve_simplify |
Overlay Functions
This section of the PostGIS reference documents functions that compute results arising from the overlay of two geometries. geo only provides 1 of 10 functions in this section, with a significant feature gap between geo and geos.
Missing features include:
- Several CPU or memory efficient implementations of the union operation, and it’s converse. Partially available in geos.
- A special case of union used to dissolve collections of lines into a single line. Available in geos.
- Difference and symmetric difference of geometries. Available in geos.
- A subdivision operation to speed up indexed queries on a large geometry. Not available in geos.
description | Compute the point-set intersection of two geometries. geo only provides a limited implementation for lines. ST_Intersection |
---|---|
geo | algorithm::line_intersection::LineIntersection |
geos | Geom::intersection |
Topological Relationships
There are 16 functions in this section of the PostGIS reference, but most are special cases of the most general ST_Relate function that determines the DE-9IM relationship matrix between two geometries. geo provides this general function. libgeos also provides this function, but it is not exposed by geos.
For the more specific functions, geos provides an optional preprocessing step that can speed up repeated relationship queries against a large geometry. geo does not provide a similar optimization.
description | Determine if a geometry is contained completely inside another. ST_Contains |
---|---|
geo | algorithm::contains::Contains |
geos | Geom::contains |
description | Determine if two geometries have any points in common. ST_Intersects |
---|---|
geo | algorithm::intersects::Intersects |
geos | Geom::intersects |
description | Evaluate the DE-9IM relationship matrix between two geometries. Although geos does not provide this function, it
provides many more methods like the two above to check specific relationships between geometries. ST_Relate |
---|---|
geo | algorithm::relate::Relate |
geos | Not available in _geos_ |
Affine Transformations
There are 8 functions in this section of the PostGIS reference, but all of them are specific cases of the most general ST_Affine function. geo provides 2 functions that cover all transformations except scaling. geos does not support any features in this section directly. In either case, this feature set can be implemented using mutable iterators on points in the geometry.
description | Rotate a geometry about a point. geo also provides a convenience method to rotate about the centroid of the
geometry. ST_Rotate |
---|---|
geo | algorithm::rotate::RotatePoint, algorithm::rotate::Rotate |
geos | Not available in _geos_ |
description | Translate a geometry. ST_Translate |
---|---|
geo | algorithm::translate::Translate |
geos | Not available in _geos_ |
Linear Referencing
geo provides 2 of 10 functions in this section of the PostGIS reference. Of the remaining, 6 are not applicable to geo because they are related to 3D or 4D geometries, and others are convenience methods easily implemented using the 2 available functions.
description | Find a point on a line at a distance a given fraction of the length from the start. ST_LineInterpolatePoint |
---|---|
geo | algorithm::line_interpolate_point::LineInterpolatePoint |
geos | Geom::interpolate, Geom::interpolate_normalized |
description | Find the point on a line closest to a given point (potentially off the line). ST_LineLocatePoint |
---|---|
geo | algorithm::line_locate_point::LineLocatePoint |
geos | Geom::project, Geom::project_normalized |
Missing classes of features
There are several sections of features in the PostGIS reference not available in geo at all:
- Geometry Editors provide methods to mutate geometries. These can be implemented using mutable references to the coordinates in the geometry. Not available in geos.
- Bounding Box Operators and Distance Operators provide syntactic sugar for an algebra using available methods to relate geometries. Not available in geos.
- Clustering Functions include widely used algorithms for clustering of geometries using distance metrics. I would expect a separate crate to provide higher-order features like clustering, built using the foundational features available in geo or geos. Not available in geos.
Some other features from PostGIS that are not applicable to geo include computation of trends in the measure dimension, specification of a spatial reference system and specific functions exported from CGAL.
Beyond PostGIS
Conversely, geo provides some features beyond those documented in the PostGIS reference.
- Geodetic computations.
- Computation of area of a geometry using an algorithm due to Chamberlain Duquette. Both PostGIS and geos project the geometry into planar coordinates for area computations. Not available in geos.
- Computation of points on a great-arc of a sphere or spheroid. These computations can be implemented in PostGIS using ST_Length. Not available in geos.
- A trait to enable robust geometric calculations based on CGAL-style computational kernels. Not available in geos.
Conclusion
I hope that my systematic comparison of the features in geo and geos will help you make an informed choice between the two alternatives as you start building your geospatial application in Rust. I invite you to also benefit from the original reason for this comparison – to inspire my contributions to the geo crate!
-
I couldn’t find the first commit for libgeos. The oldest reference I have is from this commit-hook post to the osgeo mailing list dated Nov 22, 2007. For geo, the first commit was on Jan 16, 2015. ↩