geo

repository·main·Indexed 23 days ago

https://github.com/georust/geo

A Rust crate providing fundamental geospatial primitive types, algorithms, and utilities. It includes core geometry types like Point, LineString, and Polygon, and supports operations such as topological relationships, affine transformations, boolean operations, clustering (DBSCAN, k-means), and geometry repair. The project also includes geo-traits for a standardized, trait-based interface for geospatial vector data and a JTS Test Runner to ensure behavioral consistency with the Java Topology Suite.

Tokens
18.6K
Snippets
30
Records
88
Agent score
82%

What's inside geo

  1. Overview of the geo crate

    main
    The geo crate is a collection of geospatial primitive types, algorithms, and utilities for Rust. It provides core geometry types like Point, LineString, and Polygon, and supports a wide range of spatial operations including topological relationships, affine transformations, boolean operations, and clustering. It also integrates with the broader GeoRust ecosystem for projection, serialization, and geocoding.
  2. What is JTS Test Runner

    main
    JTS Test Runner is a tool designed to compare the behavior of the geo crate against the JTS (Java Topology Suite) implementation. It uses test XML files copied from JTS to identify potential bugs or divergences in geo's geospatial algorithms. The test data is located in ./resources/testxml.
  3. Perform boolean operations on 2D geometries

    main
    The geo crate supports constructive set-theoretic operations (boolean operations) on 2D geometries, such as union and intersection. These operations are implemented using the Martinez-Rueda-Feito algorithm and are encapsulated in the BoolOp struct, which implements the Spec trait. The process involves using a planar sweep algorithm to identify segment borders and then stitching those segments into valid output polygons (exterior and interior rings) that satisfy OGC SFS validity constraints.
  4. Clip 1D geometries using 2D geometries

    main
    You can perform clipping operations where a 1D geometry (like a LineString) is clipped by a 2D geometry. This is functionally similar to an intersection between geometries of different dimensions. The algorithm calculates the region based on the 2D geometry and outputs only the segments from the 1D geometry that fall within that region. The resulting output is assembled into a MultiLineString. This behavior is captured by the ClipOp struct.
  5. Use geo primitives and algorithms

    main

    You can define geometries using macros like polygon! and line_string! and perform operations like convex_hull() on them. The crate supports various algorithms including:

    • Topological Relationships: DE-9IM support, containment, and intersection.
    • Affine Operations: Scale, rotate, skew, and translate.
    • Boolean Operations: Clip, union, difference, intersection, and xor.
    • Buffer/Offset: Buffer and offset operations on geometries.
    • Geometry Repair: Repairing invalid polygons/multipolygons using constrained Delaunay triangulation.
    • Clustering: DBSCAN and k-means.
    • Distance/Length: Euclidean, spherical, haversine, and other non-planar calculations.
    • Projections: Coordinate reference system conversion via PROJ.
    • IO: Integration with geojson and geozero crates.
    // primitives
    use geo::{line_string, polygon};
    
    // algorithms
    use geo::ConvexHull;
    
    // An L shape
    let poly = polygon![ 
        (x: 0.0, y: 0.0),
        (x: 4.0, y: 0.0),
        (x: 4.0, y: 1.0),
        (x: 1.0, y: 1.0),
        (x: 1.0, y: 4.0),
        (x: 0.0, y: 4.0),
        (x: 0.0, y: 0.0),
    ];
    
    // Calculate the polygon's convex hull
    let hull = poly.convex_hull();
    
    assert_eq!(
        hull.exterior(),
        &line_string![
            (x: 4.0, y: 0.0),
            (x: 4.0, y: 1.0),
            (x: 1.0, y: 4.0),
            (x: 0.0, y: 4.0),
            (x: 0.0, y: 0.0),
            (x: 4.0, y: 0.0),
        ]
    );
  6. How to add new test cases to JTS Test Runner

    main

    If you need to extend the runner to support new types of tests or new test cases, you must modify two main areas:

    1. Parsing Input: Update OperationInput in src/input.rs to handle the specific input formats required for the new test type (e.g., Centroid, ConcaveHull, etc.).
    2. Running Tests: The actual evaluation of geo behavior against the expected results in the XML input occurs in the TestRunner#run method in src/runner.rs.
  7. Overview of the geo crate

    main

    The geo crate provides planar geospatial geometries and algorithms. It is designed to adhere to the OpenGIS Simple Feature Access (OGC-SFA) standards, making it interoperable with other implementations like JTS and GEOS.

    Key capabilities include:

    • Topological Relationships: DE-9IM support, containment, and intersection.
    • Boolean Operations: Clip, union, difference, intersection, and XOR on geometries.
    • Affine Operations: Scale, rotate, skew, and translate.
    • Measurements: Euclidean, Haversine, Geodesic, and Rhumb line calculations for distance and length.
    • Clustering: DBSCAN and k-means.
    • Simplification: Ramer–Douglas–Peucker and Visvalingam-Whyatt algorithms.
    • Spatial Indexing: Support for R*-tree (via rstar) and BallTree for point-only data.
    • Coordinate Conversion: Support for projecting and converting between coordinate reference systems using PROJ.
  8. Efficiently run multiple outlier detection passes with PreparedDetector

    main

    If you need to run the LOF algorithm multiple times with different k_neighbours values, use prepared_detector() to create a PreparedDetector. This avoids rebuilding the underlying spatial index (R-Tree) between runs, significantly improving efficiency.

    PreparedDetector allows you to call .outliers(k_neighbours) repeatedly on the same point set with different neighbor counts.

    use geo::OutlierDetection;
    use geo::{point, Point, MultiPoint};
    
    let v = [ 
        point!(x: 0.0, y: 0.0), 
        point!(x: 0.0, y: 1.0), 
        point!(x: 3.0, y: 0.0), 
        point!(x: 1.0, y: 1.0) 
    ];
    
    let prepared = &v.prepared_detector();
    let s1 = prepared.outliers(2);
    let s2 = prepared.outliers(3);
    // different neighbour sizes give different scores
    assert_ne!(s1[2], s2[2]);
  9. Optimize repeated triangulations with Earcutter

    main

    When triangulating many polygons in a hot loop, using the standard TriangulateEarcut methods will cause significant per-call allocations. To avoid this, use the Earcutter<T> struct to reuse internal buffers.

    Workflow

    1. Create a single instance of Earcutter::new().
    2. Use earcut_triangulation_ref(&mut earcutter) to perform the triangulation. This returns an EarcutTriangulationRef, which borrows from the Earcutter's internal buffers.
    3. Ensure you finish using the returned reference before calling triangulate again on the same Earcutter instance, as the next call will clear and overwrite the buffers.

    Performance Note

    Earcutter retains its vertices and triangle_indices vectors across calls, amortizing allocation costs. The buffers will automatically adjust to the size of the largest polygon processed.

  10. Calculate the centroid of a geometry

    main

    The Centroid trait provides a method to calculate the arithmetic mean position of all points in a shape. The centroid is the point where a cutout of the shape could be perfectly balanced.

    Important Behaviors:

    • Non-convex objects: The centroid may lie outside the object itself.
    • Dimensionality in Collections: When calculating the centroid of a GeometryCollection or Multi* type, the library tracks dimensionality. Higher-dimensional elements (e.g., a 2D Polygon) will 'clobber' or take precedence over lower-dimensional elements (e.g., a 0D Point) in the calculation. For example, in a GeometryCollection, a 2D element's centroid will determine the result, effectively ignoring 0D or 1D elements.
    • Empty Geometries: For types like LineString, MultiLineString, Polygon, MultiPolygon, MultiPoint, and GeometryCollection, the centroid() method returns an Option<Point<T>>. If the geometry is empty or cannot have a centroid, it returns None.
    use geo::Centroid;
    use geo::{polygon, point};
    
    // rhombus shaped polygon
    let polygon = polygon![
        (x: -2., y: 1.),
        (x: 1., y: 3.),
        (x: 4., y: 1.),
        (x: 1., y: -1.),
        (x: -2., y: 1.),
    ];
    
    assert_eq!(
        Some(point!(x: 1., y: 1.)),
        polygon.centroid(),
    );
  11. Compose and manage AffineTransform matrices

    main

    An AffineTransform<T> represents a 2D affine transformation matrix in row-major order:

    [[a, b, xoff],
     [d, e, yoff],
     [0, 0, 1]]

    Transformations can be efficiently composed into a single matrix before being applied to geometries. This avoids the overhead of multiple passes over the coordinate data.

    Construction vs. Mutation

    • Construction (Present Tense): Methods like scale(), translate(), rotate(), and skew() create a new transform.
    • Mutation (Past Participle): Methods like scaled(), translated(), rotated(), and skewed() add the operation to the existing transform (cumulative).

    Key Methods

    • identity(): Returns an identity matrix (no-op).
    • compose(&self, other: &Self) -> Self: Composes two transforms into one.
    • compose_many(&self, transforms: &[Self]) -> Self: Composes an arbitrary number of transforms.
    • inverse(&self) -> Option<Self>: Returns the inverse of the transform if it is invertible.
    • is_identity(&self) -> bool: Checks if the transform is the identity matrix.
    use geo::AffineTransform;
    
    // Compose multiple transforms
    let mut transform = AffineTransform::identity();
    let transform1 = AffineTransform::translate(1.0, 2.0);
    let transform2 = AffineTransform::translate(-1.0, -2.0);
    let transforms = vec![transform1, transform2];
    
    let outcome = transform.compose_many(&transforms);
    assert!(outcome.is_identity());
  12. Core geometry types in geo-types

    main

    The geo-types library provides standard geometric primitives following the OpenGIS Simple Feature Access (OGC-SFA) standards. These types are designed for interoperability with other geospatial implementations like JTS and GEOS.

    Available geometries include:

    • Point: A single point represented by one Coord.
    • MultiPoint: A collection of Points.
    • Line: A line segment represented by two Coords.
    • LineString: A series of contiguous line segments represented by two or more Coords.
    • MultiLineString: A collection of LineStrings.
    • Polygon: A bounded area represented by one LineString exterior ring and zero or more LineString interior rings.
    • MultiPolygon: A collection of Polygons.
    • Rect: An axis-aligned bounded rectangle represented by minimum and maximum Coords.
    • Triangle: A bounded area represented by three Coord vertices.
    • GeometryCollection: A collection of Geometrys.
    • Geometry: An enumeration of all the above geometry types.