Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
128 commits
Select commit Hold shift + click to select a range
aed9eac
Add RelateNG engine design document
asinghvi17 Jun 11, 2026
2722bc6
Add RelateNG implementation plan
asinghvi17 Jun 11, 2026
22f9085
Add RelateNG test scaffolding
asinghvi17 Jun 11, 2026
dde4fd5
Add `DE9IM` matrix type and location/dimension codes for RelateNG
asinghvi17 Jun 11, 2026
401ad39
Fix `matches` T-pattern semantics and accept lowercase DE-9IM codes
asinghvi17 Jun 11, 2026
2887d5b
Add boundary node rule types for RelateNG
asinghvi17 Jun 11, 2026
2b6205d
Add topology predicate framework for RelateNG
asinghvi17 Jun 11, 2026
9ca0d51
Add instance-level requirement flag accessors for topology predicates
asinghvi17 Jun 11, 2026
e55ae88
Add named DE-9IM predicates and pattern matcher for RelateNG
asinghvi17 Jun 11, 2026
4210877
Note JTS null-envelope semantics for extent helpers
asinghvi17 Jun 11, 2026
3053836
Add planar RelateKernel orientation, on-segment, point-in-ring and bo…
asinghvi17 Jun 11, 2026
c8d71f4
Strengthen adversarial orientation test and fix kernel contract wording
asinghvi17 Jun 11, 2026
451a855
Add exact symbolic segment intersection classification to RelateKernel
asinghvi17 Jun 11, 2026
12466b8
Add symmetry checks and JTS outcome mapping for segment classification
asinghvi17 Jun 11, 2026
1199a3a
Add symbolic node identity with exact rational coincidence slow path
asinghvi17 Jun 11, 2026
ae609fb
Add edge ordering around symbolic nodes to RelateKernel
asinghvi17 Jun 11, 2026
6310bb1
Add RelateKernel conformance testset
asinghvi17 Jun 11, 2026
2bb994c
Harden kernel conformance suite per review
asinghvi17 Jun 11, 2026
7a21fa4
Add LinearBoundary for RelateNG point location
asinghvi17 Jun 11, 2026
868aec7
Add AdjacentEdgeLocator for polygon union semantics
asinghvi17 Jun 11, 2026
6ace94a
Mark AdjacentEdgeLocator node-wheel slice for replacement in Task 17
asinghvi17 Jun 11, 2026
3c591fb
Add RelatePointLocator
asinghvi17 Jun 11, 2026
5d0b3d3
Add RelateGeometry input facade and segment strings
asinghvi17 Jun 11, 2026
7e20d03
Document empty-extent versus no-filter contract in `extract_segment_s…
asinghvi17 Jun 11, 2026
4978935
Generalize JTS XML test reader for relate operations and vendor test …
asinghvi17 Jun 11, 2026
430543b
Disambiguate relate skiplist keys and exercise runner execute path
asinghvi17 Jun 11, 2026
fa672a5
Add NodeSection and NodeSections with symbolic node keys
asinghvi17 Jun 11, 2026
2c4c077
Add PolygonNodeConverter for shell-hole node rewriting
asinghvi17 Jun 11, 2026
29432a0
Add RelateNode and RelateEdge wheel with label propagation
asinghvi17 Jun 11, 2026
8f27abe
Rewire AdjacentEdgeLocator onto NodeSections and RelateNode
asinghvi17 Jun 11, 2026
e9d33c3
Add TopologyComputer with symbolic node grouping
asinghvi17 Jun 11, 2026
d4f1d2e
Record crossing-node location deviation and harden mixed-wheel orderi…
asinghvi17 Jun 11, 2026
849c2b7
Add edge segment intersector over symbolic classification
asinghvi17 Jun 11, 2026
01471a4
Add accelerated edge set enumeration for RelateNG
asinghvi17 Jun 11, 2026
8c6313c
Warn that edge enumeration is A-cross-B only pending engine self-pair…
asinghvi17 Jun 11, 2026
9ab51e9
Add RelateNG engine with phased evaluation
asinghvi17 Jun 11, 2026
58ce083
Amend D3 for unconditional node merging and test self-intersection tr…
asinghvi17 Jun 11, 2026
35331b1
Add prepared mode for RelateNG
asinghvi17 Jun 11, 2026
65ff094
Note PreparedRelate thread-safety and fix dangling doc reference
asinghvi17 Jun 11, 2026
de2c104
Pass JTS relate XML test suites
asinghvi17 Jun 11, 2026
43db235
Never count an errored relate op as a pass in the XML runner
asinghvi17 Jun 11, 2026
3b4cd5c
Fix StaticArrays-backed ring extraction to plain Vector found by cros…
asinghvi17 Jun 11, 2026
5962c68
Cross-validate RelateNG against existing predicates and LibGEOS
asinghvi17 Jun 11, 2026
27914dd
Add differential fuzzing of RelateNG against LibGEOS
asinghvi17 Jun 11, 2026
f886578
Export `relate` and RelateNG predicate methods
asinghvi17 Jun 11, 2026
ef89702
Assert overlaps known-gap win and extend crosses API coverage
asinghvi17 Jun 11, 2026
988d15c
Add literate documentation for RelateNG
asinghvi17 Jun 11, 2026
81f920e
Add RelateNG benchmarks and allocation tests
asinghvi17 Jun 11, 2026
8ec1dba
Loosen disjoint allocation budget for cross-version stability
asinghvi17 Jun 11, 2026
50afd00
Add RelateNG performance audit plan
asinghvi17 Jun 12, 2026
89ab9c3
Type `extract_segment_strings` output concretely
asinghvi17 Jun 12, 2026
c2fdeda
Merge coincident nodes by box-overlap clustering
asinghvi17 Jun 12, 2026
361927b
Use NaturalIndex for the RelateNG edge indexes
asinghvi17 Jun 12, 2026
5f69a22
Cache extents on RelateGeometry as a wrapper tree
asinghvi17 Jun 12, 2026
2a8ad4e
Index point-in-area location (Task 22)
asinghvi17 Jun 12, 2026
c381546
Reconcile audit-fixes plan line references to post-performance-work code
asinghvi17 Jun 12, 2026
a97ce77
Fix GC extent caching dropping point members (positional `GI.extent` …
asinghvi17 Jun 12, 2026
0a79b45
Rename `Base.merge!` method on `RelateEdge` to internal `merge_edge!`
asinghvi17 Jun 12, 2026
c0b77df
Compare kernel edge-angle results by sign in `add_edge!`
asinghvi17 Jun 12, 2026
cbd65c3
Define `Base.print` for `DE9IM` instead of `Base.string`
asinghvi17 Jun 12, 2026
7404430
Throw `ArgumentError` for unknown target dimensions in `TopologyCompu…
asinghvi17 Jun 12, 2026
e269077
Rename collision-prone internal relateng helpers (`id`, `dimension`, …
asinghvi17 Jun 12, 2026
9fa016a
Convert trait `isa` ladders in relateng to trait-dispatched methods
asinghvi17 Jun 12, 2026
146c076
Document DE9IM index codes, Float64 evaluation, `prepare` genericity,…
asinghvi17 Jun 12, 2026
36e6c2e
Expand abbreviated internal relateng helper names (`_rce`, `_iig_*`, …
asinghvi17 Jun 12, 2026
7931ab6
Gitattributes for jts test files
asinghvi17 Jun 13, 2026
3c48fd9
Generalize relateng `_node_point` to 3D
asinghvi17 Jun 15, 2026
78719cc
Canonicalize relateng crossing nodes in 3D
asinghvi17 Jun 15, 2026
73735fd
Scaffold spherical relateng kernel and conformance suite
asinghvi17 Jun 15, 2026
113d598
Add spherical rk_orient
asinghvi17 Jun 15, 2026
4ad149e
Add spherical rk_point_on_segment
asinghvi17 Jun 15, 2026
0feb7cb
Add spherical arc_extent and interaction bounds
asinghvi17 Jun 15, 2026
149c33a
Make rk_bounds_covers dimension-generic
asinghvi17 Jun 15, 2026
507e6ea
Add spherical rk_classify_intersection
asinghvi17 Jun 15, 2026
005f528
Add spherical angle-ordering kernel functions
asinghvi17 Jun 15, 2026
60181a1
Add spherical rk_nodes_coincide exact path
asinghvi17 Jun 15, 2026
97a903e
Add spherical rk_point_in_ring
asinghvi17 Jun 15, 2026
7da7a17
Extend spherical area interaction bounds to interior axis points
asinghvi17 Jun 15, 2026
2049fe4
Derive relateng point type from the manifold
asinghvi17 Jun 15, 2026
5bde578
Add spherical relate end-to-end tests
asinghvi17 Jun 15, 2026
57ea26d
Accelerate spherical edge intersection with 3D arc extents
asinghvi17 Jun 15, 2026
e98d0c4
Throw on antipodal spherical edges
asinghvi17 Jun 15, 2026
b095170
Add AntipodalEdgeSplit geometry correction
asinghvi17 Jun 15, 2026
b787637
Port S2 edge-crossing conformance tests to the spherical kernel
asinghvi17 Jun 19, 2026
7145c7c
Add `spherical_arc_extent` and spherical manifold edge decomposition
asinghvi17 Jul 6, 2026
f75d1de
Trim comments and docstrings
asinghvi17 Jul 6, 2026
34c156e
Trim the arc extent literate header
asinghvi17 Jul 6, 2026
528ac9e
Add manifold-aware `Extents.extent` with spherical region extents
asinghvi17 Jul 7, 2026
5ee72be
Replace the winding enclosure heuristic with S2-style crossing parity
asinghvi17 Jul 8, 2026
51d6cd6
Trim comments in the region extent parity code
asinghvi17 Jul 8, 2026
c21f4c3
Condense spherical extent prose
asinghvi17 Jul 11, 2026
c5fe83d
Merge branch 'spherical-arc-extent' into relateng-shared-substrate
asinghvi17 Jul 11, 2026
84e02ce
Delegate spherical relate bounds to the shared extent substrate
asinghvi17 Jul 11, 2026
ea69854
Promote ring-containment parity to UnitSpherical, graft into `rk_poin…
asinghvi17 Jul 11, 2026
06a77c2
Remove `rk_bounds_disjoint` and `rk_bounds_covers`
asinghvi17 Jul 11, 2026
8d5ea7e
Tighten substrate-unification comments and helpers
asinghvi17 Jul 11, 2026
9e629a2
Merge the byte-identical `Planar`/`Spherical` accelerator selection m…
asinghvi17 Jul 13, 2026
de5b89d
Name the relateng tree accelerator `DoubleNaturalTree`
asinghvi17 Jul 13, 2026
847241c
Remove the lazy point-in-area auto-index from unprepared relates
asinghvi17 Jul 13, 2026
184dca4
Delete the unused `is_known_entry`
asinghvi17 Jul 13, 2026
dc4f1d0
Carry the indexed collection and accept precomputed extents in `RTree`
asinghvi17 Jul 13, 2026
2c18ffb
Carry segment owners in the relate edge index
asinghvi17 Jul 13, 2026
b646c71
Re-express the point-in-area interval index on the shared `RTree`
asinghvi17 Jul 13, 2026
13e4b50
Select the XML root element robustly across XML.jl versions
asinghvi17 Jul 14, 2026
099acac
Refer to `fix` without an `@ref` link in the `AntipodalEdgeSplit` doc…
asinghvi17 Jul 14, 2026
50adb78
Make the spherical polygon interior winding-independent
asinghvi17 Jul 14, 2026
480e1c9
Type-erase input geometry references in the RelateNG engine core
asinghvi17 Jul 14, 2026
ba903d1
Add a PrecompileTools workload over the RelateNG predicates
asinghvi17 Jul 14, 2026
5ca1483
Add real-data and fresh-process TTFX benchmarks for RelateNG
asinghvi17 Jul 14, 2026
c785d5e
Compute planar interaction bounds in a single coordinate pass
asinghvi17 Jul 15, 2026
9ea3b25
Short-circuit point-in-area location on element and ring envelopes
asinghvi17 Jul 15, 2026
7b306c3
Document the extent-stamped input path for unprepared RelateNG
asinghvi17 Jul 15, 2026
01eeb5d
Fan the spherical orientation sum from a non-antipodal apex
asinghvi17 Jul 15, 2026
d0d29f2
Cache spherical rings in kernel space for point-in-area queries
asinghvi17 Jul 15, 2026
cc08a5f
Inject type-stable functors into `spherical_ring_contains`
asinghvi17 Jul 15, 2026
ed2f947
Index spherical point-in-area location by edge longitude intervals
asinghvi17 Jul 15, 2026
264a805
Re-record real-data benchmark output after the point-location work
asinghvi17 Jul 15, 2026
6ea0b98
Replace the Girard orientation fan with the S2 turning-angle curvature
asinghvi17 Jul 15, 2026
2048d96
Add an `oriented` mode to `Spherical` for winding-authoritative inter…
asinghvi17 Jul 15, 2026
833f5cd
Honor the `Spherical` interior mode in `Extents.extent`
asinghvi17 Jul 15, 2026
5952fa9
Compare kernel points on all coordinates in segment-string bookkeeping
asinghvi17 Jul 15, 2026
fd339a3
Validate ring self-crossings in `prepare`, on by default for `Spherical`
asinghvi17 Jul 15, 2026
93fcb26
Anchor enclosed-region containment parity at a definitional exterior …
asinghvi17 Jul 15, 2026
ab3e024
Add the `CrossingEdgeSplit` correction for rings that cross on the sp…
asinghvi17 Jul 15, 2026
2e6dd8a
Refer to `fix` without an `@ref` link in the `CrossingEdgeSplit` docs…
asinghvi17 Jul 15, 2026
7f9ca0b
Add GADM full-resolution RelateNG benchmarks
asinghvi17 Jul 15, 2026
8d938e8
Add certified Float64 fast paths to the spherical exact kernel
asinghvi17 Jul 16, 2026
69e4164
Restore the sign in `Eriksson` spherical-triangle area
asinghvi17 Jul 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 41 additions & 7 deletions GeometryOpsCore/src/types/manifold.jl
Original file line number Diff line number Diff line change
Expand Up @@ -54,22 +54,56 @@ struct Planar <: Manifold
end

"""
Spherical(; radius)

A spherical manifold means that the geometry is on the 3-sphere (but is represented by 2-D longitude and latitude).
Spherical(; radius, oriented = false)

A spherical manifold means that the geometry is on the 3-sphere (but is represented by 2-D longitude and latitude).

`oriented` selects how the interior of a polygon ring is interpreted:

- `oriented = false` (the default): a ring's interior is the region it
*encloses* — the smaller of the two regions it bounds — independent of
winding direction. This matches how most of the ecosystem treats
unoriented data by default (R's `s2`/`sf`, spherely, BigQuery), and means
shapefile-convention data (clockwise shells) is read the same way as
counterclockwise data. No region larger than a hemisphere can be
represented.
- `oriented = true`: polygon ring directions are known to be correct —
exterior rings counterclockwise, interior rings clockwise — so the
interior of the polygon is the region on the *left* of each ring's stored
vertex order (the convention of S2's `S2Polygon::InitOriented`). This
makes regions larger than a hemisphere representable, e.g. "the sphere
minus a small cap" as a clockwise ring.

## Extended help

!!! note
The traditional definition of spherical coordinates in physics and mathematics,
``r, \\theta, \\phi``, uses the _colatitude_, that measures angular displacement from the `z`-axis.
The traditional definition of spherical coordinates in physics and mathematics,
``r, \\theta, \\phi``, uses the _colatitude_, that measures angular displacement from the `z`-axis.

Here, we use the geographic definition of longitude and latitude, meaning
that `lon` is longitude between -180 and 180, and `lat` is latitude between
that `lon` is longitude between -180 and 180, and `lat` is latitude between
`-90` (south pole) and `90` (north pole).

!!! note
With `oriented = true`, a ring may denote a region covering most of the
sphere. Operations remain correct on such regions, but extent-based
pruning degenerates (the region's bounding box is essentially the whole
sphere), so spatial predicates against them fall back to slower paths.

!!! warning "Validity is manifold-dependent"
A ring that is valid in lon/lat can be *invalid on the sphere*: two
non-adjacent edges may cross when reinterpreted as great-circle arcs
(a planar needle a few meters wide is enough — Natural Earth 110m
Sudan is a real instance), and no planar validity tool can detect it.
Prepared spherical predicates (`GeometryOps.prepare`) therefore
validate against this class by default and throw an "edge i crosses
edge j" error; the remedy is the `GeometryOps.CrossingEdgeSplit`
correction, which splits each ring at its crossing points into
separate loops (even-odd semantics).
"""
Base.@kwdef struct Spherical{T} <: Manifold
radius::T = WGS84_EARTH_MEAN_RADIUS # this should be theWGS84 defined mean radius
oriented::Bool = false
end

"""
Expand Down
2 changes: 2 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ GeoFormatTypes = "68eda718-8dee-11e9-39e7-89f7f65f511f"
GeoInterface = "cf35fbd7-0cd7-5166-be24-54bfbe79505f"
GeometryOpsCore = "05efe853-fabf-41c8-927e-7063c8b9f013"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
PrecompileTools = "aea7be01-6a6a-4083-8856-8a6e6704d82a"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
SortTileRecursiveTree = "746ee33f-1797-42c2-866d-db2fce69d14d"
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
Expand Down Expand Up @@ -58,6 +59,7 @@ GeoInterface = "1.6"
GeometryOpsCore = "=0.1.10"
LibGEOS = "0.9.2"
LinearAlgebra = "1"
PrecompileTools = "1.2"
Proj = "1"
Random = "1"
SortTileRecursiveTree = "0.1.2"
Expand Down
152 changes: 152 additions & 0 deletions benchmarks/relateng.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
# # RelateNG benchmarks
#
#=
Benchmarks for the RelateNG DE-9IM engine (Task 28 of the relateng plan):
RelateNG vs the old GO per-pair predicate processors vs LibGEOS (plain and
prepared — GEOS >= 3.13 runs RelateNG natively), across polygon sizes, for
two workload shapes:

- `intersects`: early-exit-friendly — the engine can stop at the first
interior/interior interaction it proves.
- full `relate`: no early exit — the complete DE-9IM matrix is computed.

Run with `julia --project=docs benchmarks/relateng.jl`. Prints a comparison
table; no CI gating (representative output is recorded in the comment block
at the bottom of this file).
=#

import GeometryOps as GO,
GeoInterface as GI,
LibGEOS as LG
using Chairmarks
using Printf
using Random

include(joinpath(@__DIR__, "..", "test", "data", "polygon_generation.jl"))

# As in `benchmarks.jl`: give each package its native geometry.
lg_and_go(geometry) = (GI.convert(LG, geometry), GO.tuples(geometry))

# A valid (LibGEOS-checked) random polygon with `nverts` vertices centered at
# `(x, y)`. The generator does not guarantee non-self-intersecting rings, so
# draw until valid (low spikiness makes rejection rare).
function valid_random_poly(x, y, nverts, rng)
while true
poly = GI.Polygon(generate_random_poly(x, y, nverts, 2.0, 0.3, 0.1, rng))
LG.isValid(GI.convert(LG, poly)) && return poly
end
end

const LG_CTX = LG.get_global_context()
# LibGEOS has no high-level `relate`; the generated wrapper returns the
# DE-9IM string itself (cf. test/methods/relateng/fuzz.jl).
lg_relate(la, lb) = LG.GEOSRelate_r(LG_CTX, la, lb)

prettytime(s) =
s < 1e-6 ? @sprintf("%8.1f ns", s * 1e9) :
s < 1e-3 ? @sprintf("%8.1f μs", s * 1e6) :
s < 1.0 ? @sprintf("%8.1f ms", s * 1e3) :
@sprintf("%8.2f s ", s)

median_time(trial) = Chairmarks.median(trial).time

function print_table(title, colnames, rows)
printstyled(title; color = :green, bold = true)
println()
@printf("%8s", "nverts")
foreach(c -> @printf(" │ %18s", c), colnames)
println()
println("─"^(8 + 21 * length(colnames)))
for (n, times) in rows
@printf("%8d", n)
foreach(t -> @printf(" │ %18s", prettytime(t)), times)
println()
end
println()
end

const ALG = GO.RelateNG()
const NVERTS = [2^4, 2^6, 2^8, 2^10, 2^12]

intersects_rows = Vector{Pair{Int, Vector{Float64}}}()
relate_rows = Vector{Pair{Int, Vector{Float64}}}()

for nverts in NVERTS
rng = Xoshiro(42)
# Two overlapping random polygons (centers 2.0 apart, radius 2.0), so
# `intersects` is true and the full matrix is the overlaps pattern.
a = valid_random_poly(0.0, 0.0, nverts, rng)
b = valid_random_poly(2.0, 0.0, nverts, rng)
lg_a, go_a = lg_and_go(a)
lg_b, go_b = lg_and_go(b)
prep_go = GO.prepare(ALG, go_a)
prep_lg = LG.prepareGeom(lg_a)

#-- Workload 1: `intersects` (early-exit-friendly)
t_old = @be GO.intersects($go_a, $go_b) seconds=1
t_ng = @be GO.intersects($ALG, $go_a, $go_b) seconds=1
t_ng_prep = @be GO.relate_predicate($prep_go, GO.pred_intersects(), $go_b) seconds=1
t_lg = @be LG.intersects($lg_a, $lg_b) seconds=1
t_lg_prep = @be LG.intersects($prep_lg, $lg_b) seconds=1
push!(intersects_rows, nverts => median_time.([t_old, t_ng, t_ng_prep, t_lg, t_lg_prep]))

#-- Workload 2: full `relate` (no early exit; old GO has no `relate`)
t_ng = @be GO.relate($ALG, $go_a, $go_b) seconds=1
t_ng_prep = @be GO.relate($prep_go, $go_b) seconds=1
t_lg = @be lg_relate($lg_a, $lg_b) seconds=1
push!(relate_rows, nverts => median_time.([t_ng, t_ng_prep, t_lg]))
end

print_table("intersects (early exit, overlapping random polygons)",
["GO old", "RelateNG", "RelateNG prepared", "LibGEOS", "LibGEOS prepared"],
intersects_rows)

print_table("relate (full DE-9IM matrix, overlapping random polygons)",
["RelateNG", "RelateNG prepared", "LibGEOS"],
relate_rows)

#=
Representative output (2026-06-11, Apple M4 Pro, macOS — Darwin 25.5.0;
Julia 1.12.6, GEOS 3.14.1; `julia --project=docs benchmarks/relateng.jl`):

intersects (early exit, overlapping random polygons)
nverts │ GO old │ RelateNG │ RelateNG prepared │ LibGEOS │ LibGEOS prepared
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────
16 │ 1.7 μs │ 11.6 μs │ 11.0 μs │ 2.1 μs │ 827.4 ns
64 │ 23.0 μs │ 137.8 μs │ 70.4 μs │ 4.7 μs │ 2.1 μs
256 │ 328.4 μs │ 562.7 μs │ 282.6 μs │ 13.8 μs │ 7.5 μs
1024 │ 5.2 ms │ 38.7 μs │ 26.2 μs │ 2.0 μs │ 106.4 ns
4096 │ 79.8 ms │ 155.5 μs │ 105.1 μs │ 6.7 μs │ 469.1 ns

relate (full DE-9IM matrix, overlapping random polygons)
nverts │ RelateNG │ RelateNG prepared │ LibGEOS
───────────────────────────────────────────────────────────────────────
16 │ 38.4 μs │ 37.5 μs │ 3.6 μs
64 │ 158.7 μs │ 90.2 μs │ 5.7 μs
256 │ 681.1 μs │ 399.0 μs │ 23.7 μs
1024 │ 3.7 ms │ 2.6 ms │ 201.1 μs
4096 │ 36.3 ms │ 37.1 ms │ 3.5 ms

Reading notes:

- The `intersects` columns are non-monotonic by design of the workload, not
noise: above the accelerator threshold (1024+ vertices here) the STRtree
edge index kicks in AND the early exit fires on the first proven
interior/interior interaction, so the 1024/4096 rows are *cheaper* than
256 (which takes the below-threshold nested loop over ~65k segment pairs
to the first hit). GEOS short-circuits the same way throughout, and its
prepared `intersects` resolves these cases from the point-in-area index
alone (sub-μs).
- Old GO `intersects` scales quadratically here (5.2 ms / 80 ms at
1024/4096) — RelateNG overtakes it between 256 and 1024 vertices and is
~500x faster at 4096.
- Full `relate` has no early exit, so every engine pays for the complete
node analysis; RelateNG is within ~10x of native GEOS RelateNG at the
large end. A flat profile of the 4096-vertex A/A `relate` case shows the
time split between exact segment-pair classification (~25%, mostly
AdaptivePredicates orientation — productive work, negligible call-boundary
overhead), per-call segment-extent-table construction for the STRtree
(~28%), and node analysis (Dict-backed `evaluate_nodes!` is ~8%). No
rational-fallback (`rk_nodes_coincide`) frames appear at all — no
F1-level hotspot.
=#
Loading
Loading