Skip to content

Allow using arbitrary georust geometry in a feature layer #289

@Maximkaaa

Description

@Maximkaaa

Discussed in #288

Originally posted by C-Loftus December 19, 2025
Thank you very much for your excellent work on galileo.

I am trying to adapt the georust example here to support arbitrary geometries, not just points / geojson.

I am using to georust gdal bindings which work well, but I can't figure out how to render arbitrary geometries. No matter how I convert the types to_geo2d doesn't seem to work for an arbitrary geometry.


If I try to use something like this I get an error with trait bounds

    let dataset = Dataset::open(Path::new(json)).unwrap();
    let mut geometries = vec![];
    for mut layer in dataset.layers() {
        for feature in layer.features() {
            let geometry = feature.geometry().unwrap();
            let geom = geometry.to_geo().unwrap();
            geom.to_geo2d()
the method `to_geo2d` exists for enum `geo_types::Geometry`, but its trait bounds were not satisfied
the following trait bounds were not satisfied:
`<geo_types::Geometry as GeometryType>::Space = AmbiguousSpace`
which is required by `geo_types::Geometry: Disambiguate`
`geo_types::Geometry: GeometryType`
which is required by `geo_types::Geometry: Disambiguate`
`<&geo_types::Geometry as GeometryType>::Space = AmbiguousSpace`
which is required by `&geo_types::Geometry: Disambiguate`
`&geo_types::Geometry: GeometryType`
which is required by `&geo_types::Geometry: Disambiguate`
`<&mut geo_types::Geometry as GeometryType>::Space = AmbiguousSpace`
which is required by `&mut geo_types::Geometry: Disambiguate`
`&mut geo_types::Geometry: GeometryType`
which is required by `&mut geo_types::Geometry: Disambiguate`

My Current Code

This is some code I have that works with the rust gdal bindings, very similar to the georust example, to render points to a file. However, I am unclear how to adapt this to arbitrary geometry.

use std::error::Error;
use std::path::Path;
use std::time::Duration;
use std::vec;

use galileo::layer::FeatureLayer;
use galileo::layer::raster_tile_layer::RasterTileLayerBuilder;
use galileo::render::WgpuRenderer;
use galileo::symbol::{
    ArbitraryGeometrySymbol, CirclePointSymbol, SimpleContourSymbol, SimplePolygonSymbol,
};
use galileo::tile_schema::TileSchemaBuilder;
use galileo::{Color, Map, MapView, Messenger};
use galileo_types::cartesian::Size;
use galileo_types::geo::Crs;
use galileo_types::geometry_type::GeoSpace2d;
use galileo_types::{Disambig, Disambiguate};
use gdal::Dataset;
use gdal::vector::LayerAccess;
use image::{ImageBuffer, Rgba};

fn load_points(json: &str) -> Vec<Disambig<geo_types::Point, GeoSpace2d>> {
    let dataset = Dataset::open(Path::new(json)).unwrap();
    let mut points = vec![];
    for mut layer in dataset.layers() {
        for feature in layer.features() {
            let geometry = feature.geometry().unwrap();
            let geom = geometry.to_geo().unwrap();
            if let geo_types::Geometry::Point(point) = geom {
                points.push(point.to_geo2d());
            }
        }
    }

    points
}

pub async fn generate_map(geojson: &str) -> Result<String, Box<dyn Error>> {
    let symbol = ArbitraryGeometrySymbol::new(
        CirclePointSymbol::new(Color::RED, 10.0),
        SimpleContourSymbol::new(Color::GREEN, 2.0),
        SimplePolygonSymbol::new(Color::BLUE),
    );
    let point_layer = FeatureLayer::new(load_points(geojson), symbol, Crs::WGS84);
    // To calculate the area of the map which we want to draw, we use map's CRS instead of
    // layer CRS.
    let extent = point_layer
        .extent_projected(&Crs::EPSG3857)
        .expect("cannot project extent");
    let center = extent.center();

    let image_size = Size::new(400, 400);

    let width_resolution = extent.width() / image_size.width() as f64;
    let height_resolution = extent.height() / image_size.height() as f64;
    let resolution = (width_resolution.max(height_resolution) * 1.1).max(
        TileSchemaBuilder::web_mercator(0..=18)
            .build()
            .expect("default tile schema is valid")
            .lod_resolution(10)
            .expect("the tile schema has resolution for zoom level 17"),
    );

    // Create OSM layer for background
    let mut osm = RasterTileLayerBuilder::new_osm()
        .with_file_cache_checked(".tile_cache")
        .build()
        .expect("failed to create layer");

    // If we don't set fade in duration to 0, when the image is first drawn, all tiles will
    // be transparent.
    osm.set_fade_in_duration(Duration::default());

    let map_view = MapView::new_projected(&center, resolution).with_size(image_size.cast());

    // Load all tiles required for the given view before we request rendering.
    osm.load_tiles(&map_view).await;

    let map = Map::new(
        map_view,
        vec![Box::new(osm), Box::new(point_layer)],
        None::<Box<dyn Messenger>>,
    );

    // We create a renderer without window, so it will use internal texture to render to.
    // Every time the `render` method is called, the image is updated and can be retrieved
    // by the `get_image` method.
    let renderer = WgpuRenderer::new_with_texture_rt(image_size)
        .await
        .expect("failed to create renderer");
    renderer.render(&map).expect("failed to render the map");

    let bitmap = renderer
        .get_image()
        .await
        .expect("failed to get image bitmap from texture");
    let buffer =
        ImageBuffer::<Rgba<u8>, _>::from_raw(image_size.width(), image_size.height(), bitmap)
            .expect("failed to read bitmap");
    buffer
        .save("/tmp/output_map.png")
        .expect("failed to encode or write image");

    Ok("/tmp/output_map.png".to_string())
}

What I have tried

If I try to switch the code to just return geo_types::Geometry then I get this issue

    let features = FeatureLayer::new(load_geometries(dataset), symbol, Crs::WGS84);
the trait bound `geo_types::Geometry: GeometryType` is not satisfied
the following other types implement trait `GeometryType`:
  Coord<T>
  Disambig<T, Space>
  GeoPoint2d
  Point2<Num>
  galileo_mvt::contour::ClosedMvtContour
  galileo_mvt::contour::MvtContour
  galileo_mvt::contour::MvtContours
  galileo_mvt::contour::MvtPolygon
and 12 others
required for `Disambig<geo_types::Geometry, GeoSpace2d>` to implement `galileo::layer::feature_layer::Feature`
```</div>

Metadata

Metadata

Assignees

No one assigned

    Labels

    T-enhancementImprove an existing feature

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions