| Title: | Vertex Pool Topology for Well-Known Geometry |
|---|---|
| Description: | Establishes and maintains vertex pool topology for geometry handled by 'wk'. Segments are the atomic unit, vertices are shared via integer references into a pool. Topology is made discoverable via coincident vertex detection while not requiring modification of the input data. Topological data models follow principles described in Worboys and Duckham (2004, ISBN:978-0415283755). The edge-based topology geometry decomposed into vertices and directed edge pairs is a simplification of the quad-edge case in Guibas & Stolfi (1985) <doi:10.1145/282918.282923>. |
| Authors: | Michael Sumner [aut, cre] |
| Maintainer: | Michael Sumner <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.3.0 |
| Built: | 2026-06-03 08:35:26 UTC |
| Source: | https://github.com/hypertidy/wkpool |
Summarize arc-node structure
arc_node_summary(x)arc_node_summary(x)
x |
A wkpool (ideally after merge_coincident) |
List with counts and degree distribution
x <- wk::as_wkb(c( "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", "POLYGON ((1 0, 2 0, 2 1, 1 1, 1 0))" )) pool <- establish_topology(x) merged <- merge_coincident(pool) arc_node_summary(merged)x <- wk::as_wkb(c( "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", "POLYGON ((1 0, 2 0, 2 1, 1 1, 1 0))" )) pool <- establish_topology(x) merged <- merge_coincident(pool) arc_node_summary(merged)
Convert arcs to WKT linestrings
arcs_to_wkb(x, ...) arcs_to_wkt(x)arcs_to_wkb(x, ...) arcs_to_wkt(x)
x |
A wkpool (ideally after merge_coincident) |
... |
Passed to |
Each arc (maximal segment sequence between nodes) becomes a linestring.
A wk_wkt vector of LINESTRING geometries
x <- wk::as_wkb(c( "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", "POLYGON ((1 0, 2 0, 2 1, 1 1, 1 0))" )) pool <- establish_topology(x) merged <- merge_coincident(pool) arcs_to_wkt(merged)x <- wk::as_wkb(c( "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", "POLYGON ((1 0, 2 0, 2 1, 1 1, 1 0))" )) pool <- establish_topology(x) merged <- merge_coincident(pool) arcs_to_wkt(merged)
Convert arcs to a wkpool of arc segments
as_arcs(x, arc_id = TRUE)as_arcs(x, arc_id = TRUE)
x |
A wkpool (ideally after merge_coincident) |
arc_id |
Logical: add .arc column to track arc membership? |
A wkpool with segments grouped by arc
x <- wk::as_wkb(c( "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", "POLYGON ((1 0, 2 0, 2 1, 1 1, 1 0))" )) pool <- establish_topology(x) merged <- merge_coincident(pool) as_arcs(merged)x <- wk::as_wkb(c( "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", "POLYGON ((1 0, 2 0, 2 1, 1 1, 1 0))" )) pool <- establish_topology(x) merged <- merge_coincident(pool) as_arcs(merged)
Convert wkpool to RTriangle pslg format
as_pslg(x, ...)as_pslg(x, ...)
x |
A wkpool (will be passed through merge_coincident if not already) |
... |
passed to merge_coincident |
Produces a Planar Straight Line Graph suitable for constrained triangulation with RTriangle. The vertex pool maps directly to P, and segment indices map directly to S (both 1-indexed).
Note: Hole detection is not currently supported. The returned pslg
does not include hole points (H). For polygons with holes, you may
need to identify hole points manually using find_cycles() and
cycle_signed_area().
A list with P (vertex matrix) and S (segment matrix) for RTriangle::pslg()
x <- wk::as_wkb("POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))") pool <- establish_topology(x) as_pslg(pool)x <- wk::as_wkb("POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))") pool <- establish_topology(x) as_pslg(pool)
Classify cycles as outer rings or holes based on winding
classify_cycles(x, convention = c("sf", "ogc"))classify_cycles(x, convention = c("sf", "ogc"))
x |
A wkpool (ideally after merge_coincident) |
convention |
Which winding convention to use: "sf" (default) or "ogc"
|
A data frame with cycle index, signed area, and type (outer/hole)
x <- wk::as_wkb(c( paste0( "MULTIPOLYGON (((0 0, 0 1, 0.75 1, 1 0.8, 0.5 0.7, ", "0.8 0.6, 0.69 0, 0 0), (0.2 0.2, 0.5 0.2, ", "0.5 0.4, 0.3 0.6, 0.2 0.4, 0.2 0.2)))"), "MULTIPOLYGON (((0.69 0, 0.8 0.6, 1.1 0.63, 1.23 0.3, 0.69 0)))" )) pool <- establish_topology(x) merged <- merge_coincident(pool) classify_cycles(merged)x <- wk::as_wkb(c( paste0( "MULTIPOLYGON (((0 0, 0 1, 0.75 1, 1 0.8, 0.5 0.7, ", "0.8 0.6, 0.69 0, 0 0), (0.2 0.2, 0.5 0.2, ", "0.5 0.4, 0.3 0.6, 0.2 0.4, 0.2 0.2)))"), "MULTIPOLYGON (((0.69 0, 0.8 0.6, 1.1 0.63, 1.23 0.3, 0.69 0)))" )) pool <- establish_topology(x) merged <- merge_coincident(pool) classify_cycles(merged)
Calculate signed area of a cycle
cycle_signed_area(cycle, pool)cycle_signed_area(cycle, pool)
cycle |
Integer vector of .vx IDs forming a closed cycle |
pool |
Vertex pool data frame (from pool_vertices) |
Uses the shoelace formula to compute signed area. The sign indicates winding direction. For typical geographic data (Simple Features convention):
Negative area = outer ring
Positive area = hole
This is intrinsic to the geometry — we observe winding, not declare it.
Numeric signed area.
x <- wk::as_wkb("POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))") pool <- establish_topology(x) merged <- merge_coincident(pool) cycles <- find_cycles(merged) cycle_signed_area(cycles[[1]], pool_vertices(merged))x <- wk::as_wkb("POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))") pool <- establish_topology(x) merged <- merge_coincident(pool) cycles <- find_cycles(merged) cycle_signed_area(cycles[[1]], pool_vertices(merged))
Convert cycles to WKT polygons
cycles_to_wkt(x, feature = TRUE, convention = c("sf", "ogc")) cycles_to_wkb(x, feature = TRUE, convention = c("sf", "ogc"), ...)cycles_to_wkt(x, feature = TRUE, convention = c("sf", "ogc")) cycles_to_wkb(x, feature = TRUE, convention = c("sf", "ogc"), ...)
x |
A wkpool (ideally after merge_coincident) |
feature |
Logical: attempt to reconstruct original features? If TRUE, groups cycles by .feature and nests holes in outers. If FALSE, each cycle becomes a separate polygon. |
convention |
Winding convention: "sf" (default) or "ogc" |
... |
Passed to |
Converts cycles back to polygons. When feature = TRUE, attempts to reconstruct original polygon structure by grouping rings by feature and nesting holes within their containing outer ring.
A wk_wkt vector of POLYGON geometries
x <- wk::as_wkb(c( "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", "POLYGON ((1 0, 2 0, 2 1, 1 1, 1 0))" )) pool <- establish_topology(x) merged <- merge_coincident(pool) cycles_to_wkt(merged)x <- wk::as_wkb(c( "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", "POLYGON ((1 0, 2 0, 2 1, 1 1, 1 0))" )) pool <- establish_topology(x) merged <- merge_coincident(pool) cycles_to_wkt(merged)
Establish topology from any wk-handleable geometry
establish_topology(x, ...)establish_topology(x, ...)
x |
Any geometry wk can handle (sf, sfc, wkb, wkt, geos, s2, xy, ...) |
... |
reserved for future use |
Vertices are minted as-is from the input coordinates. No snapping or deduplication is performed - the pool represents the exact truth of the input geometry. Use merge_coincident() afterward to discover/establish shared topology.
Segments track their feature origin via .feature attribute,
enabling discovery of shared boundaries and neighbour relations.
A wkpool object (segments with vertex pool)
x <- wk::as_wkb(c( paste0( "MULTIPOLYGON (((0 0, 0 1, 0.75 1, 1 0.8, 0.5 0.7, ", "0.8 0.6, 0.69 0, 0 0), (0.2 0.2, 0.5 0.2, ", "0.5 0.4, 0.3 0.6, 0.2 0.4, 0.2 0.2)))"), "MULTIPOLYGON (((0.69 0, 0.8 0.6, 1.1 0.63, 1.23 0.3, 0.69 0)))" )) pool <- establish_topology(x)x <- wk::as_wkb(c( paste0( "MULTIPOLYGON (((0 0, 0 1, 0.75 1, 1 0.8, 0.5 0.7, ", "0.8 0.6, 0.69 0, 0 0), (0.2 0.2, 0.5 0.2, ", "0.5 0.4, 0.3 0.6, 0.2 0.4, 0.2 0.2)))"), "MULTIPOLYGON (((0.69 0, 0.8 0.6, 1.1 0.63, 1.23 0.3, 0.69 0)))" )) pool <- establish_topology(x)
Find arcs (maximal segment sequences between nodes)
find_arcs(x)find_arcs(x)
x |
A wkpool (ideally after merge_coincident) |
Arcs are maximal paths through degree-2 vertices. They start and end at nodes (degree != 2) or form closed loops through degree-2 vertices.
A list of integer vectors, each containing .vx IDs forming an arc
x <- wk::as_wkb(c( "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", "POLYGON ((1 0, 2 0, 2 1, 1 1, 1 0))" )) pool <- establish_topology(x) merged <- merge_coincident(pool) find_arcs(merged)x <- wk::as_wkb(c( "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", "POLYGON ((1 0, 2 0, 2 1, 1 1, 1 0))" )) pool <- establish_topology(x) merged <- merge_coincident(pool) find_arcs(merged)
Find closed cycles (rings) in segment graph
find_cycles(x)find_cycles(x)
x |
A wkpool (ideally after merge_coincident) |
Walks the segment graph to discover closed loops. Relies on segments being in consecutive order within each ring (as produced by establish_topology).
Each cycle is a vector of vertex IDs in traversal order. The cycle is closed (first vertex connects back to last via a segment).
A list of integer vectors, each containing .vx IDs forming a closed cycle
x <- wk::as_wkb(c( "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", "POLYGON ((1 0, 2 0, 2 1, 1 1, 1 0))" )) pool <- establish_topology(x) merged <- merge_coincident(pool) find_cycles(merged)x <- wk::as_wkb(c( "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", "POLYGON ((1 0, 2 0, 2 1, 1 1, 1 0))" )) pool <- establish_topology(x) merged <- merge_coincident(pool) find_cycles(merged)
Find internal boundaries (edges shared by exactly 2 features, opposite direction)
find_internal_boundaries(x)find_internal_boundaries(x)
x |
A wkpool after merge_coincident |
Internal boundaries are edges where one feature has segment (v0→v1) and another feature has (v1→v0) — i.e., they share the edge but traverse it in opposite directions. This is the defining characteristic of a shared polygon boundary.
A wkpool containing only internal boundary segments
x <- wk::as_wkb(c( "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", "POLYGON ((1 0, 2 0, 2 1, 1 1, 1 0))" )) pool <- establish_topology(x) merged <- merge_coincident(pool) find_internal_boundaries(merged)x <- wk::as_wkb(c( "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", "POLYGON ((1 0, 2 0, 2 1, 1 1, 1 0))" )) pool <- establish_topology(x) merged <- merge_coincident(pool) find_internal_boundaries(merged)
Build adjacency from shared edges
find_neighbours(x, type = c("edge", "vertex"))find_neighbours(x, type = c("edge", "vertex"))
x |
A wkpool after merge_coincident |
type |
"edge" for features sharing an edge, "vertex" for features sharing any vertex |
Data frame of feature pairs that are neighbours
x <- wk::as_wkb(c( "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", "POLYGON ((1 0, 2 0, 2 1, 1 1, 1 0))" )) pool <- establish_topology(x) merged <- merge_coincident(pool) find_neighbours(merged)x <- wk::as_wkb(c( "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", "POLYGON ((1 0, 2 0, 2 1, 1 1, 1 0))" )) pool <- establish_topology(x) merged <- merge_coincident(pool) find_neighbours(merged)
Find nodes (vertices where degree != 2)
find_nodes(x)find_nodes(x)
x |
A wkpool (ideally after merge_coincident) |
Nodes are branch points (degree 3+) or endpoints (degree 1). Degree-2 vertices are pass-through points within an arc.
Integer vector of .vx IDs that are nodes
x <- wk::as_wkb(c( "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", "POLYGON ((1 0, 2 0, 2 1, 1 1, 1 0))" )) pool <- establish_topology(x) merged <- merge_coincident(pool) find_nodes(merged)x <- wk::as_wkb(c( "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", "POLYGON ((1 0, 2 0, 2 1, 1 1, 1 0))" )) pool <- establish_topology(x) merged <- merge_coincident(pool) find_nodes(merged)
Get hole points for constrained triangulation
hole_points(x, convention = c("sf", "ogc"))hole_points(x, convention = c("sf", "ogc"))
x |
A wkpool (ideally after merge_coincident) |
convention |
Which winding convention to use: "sf" (default) or "ogc" |
For use with RTriangle::triangulate(). Each row is the centroid of a hole, which tells the triangulator to exclude that region.
A matrix of hole points (centroids of hole cycles), or NULL if no holes
polygons_with_holes <- wk::as_wkb(c( paste0( "MULTIPOLYGON (((0 0, 0 1, 0.75 1, 1 0.8, 0.5 0.7, ", "0.8 0.6, 0.69 0, 0 0), (0.2 0.2, 0.5 0.2, ", "0.5 0.4, 0.3 0.6, 0.2 0.4, 0.2 0.2)))"), "MULTIPOLYGON (((0.69 0, 0.8 0.6, 1.1 0.63, 1.23 0.3, 0.69 0)))" )) x <- establish_topology(polygons_with_holes) merged <- merge_coincident(x) hole_points(merged) if (requireNamespace("RTriangle", quietly = TRUE)) { pslg <- as_pslg(merged) holes <- hole_points(merged) tri <- RTriangle::triangulate( RTriangle::pslg(P = pslg$P, S = pslg$S, H = holes) ) }polygons_with_holes <- wk::as_wkb(c( paste0( "MULTIPOLYGON (((0 0, 0 1, 0.75 1, 1 0.8, 0.5 0.7, ", "0.8 0.6, 0.69 0, 0 0), (0.2 0.2, 0.5 0.2, ", "0.5 0.4, 0.3 0.6, 0.2 0.4, 0.2 0.2)))"), "MULTIPOLYGON (((0.69 0, 0.8 0.6, 1.1 0.63, 1.23 0.3, 0.69 0)))" )) x <- establish_topology(polygons_with_holes) merged <- merge_coincident(x) hole_points(merged) if (requireNamespace("RTriangle", quietly = TRUE)) { pslg <- as_pslg(merged) holes <- hole_points(merged) tri <- RTriangle::triangulate( RTriangle::pslg(P = pslg$P, S = pslg$S, H = holes) ) }
Merge coincident vertices in a pool
merge_coincident(x, tolerance = 0)merge_coincident(x, tolerance = 0)
x |
A wkpool |
tolerance |
Numeric tolerance for coordinate matching. Default 0 means exact match only. |
With tolerance = 0, only exactly identical coordinates are merged. The first occurrence becomes the canonical vertex.
This is where you discover shared boundaries between polygons, network connectivity, mesh topology, etc.
Feature provenance (.feature) is preserved - segments keep their original feature assignment even after vertex merging.
A wkpool with shared vertices merged (fewer unique vertices)
x <- wk::as_wkb(c( "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", "POLYGON ((1 0, 2 0, 2 1, 1 1, 1 0))" )) pool <- establish_topology(x) topology_report(pool) merged <- merge_coincident(pool) topology_report(merged)x <- wk::as_wkb(c( "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", "POLYGON ((1 0, 2 0, 2 1, 1 1, 1 0))" )) pool <- establish_topology(x) topology_report(pool) merged <- merge_coincident(pool) topology_report(merged)
Draws segments coloured by feature membership.
## S3 method for class 'wkpool' plot(x, col = NULL, ...)## S3 method for class 'wkpool' plot(x, col = NULL, ...)
x |
A wkpool object. |
col |
Colour(s) for segments. If |
... |
Further arguments passed to |
Invisibly returns x.
x <- wk::as_wkb(c( "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", "POLYGON ((1 0, 2 0, 2 1, 1 1, 1 0))" )) pool <- establish_topology(x) plot(pool)x <- wk::as_wkb(c( "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", "POLYGON ((1 0, 2 0, 2 1, 1 1, 1 0))" )) pool <- establish_topology(x) plot(pool)
Combine wkpool objects
pool_combine(...)pool_combine(...)
... |
wkpool objects to combine |
A single wkpool with merged pools and remapped segments
x <- wk::as_wkb("POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))") y <- wk::as_wkb("POLYGON ((2 0, 3 0, 3 1, 2 1, 2 0))") pool_a <- establish_topology(x) pool_b <- establish_topology(y) pool_combine(pool_a, pool_b)x <- wk::as_wkb("POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))") y <- wk::as_wkb("POLYGON ((2 0, 3 0, 3 1, 2 1, 2 0))") pool_a <- establish_topology(x) pool_b <- establish_topology(y) pool_combine(pool_a, pool_b)
Compact a pool by removing unreferenced vertices
pool_compact(x)pool_compact(x)
x |
A wkpool |
A wkpool with only referenced vertices, .vx remapped
x <- wk::as_wkb(c( "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", "POLYGON ((1 0, 2 0, 2 1, 1 1, 1 0))" )) pool <- establish_topology(x) merged <- merge_coincident(pool) compact <- pool_compact(merged) nrow(pool_vertices(compact))x <- wk::as_wkb(c( "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", "POLYGON ((1 0, 2 0, 2 1, 1 1, 1 0))" )) pool <- establish_topology(x) merged <- merge_coincident(pool) compact <- pool_compact(merged) nrow(pool_vertices(compact))
Reverse a cycle's winding direction
reverse_cycle(cycle)reverse_cycle(cycle)
cycle |
Integer vector of .vx IDs forming a closed cycle |
Flips the traversal direction of a cycle without changing coordinates. Useful for converting between winding conventions.
The same vertices in reversed order (opposite winding)
x <- wk::as_wkb("POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))") pool <- establish_topology(x) merged <- merge_coincident(pool) cycles <- find_cycles(merged) reverse_cycle(cycles[[1]])x <- wk::as_wkb("POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))") pool <- establish_topology(x) merged <- merge_coincident(pool) cycles <- find_cycles(merged) reverse_cycle(cycles[[1]])
Convert wkpool segments to WKT
segments_to_wkt(x, type = c("multilinestring", "linestring", "point")) segments_to_wkb(x, type = c("multilinestring", "linestring", "point"), ...)segments_to_wkt(x, type = c("multilinestring", "linestring", "point")) segments_to_wkb(x, type = c("multilinestring", "linestring", "point"), ...)
x |
A wkpool |
type |
Output type: "linestring" (segments as paths), "multilinestring" (all segments), or "point" (vertices only) |
... |
Passed to |
A wk_wkt vector
x <- wk::as_wkb(c( "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", "POLYGON ((1 0, 2 0, 2 1, 1 1, 1 0))" )) pool <- establish_topology(x) segments_to_wkt(pool) segments_to_wkt(pool, type = "linestring")x <- wk::as_wkb(c( "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", "POLYGON ((1 0, 2 0, 2 1, 1 1, 1 0))" )) pool <- establish_topology(x) segments_to_wkt(pool) segments_to_wkt(pool, type = "linestring")
Report topology diagnostics
topology_report(x, tolerance = 1e-08)topology_report(x, tolerance = 1e-08)
x |
A wkpool |
tolerance |
Tolerance for "near miss" detection |
List of diagnostic information
x <- wk::as_wkb(c( "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", "POLYGON ((1 0, 2 0, 2 1, 1 1, 1 0))" )) pool <- establish_topology(x) topology_report(pool)x <- wk::as_wkb(c( "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", "POLYGON ((1 0, 2 0, 2 1, 1 1, 1 0))" )) pool <- establish_topology(x) topology_report(pool)
This is non-functional, wkpool does not currently support vec_c().
## S3 method for class 'wkpool' vec_c(..., .ptype = NULL)## S3 method for class 'wkpool' vec_c(..., .ptype = NULL)
... |
Vectors to coerce. |
.ptype |
If Alternatively, you can supply |
Attempts to combine wkpool vectors with vec_c will suggest using [pool_combine()' instead.
nothing, used for a message side-effect (see Details)
Calculate vertex degree (number of segments touching each vertex)
vertex_degree(x)vertex_degree(x)
x |
A wkpool (ideally after merge_coincident) |
Named integer vector: names are .vx, values are degree
x <- wk::as_wkb(c( "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", "POLYGON ((1 0, 2 0, 2 1, 1 1, 1 0))" )) pool <- establish_topology(x) merged <- merge_coincident(pool) vertex_degree(merged)x <- wk::as_wkb(c( "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", "POLYGON ((1 0, 2 0, 2 1, 1 1, 1 0))" )) pool <- establish_topology(x) merged <- merge_coincident(pool) vertex_degree(merged)
Extract the vertex pool, segment table, or feature vector from a wkpool.
pool_vertices(x) pool_segments(x) pool_feature(x)pool_vertices(x) pool_segments(x) pool_feature(x)
x |
A wkpool object. |
pool_vertices(): A data frame with columns .vx (vertex ID), x, y,
and optionally z.
pool_segments(): A data frame with columns .vx0, .vx1, and
optionally .feature.
pool_feature(): An integer vector of feature IDs, or NULL if no
feature information is present.
x <- wk::as_wkb(c( "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", "POLYGON ((1 0, 2 0, 2 1, 1 1, 1 0))" )) pool <- establish_topology(x) pool_vertices(pool) pool_segments(pool) pool_feature(pool)x <- wk::as_wkb(c( "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", "POLYGON ((1 0, 2 0, 2 1, 1 1, 1 0))" )) pool <- establish_topology(x) pool_vertices(pool) pool_segments(pool) pool_feature(pool)