Skip to content

Commit

Permalink
Polygonize a GeometryGraph
Browse files Browse the repository at this point in the history
  • Loading branch information
Notgnoshi committed Feb 25, 2024
1 parent 2f06dcc commit a363dff
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 4 deletions.
6 changes: 6 additions & 0 deletions generative/cxxbridge/coord_ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ pub mod ffi {
dst: usize,
}
impl Vec<GraphEdge> {}

#[derive(Debug, Clone, PartialEq)]
struct PolygonizationResult {
polygons: Vec<LineStringShim>,
dangles: Vec<LineStringShim>,
}
}

impl From<geo::Coord> for ffi::CoordShim {
Expand Down
6 changes: 6 additions & 0 deletions generative/cxxbridge/geometry_graph.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ class GeometryGraphShim
{
}

[[nodiscard]] const generative::noding::GeometryGraph& inner() const noexcept
{
return m_inner;
}
[[nodiscard]] generative::noding::GeometryGraph& inner() noexcept { return m_inner; }

[[nodiscard]] rust::Vec<CoordShim> nodes() const noexcept
{
const auto& cxx_nodes = m_inner.get_nodes();
Expand Down
4 changes: 2 additions & 2 deletions generative/cxxbridge/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ mod geometry_collection_ffi;
mod geometry_graph_ffi;
mod noder_ffi;

pub use coord_ffi::ffi::{CoordShim, GraphEdge, LineStringShim, PolygonShim};
pub use coord_ffi::ffi::{CoordShim, GraphEdge, LineStringShim, PolygonShim, PolygonizationResult};
pub use geometry_collection::GeometryCollectionShim;
pub use geometry_graph_ffi::ffi::{from_nodes_edges, GeometryGraphShim};
pub use noder_ffi::ffi::node;
pub use noder_ffi::ffi::{node, polygonize};

pub fn to_ffi_graph<Direction: petgraph::EdgeType>(
graph: &crate::graph::GeometryGraph<Direction>,
Expand Down
62 changes: 62 additions & 0 deletions generative/cxxbridge/noder.hpp
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
#pragma once
#include "generative/generative/cxxbridge/coord_ffi.rs.h"
#include "generative/generative/cxxbridge/geometry_graph_ffi.rs.h"
#include "geometry_collection.hpp"
#include "geometry_graph.hpp"
#include <generative/noding/geometry-graph.h>
#include <generative/noding/geometry-noder.h>

#include <geos/geom/Geometry.h>
#include <geos/noding/snap/SnappingNoder.h>
#include <geos/operation/overlay/snap/GeometrySnapper.h>
#include <geos/operation/polygonize/Polygonizer.h>

#include <iterator>
#include <memory>

[[nodiscard]] inline std::unique_ptr<GeometryGraphShim>
Expand All @@ -27,3 +33,59 @@ node(const GeometryCollectionShim& rust_geoms, double tolerance) noexcept

return graph_shim;
}

[[nodiscard]] inline PolygonizationResult polygonize(const GeometryGraphShim& graph) noexcept
{
const auto owned_edges = graph.inner().get_edges();
std::vector<const geos::geom::Geometry*> non_owned_edges;
non_owned_edges.reserve(owned_edges.size());
std::transform(
owned_edges.cbegin(),
owned_edges.cend(),
std::back_inserter(non_owned_edges),
[](const std::unique_ptr<geos::geom::LineString>& edge) -> const geos::geom::Geometry* {
return edge.get();
});

auto polygonizer = geos::operation::polygonize::Polygonizer();
// Adding the edges doesn't actually polygonize. That's deferred to the first time the polygons
// or dangles are accessed.
polygonizer.add(&non_owned_edges);

const auto polys = polygonizer.getPolygons();
// The dangles are just pointers back to the LineString pointers we passed in?
const auto dangles = polygonizer.getDangles();

PolygonizationResult retval;
retval.polygons.reserve(polys.size());
retval.dangles.reserve(dangles.size());

for (const auto& poly : polys)
{
LineStringShim result;
const auto* shell = poly->getExteriorRing();
result.vec.reserve(shell->getNumPoints());
const auto* coords = shell->getCoordinatesRO();
for (size_t i = 0; i < coords->size(); i++)
{
const auto coord = coords->getAt(i);
result.vec.push_back(CoordShim{coord.x, coord.y});
}
retval.polygons.push_back(result);
}

for (const auto* dangle : dangles)
{
LineStringShim result;
const auto* coords = dangle->getCoordinatesRO();
result.vec.reserve(coords->size());
for (size_t i = 0; i < coords->size(); i++)
{
const auto coord = coords->getAt(i);
result.vec.push_back(CoordShim{coord.x, coord.y});
}
retval.dangles.push_back(result);
}

return retval;
}
3 changes: 3 additions & 0 deletions generative/cxxbridge/noder_ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pub mod ffi {

type GeometryCollectionShim = crate::cxxbridge::GeometryCollectionShim;
type GeometryGraphShim = crate::cxxbridge::GeometryGraphShim;
type PolygonizationResult = crate::cxxbridge::PolygonizationResult;

/// Node the given collection of geometries
///
Expand All @@ -19,6 +20,8 @@ pub mod ffi {
geoms: &GeometryCollectionShim,
tolerance: f64,
) -> UniquePtr<GeometryGraphShim>;

fn polygonize(graph: &GeometryGraphShim) -> PolygonizationResult;
}
}

Expand Down
61 changes: 59 additions & 2 deletions generative/noding/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use geo::Geometry;
use geo::{Coord, Geometry, LineString, Polygon};

use crate::cxxbridge;
use crate::graph::GeometryGraph;
Expand All @@ -20,15 +20,45 @@ where
(&*ffi_graph).into()
}

pub fn polygonize<Direction: petgraph::EdgeType>(graph: &GeometryGraph<Direction>) {
pub fn polygonize<Direction: petgraph::EdgeType>(
graph: &GeometryGraph<Direction>,
) -> (Vec<Polygon>, Vec<LineString>) {
let ffi_graph = cxxbridge::to_ffi_graph(graph);
let result = cxxbridge::polygonize(&ffi_graph);

let mut polys = Vec::new();
polys.reserve_exact(result.polygons.len());
for coordseq in result.polygons {
let coords: Vec<_> = coordseq
.vec
.into_iter()
.map(|c| Coord { x: c.x, y: c.y })
.collect();
let exterior = LineString::new(coords);
let interiors = Vec::new();
polys.push(Polygon::new(exterior, interiors));
}

let mut dangles = Vec::new();
dangles.reserve_exact(result.dangles.len());
for coordseq in result.dangles {
let coords: Vec<_> = coordseq
.vec
.into_iter()
.map(|c| Coord { x: c.x, y: c.y })
.collect();
dangles.push(LineString::new(coords));
}

(polys, dangles)
}

#[cfg(test)]
mod tests {
use geo::Point;
use petgraph::graph::{EdgeIndex, NodeIndex};
use petgraph::Undirected;
use wkt::TryFromWkt;

use super::*;
use crate::io::read_wkt_geometries;
Expand Down Expand Up @@ -174,4 +204,31 @@ mod tests {
];
assert_eq!(edges, expected);
}

#[test]
fn test_polygonize() {
let wkt = b"GEOMETRYCOLLECTION( LINESTRING(2 0, 2 8), LINESTRING(6 0, 6 8), LINESTRING(0 2, 8 2), LINESTRING(0 6, 8 6))";
let geoms = read_wkt_geometries(&wkt[..]);
let graph = node::<_, Undirected>(geoms);

let (polygons, dangles) = polygonize(&graph);
assert_eq!(polygons.len(), 1);
assert_eq!(dangles.len(), 8);

let expected: Polygon =
Polygon::try_from_wkt_str("POLYGON((2 2, 2 6, 6 6, 6 2, 2 2))").unwrap();
assert_eq!(polygons[0], expected);

let expected = [
LineString::try_from_wkt_str("LINESTRING(6 6, 8 6)").unwrap(),
LineString::try_from_wkt_str("LINESTRING(6 2, 8 2)").unwrap(),
LineString::try_from_wkt_str("LINESTRING(6 6, 6 8)").unwrap(),
LineString::try_from_wkt_str("LINESTRING(6 0, 6 2)").unwrap(),
LineString::try_from_wkt_str("LINESTRING(2 6, 2 8)").unwrap(),
LineString::try_from_wkt_str("LINESTRING(2 0, 2 2)").unwrap(),
LineString::try_from_wkt_str("LINESTRING(2 6, 0 6)").unwrap(),
LineString::try_from_wkt_str("LINESTRING(2 2, 0 2)").unwrap(),
];
assert_eq!(dangles, expected);
}
}

0 comments on commit a363dff

Please sign in to comment.