Full GeometryOps API documentation
Warning
This page is still very much WIP!
Documentation for GeometryOps's full API (only for reference!).
GeometryOps.LoopStateMachineGeometryOps.GEOMETRYOPS_NO_OPTIMIZE_EDGEINTERSECT_NUMVERTSGeometryOpsCore.WGS84_EARTH_INV_FLATTENINGGeometryOpsCore.WGS84_EARTH_MEAN_RADIUSGeometryOpsCore.WGS84_EARTH_SEMI_MAJOR_RADIUSGeometryOps.AbstractBarycentricCoordinateMethodGeometryOps.AdjacentEdgeLocatorGeometryOps.AntipodalEdgeSplitGeometryOps.ArcCrossingCounterGeometryOps.AutoAcceleratorGeometryOps.BoundaryNodeRuleGeometryOps.CLibraryPlanarAlgorithmGeometryOps.ChaikinGeometryOps.ClosedRingGeometryOps.ConvexConvexSutherlandHodgmanGeometryOps.CrossingEdgeSplitGeometryOps.DE9IMGeometryOps.DiffIntersectingPolygonsGeometryOps.DouglasPeuckerGeometryOps.EdgeSourceInfoGeometryOps.EndpointBoundaryGeometryOps.FlexibleRTrees.BulkLoadAlgorithmGeometryOps.FlexibleRTrees.HPRGeometryOps.FlexibleRTrees.RTreeGeometryOps.FlexibleRTrees.RTreeGeometryOps.FlexibleRTrees.RTreeNodeGeometryOps.FlexibleRTrees.STRGeometryOps.FlexibleRTrees.UnsortedGeometryOps.FosterHormannClippingGeometryOps.GEOSGeometryOps.GeodesicSegmentsGeometryOps.GeometryCorrectionGeometryOps.IndexedPointInAreaLocatorGeometryOps.IntersectionAcceleratorGeometryOps.LineOrientationGeometryOps.LinearBoundaryGeometryOps.LinearSegmentsGeometryOps.LoopStateMachine.ActionGeometryOps.MeanValueGeometryOps.Mod2BoundaryGeometryOps.MonotoneChainMethodGeometryOps.MonovalentEndpointBoundaryGeometryOps.MultivalentEndpointBoundaryGeometryOps.NaturalIndexing.NaturalIndexGeometryOps.NaturalIndexing.NaturalIndexGeometryOps.NaturalIndexing.NaturalIndexNodeGeometryOps.NaturalIndexing.NaturalLevelGeometryOps.NaturalIndexing.NaturallyIndexedRingGeometryOps.NodeKeyGeometryOps.NodeSectionGeometryOps.NodeSectionsGeometryOps.NodedArrangementGeometryOps.OverlayGraphGeometryOps.OverlayGraphGeometryOps.OverlayLabelGeometryOps.OverlayNGGeometryOps.PROJGeometryOps.PlanarCircleGeometryOps.PlanarCircleRingGeometryOps.PointOrientationGeometryOps.PreparedRelateGeometryOps.RadialDistanceGeometryOps.RayCrossingCounterGeometryOps.RelateEdgeGeometryOps.RelateGeometryGeometryOps.RelateNGGeometryOps.RelateNodeGeometryOps.RelatePointLocatorGeometryOps.RelateSegmentStringGeometryOps.SegSegClassGeometryOps.SimplifyAlgGeometryOps.SpatialTreeInterface.FlatNoTreeGeometryOps.SphericalKernelRingGeometryOps.SphericalRingPointsGeometryOps.SutherlandHodgmanCacheGeometryOps.TGGeometryOps.TopologyComputerGeometryOps.TopologyPredicateGeometryOps.TracingErrorGeometryOps.UnionIntersectingPolygonsGeometryOps.UnitSpherical.ArcIntersectionResultGeometryOps.UnitSpherical.ArcIntersectionTypeGeometryOps.UnitSpherical.GeographicFromUnitSphereGeometryOps.UnitSpherical.SphericalCapGeometryOps.UnitSpherical.SphericalCapGeometryOps.UnitSpherical.UnitSphereFromGeographicGeometryOps.UnitSpherical.UnitSphericalPointGeometryOps.VisvalingamWhyattGeometryOps.WelzlGeometryOps._OverlayTopologyErrorGeometryOps._VecTypesGeometryOpsCore.AlgorithmGeometryOpsCore.ApplicatorGeometryOpsCore.ApplyToArrayGeometryOpsCore.ApplyToFeaturesGeometryOpsCore.ApplyToGeomGeometryOpsCore.AutoAlgorithmGeometryOpsCore.AutoManifoldGeometryOpsCore.BoolsAsTypesGeometryOpsCore.FalseGeometryOpsCore.GeodesicGeometryOpsCore.ManifoldGeometryOpsCore.ManifoldIndependentAlgorithmGeometryOpsCore.MissingKeywordInAlgorithmExceptionGeometryOpsCore.NoAlgorithmGeometryOpsCore.OperationGeometryOpsCore.PlanarGeometryOpsCore.SingleManifoldAlgorithmGeometryOpsCore.SphericalGeometryOpsCore.TaskFunctorsGeometryOpsCore.TraitTargetGeometryOpsCore.TrueGeometryOpsCore.WithTraitGeometryOpsCore.WrongManifoldExceptionGeometryOpsCore._InitialValueBase.convertExtents.containsExtents.extentExtents.growExtents.intersectsExtents.intersectsExtents.unionExtents.withinGeometryOps.FlexibleRTrees.hilbert_keyGeometryOps.FlexibleRTrees.loadorderGeometryOps.FlexibleRTrees.queryGeometryOps.NaturalIndexing._number_of_levelsGeometryOps.SpatialTreeInterface.child_indices_extentsGeometryOps.SpatialTreeInterface.depth_first_searchGeometryOps.SpatialTreeInterface.dual_depth_first_searchGeometryOps.SpatialTreeInterface.getchildGeometryOps.SpatialTreeInterface.getchildGeometryOps.SpatialTreeInterface.isleafGeometryOps.SpatialTreeInterface.isspatialtreeGeometryOps.SpatialTreeInterface.nchildGeometryOps.SpatialTreeInterface.node_extentGeometryOps.SpatialTreeInterface.node_extent_is_expensiveGeometryOps.SpatialTreeInterface.queryGeometryOps.SpatialTreeInterface.sanitize_predicateGeometryOps.SpatialTreeInterface.spatialtreeGeometryOps.UnitSpherical.RobustCrossProduct.ensureNormalizableGeometryOps.UnitSpherical.RobustCrossProduct.exact_cross_productGeometryOps.UnitSpherical.RobustCrossProduct.isNormalizableGeometryOps.UnitSpherical.RobustCrossProduct.isUnitLengthGeometryOps.UnitSpherical.RobustCrossProduct.isless_vectorGeometryOps.UnitSpherical.RobustCrossProduct.min_stable_normGeometryOps.UnitSpherical.RobustCrossProduct.normalizableFromExactGeometryOps.UnitSpherical.RobustCrossProduct.normalization_neededGeometryOps.UnitSpherical.RobustCrossProduct.robust_cross_productGeometryOps.UnitSpherical.RobustCrossProduct.stable_cross_productGeometryOps.UnitSpherical.RobustCrossProduct.symbolic_cross_productGeometryOps.UnitSpherical.RobustCrossProduct.symbolic_cross_product_sortedGeometryOps.UnitSpherical._arc_fractionGeometryOps.UnitSpherical._find_collinear_arc_intersectionGeometryOps.UnitSpherical._make_hinge_resultGeometryOps.UnitSpherical._points_equalGeometryOps.UnitSpherical.circumcenter_on_unit_sphereGeometryOps.UnitSpherical.point_on_spherical_arcGeometryOps.UnitSpherical.slerpGeometryOps.UnitSpherical.spherical_arc_extentGeometryOps.UnitSpherical.spherical_arc_intersectionGeometryOps.UnitSpherical.spherical_distanceGeometryOps.UnitSpherical.spherical_exterior_anchorGeometryOps.UnitSpherical.spherical_orientGeometryOps.UnitSpherical.spherical_ring_containsGeometryOps.UnitSpherical.spherical_ring_enclosesGeometryOps.UnitSpherical.to_unit_spherical_pointsGeometryOps._detGeometryOps._equals_curvesGeometryOps._exact_crossing_pointGeometryOps._is_canonical_incidenceGeometryOps._overlapsGeometryOps._overlapsGeometryOps._overlapsGeometryOps._overlapsGeometryOps._overlapsGeometryOps._overlapsGeometryOps._overlapsGeometryOps._overlapsGeometryOps._overlay_ngGeometryOps.add_area_vertex!GeometryOps.add_edge!GeometryOps.add_intersections!GeometryOps.add_line_end_on_geometry!GeometryOps.anglesGeometryOps.anglesGeometryOps.antimeridian_splitGeometryOps.areaGeometryOps.areaGeometryOps.barycentric_coordinatesGeometryOps.barycentric_coordinatesGeometryOps.barycentric_coordinates!GeometryOps.barycentric_coordinates!GeometryOps.barycentric_interpolateGeometryOps.barycentric_interpolateGeometryOps.centroidGeometryOps.centroidGeometryOps.centroid_and_areaGeometryOps.centroid_and_lengthGeometryOps.compare_toGeometryOps.containsGeometryOps.containsGeometryOps.containsGeometryOps.containsGeometryOps.convex_hullGeometryOps.count_segment!GeometryOps.coverageGeometryOps.coveredbyGeometryOps.coveredbyGeometryOps.coveredbyGeometryOps.coveredbyGeometryOps.coversGeometryOps.coversGeometryOps.coversGeometryOps.coversGeometryOps.create_nodeGeometryOps.create_node_sectionGeometryOps.crossesGeometryOps.crossesGeometryOps.crossesGeometryOps.crossesGeometryOps.crossing_nodeGeometryOps.cutGeometryOps.differenceGeometryOps.differenceGeometryOps.disjointGeometryOps.disjointGeometryOps.disjointGeometryOps.disjointGeometryOps.distanceGeometryOps.distanceGeometryOps.eachedgeGeometryOps.edge_angle_compareGeometryOps.edge_extentsGeometryOps.embed_extentGeometryOps.embed_extentGeometryOps.enforceGeometryOps.equalsGeometryOps.equalsGeometryOps.equalsGeometryOps.equalsGeometryOps.equalsGeometryOps.equalsGeometryOps.equalsGeometryOps.equalsGeometryOps.equalsGeometryOps.equalsGeometryOps.equalsGeometryOps.equalsGeometryOps.equalsGeometryOps.equalsGeometryOps.equalsGeometryOps.equalsGeometryOps.equalsGeometryOps.extent_to_polygonGeometryOps.extract_segment_stringsGeometryOps.finish!GeometryOps.flipGeometryOps.forcexyGeometryOps.forcexyzGeometryOps.foreach_pair_of_maybe_intersecting_edges_in_orderGeometryOps.get_dimension_realGeometryOps.get_locationGeometryOps.get_polygonalGeometryOps.get_polygonalGeometryOps.intersectionGeometryOps.intersectionGeometryOps.intersection_areaGeometryOps.intersection_pointsGeometryOps.intersectsGeometryOps.intersectsGeometryOps.intersectsGeometryOps.intersectsGeometryOps.is_containing_segmentGeometryOps.is_polygonalGeometryOps.is_self_noding_requiredGeometryOps.is_self_noding_requiredGeometryOps.isclockwiseGeometryOps.isconcaveGeometryOps.lazy_edge_extentsGeometryOps.lazy_edgelistGeometryOps.locateGeometryOps.locateGeometryOps.locateGeometryOps.locate_area_vertexGeometryOps.locate_line_end_with_dimGeometryOps.locate_nodeGeometryOps.locate_node_with_dimGeometryOps.locate_with_dimGeometryOps.matches_entryGeometryOps.minimum_bounding_circleGeometryOps.node_pointGeometryOps.overlapsGeometryOps.overlapsGeometryOps.overlapsGeometryOps.overlapsGeometryOps.polygon_node_convertGeometryOps.polygon_to_lineGeometryOps.polygonizeGeometryOps.prepareGeometryOps.prepareGeometryOps.prepare_sections!GeometryOps.process_edge_intersections!GeometryOps.process_intersections!GeometryOps.process_self_intersections!GeometryOps.radiusGeometryOps.relateGeometryOps.relateGeometryOps.relateGeometryOps.relateGeometryOps.relate_predicateGeometryOps.relate_predicateGeometryOps.reprojectGeometryOps.rk_crossing_dirs_ccwGeometryOps.segmentizeGeometryOps.signed_areaGeometryOps.signed_areaGeometryOps.signed_distanceGeometryOps.signed_distanceGeometryOps.simplifyGeometryOps.smoothGeometryOps.symdifferenceGeometryOps.t_valueGeometryOps.to_edgelistGeometryOps.to_edgelistGeometryOps.to_edgesGeometryOps.touchesGeometryOps.touchesGeometryOps.touchesGeometryOps.touchesGeometryOps.transformGeometryOps.transformGeometryOps.tuplesGeometryOps.unionGeometryOps.unionGeometryOps.vertex_nodeGeometryOps.voronoiGeometryOps.weighted_meanGeometryOps.withinGeometryOps.withinGeometryOps.withinGeometryOpsCore.applyGeometryOpsCore.applyGeometryOpsCore.applyreduceGeometryOpsCore.applyreduceGeometryOpsCore.best_manifoldGeometryOpsCore.booltypeGeometryOpsCore.flattenGeometryOpsCore.manifoldGeometryOpsCore.rebuildGeometryOpsCore.reconstructGeometryOpsCore.reconstruct_tableGeometryOpsCore.unwrapGeometryOpsCore.used_reconstruct_table_kwargsGeometryOps.LoopStateMachine.@controlflow
apply and associated functions
GeometryOpsCore.apply Function
apply(f, target::Union{TraitTarget, GI.AbstractTrait}, obj; kw...)Reconstruct a geometry, feature, feature collection, or nested vectors of either using the function f on the target trait.
f(target_geom) => x where x also has the target trait, or a trait that can be substituted. For example, swapping PolgonTrait to MultiPointTrait will fail if the outer object has MultiPolygonTrait, but should work if it has FeatureTrait.
Objects "shallower" than the target trait are always completely rebuilt, like a Vector of FeatureCollectionTrait of FeatureTrait when the target has PolygonTrait and is held in the features. These will always be GeoInterface geometries/feature/feature collections. But "deeper" objects may remain unchanged or be whatever GeoInterface compatible objects f returns.
The result is a functionally similar geometry with values depending on f.
threaded:trueorfalse. Whether to use multithreading. Defaults tofalse.crs: The CRS to attach to geometries. Defaults tonothing.calc_extent:trueorfalse. Whether to calculate the extent. Defaults tofalse.
Example
Flipped point the order in any feature or geometry, or iterables of either:
import GeoInterface as GI
import GeometryOps as GO
geom = GI.Polygon([GI.LinearRing([(1, 2), (3, 4), (5, 6), (1, 2)]),
GI.LinearRing([(3, 4), (5, 6), (6, 7), (3, 4)])])
flipped_geom = GO.apply(GI.PointTrait, geom) do p
(GI.y(p), GI.x(p))
endGeometryOpsCore.applyreduce Function
applyreduce(f, op, target::Union{TraitTarget, GI.AbstractTrait}, obj; threaded, init, kw...)Apply function f to all objects with the target trait, and reduce the result with an op like +.
The order and grouping of application of op is not guaranteed.
If threaded==true threads will be used over arrays and iterables, feature collections and nested geometries.
init specifies the initial value for the reduction. If not provided, the reduction uses the first result as the starting point (like reduce without init). For operations like vcat, you typically don't need to provide init. For numeric reductions like +, you may want to provide init=zero(T) to ensure type stability.
GeometryOps.reproject Function
reproject(geometry; source_crs, target_crs, transform, always_xy, time)
reproject(geometry, source_crs, target_crs; always_xy, time)
reproject(geometry, transform; always_xy, time)Reproject any GeoInterface.jl compatible geometry from source_crs to target_crs.
The returned object will be constructed from GeoInterface wrapper geometries, wrapping Vector{NTuple{D, Float64}}, where D is the dimension.
Tip
The Proj.jl package must be loaded for this method to work, since it is implemented in a package extension.
Arguments
geometry: Any GeoInterface.jl compatible geometries.source_crs: the source coordinate reference system, as a GeoFormatTypes.jl object or a string.target_crs: the target coordinate reference system, as a GeoFormatTypes.jl object or a string.
If these a passed as keywords, transform will take priority. Without it target_crs is always needed, and source_crs is needed if it is not retrievable from the geometry with GeoInterface.crs(geometry).
Keywords
always_xy: force x, y coordinate order,trueby default.falsewill expect and return points in the crs coordinate order.time: the time for the coordinates.Infby default.threaded:trueorfalse. Whether to use multithreading. Defaults tofalse.crs: The CRS to attach to geometries. Defaults tonothing.calc_extent:trueorfalse. Whether to calculate the extent. Defaults tofalse.
GeometryOps.transform Function
transform(f, obj)Apply a function f to all the points in obj.
Points will be passed to f as an SVector to allow using CoordinateTransformations.jl and Rotations.jl without hassle.
SVector is also a valid GeoInterface.jl point, so will work in all GeoInterface.jl methods.
Example
julia> import GeoInterface as GI
julia> import GeometryOps as GO
julia> geom = GI.Polygon([GI.LinearRing([(1, 2), (3, 4), (5, 6), (1, 2)]), GI.LinearRing([(3, 4), (5, 6), (6, 7), (3, 4)])]);
julia> f = CoordinateTransformations.Translation(3.5, 1.5)
Translation(3.5, 1.5)
julia> GO.transform(f, geom)
GeoInterface.Wrappers.Polygon{false, false, Vector{GeoInterface.Wrappers.LinearRing{false, false, Vector{StaticArraysCore.SVector{2, Float64}}, Nothing, Nothing}}, Nothing, Nothing}(GeoInterface.Wrappers.Linea
rRing{false, false, Vector{StaticArraysCore.SVector{2, Float64}}, Nothing, Nothing}[GeoInterface.Wrappers.LinearRing{false, false, Vector{StaticArraysCore.SVector{2, Float64}}, Nothing, Nothing}(StaticArraysCo
re.SVector{2, Float64}[[4.5, 3.5], [6.5, 5.5], [8.5, 7.5], [4.5, 3.5]], nothing, nothing), GeoInterface.Wrappers.LinearRing{false, false, Vector{StaticArraysCore.SVector{2, Float64}}, Nothing, Nothing}(StaticA
rraysCore.SVector{2, Float64}[[6.5, 5.5], [8.5, 7.5], [9.5, 8.5], [6.5, 5.5]], nothing, nothing)], nothing, nothing)With Rotations.jl you need to actually multiply the Rotation by the SVector point, which is easy using an anonymous function.
julia> using Rotations
julia> GO.transform(p -> one(RotMatrix{2}) * p, geom)
GeoInterface.Wrappers.Polygon{false, false, Vector{GeoInterface.Wrappers.LinearRing{false, false, Vector{StaticArraysCore.SVector{2, Int64}}, Nothing, Nothing}}, Nothing, Nothing}(GeoInterface.Wrappers.LinearR
ing{false, false, Vector{StaticArraysCore.SVector{2, Int64}}, Nothing, Nothing}[GeoInterface.Wrappers.LinearRing{false, false, Vector{StaticArraysCore.SVector{2, Int64}}, Nothing, Nothing}(StaticArraysCore.SVe
ctor{2, Int64}[[2, 1], [4, 3], [6, 5], [2, 1]], nothing, nothing), GeoInterface.Wrappers.LinearRing{false, false, Vector{StaticArraysCore.SVector{2, Int64}}, Nothing, Nothing}(StaticArraysCore.SVector{2, Int64
}[[4, 3], [6, 5], [7, 6], [4, 3]], nothing, nothing)], nothing, nothing)General geometry methods
OGC methods
GeometryOps.contains Function
contains(g1::AbstractGeometry, g2::AbstractGeometry)::BoolReturn true if the second geometry is completely contained by the first geometry. The interiors of both geometries must intersect and the interior and boundary of the secondary (g2) must not intersect the exterior of the first (g1).
contains returns the exact opposite result of within.
Examples
import GeometryOps as GO, GeoInterface as GI
line = GI.LineString([(1, 1), (1, 2), (1, 3), (1, 4)])
point = GI.Point((1, 2))
GO.contains(line, point)
# output
truecontains(g1)Return a function that checks if its input contains g1. This is equivalent to x -> contains(x, g1).
This functionality is experimental and may change at any time.
sourceGeometryOps.coveredby Function
coveredby([manifold::Manifold], g1, g2)::BoolReturn true if the first geometry is completely covered by the second geometry. The interior and boundary of the primary geometry (g1) must not intersect the exterior of the secondary geometry (g2).
Furthermore, coveredby returns the exact opposite result of covers. They are equivalent with the order of the arguments swapped.
Examples
import GeometryOps as GO, GeoInterface as GI
p1 = GI.Point(0.0, 0.0)
p2 = GI.Point(1.0, 1.0)
l1 = GI.Line([p1, p2])
GO.coveredby(p1, l1)
# output
truecoveredby(g1)Return a function that checks if its input is covered by g1. This is equivalent to x -> coveredby(x, g1).
This functionality is experimental and may change at any time.
sourceGeometryOps.covers Function
covers(g1::AbstractGeometry, g2::AbstractGeometry)::BoolReturn true if the first geometry is completely covers the second geometry, The exterior and boundary of the second geometry must not be outside of the interior and boundary of the first geometry. However, the interiors need not intersect.
covers returns the exact opposite result of coveredby.
Examples
import GeometryOps as GO, GeoInterface as GI
l1 = GI.LineString([(1.0, 1.0), (1.0, 2.0), (1.0, 3.0), (1.0, 4.0)])
l2 = GI.LineString([(1.0, 1.0), (1.0, 2.0)])
GO.covers(l1, l2)
# output
truecovers(g1)Return a function that checks if its input covers g1. This is equivalent to x -> covers(x, g1).
This functionality is experimental and may change at any time.
sourceGeometryOps.crosses Function
crosses(geom1, geom2)::BoolReturn true if the intersection results in a geometry whose dimension is one less than the maximum dimension of the two source geometries and the intersection set is interior to both source geometries.
TODO: broken
Examples
import GeoInterface as GI, GeometryOps as GO
# TODO: Add working examplecrosses(g1)Return a function that checks if its input crosses g1. This is equivalent to x -> crosses(x, g1).
This functionality is experimental and may change at any time.
sourceGeometryOps.disjoint Function
disjoint([manifold::Manifold], geom1, geom2)::BoolReturn true if the first geometry is disjoint from the second geometry.
Return true if the first geometry is disjoint from the second geometry. The interiors and boundaries of both geometries must not intersect.
Examples
import GeometryOps as GO, GeoInterface as GI
line = GI.LineString([(1, 1), (1, 2), (1, 3), (1, 4)])
point = (2, 2)
GO.disjoint(point, line)
# output
truedisjoint(g1)Return a function that checks if its input is disjoint from g1. This is equivalent to x -> disjoint(x, g1).
This functionality is experimental and may change at any time.
sourceGeometryOps.intersects Function
intersects(geom1, geom2)::BoolReturn true if the interiors or boundaries of the two geometries interact.
intersects returns the exact opposite result of disjoint.
Example
import GeoInterface as GI, GeometryOps as GO
line1 = GI.Line([(124.584961,-12.768946), (126.738281,-17.224758)])
line2 = GI.Line([(123.354492,-15.961329), (127.22168,-14.008696)])
GO.intersects(line1, line2)
# output
trueintersects(g1)Return a function that checks if its input intersects g1. This is equivalent to x -> intersects(x, g1).
This functionality is experimental and may change at any time.
sourceGeometryOps.overlaps Function
overlaps(geom1, geom2)::BoolCompare two Geometries of the same dimension and return true if their intersection set results in a geometry different from both but of the same dimension. This means one geometry cannot be within or contain the other and they cannot be equal
Examples
import GeometryOps as GO, GeoInterface as GI
poly1 = GI.Polygon([[(0,0), (0,5), (5,5), (5,0), (0,0)]])
poly2 = GI.Polygon([[(1,1), (1,6), (6,6), (6,1), (1,1)]])
GO.overlaps(poly1, poly2)
# output
trueoverlaps(g1)Return a function that checks if its input overlaps g1. This is equivalent to x -> overlaps(x, g1).
This functionality is experimental and may change at any time.
sourceGeometryOps.touches Function
touches([manifold::Manifold], geom1, geom2)::BoolReturn true if the first geometry touches the second geometry. In other words, the two interiors cannot interact, but one of the geometries must have a boundary point that interacts with either the other geometry's interior or boundary.
Examples
import GeometryOps as GO, GeoInterface as GI
l1 = GI.Line([(0.0, 0.0), (1.0, 0.0)])
l2 = GI.Line([(1.0, 1.0), (1.0, -1.0)])
GO.touches(l1, l2)
# output
truetouches(g1)Return a function that checks if its input touches g1. This is equivalent to x -> touches(x, g1).
This functionality is experimental and may change at any time.
sourceGeometryOps.within Function
within(g1)Return a function that checks if its input is within g1. This is equivalent to x -> within(x, g1).
This functionality is experimental and may change at any time.
sourceOther general methods
GeometryOps.equals Function
equals(geom1, geom2)::BoolCompare two Geometries return true if they are the same geometry.
Examples
import GeometryOps as GO, GeoInterface as GI
poly1 = GI.Polygon([[(0,0), (0,5), (5,5), (5,0), (0,0)]])
poly2 = GI.Polygon([[(0,0), (0,5), (5,5), (5,0), (0,0)]])
GO.equals(poly1, poly2)
# output
trueequals(::T, geom_a, ::T, geom_b)::BoolTwo geometries of the same type, which don't have a equals function to dispatch off of should throw an error.
sourceequals(trait_a, geom_a, trait_b, geom_b)Two geometries which are not of the same type cannot be equal so they always return false.
sourceequals(::GI.PointTrait, p1, ::GI.PointTrait, p2)::BoolTwo points are the same if they have the same x and y (and z if 3D) coordinates.
sourceequals(::GI.PointTrait, p1, ::GI.MultiPointTrait, mp2)::BoolA point and a multipoint are equal if the multipoint is composed of a single point that is equivalent to the given point.
sourceequals(::GI.MultiPointTrait, mp1, ::GI.PointTrait, p2)::BoolA point and a multipoint are equal if the multipoint is composed of a single point that is equivalent to the given point.
sourceequals(::GI.MultiPointTrait, mp1, ::GI.MultiPointTrait, mp2)::BoolTwo multipoints are equal if they share the same set of points.
sourceequals(
::Union{GI.LineTrait, GI.LineStringTrait}, l1,
::Union{GI.LineTrait, GI.LineStringTrait}, l2,
)::BoolTwo lines/linestrings are equal if they share the same set of points going along the curve. Note that lines/linestrings aren't closed by definition.
sourceequals(
::Union{GI.LineTrait, GI.LineStringTrait}, l1,
::GI.LinearRingTrait, l2,
)::BoolA line/linestring and a linear ring are equal if they share the same set of points going along the curve. Note that lines aren't closed by definition, but rings are, so the line must have a repeated last point to be equal
sourceequals(
::GI.LinearRingTrait, l1,
::Union{GI.LineTrait, GI.LineStringTrait}, l2,
)::BoolA linear ring and a line/linestring are equal if they share the same set of points going along the curve. Note that lines aren't closed by definition, but rings are, so the line must have a repeated last point to be equal
sourceequals(
::GI.LinearRingTrait, l1,
::GI.LinearRingTrait, l2,
)::BoolTwo linear rings are equal if they share the same set of points going along the curve. Note that rings are closed by definition, so they can have, but don't need, a repeated last point to be equal.
sourceequals(::GI.PolygonTrait, geom_a, ::GI.PolygonTrait, geom_b)::BoolTwo polygons are equal if they share the same exterior edge and holes.
sourceequals(::GI.PolygonTrait, geom_a, ::GI.MultiPolygonTrait, geom_b)::BoolA polygon and a multipolygon are equal if the multipolygon is composed of a single polygon that is equivalent to the given polygon.
sourceequals(::GI.MultiPolygonTrait, geom_a, ::GI.PolygonTrait, geom_b)::BoolA polygon and a multipolygon are equal if the multipolygon is composed of a single polygon that is equivalent to the given polygon.
sourceequals(::GI.PolygonTrait, geom_a, ::GI.PolygonTrait, geom_b)::BoolTwo multipolygons are equal if they share the same set of polygons.
sourceThis functionality is experimental and may change at any time.
sourceGeometryOps.centroid Function
centroid(geom, [T=Float64])::Tuple{T, T}Returns the centroid of a given line segment, linear ring, polygon, or mutlipolygon.
sourceGeometryOps.distance Function
distance(point, geom, ::Type{T} = Float64)::TCalculates the distance from the geometry g1 to the point. The distance will always be positive or zero.
The method will differ based on the type of the geometry provided: - The distance from a point to a point is just the Euclidean distance between the points. - The distance from a point to a line is the minimum distance from the point to the closest point on the given line. - The distance from a point to a linestring is the minimum distance from the point to the closest segment of the linestring. - The distance from a point to a linear ring is the minimum distance from the point to the closest segment of the linear ring. - The distance from a point to a polygon is zero if the point is within the polygon and otherwise is the minimum distance from the point to an edge of the polygon. This includes edges created by holes. - The distance from a point to a multigeometry or a geometry collection is the minimum distance between the point and any of the sub-geometries.
Result will be of type T, where T is an optional argument with a default value of Float64.
sourceGeometryOps.signed_distance Function
signed_distance(point, geom, ::Type{T} = Float64)::TCalculates the signed distance from the geometry geom to the given point. Points within geom have a negative signed distance, and points outside of geom have a positive signed distance. - The signed distance from a point to a point, line, linestring, or linear ring is equal to the distance between the two. - The signed distance from a point to a polygon is negative if the point is within the polygon and is positive otherwise. The value of the distance is the minimum distance from the point to an edge of the polygon. This includes edges created by holes. - The signed distance from a point to a multigeometry or a geometry collection is the minimum signed distance between the point and any of the sub-geometries.
Result will be of type T, where T is an optional argument with a default value of Float64.
sourceGeometryOps.area Function
area(geom, [T = Float64])::T
area(manifold::Manifold, geom, [T = Float64])::T
area(algorithm::Algorithm, geom, [T = Float64])::TReturns the area of a geometry or collection of geometries. This is computed slightly differently for different geometries:
- The area of a point/multipoint is always zero.
- The area of a curve/multicurve is always zero.
- The area of a polygon is the absolute value of the signed area.
- The area multi-polygon is the sum of the areas of all of the sub-polygons.
- The area of a geometry collection, feature collection of array/iterable
is the sum of the areas of all of the sub-geometries.Manifold support
AutoManifold()(default): When the Proj extension is loaded, recognized geographic CRSs use a geodesic calculation on the CRS ellipsoid and recognized projected CRSs use native-unitPlanar()calculations. Without Proj, geographic geometries use degree-basedSpherical()calculations, while projected and unknown geometries use native-unitPlanar()calculations. The manifold is selected once from the top-level input's CRS and applies to all geometries contained in that input.Planar(): Uses the shoelace formula in native coordinate units squared, regardless of CRS.Spherical(): Uses Girard's theorem for spherical polygons. Coordinates are interpreted as (longitude, latitude) in degrees. Returns area in square units of the sphere's radius (default: Earth's mean radius in meters).Geodesic(): Uses geodesic calculations (requires Proj extension).
Projected map area is a planar grid measurement and can differ from surface area, particularly for projections with distortion.
Examples
import GeometryOps as GO
import GeoInterface as GI
# CRS-free planar area (the AutoManifold default)
rect = GI.Polygon([[(0,0), (1,0), (1,1), (0,1), (0,0)]])
GO.area(rect) # 1.0
# Spherical area (1/8 of Earth's surface)
octant = GI.Polygon([[(0.0, 0.0), (90.0, 0.0), (0.0, 90.0), (0.0, 0.0)]])
GO.area(GO.Spherical(), octant) # ≈ 6.38e13 m²
# Spherical area with custom radius (unit sphere)
GO.area(GO.Spherical(radius=1.0), octant) # ≈ π/2Result will be of type T, where T is an optional argument with a default value of Float64.
sourceGeometryOps.signed_area Function
signed_area(geom, [T = Float64])::TReturns the signed area of a single geometry, based on winding order. This is computed slightly differently for different geometries:
- The signed area of a point is always zero.
- The signed area of a curve is always zero.
- The signed area of a polygon is computed with the shoelace formula and is
positive if the polygon coordinates wind clockwise and negative if
counterclockwise.
- You cannot compute the signed area of a multipolygon as it doesn't have a
meaning as each sub-polygon could have a different winding order.Result will be of type T, where T is an optional argument with a default value of Float64.
sourceGeometryOps.angles Function
angles(geom, ::Type{T} = Float64)Returns the angles of a geometry or collection of geometries. This is computed differently for different geometries:
- The angles of a point is an empty vector.
- The angles of a single line segment is an empty vector.
- The angles of a linestring or linearring is a vector of angles formed by the curve.
- The angles of a polygon is a vector of vectors of angles formed by each ring.
- The angles of a multi-geometry collection is a vector of the angles of each of the
sub-geometries as defined above.Result will be a Vector, or nested set of vectors, of type T where an optional argument with a default value of Float64.
sourceGeometryOps.embed_extent Function
embed_extent(obj)Recursively wrap the object with a GeoInterface.jl geometry, calculating and adding an Extents.Extent to all objects.
This can improve performance when extents need to be checked multiple times, such when needing to check if many points are in geometries, and using their extents as a quick filter for obviously exterior points.
Keywords
threaded:trueorfalse. Whether to use multithreading. Defaults tofalse.crs: The CRS to attach to geometries. Defaults tonothing.
Barycentric coordinates
GeometryOps.barycentric_coordinates Function
barycentric_coordinates(method, geom, point; normalize = true)Return the barycentric coordinates of point with respect to the vertices of geom, as a newly allocated Vector holding one weight per vertex.
method is an AbstractBarycentricCoordinateMethod, currently always MeanValue. geom may be any GeoInterface-compatible curve (a linear ring or linestring) with at least three points, and point any GeoInterface-compatible point.
If normalize is true (the default) the returned weights are scaled to sum to 1.
See also barycentric_coordinates!, which writes into a preallocated vector, and barycentric_interpolate, which uses these weights to interpolate values.
GeometryOps.barycentric_coordinates! Function
barycentric_coordinates!(λs, method, geom, point; normalize = true)Write the barycentric coordinates of point with respect to the vertices of geom into λs, and return λs.
This is the allocation-free form of barycentric_coordinates; λs must have one element per vertex of geom.
GeometryOps.barycentric_interpolate Function
barycentric_interpolate(method, geom, values, point)Interpolate values, given one per vertex of geom, to point using barycentric coordinates, and return the interpolated value.
geom may be a curve (linear ring or linestring) or a polygon; for polygons the values are taken around the exterior ring first and then around each hole, in the order GeoInterface.getring returns them. values may be numbers or, for the mean value method, colors.
This is equivalent to weighting values by barycentric_coordinates, but accumulates in place and so allocates less.
Other methods
GeometryOps.GEOMETRYOPS_NO_OPTIMIZE_EDGEINTERSECT_NUMVERTS Constant
The number of vertices past which we should use a STRtree for edge intersection checking.
sourceGeometryOps.AbstractBarycentricCoordinateMethod Type
abstract type AbstractBarycentricCoordinateMethodAbstract supertype for barycentric coordinate methods. The subtypes may serve as dispatch types, or may cache some information about the target polygon.
API
The following methods must be implemented for all subtypes:
barycentric_coordinates!(λs::Vector{<: Real}, method::AbstractBarycentricCoordinateMethod, exterior::Vector{<: Point{2, T1}}, point::Point{2, T2})barycentric_interpolate(method::AbstractBarycentricCoordinateMethod, exterior::Vector{<: Point{2, T1}}, values::Vector{V}, point::Point{2, T2})::Vbarycentric_interpolate(method::AbstractBarycentricCoordinateMethod, exterior::Vector{<: Point{2, T1}}, interiors::Vector{<: Vector{<: Point{2, T1}}} values::Vector{V}, point::Point{2, T2})::V
The rest of the methods will be implemented in terms of these, and have efficient dispatches for broadcasting.
sourceGeometryOps.AdjacentEdgeLocator Type
AdjacentEdgeLocator(m::Manifold, geom; exact)Determines the location for a point which is known to lie on at least one edge of a set of polygons. This provides the union-semantics for determining point location in a GeometryCollection, which may have polygons with adjacent edges which are effectively in the interior of the geometry. Note that it is also possible to have adjacent edges which lie on the boundary of the geometry (e.g. a polygon contained within another polygon with adjacent edges).
The manifold m and the exact flag are stored in the struct (rather than threaded through every call) for consistency with how RelateGeometry holds them (Task 13); locate uses the stored values for all kernel queries.
The Java constructor signature is AdjacentEdgeLocator(Geometry geom); the manifold/exact parameters are the only additions.
GeometryOps.AntipodalEdgeSplit Type
AntipodalEdgeSplit() <: GeometryCorrectionSplit every edge whose endpoints map to exactly-antipodal unit vectors by inserting the lon/lat midpoint of the edge, so each edge has a well-defined great-circle arc. This is the remedy for the antipodal-edge ArgumentError thrown by relate on the Spherical manifold.
It can be called on any geometry as usual (AntipodalEdgeSplit()(geom)), or passed to GeometryOps.fix.
See also GeometryCorrection.
GeometryOps.ArcCrossingCounter Type
ArcCrossingCounter(m::Spherical, exact, p, anchor, 0, false)Counts ring edges crossing the reference meridian arc from the query point p to the anchor pole, in an incremental fashion — the spherical counterpart of RayCrossingCounter. As there, the location is only correct once all edges whose longitude interval contains p's longitude have been counted, and a query point found to lie on an edge is recorded in is_point_on_segment (final location LOC_BOUNDARY).
The final location is anchor's location, flipped once per crossing. Vertex grazing — an edge endpoint exactly on the reference arc — is resolved symbolically, S2-VertexCrossing style: the edge counts iff its off-arc endpoint lies strictly on the positive side of the meridian great circle, so a crossing pair of incident edges counts once, a same-side pair counts zero or twice (parity-equal), and edges collinear with the meridian count never (their chain's terminal edges decide). With exact = True() every branch below is decided by exact predicates.
GeometryOps.AutoAccelerator Type
AutoAccelerator()Let the algorithm choose the best accelerator based on the size of the input polygons.
Once we have prepared geometry, this will also consider the existing preparations on the geoms.
sourceGeometryOps.BoundaryNodeRule Type
BoundaryNodeRuleThis functionality is experimental and may change at any time.
Abstract supertype for rules deciding which endpoints of a linear geometry are on its boundary, given the number of line ends meeting at the point (port of JTS BoundaryNodeRule). Concrete rules are Mod2Boundary (the OGC SFS default), EndpointBoundary, MultivalentEndpointBoundary, and MonovalentEndpointBoundary; each implements is_in_boundary(rule, boundary_count).
GeometryOps.CLibraryPlanarAlgorithm Type
abstract type CLibraryPlanarAlgorithm <: GeometryOpsCore.SingleManifoldAlgorithm{Planar} endThis is a type which extends GeometryOpsCore.SingleManifoldAlgorithm{Planar}, and is used as an abstract supertype for some C library based algorithms.
The type requires that algorithm structs be arranged as:
struct MyAlgorithm <: CLibraryPlanarAlgorithm
manifold::Planar
params::NamedTuple
endThen you get a nice constructor for free, as well as the get(alg, key, value) and get(alg, key) do ... syntax. Plus the enforce method, which will check that given keyword arguments are present.
GeometryOps.Chaikin Type
Chaikin(; iterations=1, manifold=Planar())Smooths geometries using Chaikin's corner-cutting algorithm [1]. This algorithm "slices" off every corner of the geometry to smooth it out, equivalent to a sequence of quadratic Bezier curves.
Keywords
iterations: the number of times to apply the algorithm.manifold: theManifoldto smooth the geometry on. Currently,PlanarandSphericalare supported.
Extended help
The algorithm is very simple; for each corner of the line (a -> b -> c), insert two new points and remove b, such that a -> b -> c becomes a -> q -> r -> c, where q and r are the new points such that:
In practice the replacement happens on the level of each edge.
References
sourceGeometryOps.ClosedRing Type
ClosedRing() <: GeometryCorrectionThis correction ensures that a polygon's exterior and interior rings are closed.
It can be called on any geometry correction as usual.
See also GeometryCorrection.
GeometryOps.ConvexConvexSutherlandHodgman Type
ConvexConvexSutherlandHodgman{M <: Manifold} <: GeometryOpsCore.Algorithm{M}Sutherland-Hodgman polygon clipping algorithm optimized for convex-convex intersection.
Both input polygons MUST be convex. If either polygon is non-convex, results are undefined.
This is simpler and faster than Foster-Hormann for small convex polygons, with O(n*m) complexity where n and m are vertex counts.
When intersecting many polygon pairs in a hot loop, pass a SutherlandHodgmanCache via the cache keyword argument to intersection to avoid intermediate allocations.
Spherical manifold
For Spherical() manifold, input polygons must have counter-clockwise winding when viewed from outside the sphere (i.e., the interior is on the left when traversing edges). Polygons with clockwise winding will produce incorrect results (typically a degenerate polygon). Use GO.fix(geom; corrections=[GO.ClosedRing(), GO.GeometryCorrection()]) or manually reverse the coordinates if your input has the wrong winding order.
Example
import GeometryOps as GO, GeoInterface as GI
square1 = GI.Polygon([[(0.0, 0.0), (2.0, 0.0), (2.0, 2.0), (0.0, 2.0), (0.0, 0.0)]])
square2 = GI.Polygon([[(1.0, 1.0), (3.0, 1.0), (3.0, 3.0), (1.0, 3.0), (1.0, 1.0)]])
result = GO.intersection(GO.ConvexConvexSutherlandHodgman(), square1, square2)GeometryOps.CrossingEdgeSplit Type
CrossingEdgeSplit() <: GeometryCorrectionSplit every polygon ring at the points where two of its non-adjacent edges cross properly as great-circle arcs, reassembling the resulting loops as separate rings (even-odd semantics: both lobes of a figure-eight are kept, matching S2Builder's undirected split_crossing_edges repair). A polygon whose shell splits becomes a MultiPolygon; when it carries holes, each (likewise repaired) hole loop is assigned to the shell loop that contains it. This is the remedy for the ring-crossing ArgumentError thrown by prepare on the Spherical manifold.
Crossing points are constructed in Float64 (lon/lat of the exact crossing direction) — corrections construct geometry; they don't decide predicates — the same standard as AntipodalEdgeSplit's midpoint insertion.
Scope
The correction handles isolated pairwise crossings — the needle and bowtie class, where no edge participates in more than one crossing and no two crossings interleave around the ring. Rings with tangled multi-crossing topology beyond that throw an ArgumentError rather than emitting wrong geometry. Vertex touches (rings meeting at a point) are valid and are not split.
It can be called on any polygonal geometry as usual (CrossingEdgeSplit()(geom)), or passed to GeometryOps.fix. Because a split changes the geometry type (Polygon → MultiPolygon), apply it directly to MultiPolygon inputs rather than through fix's per-polygon traversal.
See also GeometryCorrection, AntipodalEdgeSplit.
GeometryOps.DE9IM Type
DE9IMThis functionality is experimental and may change at any time.
An immutable DE-9IM intersection matrix. Entries are dimension codes (DIM_FALSE, DIM_P, DIM_L, DIM_A) stored row-major over (Interior, Boundary, Exterior) of A × B, matching the standard string form "212101212". Construct from a 9-character string or empty (all-F) via DE9IM(). Index with im[locA, locB], where the indices are the JTS location codes (0 = Interior, 1 = Boundary, 2 = Exterior — the internal LOC_* constants), not 1-based array positions: im[0, 0] is the Interior/Interior entry.
GeometryOps.DiffIntersectingPolygons Type
DiffIntersectingPolygons() <: GeometryCorrectionThis correction ensures that the polygons included in a multipolygon aren't intersecting. If any polygon's are intersecting, they will be made nonintersecting through the difference operation to create a unique set of disjoint (other than potentially connections by a single point) polygons covering the same area. See also GeometryCorrection, UnionIntersectingPolygons.
GeometryOps.DouglasPeucker Type
DouglasPeucker <: SimplifyAlg
DouglasPeucker(; number, ratio, tol)Simplifies geometries by removing points below tol distance from the line between its neighboring points.
Keywords
ratio: the fraction of points that should remain aftersimplify. Useful as it will generalise for large collections of objects.number: the number of points that should remain aftersimplify. Less useful for large collections of mixed size objects.tol: the minimum distance a point will be from the line joining its neighboring points.
Note: user input tol is squared to avoid unnecessary computation in algorithm.
GeometryOps.EdgeSourceInfo Type
EdgeSourceInfoSource topology of one input linework (port of JTS EdgeSourceInfo): index (0 = A, 1 = B), dim (DIM_A for a ring, DIM_L for a line), is_hole (ring role), and depth_delta (the signed side-labelling delta of an area ring, 0 for a line). Consumed by the edge merger to build OverlayLabels.
GeometryOps.EndpointBoundary Type
EndpointBoundary()This functionality is experimental and may change at any time.
BoundaryNodeRule under which any endpoint of a line is on the boundary, regardless of how many line ends meet there (JTS EndPointBoundaryNodeRule).
GeometryOps.FosterHormannClipping Type
FosterHormannClipping{M <: Manifold, A <: Union{Nothing, Accelerator}} <: GeometryOpsCore.Algorithm{M}Applies the Foster-Hormann clipping algorithm.
Arguments
manifold::M: The manifold on which the algorithm operates.accelerator::A: The accelerator to use for the algorithm. Can benothingfor automatic choice, or a custom accelerator.
GeometryOps.GEOS Type
GEOS(; params...)A struct which instructs the method it's passed to as an algorithm to use the appropriate GEOS function via LibGEOS.jl for the operation.
Dispatch is generally carried out using the names of the keyword arguments. For example, segmentize will only accept a GEOS struct with only a max_distance keyword, and no other.
It's generally somewhat slower than the native Julia implementations, since it must convert to the LibGEOS implementation and back - so be warned!
Extended help
This uses the LibGEOS.jl package, which is a Julia wrapper around the C library GEOS (https://trac.osgeo.org/geos).
sourceGeometryOps.GeodesicSegments Type
GeodesicSegments(; max_distance::Real, equatorial_radius::Real=6378137, flattening::Real=1/298.257223563)Warning
This is deprecated - call segmentize(Geodesic(; semimajor_axis, inv_flattening), geom; max_distance) instead.
A method for segmentizing geometries by adding extra vertices to the geometry so that no segment is longer than a given distance. This method calculates the distance between points on the geodesic, and assumes input in lat/long coordinates.
Warning
Any input geometries must be in lon/lat coordinates! If not, the method may fail or error.
Arguments
max_distance::Real: The maximum distance, in meters, between vertices in the geometry.equatorial_radius::Real=6378137: The equatorial radius of the Earth, in meters. Passed toProj.geod_geodesic.flattening::Real=1/298.257223563: The flattening of the Earth, which is the ratio of the difference between the equatorial and polar radii to the equatorial radius. Passed toProj.geod_geodesic.
One can also omit the equatorial_radius and flattening keyword arguments, and pass a geodesic object directly to the eponymous keyword.
This method uses the Proj/GeographicLib API for geodesic calculations.
sourceGeometryOps.GeometryCorrection Type
abstract type GeometryCorrectionThis abstract type represents a geometry correction.
Interface
Any GeometryCorrection must implement two functions: * application_level(::GeometryCorrection)::AbstractGeometryTrait: This function should return the GeoInterface trait that the correction is intended to be applied to, like PointTrait or LineStringTrait or PolygonTrait. * (::GeometryCorrection)(::AbstractGeometryTrait, geometry)::(some_geometry): This function should apply the correction to the given geometry, and return a new geometry.
GeometryOps.IndexedPointInAreaLocator Type
IndexedPointInAreaLocator(m::Manifold, geom; exact)Determines the location (LOC_* code) of points relative to an areal geometry, using indexing for efficiency. This algorithm is suitable for use in cases where many points will be tested against a given area. The location is computed precisely: points located on the geometry boundary or segments return LOC_BOUNDARY.
Port of JTS IndexedPointInAreaLocator together with its private IntervalIndexedGeometry (the y-interval segment index; its isEmpty flag is index === nothing here, since a recursively empty polygonal geometry contributes no rings, hence no segments). JTS lazy-loads the index on the first locate; here the index is built in the constructor, since RelatePointLocator already creates the locator itself lazily on the first use per polygonal element (_get_poly_locator, the port of RelatePointLocator.getLocator).
On Spherical, the locator caches the element's rings in kernel space (polys — SphericalKernelRings; Layer 1 of the 2026-07-14 spherical-indexed-locator design), so queries never reconvert vertices, and — with indexed = true, the default — builds the longitude-interval edge index plus the parity anchor (Layer 2): the location of a reference pole, computed once by the exact scan, from which each query is a 1-D stab at its longitude and a crossing-parity count along its meridian arc to the pole. With indexed = false (the unprepared arm, where a one-shot query cannot amortize the build) it locates by the exact ring scan over the cached rings. The polys vector is empty on Planar, where the corresponding roles are played by index and the implicit EXTERIOR at the ray's far end.
GeometryOps.IntersectionAccelerator Type
abstract type IntersectionAcceleratorThe abstract supertype for all intersection accelerator types.
The idea is that these speed up the edge-edge intersection checking process, perhaps at the cost of memory.
The naive case is NestedLoop, which is just a nested loop, running in O(n*m) time.
Then we have SingleSTRtree, which is a single STRtree, running in O(n*log(m)) time.
Then we have DoubleSTRtree, which is a simultaneous double-tree traversal of two STRtrees.
Finally, we have AutoAccelerator, which chooses the best accelerator based on the size of the input polygons. This gets materialized in build_a_list for now. AutoAccelerator should also try to respect existing spatial indices, if they exist.
GeometryOps.LineOrientation Type
Enum LineOrientationEnum for the orientation of a line with respect to a curve. A line can be line_cross (crossing over the curve), line_hinge (crossing the endpoint of the curve), line_over (collinear with the curve), or line_out (not interacting with the curve).
GeometryOps.LinearBoundary Type
LinearBoundary(lines, rule::BoundaryNodeRule)Determines the boundary points of a linear geometry, using a BoundaryNodeRule. lines is an iterable of linestrings (any GeoInterface linestring-like geometries); the endpoint degree of every line endpoint is counted and the rule decides which degrees are boundary points.
Coordinate keys are normalized via _node_point (kernel.jl): exact (Float64, Float64) tuples with signed zeros normalized (-0.0 → +0.0), so lookups here agree with the NodeKey vertex-node identity from the kernel (Task 7) under Dict bit-pattern hashing.
Faithful to Java: only empty lines are skipped. Closed lines are NOT special-cased — a closed line contributes degree 2 to its closure vertex (both endpoints coincide), which is never a boundary under the Mod-2 or monovalent rules but would be under e.g. the endpoint rule.
sourceGeometryOps.LinearSegments Type
LinearSegments(; max_distance::Real)Warning
This is deprecated - call segmentize(Planar(), geom; max_distance) instead.
A method for segmentizing geometries by adding extra vertices to the geometry so that no segment is longer than a given distance.
Here, max_distance is a purely nondimensional quantity and will apply in the input space. This is to say, that if the polygon is provided in lat/lon coordinates then the max_distance will be in degrees of arc. If the polygon is provided in meters, then the max_distance will be in meters.
GeometryOps.MeanValue Type
MeanValue() <: AbstractBarycentricCoordinateMethodThis method calculates barycentric coordinates using the mean value method.
References
sourceGeometryOps.Mod2Boundary Type
Mod2Boundary()This functionality is experimental and may change at any time.
The OGC SFS standard BoundaryNodeRule (and the RelateNG default): an endpoint is on the boundary iff an odd number of line ends meet it (the "Mod-2 rule"; JTS Mod2BoundaryNodeRule).
GeometryOps.MonotoneChainMethod Type
MonotoneChainMethod()This is an algorithm for the convex_hull function.
Uses DelaunayTriangulation.jl to compute the convex hull. This is a pure Julia algorithm which provides an optimal Delaunay triangulation.
See also convex_hull
GeometryOps.MonovalentEndpointBoundary Type
MonovalentEndpointBoundary()This functionality is experimental and may change at any time.
BoundaryNodeRule under which an endpoint is on the boundary iff exactly one line end meets it (JTS MonoValentEndPointBoundaryNodeRule).
GeometryOps.MultivalentEndpointBoundary Type
MultivalentEndpointBoundary()This functionality is experimental and may change at any time.
BoundaryNodeRule under which an endpoint is on the boundary iff more than one line end meets it (JTS MultiValentEndPointBoundaryNodeRule).
GeometryOps.NodeKey Type
NodeKey{P}Symbolic identity of a node (design D2). Vertex nodes key exactly by their coordinate (is_crossing == false, all point fields equal to the vertex); proper-crossing nodes key by their canonicalized defining segment pair (is_crossing == true, fields (pt, a1) and (b0, b1) are the two segments). No intersection coordinate is ever computed for the key. Construct via vertex_node and crossing_node.
GeometryOps.NodeSection Type
NodeSection{P}Represents a computed node along with the incident edges on either side of it (if they exist). This captures the information about a node in a geometry component required to determine the component's contribution to the node topology. A node in an area geometry always has edges on both sides of the node. A node in a linear geometry may have one or other incident edge missing, if the node occurs at an endpoint of the line.
The edges of an area node are assumed to be provided with CW-shell orientation (as per JTS norm). This must be enforced by the caller.
Port of JTS NodeSection, with one symbolic twist (design D2): the Java class stores the node as a Coordinate; here node is a NodeKey, so proper-crossing nodes never need a constructed coordinate. v0/v1 are coordinate tuples of type P (or nothing for a missing incident edge at a line endpoint); polygonal is the parent polygonal geometry of an area section, or nothing if the section is not on a polygon boundary — an opaque payload (abstract field type, as in Java): it is only compared by identity, so parameterizing on it would re-specialize the node machinery per input geometry type for no runtime gain.
The field order matches the Java constructor argument order (isA, dimension, id, ringId, poly, isNodeAtVertex, v0, nodePt, v1). (The struct is declared before the comparator helpers below because their signatures reference it; the Java file declares EdgeAngleComparator and isAreaArea first.)
GeometryOps.NodeSections Type
NodeSections(node::NodeKey)Collects the NodeSections of all geometry components incident on one node, and assembles them into the node's edge topology (create_node).
Port of JTS NodeSections; the Java class is keyed by the node Coordinate, here by the symbolic NodeKey (design D2).
GeometryOps.NodedArrangement Type
NodedArrangement{P,T}The exactly-noded arrangement of two input geometries (design §2.1). P is the manifold's kernel point type — exactly two instantiations, Tuple{Float64,Float64} (planar) and UnitSphericalPoint{Float64} (spherical) — so the engine is type-erased over the input geometry types. T is the OUTPUT point type node_point realizes into (point_type in the OverlayNG API); it defaults to P, which is what makes an emitted vertex bit-identical to the ingested one.
Fields:
segstrings: the ingested inputs asRelateSegmentStrings (A side first, then B side);NodedEdge.string_idxindexes here.nodes: the symbolic node table (NodeTable).seg_nodes: per-parent-segment ordered interior node-id lists, keyed by(string_idx, seg_idx); absent for unsplit segments.edges: every noded sub-segment of every parent segment.truncated: node ids whose incident-edge set was reduced by clip pruning (seeclip_a/clip_bbelow). Empty (BitVector()) whenever no pruning ran, which is every construction that does not pass a clip envelope.
Construct with NodedArrangement(m, a, b) (raw geometries) or NodedArrangement(m, ssa, ssb) (pre-ingested segment strings); both take point_type to choose T.
GeometryOps.OverlayGraph Type
OverlayGraph{P,T}The topology graph of an overlay operation (port of JTS OverlayGraph). Holds the arrangement it was built from, the vector of half-edges (both orientations of every merged edge), and one representative outgoing half-edge index per node id (node_edges[nid], 0 if the node has no incident edges). P is the manifold kernel point type, so the graph is type-erased over input geometry types; T is the arrangement's output point type, carried so the builders reading node_point off it stay concrete.
GeometryOps.OverlayGraph Method
OverlayGraph(m, arr::NodedArrangement, sources) -> OverlayGraphBuild the overlay graph from a noded arrangement and its EdgeSourceInfo table. Coincident noded edges are merged (JTS Edge.merge semantics), each merged edge becomes a symmetric OverlayEdge pair sharing one label, and every node's star is ordered CCW about its symbolic apex via the exact kernel comparator.
GeometryOps.OverlayLabel Type
OverlayLabelThe topological label of one edge of the overlay graph (port of JTS OverlayLabel). Mutable plain fields, one instance shared between a symmetric OverlayEdge pair; the is_forward-parameterized accessors swap Left/Right for the reverse half-edge. Index 0 selects input A, index 1 input B.
GeometryOps.OverlayNG Type
OverlayNG(; manifold = Planar(), exact = True(), point_type = ...)
OverlayNG(manifold::Manifold; kwargs...)This functionality is experimental and may change at any time.
The exact-arrangement overlay algorithm, a port of the JTS OverlayNG engine by Martin Davis, extended to the sphere.
OverlayNG computes intersection, union, difference and symdifference of two geometries of any dimension. It is opt-in: the algorithm is the first argument.
GO.intersection(GO.OverlayNG(), a, b)
GO.union(GO.OverlayNG(GO.Spherical()), a, b)Foster–Hormann clipping remains the default engine for intersection, union and difference when no algorithm is given; those defaults are unchanged by the presence of this algorithm. symdifference is the one exception — see its docstring.
Keyword arguments
manifold:Planar()(default) orSpherical().Spherical(; oriented)selects how a ring denotes its region, and overlay follows that choice throughout — nothing extra is needed here.exact:True()(default) to decide every uncertain filter with an exact predicate,False()to stay in Float64. Leave it at the default unless you are measuring the cost of exactness.point_type: the type of the coordinates in the result. Defaults to the manifold's own working point type —Tuple{Float64,Float64}onPlanar(),UnitSphericalPoint{Float64}(3D unit-spherexyz) onSpherical(). On the sphereTuple{Float64,Float64}is also accepted and gives(lon, lat)degrees; see "Output coordinates" below for what that costs.
Output coordinates
The arrangement is exact and symbolic: rounding to Float64 happens exactly once, when a node's coordinate is realized for output. point_type chooses the chart that rounding lands in, and on the sphere the choice is not neutral.
Spherical() works in unit-sphere xyz throughout — an input vertex is converted to a UnitSphericalPoint at ingest and every predicate reads it there — so emitting UnitSphericalPoint{Float64} is a pass-through for every result vertex that is an input vertex: the coordinate that comes out is bit-for-bit the one that went in, and GO.area of a clipped cell agrees with the uncut original exactly rather than in all but the last ULP.
Emitting (lon, lat) instead sends that vertex back out through atan/asin, which is a rounding of a value that had an exact image in the format it was already in. Measured over 200 000 uniformly random directions, the round trip displaces a point by up to 3.2 ULPs of the unit sphere below 60° latitude, 7.9 at 75°, and 126 above 89.5°; swept along single parallels the worst displacement is 7.8 ULPs at 89°, 1 364 at 89.99° and 2 915 (6.5e-13 rad) at 89.999°. The growth is the chart's, not the arithmetic's — a degree of longitude is cos φ of an arc — and it is why a polar grid cell survives an overlay in xyz and does not in lon/lat.
For a crossing node there is no exact Float64 answer in either chart: the position is a Rational{BigInt} direction with no finite decimal form. What xyz buys there is rounding once (normalize the direction) rather than twice (normalize, then trigonometry).
Tuple{Float64,Float64} therefore exists for callers that need lon/lat coordinates back and are willing to pay for them, not as a lossless alternative.
Result shape
The result is a single GeoInterface geometry, the most specific one that fits:
one component of one dimension →
Point/LineString/Polygon;several components of one dimension → the corresponding
Multigeometry;components of several dimensions → a
GeometryCollection, ordered areas, then lines, then points;nothing → an empty
MultiPoint/MultiLineString/MultiPolygon, whose dimension follows the OGC rule for the operation (minof the inputs for intersection,maxfor union and symmetric difference, the left input's for difference).
Lower-dimension components are included: the intersection of two polygons that share a boundary segment and also overlap is a GeometryCollection of the overlap polygon and the shared line. This is JTS's original (non-strict) overlay semantics.
target: asking for one dimension
Each operation takes a target keyword that narrows the result to a single dimension, chosen up front rather than by the data. It is the target of the Foster–Hormann entry points, with the same return shapes — a singular trait gives the Vector of atomic components, a Multi trait gives the one multi-geometry:
target | returns |
|---|---|
nothing (default) | as above — most specific over every dimension |
GI.PolygonTrait() | Vector{<:Polygon} |
GI.MultiPolygonTrait() | MultiPolygon |
GI.LineStringTrait() | Vector{<:LineString} |
GI.MultiLineStringTrait() | MultiLineString |
GI.PointTrait() | Vector{<:Point} |
GI.MultiPointTrait() | MultiPoint |
#-- always a MultiPolygon, whatever `a` and `b` turn out to share
GO.intersection(GO.OverlayNG(), a, b; target = GI.MultiPolygonTrait())An empty targeted result is the empty Vector or empty multi-geometry of that same concrete type, so the return type no longer depends on the inputs — which is the point: without target, code that only wants areas has to handle a GeometryCollection that appears only when the inputs happen to touch.
target also removes work. A target above the result's OGC dimension is answered from the input dimensions alone, with no noding at all (an areal target on a line ∩ area is empty for every possible input), and the builds the target cannot want are skipped. The saving is one-directional, because the three builds form a dependency chain: an areal target skips both the line and point builds, a line target skips the point build, and a point target skips neither. Result polygons are built even for a line or point target — whether a line lies inside the result area is part of deciding it, and there is no cheaper equivalent test.
Inputs
Point, MultiPoint, LineString, LinearRing, MultiLineString, Polygon and MultiPolygon, in any A × B combination. GeometryCollection inputs raise an ArgumentError.
Inputs must be valid: rings may not self-cross, and the components of a multi-geometry may not overlap. The engine nodes A against B but never A against itself, so an invalid input yields an undefined result rather than an error. Fix inputs first (e.g. with CrossingEdgeSplit / AntipodalEdgeSplit on the sphere) if you are not sure.
Robustness
All topological decisions are made by exact predicates on the input coordinates and on symbolic crossing keys. Float64 values are used only as filters with certified error bounds, and only appear in the output. There is therefore no snapping, no tolerance and no precision model to configure, and no robustness failure mode to retry around.
Limitation: the full sphere
On Spherical(), an overlay whose result covers the entire sphere and has no boundary at all cannot be returned, and raises an ArgumentError. A polygon denotes the region bounded by its rings, and a polygon with no rings already means the empty geometry, so GeometryOps has no spelling for the full sphere. Reformulate such an operation as a difference from the covering region.
GeometryOps.PROJ Type
PROJ(; params...)A struct which instructs the method it's passed to as an algorithm to use the appropriate PROJ function via Proj.jl for the operation.
Extended help
This is the default algorithm for reproject, and will also be the default algorithm for operations on geodesics like area and arclength.
GeometryOps.PlanarCircle Type
PlanarCircle{T}A circle in 2D Euclidean space, represented by its center and squared radius.
Use radius(circle) to get the actual radius (computes sqrt).
Experimental
This type is not part of the public API and is subject to change without a breaking version. It implements GeoInterface's PolygonTrait, so code using GeoInterface methods will remain compatible even if the concrete type changes.
Fields
center::Tuple{T, T}: The (x, y) coordinates of the centerradius_squared::T: The squared radius (avoids sqrt in distance comparisons)
GeometryOps.PlanarCircleRing Type
PlanarCircleRing{T}A lazy wrapper that presents a PlanarCircle as a LinearRing with interpolated points. Used internally for GeoInterface compatibility.
Internal
This is an internal type and not part of the public API.
GeometryOps.PointOrientation Type
Enum PointOrientationEnum for the orientation of a point with respect to a curve. A point can be point_in the curve, point_on the curve, or point_out of the curve.
GeometryOps.PreparedRelate Type
PreparedRelate{ALG, G, RG, SS, T}This functionality is experimental and may change at any time.
A prepared RelateNG instance for optimized repeated evaluation of topological relationships against a single geometry a (the "prepared mode" of JTS RelateNG.prepare). Holds:
alg: theRelateNGalgorithm configuration,input: the A geometry as passed toprepare, which re-prepares from it under a differentalg(geom_abelow cachesalg's manifold's extents, so it cannot serve another),geom_a: the A-sideRelateGeometry, constructed withis_prepared = trueand with its lazy locator/unique-points caches forced,segs_a: the A segment strings, extracted once without an interaction-envelope filter so they serve any B geometry,edge_tree: the prebuilt segment index oversegs_a(_relate_edge_index, edge_intersector.jl — the stand-in for Java's cachedMCIndexSegmentSetMutualIntersector), ornothingbelow the accelerator size threshold (where the nested loop wins).
Construct with prepare; evaluate with relate / relate_predicate.
Warning
Not safe for concurrent use: self-noding evaluations mutate the held RelateGeometry (edge re-extraction, element-id counter). Use one PreparedRelate per thread.
GeometryOps.RadialDistance Type
RadialDistance <: SimplifyAlgSimplifies geometries by removing points less than tol distance from the line between its neighboring points.
Keywords
ratio: the fraction of points that should remain aftersimplify. Useful as it will generalise for large collections of objects.number: the number of points that should remain aftersimplify. Less useful for large collections of mixed size objects.tol: the minimum distance between points.
Note: user input tol is squared to avoid unnecessary computation in algorithm.
GeometryOps.RayCrossingCounter Type
RayCrossingCounter(m::Manifold, p; exact)Counts the number of segments crossed by a horizontal ray extending to the right from a given point, in an incremental fashion. This can be used to determine whether a point lies in a polygonal geometry. The class determines the situation where the point lies exactly on a segment. This handles polygonal geometries with any number of shells and holes; ring orientation is unimportant. In order to compute a correct location for a given polygonal geometry, it is essential that all segments are counted which touch the ray or lie in any ring which may contain the point — which is what allows optimization by y-interval indexing, since segments whose y-extent misses the ray a priori cannot touch it.
The manifold m and the exact flag are stored in the struct (consistent with AdjacentEdgeLocator); the orientation test goes through rk_orient (JTS uses the extended-precision Orientation.index, matching exact = True()). The horizontal-ray sweep itself is coordinate-plane logic, exactly as in JTS.
GeometryOps.RelateEdge Type
RelateEdge{P}An edge of a RelateNode's wheel: the direction node → dir_pt, labeled per input geometry with the dimension of the geometry element the edge came from and the geometry's location on the left of, right of, and on the edge. Unknown dimensions are DIM_UNKNOWN_EDGE; unknown locations are LOC_NONE.
Port of JTS RelateEdge; the Java class stores its parent RelateNode to reach the node coordinate, here the symbolic NodeKey is stored directly (design D2).
GeometryOps.RelateGeometry Type
RelateGeometry(m::Manifold, geom; exact, is_prepared = false,
boundary_rule = Mod2Boundary())The input-geometry facade of RelateNG: wraps one of the two operand geometries and caches its metadata — recursive emptiness, extent, dimension analysis (has_points/has_lines/has_areas), zero-length-line degeneracy — plus lazily created unique points and a RelatePointLocator.
The Java constructor signature is RelateGeometry(Geometry input, boolean isPrepared, BoundaryNodeRule bnRule); the manifold/exact parameters are the only additions (consistent with RelatePointLocator). Where the Java caches geomEnv = input.getEnvelopeInternal(), here extent is the union of the interaction bounds (rk_interaction_bounds) of the non-empty elements, or nothing if the geometry is empty.
GeometryOps.RelateNG Type
RelateNG{M <: Manifold, A <: IntersectionAccelerator, E, BR <: BoundaryNodeRule}This functionality is experimental and may change at any time.
The next-generation DE-9IM topological-relationship algorithm, a port of the JTS RelateNG algorithm by Martin Davis. Capabilities: 2. Efficient short-circuited evaluation of topological predicates (including matching custom DE-9IM matrix patterns).
Robust evaluation: all answers are computed from exact predicates on the input coordinates (no constructed intersection points), so invalid topology does not cause failures.
GeometryCollectioninputs containing mixed types and overlapping polygons are supported, using union semantics.Zero-length LineStrings are treated as being topologically identical to Points.
Support for
BoundaryNodeRules.
All coordinates are evaluated as Float64: input coordinates are converted on extraction, and the exact-predicate machinery (adaptive orientation predicates, rational-arithmetic node coincidence) assumes Float64 inputs. Non-Float64 geometries are accepted but evaluated at Float64 precision.
Keyword arguments (all optional): manifold (default Planar()), accelerator (default AutoAccelerator), exact (default True()), boundary_rule (default Mod2Boundary, the OGC SFS rule).
Unprepared performance
Every unprepared evaluation rebuilds an extent-annotated view of both inputs (the stand-in for the envelope cache JTS keeps on each Geometry), one coordinate pass per call. Inputs that already carry extents at every level skip that pass — stamp them once with
geom = GO.tuples(geom; calc_extent = true)and repeated unprepared calls read the stored extents instead. When one geometry is queried many times, prepare it instead: the prepared form also caches the point locators and edge index.
See relate and relate_predicate for the entry points.
GeometryOps.RelateNode Type
RelateNode(m::Manifold, node::NodeKey; exact)The topology at a node between the edges of two input geometries: a list of the RelateEdges around the node in CCW order, ordered by their CCW angle with the positive X-axis.
Port of JTS RelateNode; the Java class is keyed by the node Coordinate, here by the symbolic NodeKey (design D2). The manifold and the exact flag (absent in Java) are stored for the edge-angle comparisons in add_edge!.
GeometryOps.RelatePointLocator Type
RelatePointLocator(m::Manifold, geom; exact, is_prepared = false,
boundary_rule = Mod2Boundary())Locates a point on a geometry, including mixed-type collections. The dimension of the containing geometry element is also determined. GeometryCollections are handled with union semantics; i.e. the location of a point is that location of that point on the union of the elements of the collection.
Union semantics for GeometryCollections has the following behaviours: 2. For a mixed-dimension (heterogeneous) collection a point may lie on two geometry elements with different dimensions. In this case the location on the largest-dimension element is reported.
- For a collection with overlapping or adjacent polygons, points on polygon element boundaries may lie in the effective interior of the collection geometry.
Supports specifying the BoundaryNodeRule to use for line endpoints (RelateGeometry passes its rule down here; the default matches Java's BoundaryNodeRule.OGC_SFS_BOUNDARY_RULE, i.e. Mod-2).
The Java constructor signature is RelatePointLocator(geom, isPrepared, bnRule); the manifold/exact parameters are the only additions (consistent with AdjacentEdgeLocator). As in JTS, prepared mode swaps the per-polygon SimplePointInAreaLocator ring loop for a cached IndexedPointInAreaLocator (indexed_point_in_area.jl), created lazily on the first use per polygonal element (Task 22); unprepared planar mode scans the rings directly on every query, while the spherical path caches a per-element locator in both modes (its kernel-space ring cache is what makes queries conversion-free). Repeated point location against one geometry is what prepare is for.
GeometryOps.RelateSegmentString Type
RelateSegmentString{P}Models a linear edge of a RelateGeometry: the coordinate vector of one line or one polygon ring, tagged with which input geometry it came from (is_a), its dimension, the element/ring ids assigned during extraction, and (for rings) the parent polygonal geometry.
In JTS this extends BasicSegmentString; here the coordinates are stored directly in pts. Segment indices are 1-based: segment i runs from pts[i] to pts[i + 1] (the Java equivalents are 0-based).
The geometry references are deliberately opaque (abstract field types, as they are in Java): parent_polygonal is only ever compared by identity and carried into NodeSections, so parameterizing on the input geometry type would only re-specialize the whole edge machinery per geometry-type pair (a pure compile-time cost). Only pts — the per-segment hot path — stays concretely typed, on the manifold's kernel point type P.
GeometryOps.SegSegClass Type
SegSegClassCombinatorial classification of the intersection of closed segments (a0,a1) × (b0,b1). kind is SS_PROPER only for a crossing in both segments' interiors (the node is symbolic: no coordinate exists for it anywhere in the engine). All vertex incidences are reported via the *_on_* flags, whose coordinates are exact input vertices.
GeometryOps.SimplifyAlg Type
abstract type SimplifyAlgAbstract type for simplification algorithms.
API
For now, the algorithm must hold the number, ratio and tol properties.
Simplification algorithm types can hook into the interface by implementing the _simplify(trait, alg, geom) methods for whichever traits are necessary.
GeometryOps.SphericalKernelRing Type
SphericalKernelRing(m::Spherical, ring; exact, is_hole = false)The cached kernel-space form of one ring: the converted UnitSphericalPoint vertex vector (pts — the boundary edge walk), its deduped open form (ded/n — the parity walk; aliases pts when the ring has no repeated vertices), the ring's denoted-region bit (_ring_interior_on_left, from the ring's winding or — on an oriented manifold — its declared role; the same bit edge topology and interaction bounds use), and — in enclosed-region mode — the definitional-exterior parity anchor (spherical_exterior_anchor; nothing on an oriented manifold, which never consults it, or for a degenerate vertex mass, where queries fall back to the wedge bootstrap).
rk_point_in_ring re-derived all of this from lon/lat on every query — vertex conversion alone was ~60% of a prepared spherical point query. The point-in-area locators (indexed_point_in_area.jl) convert each ring once and query on this form (Layer 1 of the 2026-07-14 spherical-indexed-locator design).
Repeated consecutive vertices are dropped from the parity walk (real rings carry them — NE 110m North Korea's sliver is [A, A, B, A]; JTS removes them at ingest, but this path receives the raw ring): a retraced edge lies exactly under the anchor midpoint and breaks the parity count. After dedup a ring with fewer than 3 distinct vertices bounds no area.
GeometryOps.SphericalRingPoints Type
SphericalRingPoints(ring)A ring's vertices as UnitSphericalPoints, converted on indexing rather than up front, with any repeated closing vertex excluded from length.
Exists so that the spherical predicates can hand a ring to the UnitSpherical ring primitives — which index into a vector of unit points — without allocating that vector. Converting lazily costs one UnitSphereFromGeographic per access; a ring walked several times in one predicate call pays that more than once, which is the trade a prepared target would remove.
GeometryOps.SutherlandHodgmanCache Type
SutherlandHodgmanCache{P}()
SutherlandHodgmanCache(manifold::Manifold, [T = Float64])
SutherlandHodgmanCache(alg::ConvexConvexSutherlandHodgman, [T = Float64])Preallocated buffers for ConvexConvexSutherlandHodgman clipping.
Pass this as the cache keyword argument to intersection to avoid allocating intermediate vectors on every call - useful when intersecting many polygon pairs in a hot loop. The returned polygon never aliases the cache, so results remain valid after the cache is reused.
The point type P must match the algorithm's manifold and float type: Tuple{T,T} for Planar(), UnitSpherical.UnitSphericalPoint{T} for Spherical(). The manifold/algorithm constructors take care of this.
Thread safety
A cache must not be shared across concurrent tasks or threads. Create one cache per task. The default (cache = nothing) allocates fresh buffers on each call and is always safe.
Example
import GeometryOps as GO
alg = GO.ConvexConvexSutherlandHodgman()
cache = GO.SutherlandHodgmanCache(alg)
for (a, b) in polygon_pairs
result = GO.intersection(alg, a, b; cache)
endGeometryOps.TG Type
TG(; params...)A struct which instructs the method it's passed to as an algorithm to use the appropriate TG function via TGGeometry.jl for the operation.
It's generally a lot faster than the native Julia implementations, but only supports planar manifolds / operations. Also, it only supports geometric predicates, specifically the ones which the underlying tg library supports. These are:
equals, intersects, disjoint, contains, within, covers, coveredby, and touches.
Extended help
This uses the TGGeometry.jl package, which is a Julia wrapper around the tg C library (https://github.com/tidwall/tg).
GeometryOps.TopologyComputer Type
TopologyComputer(predicate, geom_a::RelateGeometry, geom_b::RelateGeometry)The DE-9IM accumulation engine of RelateNG: translates topological events into dimension updates on predicate and collects edge-intersection NodeSections per node (keyed by symbolic NodeKey, design D2) for evaluate_nodes!.
Port of JTS TopologyComputer.
GeometryOps.TopologyPredicate Type
TopologyPredicateThe abstract supertype for strategy types implementing spatial predicates based on the DE-9IM topology model (port of JTS TopologyPredicate). Concrete predicates implement predicate_name(p), update_dim!(p, locA, locB, dim), finish!(p), is_known(p), and predicate_value(p), and may override the requirement flags and the init_dims!/init_bounds! hooks. Evaluate one against a pair of geometries with relate_predicate.
GeometryOps.TracingError Type
TracingError{T1, T2} <: ExceptionAn error that is thrown when the clipping tracing algorithm fails somehow. This is a bug in the algorithm, and should be reported.
The polygons are contained in the exception object, accessible by try-catch or as err in the REPL.
GeometryOps.UnionIntersectingPolygons Type
UnionIntersectingPolygons() <: GeometryCorrectionThis correction ensures that the polygon's included in a multipolygon aren't intersecting. If any polygon's are intersecting, they will be combined through the union operation to create a unique set of disjoint (other than potentially connections by a single point) polygons covering the same area.
See also GeometryCorrection.
GeometryOps.VisvalingamWhyatt Type
VisvalingamWhyatt <: SimplifyAlg
VisvalingamWhyatt(; kw...)Simplifies geometries by removing points below tol distance from the line between its neighboring points.
Keywords
ratio: the fraction of points that should remain aftersimplify. Useful as it will generalise for large collections of objects.number: the number of points that should remain aftersimplify. Less useful for large collections of mixed size objects.tol: the minimum area of a triangle made with a point and its neighboring points.
Note: user input tol is doubled to avoid unnecessary computation in algorithm.
GeometryOps.Welzl Type
Welzl{M <: Manifold} <: ManifoldIndependentAlgorithm{M}Welzl's algorithm for computing the minimum bounding circle.
This is a randomized algorithm with expected O(n) time complexity. Works on any manifold given an appropriate distance function.
Constructor
Welzl(; manifold=Planar())Example
import GeometryOps as GO
points = [(0.0, 0.0), (1.0, 0.0), (0.0, 1.0)]
circle = GO.minimum_bounding_circle(GO.Welzl(), points)GeometryOps._OverlayTopologyError Type
_OverlayTopologyError(msg)A robustness/topology error raised by the overlay engine (port of JTS TopologyException). Signals an inconsistency the builder could not resolve (e.g. a side-location conflict during area propagation, or a ring that cannot be closed).
Extents.extent Method
extent(m::Manifold, geom, [::Type{T} = Float64])::Extents.ExtentThe extent of geom on the manifold m, as an Extents.Extent. The method extends Extents.extent (GeometryOps does not export extent), so call it as GO.extent(m, geom).
On Planar(), GI.extent(geom). On Spherical(), the 3D Cartesian extent of the geometry on the unit sphere, with geographic (longitude, latitude) input converted like UnitSphericalPoint: curves are covered by the union of their edges' great-circle arc extents; rings and polygons are regions, whose extent also covers any enclosed axis point (a pole, say). Which region a ring bounds follows the manifold's interior mode (see Spherical): by default the region it encloses, independent of winding; with Spherical(; oriented = true) the region on the left of the stored vertex order, so a clockwise ring denotes the complement (and gets a box covering essentially the whole sphere).
Example
julia> import GeometryOps as GO, GeoInterface as GI
julia> cap = GI.Polygon([[(lon, 60.0) for lon in 0.0:30.0:360.0]]); # around the north pole
julia> GO.extent(GO.Spherical(), cap).Z[2]
1.0GeometryOps._det Method
_det(s1::Point2{T1}, s2::Point2{T2}) where {T1 <: Real, T2 <: Real}Returns the determinant of the matrix formed by hcat'ing two points s1 and s2.
Specifically, this is:
s1[1] * s2[2] - s1[2] * s2[1]GeometryOps._equals_curves Method
_equals_curves(c1, c2, closed_type1, closed_type2)::BoolTwo curves are equal if they share the same set of point, representing the same geometry. Both curves must must be composed of the same set of points, however, they do not have to wind in the same direction, or start on the same point to be equivalent. Inputs: c1 first geometry c2 second geometry closed_type1::Bool true if c1 is closed by definition (polygon, linear ring) closed_type2::Bool true if c2 is closed by definition (polygon, linear ring)
sourceGeometryOps._exact_crossing_point Method
Exact intersection point of two properly crossing segments, as rationals.
sourceGeometryOps._is_canonical_incidence Method
_is_canonical_incidence(ss::RelateSegmentString, seg_index::Integer, pt)The once-only rule for vertex incidences: whether segment seg_index of ss is the canonical owner of the intersection point pt. Segments are half-closed — a segment owns its start vertex but not its end vertex, except for the final segment of a non-closed string, which also owns its endpoint; in a closed ring the closing vertex is owned by the first segment (wraparound). This attributes every vertex of a segment string to exactly one of its segments, so an endpoint intersection enumerated against both incident segments produces node sections once, not twice.
Encodes the canonicality semantics of the Java addIntersections/RelateSegmentString.isContainingSegment pairing; delegates to is_containing_segment (its direct port).
GeometryOps._overlaps Method
_overlaps(::GI.AbstractTrait, geom1, ::GI.AbstractTrait, geom2)::BoolFor any non-specified pair, all have non-matching dimensions, return false.
sourceGeometryOps._overlaps Method
_overlaps(::GI.LineTrait, line1, ::GI.LineTrait, line2)::BoolIf the lines overlap, meaning that they are collinear but each have one endpoint outside of the other line, return true. Else false.
sourceGeometryOps._overlaps Method
_overlaps(
::GI.MultiPointTrait, points1,
::GI.MultiPointTrait, points2,
)::BoolIf the multipoints overlap, meaning some, but not all, of the points within the multipoints are shared, return true.
sourceGeometryOps._overlaps Method
_overlaps(
::GI.MultiPolygonTrait, polys1,
::GI.MultiPolygonTrait, polys2,
)::BoolReturn true if at least one pair of polygons from multipolygons overlap. Else false.
sourceGeometryOps._overlaps Method
_overlaps(
::GI.MultiPolygonTrait, polys1,
::GI.PolygonTrait, poly2,
)::BoolReturn true if polygon overlaps with at least one of the polygons within the multipolygon. Else false.
sourceGeometryOps._overlaps Method
_overlaps(
::GI.PolygonTrait, poly1,
::GI.MultiPolygonTrait, polys2,
)::BoolReturn true if polygon overlaps with at least one of the polygons within the multipolygon. Else false.
sourceGeometryOps._overlaps Method
_overlaps(
trait_a::GI.PolygonTrait, poly_a,
trait_b::GI.PolygonTrait, poly_b,
)::BoolIf the two polygons intersect with one another, but are not equal, return true. Else false.
sourceGeometryOps._overlaps Method
_overlaps(
::Union{GI.LineStringTrait, GI.LinearRing}, line1,
::Union{GI.LineStringTrait, GI.LinearRing}, line2,
)::BoolIf the curves overlap, meaning that at least one edge of each curve overlaps, return true. Else false.
sourceGeometryOps._overlay_ng Method
_overlay_ng(m, op::_OverlayOpCode, a, b; exact=True(), tree_a=nothing, tree_b=nothing, target=nothing, point_type=_kernel_point_type(m))Compute the overlay of a and b under op on manifold m, returning a GeoInterface geometry. Internal engine entry point for OverlayNG — point, line and area inputs are supported in any A×B combination. tree_a/tree_b accept caller-prebuilt segment indices (threaded to the noding substrate). target narrows the result to one dimension — see "Result targeting" below. point_type is the output coordinate type (OverlayNG's keyword of the same name).
point_type becomes a POSITIONAL ::Type{T} one call in, for the reason NodedArrangement does the same: a keyword-bound static parameter does not specialize, and every result type in this file is a function of T.
GeometryOps.add_area_vertex! Method
add_area_vertex!(tc, is_area_a, loc_area, loc_target, dim_target, pt)Adds topology for an area vertex interaction with a target geometry element. Assumes the target geometry element has highest dimension (i.e. if the point lies on two elements of different dimension, the location on the higher dimension element is provided. This is the semantic provided by RelatePointLocator.)
Note that in a GeometryCollection containing overlapping or adjacent polygons, the area vertex location may be INTERIOR instead of BOUNDARY.
Port of TopologyComputer.addAreaVertex.
sourceGeometryOps.add_edge! Method
add_edge!(n::RelateNode, is_a, dir_pt, dim, is_forward)Adds or merges an edge to the node, keeping the wheel sorted by CCW angle with the positive X-axis. dim is the dimension of the geometry element containing the edge, is_forward the direction of the edge. Returns the created or merged edge for this point, or nothing for a malformed (nothing or zero-length) input edge.
Port of RelateNode.addEdge.
sourceGeometryOps.add_intersections! Method
add_intersections!(tc::TopologyComputer, ssA, seg_index_a, ssB, seg_index_b; m, exact)Classify the intersection of one segment pair via rk_classify_intersection and add a NodeSection pair (one on ssA, one on ssB) to tc for each distinct intersection point:
SS_DISJOINT: nothing to add.SS_PROPER: one section pair at the symboliccrossing_node.SS_TOUCH/SS_COLLINEAR: a section pair at thevertex_nodeof each distinct flagged vertex — but only when both segments contain the vertex canonically (_is_canonical_incidence), which ensures endpoint intersections are added once only across adjacent segments.
Port of EdgeSegmentIntersector.addIntersections (private).
sourceGeometryOps.add_line_end_on_geometry! Method
add_line_end_on_geometry!(tc, is_line_a, loc_line_end, loc_target, dim_target, pt)Add topology for a line end. The line end point must be "significant"; i.e. not contained in an area if the source is a mixed-dimension GC. loc_line_end is the location of the line end (Interior or Boundary); loc_target the location on the target geometry; dim_target the dimension of the interacting target geometry element (if any), or the dimension of the target.
Port of TopologyComputer.addLineEndOnGeometry.
sourceGeometryOps.angles Method
angles(geom, ::Type{T} = Float64)Returns the angles of a geometry or collection of geometries. This is computed differently for different geometries:
- The angles of a point is an empty vector.
- The angles of a single line segment is an empty vector.
- The angles of a linestring or linearring is a vector of angles formed by the curve.
- The angles of a polygon is a vector of vectors of angles formed by each ring.
- The angles of a multi-geometry collection is a vector of the angles of each of the
sub-geometries as defined above.Result will be a Vector, or nested set of vectors, of type T where an optional argument with a default value of Float64.
sourceGeometryOps.antimeridian_split Method
antimeridian_split(geom; antimeridian = 180.0, north_pole = nothing, pole_spacing = 5.0)Split a lon/lat Polygon or MultiPolygon at the antimeridian, returning a GI.MultiPolygon whose pieces each stay within one 360°-wide longitude branch (none crosses the seam). The spherical region is preserved exactly — this is an encoding repair, not a geometry change.
Keyword arguments
antimeridian = 180.0: the seam longitudeλ. The cut runs along the meridianλ(normalised toλn ∈ (-180, 180]); the default reproduces the ±180° antimeridian. Emitted longitudes lie in the closed branch[λn - 360, λn]: the two seam lips are exactlyλn(west-side pieces) andλn - 360(east-side pieces), and every non-seam vertex is strictly interior, in(λn - 360, λn). At the default seam this is the usual[-180, 180]with lips at +180 / −180.north_pole = nothing: when set to(λp, φp), cut along a seam through the rotated pole at geographic(λp, φp)(CFrotated_latitude_longitude/+proj=ob_tran,north_pole_grid_longitude = 0). The returned coordinates are in the rotated frame, not geographic — this is deliberate.pole_spacing = 5.0: maximum longitude step (degrees) of the constant-latitude row emitted along a pole edge of a pole-enclosing piece.nothingemits only the two branch corners. The corners are never optional — they are the topological product of the face walk; only the infill between them is controlled here.
Only PolygonTrait and MultiPolygonTrait inputs are supported; other traits throw an ArgumentError (LineString support is future work).
GeometryOps.area Method
area(geom, [T = Float64])::T
area(manifold::Manifold, geom, [T = Float64])::T
area(algorithm::Algorithm, geom, [T = Float64])::TReturns the area of a geometry or collection of geometries. This is computed slightly differently for different geometries:
- The area of a point/multipoint is always zero.
- The area of a curve/multicurve is always zero.
- The area of a polygon is the absolute value of the signed area.
- The area multi-polygon is the sum of the areas of all of the sub-polygons.
- The area of a geometry collection, feature collection of array/iterable
is the sum of the areas of all of the sub-geometries.Manifold support
AutoManifold()(default): When the Proj extension is loaded, recognized geographic CRSs use a geodesic calculation on the CRS ellipsoid and recognized projected CRSs use native-unitPlanar()calculations. Without Proj, geographic geometries use degree-basedSpherical()calculations, while projected and unknown geometries use native-unitPlanar()calculations. The manifold is selected once from the top-level input's CRS and applies to all geometries contained in that input.Planar(): Uses the shoelace formula in native coordinate units squared, regardless of CRS.Spherical(): Uses Girard's theorem for spherical polygons. Coordinates are interpreted as (longitude, latitude) in degrees. Returns area in square units of the sphere's radius (default: Earth's mean radius in meters).Geodesic(): Uses geodesic calculations (requires Proj extension).
Projected map area is a planar grid measurement and can differ from surface area, particularly for projections with distortion.
Examples
import GeometryOps as GO
import GeoInterface as GI
# CRS-free planar area (the AutoManifold default)
rect = GI.Polygon([[(0,0), (1,0), (1,1), (0,1), (0,0)]])
GO.area(rect) # 1.0
# Spherical area (1/8 of Earth's surface)
octant = GI.Polygon([[(0.0, 0.0), (90.0, 0.0), (0.0, 90.0), (0.0, 0.0)]])
GO.area(GO.Spherical(), octant) # ≈ 6.38e13 m²
# Spherical area with custom radius (unit sphere)
GO.area(GO.Spherical(radius=1.0), octant) # ≈ π/2Result will be of type T, where T is an optional argument with a default value of Float64.
sourceGeometryOps.barycentric_coordinates! Method
barycentric_coordinates!(λs, method, geom, point; normalize = true)Write the barycentric coordinates of point with respect to the vertices of geom into λs, and return λs.
This is the allocation-free form of barycentric_coordinates; λs must have one element per vertex of geom.
GeometryOps.barycentric_coordinates Method
barycentric_coordinates(method, geom, point; normalize = true)Return the barycentric coordinates of point with respect to the vertices of geom, as a newly allocated Vector holding one weight per vertex.
method is an AbstractBarycentricCoordinateMethod, currently always MeanValue. geom may be any GeoInterface-compatible curve (a linear ring or linestring) with at least three points, and point any GeoInterface-compatible point.
If normalize is true (the default) the returned weights are scaled to sum to 1.
See also barycentric_coordinates!, which writes into a preallocated vector, and barycentric_interpolate, which uses these weights to interpolate values.
GeometryOps.barycentric_interpolate Method
barycentric_interpolate(method, geom, values, point)Interpolate values, given one per vertex of geom, to point using barycentric coordinates, and return the interpolated value.
geom may be a curve (linear ring or linestring) or a polygon; for polygons the values are taken around the exterior ring first and then around each hole, in the order GeoInterface.getring returns them. values may be numbers or, for the mean value method, colors.
This is equivalent to weighting values by barycentric_coordinates, but accumulates in place and so allocates less.
GeometryOps.centroid Method
centroid(geom, [T=Float64])::Tuple{T, T}Returns the centroid of a given line segment, linear ring, polygon, or mutlipolygon.
sourceGeometryOps.centroid_and_area Method
centroid_and_area(geom, [T=Float64])::(::Tuple{T, T}, ::Real)Returns the centroid and area of a given geometry.
sourceGeometryOps.centroid_and_length Method
centroid_and_length(geom, [T=Float64])::(::Tuple{T, T}, ::Real)Returns the centroid and length of a given line/ring. Note this is only valid for line strings and linear rings.
sourceGeometryOps.compare_to Method
compare_to(ns::NodeSection, other::NodeSection)Compare node sections by parent geometry, dimension, element id and ring id, and edge vertices. Sections are assumed to be at the same node point. Returns a negative/zero/positive Int (Java compareTo contract).
Port of NodeSection.compareTo.
sourceGeometryOps.contains Method
contains(g1::AbstractGeometry, g2::AbstractGeometry)::BoolReturn true if the second geometry is completely contained by the first geometry. The interiors of both geometries must intersect and the interior and boundary of the secondary (g2) must not intersect the exterior of the first (g1).
contains returns the exact opposite result of within.
Examples
import GeometryOps as GO, GeoInterface as GI
line = GI.LineString([(1, 1), (1, 2), (1, 3), (1, 4)])
point = GI.Point((1, 2))
GO.contains(line, point)
# output
trueGeometryOps.contains Method
contains(g1)Return a function that checks if its input contains g1. This is equivalent to x -> contains(x, g1).
GeometryOps.convex_hull Function
convex_hull([method], geometries)Compute the convex hull of the points in geometries. Returns a GI.Polygon representing the convex hull.
Note that the polygon returned is wound counterclockwise as in the Simple Features standard by default. If you choose GEOS, the winding order will be inverted.
Warning
This interface only computes the 2-dimensional convex hull!
For higher dimensional hulls, use the relevant package (Qhull.jl, Quickhull.jl, or similar).
GeometryOps.count_segment! Method
count_segment!(rcc::RayCrossingCounter, p1, p2)Counts a segment with endpoints p1, p2. Port of RayCrossingCounter.countSegment.
GeometryOps.coverage Method
coverage(geom, xmin, xmax, ymin, ymax, [T = Float64])::TReturns the area of intersection between given geometry and grid cell defined by its minimum and maximum x and y-values. This is computed differently for different geometries:
The signed area of a point is always zero.
The signed area of a curve is always zero.
The signed area of a polygon is calculated by tracing along its edges and switching to the cell edges if needed.
The coverage of a geometry collection, multi-geometry, feature collection of array/iterable is the sum of the coverages of all of the sub-geometries.
Result will be of type T, where T is an optional argument with a default value of Float64.
sourceGeometryOps.coveredby Method
coveredby([manifold::Manifold], g1, g2)::BoolReturn true if the first geometry is completely covered by the second geometry. The interior and boundary of the primary geometry (g1) must not intersect the exterior of the secondary geometry (g2).
Furthermore, coveredby returns the exact opposite result of covers. They are equivalent with the order of the arguments swapped.
Examples
import GeometryOps as GO, GeoInterface as GI
p1 = GI.Point(0.0, 0.0)
p2 = GI.Point(1.0, 1.0)
l1 = GI.Line([p1, p2])
GO.coveredby(p1, l1)
# output
trueGeometryOps.coveredby Method
coveredby(g1)Return a function that checks if its input is covered by g1. This is equivalent to x -> coveredby(x, g1).
GeometryOps.covers Method
covers(g1::AbstractGeometry, g2::AbstractGeometry)::BoolReturn true if the first geometry is completely covers the second geometry, The exterior and boundary of the second geometry must not be outside of the interior and boundary of the first geometry. However, the interiors need not intersect.
covers returns the exact opposite result of coveredby.
Examples
import GeometryOps as GO, GeoInterface as GI
l1 = GI.LineString([(1.0, 1.0), (1.0, 2.0), (1.0, 3.0), (1.0, 4.0)])
l2 = GI.LineString([(1.0, 1.0), (1.0, 2.0)])
GO.covers(l1, l2)
# output
trueGeometryOps.covers Method
covers(g1)Return a function that checks if its input covers g1. This is equivalent to x -> covers(x, g1).
GeometryOps.create_node Method
create_node(m::Manifold, nss::NodeSections; exact)Creates the node topology: prepares the sections, builds a RelateNode at the node and feeds it the sections via add_edges!. Per-polygon section groups are first rewritten into maximal-ring structure by polygon_node_convert (the PolygonNodeConverter.convert port). Returns the assembled node.
Port of NodeSections.createNode. The manifold/exact parameters (absent in Java, where createNode() is nullary) are threaded through for the angle comparisons in the converter and the node's edge wheel. (RelateNode is defined in relate_node.jl, included after this file; the reference resolves at call time.)
GeometryOps.create_node_section Method
create_node_section(ss::RelateSegmentString, seg_index::Integer, node::NodeKey)The NodeSection of this segment string at the node node, known to lie on segment seg_index.
Port of RelateSegmentString.createNodeSection, with the symbolic twist (design D2): the Java method takes the intersection Coordinate; here the node is identified by its NodeKey. For a vertex node (SS_TOUCH or other vertex incidences) the key carries the exact coordinate and the incident vertices are found as in Java. A proper-crossing node (SS_PROPER) lies strictly inside the segment, so the incident vertices are the segment endpoints and the node is never at a vertex.
GeometryOps.crosses Method
crosses(geom1, geom2)::BoolReturn true if the intersection results in a geometry whose dimension is one less than the maximum dimension of the two source geometries and the intersection set is interior to both source geometries.
TODO: broken
Examples
import GeoInterface as GI, GeometryOps as GO
# TODO: Add working exampleGeometryOps.crosses Method
crosses(g1)Return a function that checks if its input crosses g1. This is equivalent to x -> crosses(x, g1).
GeometryOps.crossing_node Method
crossing_node(a0, a1, b0, b1)::NodeKeyNode key of the proper crossing of segments (a0, a1) and (b0, b1). Canonicalize: each segment ordered lexicographically by (x, y); segments ordered lexicographically by their endpoint tuples — so any order/orientation of the same pair produces an identical key.
Only construct crossing keys for properly crossing segments (SS_PROPER from rk_classify_intersection): the exact rational slow path in rk_nodes_coincide divides by the segments' direction cross product, which is nonzero precisely when the crossing is proper.
GeometryOps.cut Method
cut(geom, line, [T::Type])Return given geom cut by given line as a list of geometries of the same type as the input geom. Return the original geometry as only list element if none are found. Line must cut fully through given geometry or the original geometry will be returned.
Note: This currently doesn't work for degenerate cases there line crosses through vertices.
Example
import GeoInterface as GI, GeometryOps as GO
poly = GI.Polygon([[(0.0, 0.0), (10.0, 0.0), (10.0, 10.0), (0.0, 10.0), (0.0, 0.0)]])
line = GI.Line([(5.0, -5.0), (5.0, 15.0)])
cut_polys = GO.cut(poly, line)
GI.coordinates.(cut_polys)
# output
2-element Vector{Vector{Vector{Vector{Float64}}}}:
[[[0.0, 0.0], [5.0, 0.0], [5.0, 10.0], [0.0, 10.0], [0.0, 0.0]]]
[[[5.0, 0.0], [10.0, 0.0], [10.0, 10.0], [5.0, 10.0], [5.0, 0.0]]]GeometryOps.difference Method
difference(geom_a, geom_b, [T::Type]; target::Type, fix_multipoly = UnionIntersectingPolygons())Return the difference between two geometries as a list of geometries. Return an empty list if none are found. The type of the list will be constrained as much as possible given the input geometries. Furthermore, the user can provide a taget type as a keyword argument and a list of target geometries found in the difference will be returned. The user can also provide a float type that they would like the points of returned geometries to be. If the user is taking a intersection involving one or more multipolygons, and the multipolygon might be comprised of polygons that intersect, if fix_multipoly is set to an IntersectingPolygons correction (the default is UnionIntersectingPolygons()), then the needed multipolygons will be fixed to be valid before performing the intersection to ensure a correct answer. Only set fix_multipoly to false if you know that the multipolygons are valid, as it will avoid unneeded computation.
Example
import GeoInterface as GI, GeometryOps as GO
poly1 = GI.Polygon([[[0.0, 0.0], [5.0, 5.0], [10.0, 0.0], [5.0, -5.0], [0.0, 0.0]]])
poly2 = GI.Polygon([[[3.0, 0.0], [8.0, 5.0], [13.0, 0.0], [8.0, -5.0], [3.0, 0.0]]])
diff_poly = GO.difference(poly1, poly2; target = GI.PolygonTrait())
GI.coordinates.(diff_poly)
# output
1-element Vector{Vector{Vector{Vector{Float64}}}}:
[[[6.5, 3.5], [5.0, 5.0], [0.0, 0.0], [5.0, -5.0], [6.5, -3.5], [3.0, 0.0], [6.5, 3.5]]]GeometryOps.disjoint Method
disjoint([manifold::Manifold], geom1, geom2)::BoolReturn true if the first geometry is disjoint from the second geometry.
Return true if the first geometry is disjoint from the second geometry. The interiors and boundaries of both geometries must not intersect.
Examples
import GeometryOps as GO, GeoInterface as GI
line = GI.LineString([(1, 1), (1, 2), (1, 3), (1, 4)])
point = (2, 2)
GO.disjoint(point, line)
# output
trueGeometryOps.disjoint Method
disjoint(g1)Return a function that checks if its input is disjoint from g1. This is equivalent to x -> disjoint(x, g1).
GeometryOps.distance Method
distance(point, geom, ::Type{T} = Float64)::TCalculates the distance from the geometry g1 to the point. The distance will always be positive or zero.
The method will differ based on the type of the geometry provided: - The distance from a point to a point is just the Euclidean distance between the points. - The distance from a point to a line is the minimum distance from the point to the closest point on the given line. - The distance from a point to a linestring is the minimum distance from the point to the closest segment of the linestring. - The distance from a point to a linear ring is the minimum distance from the point to the closest segment of the linear ring. - The distance from a point to a polygon is zero if the point is within the polygon and otherwise is the minimum distance from the point to an edge of the polygon. This includes edges created by holes. - The distance from a point to a multigeometry or a geometry collection is the minimum distance between the point and any of the sub-geometries.
Result will be of type T, where T is an optional argument with a default value of Float64.
sourceGeometryOps.eachedge Method
eachedge(geom, [::Type{T}])
eachedge(m::Manifold, geom, [::Type{T}])Decompose a geometry into a list of edges. Currently only works for LineString and LinearRing.
Returns some iterator, which yields tuples of points. Each tuple is an edge.
It goes (p1, p2), (p2, p3), (p3, p4), ... etc.
On Planar() (the default) points are 2D coordinate tuples. On Spherical() they are UnitSphericalPoints: geographic (longitude, latitude) input is converted, UnitSphericalPoints pass through.
GeometryOps.edge_angle_compare Method
edge_angle_compare(m::Manifold, ns1::NodeSection, ns2::NodeSection; exact)Compares sections by the angle the entering edge (get_vertex(ns, 0)) makes with the positive X axis at the node, angles increasing CCW.
Port of NodeSection.EdgeAngleComparator (a static nested Comparator class in Java, compareAngle(ns1.nodePt, ns1.getVertex(0), ns2.getVertex(0))); here a comparator function over rk_compare_edge_dir with the symbolic node of ns1 as apex, taking the manifold and exact flag the kernel comparison needs. Use as a sort predicate via lt = (a, b) -> edge_angle_compare(m, a, b; exact) < 0.
At a crossing node (a NodeKey with is_crossing) the sections' v0 are normally among the four endpoints of the node's defining segments (the sections are built from those segments themselves), where the comparison is derived from the original endpoints. Sections merged onto the node by the D3 coincidence pass (TopologyComputer) may carry foreign directions; rk_compare_edge_dir then compares around the exact rational apex.
GeometryOps.edge_extents Method
edge_extents(geom, [::Type{T}])Return a vector of the extents of the edges (line segments) of geom.
GeometryOps.embed_extent Method
embed_extent(obj)Recursively wrap the object with a GeoInterface.jl geometry, calculating and adding an Extents.Extent to all objects.
This can improve performance when extents need to be checked multiple times, such when needing to check if many points are in geometries, and using their extents as a quick filter for obviously exterior points.
Keywords
threaded:trueorfalse. Whether to use multithreading. Defaults tofalse.crs: The CRS to attach to geometries. Defaults tonothing.
GeometryOps.enforce Method
enforce(alg::CLibraryPlanarAlgorithm, kw::Symbol, f)Enforce the presence of a keyword argument in a GEOS algorithm, and return alg.params[kw].
Throws an error if the key is not present, and mentions f in the error message (since there isn't a good way to get the name of the function that called this method).
This applies to all CLibraryPlanarAlgorithm types, like GEOS and TG.
GeometryOps.equals Method
equals(trait_a, geom_a, trait_b, geom_b)Two geometries which are not of the same type cannot be equal so they always return false.
sourceGeometryOps.equals Method
equals(geom1, geom2)::BoolCompare two Geometries return true if they are the same geometry.
Examples
import GeometryOps as GO, GeoInterface as GI
poly1 = GI.Polygon([[(0,0), (0,5), (5,5), (5,0), (0,0)]])
poly2 = GI.Polygon([[(0,0), (0,5), (5,5), (5,0), (0,0)]])
GO.equals(poly1, poly2)
# output
trueGeometryOps.equals Method
equals(
::GI.LinearRingTrait, l1,
::GI.LinearRingTrait, l2,
)::BoolTwo linear rings are equal if they share the same set of points going along the curve. Note that rings are closed by definition, so they can have, but don't need, a repeated last point to be equal.
sourceGeometryOps.equals Method
equals(
::GI.LinearRingTrait, l1,
::Union{GI.LineTrait, GI.LineStringTrait}, l2,
)::BoolA linear ring and a line/linestring are equal if they share the same set of points going along the curve. Note that lines aren't closed by definition, but rings are, so the line must have a repeated last point to be equal
sourceGeometryOps.equals Method
equals(::GI.MultiPointTrait, mp1, ::GI.MultiPointTrait, mp2)::BoolTwo multipoints are equal if they share the same set of points.
sourceGeometryOps.equals Method
equals(::GI.MultiPointTrait, mp1, ::GI.PointTrait, p2)::BoolA point and a multipoint are equal if the multipoint is composed of a single point that is equivalent to the given point.
sourceGeometryOps.equals Method
equals(::GI.PolygonTrait, geom_a, ::GI.PolygonTrait, geom_b)::BoolTwo multipolygons are equal if they share the same set of polygons.
sourceGeometryOps.equals Method
equals(::GI.MultiPolygonTrait, geom_a, ::GI.PolygonTrait, geom_b)::BoolA polygon and a multipolygon are equal if the multipolygon is composed of a single polygon that is equivalent to the given polygon.
sourceGeometryOps.equals Method
equals(::GI.PointTrait, p1, ::GI.MultiPointTrait, mp2)::BoolA point and a multipoint are equal if the multipoint is composed of a single point that is equivalent to the given point.
sourceGeometryOps.equals Method
equals(::GI.PointTrait, p1, ::GI.PointTrait, p2)::BoolTwo points are the same if they have the same x and y (and z if 3D) coordinates.
sourceGeometryOps.equals Method
equals(::GI.PolygonTrait, geom_a, ::GI.MultiPolygonTrait, geom_b)::BoolA polygon and a multipolygon are equal if the multipolygon is composed of a single polygon that is equivalent to the given polygon.
sourceGeometryOps.equals Method
equals(::GI.PolygonTrait, geom_a, ::GI.PolygonTrait, geom_b)::BoolTwo polygons are equal if they share the same exterior edge and holes.
sourceGeometryOps.equals Method
equals(
::Union{GI.LineTrait, GI.LineStringTrait}, l1,
::GI.LinearRingTrait, l2,
)::BoolA line/linestring and a linear ring are equal if they share the same set of points going along the curve. Note that lines aren't closed by definition, but rings are, so the line must have a repeated last point to be equal
sourceGeometryOps.equals Method
equals(
::Union{GI.LineTrait, GI.LineStringTrait}, l1,
::Union{GI.LineTrait, GI.LineStringTrait}, l2,
)::BoolTwo lines/linestrings are equal if they share the same set of points going along the curve. Note that lines/linestrings aren't closed by definition.
sourceGeometryOps.equals Method
equals(::T, geom_a, ::T, geom_b)::BoolTwo geometries of the same type, which don't have a equals function to dispatch off of should throw an error.
sourceGeometryOps.extent_to_polygon Method
extent_to_polygon(ext::Extents.Extent)Convert an extent to a polygon.
Examples
import GeometryOps as GO, Extents
ext = Extents.Extent(X=(1.0, 2.0), Y=(1.0, 2.0))
GO.extent_to_polygon(ext)
# output
GeoInterface.Wrappers.Polygon{false, false}([GeoInterface.Wrappers.LinearRing([(1.0, 1.0), … (3) … , (1.0, 1.0)])])GeometryOps.extract_segment_strings Method
extract_segment_strings(rg::RelateGeometry, is_a::Bool, ext_filter)Extract RelateSegmentStrings from the geometry which intersect a given extent (one per line, one per polygon ring). If ext_filter is nothing all edges are extracted.
Warning
nothing here means no filter (Java's prepared-mode null), while get_extent(rg) returns nothing for an empty geometry, where JTS's null Envelope intersects nothing. Never forward an empty geometry's extent as the filter — callers (the engine's computeAtEdges port) must early-return on empty inputs before extraction.
GeometryOps.finish! Method
finish!(n::RelateNode, is_area_interior_a::Bool, is_area_interior_b::Bool)Computes the final topology for the edges around this node. Although nodes lie on the boundary of areas or the interior of lines, in a mixed GC they may also lie in the interior of an area. This changes the locations of the sides and line to Interior.
Port of RelateNode.finish.
sourceGeometryOps.flip Method
flip(obj)Swap all of the x and y coordinates in obj, otherwise keeping the original structure (but not necessarily the original type).
Keywords
threaded:trueorfalse. Whether to use multithreading. Defaults tofalse.crs: The CRS to attach to geometries. Defaults tonothing.calc_extent:trueorfalse. Whether to calculate the extent. Defaults tofalse.
GeometryOps.forcexy Method
forcexy(geom)Force the geometry to be 2D. Works on any geometry, vector of geometries, feature collection, or table!
sourceGeometryOps.forcexyz Function
forcexyz(geom, z = 0)Force the geometry to be 3D. Works on any geometry, vector of geometries, feature collection, or table!
The z parameter is the default z value - if a point has no z value, it will be set to this value. If it does, then the z value will be kept.
GeometryOps.foreach_pair_of_maybe_intersecting_edges_in_order Method
foreach_pair_of_maybe_intersecting_edges_in_order(
manifold::M, accelerator::A,
f_on_each_a::FA,
f_after_each_a::FAAfter,
f_on_each_maybe_intersect::FI,
geom_a,
geom_b,
::Type{T} = Float64
) where {FA, FAAfter, FI, T, M <: Manifold, A <: IntersectionAccelerator}Decompose geom_a and geom_b into edge lists (unsorted), and then, logically, perform the following iteration:
for (a_edge, i) in enumerate(eachedge(geom_a))
f_on_each_a(a_edge, i)
for (b_edge, j) in enumerate(eachedge(geom_b))
if may_intersect(a_edge, b_edge)
f_on_each_maybe_intersect(a_edge, b_edge)
end
end
f_after_each_a(a_edge, i)
endThis may not be the exact acceleration that is performed - but it is the logical sequence of events. It also uses the accelerator, and can automatically choose the best one based on an internal heuristic if you pass in an AutoAccelerator.
For example, the SingleSTRtree accelerator is used along with extent thinning to avoid unnecessary edge intersection checks in the inner loop.
GeometryOps.get_dimension_real Method
get_dimension_real(rg::RelateGeometry)Gets the actual non-empty dimension of the geometry. Zero-length LineStrings are treated as Points.
sourceGeometryOps.get_location Method
get_location(label, index, position, is_forward) -> Int8The location of position (POS_LEFT / POS_RIGHT / POS_ON) of input index, for a containing half-edge whose orientation is is_forward. When the edge is the reverse half-edge (is_forward == false) the Left/Right stored sides are swapped, so a single label serves both members of a symmetric pair (port of JTS OverlayLabel.getLocation(index, position, isForward)).
GeometryOps.get_polygonal Method
get_polygonal(nss::NodeSections, is_a::Bool)The parent polygonal geometry of the first section of input geometry is_a that has one, or nothing.
Port of NodeSections.getPolygonal(boolean isA).
sourceGeometryOps.get_polygonal Method
get_polygonal(ns::NodeSection)Gets the polygon this section is part of. Will be nothing if section is not on a polygon boundary.
Port of NodeSection.getPolygonal.
sourceGeometryOps.intersection Method
intersection(geom_a, geom_b, [T::Type]; target::Type, fix_multipoly = UnionIntersectingPolygons())Return the intersection between two geometries as a list of geometries. Return an empty list if none are found. The type of the list will be constrained as much as possible given the input geometries. Furthermore, the user can provide a target type as a keyword argument and a list of target geometries found in the intersection will be returned. The user can also provide a float type that they would like the points of returned geometries to be. If the user is taking a intersection involving one or more multipolygons, and the multipolygon might be comprised of polygons that intersect, if fix_multipoly is set to an IntersectingPolygons correction (the default is UnionIntersectingPolygons()), then the needed multipolygons will be fixed to be valid before performing the intersection to ensure a correct answer. Only set fix_multipoly to nothing if you know that the multipolygons are valid, as it will avoid unneeded computation.
Example
import GeoInterface as GI, GeometryOps as GO
line1 = GI.Line([(124.584961,-12.768946), (126.738281,-17.224758)])
line2 = GI.Line([(123.354492,-15.961329), (127.22168,-14.008696)])
inter_points = GO.intersection(line1, line2; target = GI.PointTrait())
GI.coordinates.(inter_points)
# output
1-element Vector{Vector{Float64}}:
[125.58375366067548, -14.83572303404496]GeometryOps.intersection_area Function
intersection_area(alg::Algorithm, geom_a, geom_b, [T = Float64]; kwargs...)Area of the intersection of geom_a and geom_b, computed without constructing the intersection geometry.
Equivalent to area(manifold(alg), intersection(alg, geom_a, geom_b)), but the result polygon is never built. On Spherical() the result is in square units of the manifold radius.
The algorithm is required, and the supported list is closed:
| algorithm | manifolds | notes |
|---|---|---|
ConvexConvexSutherlandHodgman | Planar(), Spherical() | takes the same cache keyword as intersection; with one, the call allocates nothing |
OverlayNG | Planar(), Spherical() | T sets the accumulator and return type only — the arrangement always runs at the algorithm's point_type |
FosterHormannClipping | as intersection | accumulates during the trace for hole-free polygon pairs; other inputs delegate to the polygon path |
Example
import GeometryOps as GO, GeoInterface as GI
a = GI.Polygon([[(0.0, 0.0), (2.0, 0.0), (2.0, 2.0), (0.0, 2.0), (0.0, 0.0)]])
b = GI.Polygon([[(1.0, 1.0), (3.0, 1.0), (3.0, 3.0), (1.0, 3.0), (1.0, 1.0)]])
GO.intersection_area(GO.OverlayNG(), a, b) # 1.0
alg = GO.ConvexConvexSutherlandHodgman()
cache = GO.SutherlandHodgmanCache(alg)
GO.intersection_area(alg, a, b; cache) # 1.0, allocation-freeGeometryOps.intersection_points Method
intersection_points(geom_a, geom_b, [T::Type])Return a list of intersection tuple points between two geometries. If no intersection points exist, returns an empty list.
Example
line1 = GI.Line([(124.584961,-12.768946), (126.738281,-17.224758)]) line2 = GI.Line([(123.354492,-15.961329), (127.22168,-14.008696)]) inter_points = GO.intersection_points(line1, line2)
**output**
1-element Vector{Tuple{Float64, Float64}}: (125.58375366067548, -14.83572303404496)
<Badge type="info" class="source-link" text="source"><a href="https://github.com/JuliaGeo/GeometryOps.jl/blob/a2a8b8283322586a2cb4913a213b973e9108e257/src/methods/clipping/intersection.jl#L202-L220" target="_blank" rel="noreferrer">source</a></Badge>
</details>
<details class='jldocstring custom-block' open>
<summary><a id='GeometryOps.intersects-Tuple{Any, Any}' href='#GeometryOps.intersects-Tuple{Any, Any}'><span class="jlbinding">GeometryOps.intersects</span></a> <Badge type="info" class="jlObjectType jlMethod" text="Method" /></summary>
```julia
intersects(geom1, geom2)::BoolReturn true if the interiors or boundaries of the two geometries interact.
intersects returns the exact opposite result of disjoint.
Example
import GeoInterface as GI, GeometryOps as GO
line1 = GI.Line([(124.584961,-12.768946), (126.738281,-17.224758)])
line2 = GI.Line([(123.354492,-15.961329), (127.22168,-14.008696)])
GO.intersects(line1, line2)
# output
trueGeometryOps.intersects Method
intersects(g1)Return a function that checks if its input intersects g1. This is equivalent to x -> intersects(x, g1).
GeometryOps.is_containing_segment Method
is_containing_segment(ss::RelateSegmentString, seg_index::Integer, pt)Tests if a segment intersection point has that segment as its canonical containing segment. Segments are half-closed, and contain their start point but not the endpoint, except for the final segment in a non-closed segment string, which contains its endpoint as well. This test ensures that vertices are assigned to a unique segment in a segment string. In particular, this avoids double-counting intersections which lie exactly at segment endpoints.
sourceGeometryOps.is_polygonal Method
is_polygonal(rg::RelateGeometry)Tests whether the geometry has polygonal topology. This is not the case if it is a GeometryCollection containing more than one polygon (since they may overlap or be adjacent). The significance is that polygonal topology allows more assumptions about the location of boundary vertices.
sourceGeometryOps.is_self_noding_required Method
is_self_noding_required(rg::RelateGeometry)Indicates whether the geometry requires self-noding for correct evaluation of specific spatial predicates. Self-noding is required for geometries which may self-cross — i.e. lines, and overlapping elements in GeometryCollections. Self-noding is not required for polygonal geometries, since they can only touch at vertices.
sourceGeometryOps.is_self_noding_required Method
is_self_noding_required(tc::TopologyComputer)Indicates whether the input geometries require self-noding for correct evaluation of specific spatial predicates. Self-noding is required for geometries which may have self-crossing linework, or may have lines lying in the boundary of an area. This ensures that node locations match in situations where a self-crossing and mutual crossing occur at the same logical location (here via the D3 coincidence-merge pass, since node identities are symbolic).
Currently self-noding is required for:
A geoms which require self-noding (lines or GCs, except for single-polygon GCs)
B geoms which are mixed A/L GCs
Port of TopologyComputer.isSelfNodingRequired.
sourceGeometryOps.isclockwise Method
isclockwise(line::Union{LineString, Vector{Position}})::BoolTake a ring and return true if the line goes clockwise, or false if the line goes counter-clockwise. "Going clockwise" means, mathematically,
Example
julia> import GeoInterface as GI, GeometryOps as GO
julia> ring = GI.LinearRing([(0, 0), (1, 1), (1, 0), (0, 0)]);
julia> GO.isclockwise(ring)
# output
trueGeometryOps.isconcave Method
isconcave(poly::Polygon)::BoolTake a polygon and return true or false as to whether it is concave or not.
Examples
import GeoInterface as GI, GeometryOps as GO
poly = GI.Polygon([[(0, 0), (0, 1), (1, 1), (1, 0), (0, 0)]])
GO.isconcave(poly)
# output
falseGeometryOps.lazy_edge_extents Method
lazy_edge_extents(geom)Return an iterator over the extents of the edges (line segments) of geom. This is lazy but nonallocating.
GeometryOps.lazy_edgelist Method
lazy_edgelist(geom, [::Type{T}])
lazy_edgelist(m::Manifold, geom, [::Type{T}])Return an iterator over GI.Line objects with attached extents.
GeometryOps.locate Method
locate(ael::AdjacentEdgeLocator, p)Location (LOC_INTERIOR or LOC_BOUNDARY) of point p, which must lie on at least one polygon edge of the locator's geometry, under union semantics.
GeometryOps.locate Method
locate(loc::IndexedPointInAreaLocator, p)The location (LOC_* code) of point p in the locator's areal geometry. Port of IndexedPointInAreaLocator.locate.
GeometryOps.locate Method
locate(loc::RelatePointLocator, p)The location (LOC_* code) of point p relative to the locator's geometry, under GC union semantics.
GeometryOps.locate_area_vertex Method
locate_area_vertex(rg::RelateGeometry, pt)Locates a vertex of a polygon. A vertex of a Polygon or MultiPolygon is on the boundary; but a vertex of an overlapped polygon in a GeometryCollection may be in the interior.
sourceGeometryOps.locate_line_end_with_dim Method
locate_line_end_with_dim(loc::RelatePointLocator, p)Locates a line endpoint, as a DL_* dimension-location code. In a mixed-dim GC, the line end point may also lie in an area. In this case the area location is reported. Otherwise, the dimloc is either DL_LINE_BOUNDARY or DL_LINE_INTERIOR, depending on the endpoint valence and the BoundaryNodeRule in place.
GeometryOps.locate_node Method
locate_node(loc::RelatePointLocator, p, parent_polygonal)The location (LOC_* code) of a point p which is known to be a node of the geometry (i.e. a vertex or on an edge). parent_polygonal is the polygonal element the point is a node of (or nothing).
GeometryOps.locate_node_with_dim Method
locate_node_with_dim(loc::RelatePointLocator, p, parent_polygonal)The dimension-location (DL_* code) of a point p which is known to be a node of the geometry.
GeometryOps.locate_with_dim Method
locate_with_dim(loc::RelatePointLocator, p)Computes the topological location (DL_* dimension-location code) of a single point in a geometry, including the dimension of the geometry element the point is located in (if not in the exterior). It handles both single-element and multi-element geometries. The algorithm for multi-part geometries takes into account the SFS Boundary Determination Rule.
GeometryOps.matches_entry Method
Match a single matrix entry against a pattern code (JTS IntersectionMatrix.matches).
GeometryOps.minimum_bounding_circle Function
minimum_bounding_circle([algorithm], geometry)Compute the minimum bounding circle of geometry.
Returns a circle geometry containing all points of the input. For planar geometries, returns a PlanarCircle; for spherical geometries, returns a SphericalCap.
Return type subject to change
The concrete return type (currently PlanarCircle for planar manifold) may change in future versions without a breaking release. However, the return type will always implement GeoInterface, so code using GeoInterface methods (e.g., GI.getexterior, GI.getpoint) will remain compatible.
Arguments
algorithm: The algorithm to use. Defaults toWelzl()which uses Welzl's expected O(n) algorithm.geometry: Any geometry compatible with GeoInterface, or a vector of point-like objects.
Example
import GeometryOps as GO, GeoInterface as GI
# From points
points = [(0.0, 0.0), (1.0, 0.0), (0.0, 1.0), (1.0, 1.0)]
circle = GO.minimum_bounding_circle(points)
# From any geometry
polygon = GI.Polygon([[(0, 0), (1, 0), (1, 1), (0, 1), (0, 0)]])
circle = GO.minimum_bounding_circle(polygon)
# Access via GeoInterface for forward compatibility
ring = GI.getexterior(circle)GeometryOps.node_point Method
node_point(arr::NodedArrangement{P,T}, id) -> TThe realized output coordinate of node id, in the arrangement's output point type T (see output_point_type), memoized in the node table (design §2.6). The only place a constructed coordinate enters the substrate.
GeometryOps.overlaps Method
overlaps(geom1, geom2)::BoolCompare two Geometries of the same dimension and return true if their intersection set results in a geometry different from both but of the same dimension. This means one geometry cannot be within or contain the other and they cannot be equal
Examples
import GeometryOps as GO, GeoInterface as GI
poly1 = GI.Polygon([[(0,0), (0,5), (5,5), (5,0), (0,0)]])
poly2 = GI.Polygon([[(1,1), (1,6), (6,6), (6,1), (1,1)]])
GO.overlaps(poly1, poly2)
# output
trueGeometryOps.overlaps Method
overlaps(g1)Return a function that checks if its input overlaps g1. This is equivalent to x -> overlaps(x, g1).
GeometryOps.polygon_node_convert Method
polygon_node_convert(m::Manifold, poly_sections::Vector{<:NodeSection}; exact)Converts the node sections at a polygon node where a shell and one or more holes touch, or two or more holes touch. This converts the node topological structure from the OGC "touching-rings" (AKA "minimal-ring") model to the equivalent "self-touch" (AKA "inverted/exverted ring" or "maximal ring") model. In the "self-touch" model the converted NodeSection corners enclose areas which all lie inside the polygon (i.e. they do not enclose hole edges). This allows RelateNode (Task 17) to use simple area-additive semantics for adding edges and propagating edge locations.
The input node sections are assumed to have canonical orientation (CW shells and CCW holes). The arrangement of shells and holes must be topologically valid. Specifically, the node sections must not cross or be collinear.
This supports multiple shell-shell touches (including ones containing holes), and hole-hole touches. This generalizes the relate algorithm to support both the OGC model and the self-touch model.
Converts a list of sections of valid polygon rings to have "self-touching" structure. There are the same number of output sections as input ones. Sorts (and thereby mutates) poly_sections; returns the converted sections.
Port of PolygonNodeConverter.convert. The angle sort goes through edge_angle_compare (the NodeSection.EdgeAngleComparator port), which is why the manifold and the exact flag are threaded in (the Java method is geometry-context-free).
GeometryOps.polygon_to_line Method
polygon_to_line(poly::Polygon)Converts a Polygon to LineString or MultiLineString
Examples
import GeometryOps as GO, GeoInterface as GI
poly = GI.Polygon([[(-2.275543, 53.464547), (-2.275543, 53.489271), (-2.215118, 53.489271), (-2.215118, 53.464547), (-2.275543, 53.464547)]])
GO.polygon_to_line(poly)
# output
GeoInterface.Wrappers.LineString{false, false}([(-2.275543, 53.464547), … (3) … , (-2.275543, 53.464547)])GeometryOps.polygonize Method
polygonize(A::AbstractMatrix{Bool}; kw...)
polygonize(f, A::AbstractMatrix; kw...)
polygonize(xs, ys, A::AbstractMatrix{Bool}; kw...)
polygonize(f, xs, ys, A::AbstractMatrix; kw...)Polygonize an AbstractMatrix of values, currently to a single class of polygons.
Returns a MultiPolygon for Bool values and f return values, and a FeatureCollection of Features holding MultiPolygon for all other values.
Function f should return either true or false or a transformation of values into simpler groups, especially useful for floating point arrays.
If xs and ys are ranges, they are used as the pixel/cell center points. If they are Vector of Tuple they are used as the lower and upper bounds of each pixel/cell.
Keywords
minpoints: ignore polygons with less thanminpointspoints.values: the values to turn into polygons. By default these areunion(A), If functionfis passed these refer to the return values off, by defaultunion(map(f, A). If valuesBool, false is ignored and a singleMultiPolygonis returned rather than aFeatureCollection.
Example
using GeometryOps
A = rand(100, 100)
multipolygon = polygonize(>(0.5), A);GeometryOps.prepare Method
prepare(alg::RelateNG, a; validate = <manifold-dependent>)::PreparedRelateThis functionality is experimental and may change at any time.
prepare is the generic entry point for prepared-geometry optimizations in GeometryOps; RelateNG is currently the only algorithm implementing it.
Creates a prepared relate instance to optimize the repeated evaluation of relationships against the single geometry a.
Port of RelateNG.prepare(Geometry) (the algorithm's boundary_rule plays the role of the prepare(Geometry, BoundaryNodeRule) overload). The A-side RelateGeometry is constructed with is_prepared = true, and the lazy caches that the Java instance accumulates across evaluations are forced eagerly:
the
RelatePointLocator— the prepared flag selects the per-polygonal-elementIndexedPointInAreaLocatorcaches, as in Java (seepoint_locator.jl/indexed_point_in_area.jl);the unique-point set, when
ahas effective dimension P (the only case the P/P fast path consults it);the A segment strings, extracted ONCE without an interaction-envelope filter (Java's prepared-mode
envExtract = nullincomputeEdgesMutual) so the cache serves any future B, plus the prebuilt segment tree over them.
Note
Predicates whose evaluation requires self-noding (is_self_noding_required) bypass the cached edges entirely: as in the Java prepared branch, computeEdgesAll re-extracts the A edges per evaluation, filtered by the A/B interaction envelope.
Validation
validate controls a ring self-crossing check over the prepared geometry: a self-join of the segment set (reusing the prepared edge index) that detects PROPER crossings — transversal, interior to both edges — between non-adjacent edges of the same polygonal element. Shared-endpoint adjacency is excluded and vertex touches are NOT flagged: the scope is exactly the crossing class that breaks the engine's containment parity, not full OGC validity. On the first crossing found an ArgumentError is thrown naming the ring and the edge pair ("edge i crosses edge j"); the documented remedy is the CrossingEdgeSplit correction.
The default is manifold-dependent, and deliberately so:
Spherical→validate = true. A planar-valid ring whose edges cross when reinterpreted as great-circle arcs is undetectable by standard planar tooling (planar validity checks pass it), and undetected it can invert containment globally — the figure-eight's lobes cancel the curvature the interior bootstrap reads, so every query lands on the wrong side (Natural Earth 110m Sudan is a real instance). The check is a small fraction of the ~100 ms spherical prepare build.Planar→validate = false. Planar invalidity of the same class is visible to ordinary planar tools, and the planar engine degrades gracefully under even-odd ray-crossing parity instead of inverting; JTS/GEOS prepared geometries do not validate either. A planar prepare costs ~600 µs, which a validation join would dominate, destroying the build-cost amortization. Passvalidate = trueto opt in.
GeometryOps.prepare Method
prepare(alg::RelateNG, p::PreparedRelate; validate = <manifold-dependent>)::PreparedRelateThis functionality is experimental and may change at any time.
Returns p under the algorithm it was prepared with, so a PreparedRelate can be passed as the A geometry to any RelateNG entry point. Under any other alg — every setting is baked into the prepared structures — it is prepared afresh from p.input, at the full cost of prepare on every call, so prepare under the algorithm you query with. validate applies only to that rebuild.
GeometryOps.prepare_sections! Method
prepare_sections!(nss::NodeSections)Sorts the sections (by compare_to, the Java natural ordering) so that:
lines are before areas
edges from the same polygon are contiguous
Port of NodeSections.prepareSections.
sourceGeometryOps.process_edge_intersections! Method
process_edge_intersections!(computer, ssa_list, ssb_list,
accelerator = AutoAccelerator(); m, exact)Enumerate all extent-interacting segment pairs between the A-side segment strings ssa_list and the B-side segment strings ssb_list, feeding each pair through process_intersections! so the intersections are recorded on computer.
accelerator selects the enumeration strategy:
NestedLoop: a plain double loop over string pairs and segment pairs, with a per-pair segment-extent disjointness skip (onPlanar).Any tree-backed accelerator (canonically
DoubleNaturalTree): a spatial index (_relate_edge_index, a natural-orderRTreecarrying segment owners as data) is built over the per-segment extents of each side and traversed withSpatialTreeInterface.dual_depth_first_searchunder theExtents.intersectspredicate.AutoAccelerator: picksNestedLoopbelow the clipping size threshold (GEOMETRYOPS_NO_OPTIMIZE_EDGEINTERSECT_NUMVERTS) and on manifolds without a segment-extent kernel (neitherPlanarnorSpherical), and the tree path otherwise.
After each processed pair is_result_known(computer) is consulted and the enumeration stops early once the predicate value is determined (the port of the Java noder's isDone() hook used by EdgeSetIntersector.process).
Warning
This enumerates A×B pairs only. JTS's EdgeSetIntersector also feeds A×A and B×B pairs (self-noding) with an id-ordering guard so each unordered pair is processed once. Calling this with the same list on both sides would process every pair twice — the engine's computeAtEdges port uses process_self_intersections! for the self-pair path instead.
GeometryOps.process_intersections! Method
process_intersections!(tc::TopologyComputer, ss0, seg_index0, ss1, seg_index1; m, exact)Classify the intersection of segment seg_index0 of ss0 with segment seg_index1 of ss1 and record any intersections in tc. The strings are ordered so the A geometry's string is processed first (the computer's A/B matrix updates rely on it).
Port of EdgeSegmentIntersector.processIntersections.
sourceGeometryOps.process_self_intersections! Method
process_self_intersections!(computer, ss_list,
accelerator = AutoAccelerator(); m, exact)Enumerate all extent-interacting segment pairs within the segment-string list ss_list (each unordered pair once, never a segment against itself), feeding each through process_intersections! — the self-noding (A×A or B×B) counterpart of process_edge_intersections!.
After each processed pair is_result_known(computer) is consulted and the enumeration stops early once the predicate value is determined.
GeometryOps.radius Method
radius(circle::PlanarCircle)Return the radius of the circle. Computes sqrt(radius_squared).
GeometryOps.relate Method
relate(p::PreparedRelate, b, im_pattern::AbstractString)::BoolThis functionality is experimental and may change at any time.
Tests whether the topological relationship of the prepared geometry to b matches the DE-9IM pattern. Port of RelateNG.evaluate(Geometry, String).
GeometryOps.relate Method
relate(p::PreparedRelate, b)::DE9IMThis functionality is experimental and may change at any time.
Computes the DE-9IM matrix for the topological relationship of the prepared geometry to b. Port of the instance method RelateNG.evaluate(Geometry).
GeometryOps.relate Method
relate([alg::RelateNG], a, b, im_pattern::AbstractString)::BoolThis functionality is experimental and may change at any time.
Tests whether the topological relationship between geometries a and b matches the DE-9IM matrix pattern im_pattern (9 characters over 012TF*).
Port of RelateNG.relate(Geometry, Geometry, String).
GeometryOps.relate Method
relate([alg::RelateNG], a, b)::DE9IMThis functionality is experimental and may change at any time.
Computes the DE9IM matrix for the topological relationship between geometries a and b.
Port of RelateNG.relate(Geometry, Geometry).
GeometryOps.relate_predicate Method
relate_predicate(p::PreparedRelate, predicate::TopologyPredicate, b)::BoolThis functionality is experimental and may change at any time.
Tests whether the topological relationship of the prepared geometry to b satisfies the predicate. Port of the instance method RelateNG.evaluate(Geometry, TopologyPredicate) in prepared mode.
GeometryOps.relate_predicate Method
relate_predicate(alg::RelateNG, predicate::TopologyPredicate, a, b)::BoolThis functionality is experimental and may change at any time.
Tests whether the topological relationship between geometries a and b satisfies the given TopologyPredicate. This is the core evaluation entry point (the port of RelateNG.evaluate(Geometry, TopologyPredicate), via the static RelateNG.relate(a, b, pred)).
Note
Predicates are mutable accumulators — pass a freshly constructed one (e.g. pred_intersects()) per evaluation.
GeometryOps.rk_crossing_dirs_ccw Method
CCW cyclic order of the four half-edge directions incident to the proper crossing of (a0,a1) × (b0,b1), starting from a1. Since the crossing is proper, b0/b1 are strictly on opposite sides of line(a0,a1): if b1 is to the left, CCW order is (a1, b1, a0, b0), else (a1, b0, a0, b1).
sourceGeometryOps.segmentize Method
segmentize([method = Planar()], geom; max_distance::Real, threaded)Segmentize a geometry by adding extra vertices to the geometry so that no segment is longer than a given distance. This is useful for plotting geometries with a limited number of vertices, or for ensuring that a geometry is not too "coarse" for a given application.
Arguments
method::Manifold = Planar(): The method to use for segmentizing the geometry. At the moment,Planar(assumes a flat plane),Spherical(assumes geometry on a sphere and interpolates along great circles) andGeodesic(assumes geometry on the ellipsoidal Earth and uses Vincenty's formulae) are available.geom: The geometry to segmentize. Must be aLineString,LinearRing,Polygon,MultiPolygon, orGeometryCollection, or some vector or table of those.max_distance::Real: The maximum distance between vertices in the geometry. ForPlanarmanifolds, this is in the units of the geometry. ForSphericalandGeodesic, this is in the units of the manifold (the units of the radius of the sphere/ellipsoid). By default this is meters.
Spherical and Geodesic both assume that the input geometry is in lon/lat coordinates, in degrees.
Returns a geometry of similar type to the input geometry, but resampled.
sourceGeometryOps.signed_area Method
signed_area(geom, [T = Float64])::TReturns the signed area of a single geometry, based on winding order. This is computed slightly differently for different geometries:
- The signed area of a point is always zero.
- The signed area of a curve is always zero.
- The signed area of a polygon is computed with the shoelace formula and is
positive if the polygon coordinates wind clockwise and negative if
counterclockwise.
- You cannot compute the signed area of a multipolygon as it doesn't have a
meaning as each sub-polygon could have a different winding order.Result will be of type T, where T is an optional argument with a default value of Float64.
sourceGeometryOps.signed_distance Method
signed_distance(point, geom, ::Type{T} = Float64)::TCalculates the signed distance from the geometry geom to the given point. Points within geom have a negative signed distance, and points outside of geom have a positive signed distance. - The signed distance from a point to a point, line, linestring, or linear ring is equal to the distance between the two. - The signed distance from a point to a polygon is negative if the point is within the polygon and is positive otherwise. The value of the distance is the minimum distance from the point to an edge of the polygon. This includes edges created by holes. - The signed distance from a point to a multigeometry or a geometry collection is the minimum signed distance between the point and any of the sub-geometries.
Result will be of type T, where T is an optional argument with a default value of Float64.
sourceGeometryOps.simplify Method
simplify(obj; kw...)
simplify(::SimplifyAlg, obj; kw...)Simplify a geometry, feature, feature collection, or nested vectors or a table of these.
RadialDistance, DouglasPeucker, or VisvalingamWhyatt algorithms are available, listed in order of increasing quality but decreasing performance.
PoinTrait and MultiPointTrait are returned unchanged.
The default behaviour is simplify(DouglasPeucker(; kw...), obj). Pass in other SimplifyAlg to use other algorithms.
Keywords
prefilter_alg:SimplifyAlgalgorithm used to pre-filter object before using primary filtering algorithm.threaded:trueorfalse. Whether to use multithreading. Defaults tofalse.crs: The CRS to attach to geometries. Defaults tonothing.calc_extent:trueorfalse. Whether to calculate the extent. Defaults tofalse.
Keywords for DouglasPeucker are allowed when no algorithm is specified:
Keywords
ratio: the fraction of points that should remain aftersimplify. Useful as it will generalise for large collections of objects.number: the number of points that should remain aftersimplify. Less useful for large collections of mixed size objects.tol: the minimum distance a point will be from the line joining its neighboring points.
Example
Simplify a polygon to have six points:
import GeoInterface as GI
import GeometryOps as GO
poly = GI.Polygon([[
[-70.603637, -33.399918],
[-70.614624, -33.395332],
[-70.639343, -33.392466],
[-70.659942, -33.394759],
[-70.683975, -33.404504],
[-70.697021, -33.419406],
[-70.701141, -33.434306],
[-70.700454, -33.446339],
[-70.694274, -33.458369],
[-70.682601, -33.465816],
[-70.668869, -33.472117],
[-70.646209, -33.473835],
[-70.624923, -33.472117],
[-70.609817, -33.468107],
[-70.595397, -33.458369],
[-70.587158, -33.442901],
[-70.587158, -33.426283],
[-70.590591, -33.414248],
[-70.594711, -33.406224],
[-70.603637, -33.399918]]])
simple = GO.simplify(poly; number=6)
GI.npoint(simple)
# output
6GeometryOps.smooth Method
smooth(alg::Algorithm, geom)
smooth(geom; kw...)Smooths a geometry using the provided algorithm.
The default algorithm is Chaikin(), which can be used on the spherical or planar manifolds.
GeometryOps.symdifference Method
symdifference([alg::OverlayNG], geom_a, geom_b)
symdifference(manifold::Manifold, geom_a, geom_b)This functionality is experimental and may change at any time.
The symmetric difference of two geometries: everything that lies in exactly one of them, i.e. union(difference(a, b), difference(b, a)).
import GeoInterface as GI, GeometryOps as GO
a = GI.Polygon([[(0.0, 0.0), (2.0, 0.0), (2.0, 2.0), (0.0, 2.0), (0.0, 0.0)]])
b = GI.Polygon([[(1.0, 0.0), (3.0, 0.0), (3.0, 2.0), (1.0, 2.0), (1.0, 0.0)]])
GO.area(GO.symdifference(a, b))
# output
4.0Engine
symdifference is OverlayNG-only: there is no Foster–Hormann symmetric difference, and none is planned — symmetric difference is the operation the exact arrangement gets for free and the ent/exit tracer does not.
That makes it the one member of the overlay family whose algorithm-free form does not run Foster–Hormann: symdifference(a, b) is symdifference(OverlayNG(Planar()), a, b), and symdifference(m, a, b) is symdifference(OverlayNG(m), a, b). The defaults of intersection, union and difference are unaffected.
See OverlayNG for the result shape, the input contract, and the spherical full-sphere limitation.
GeometryOps.t_value Method
t_value(sᵢ, sᵢ₊₁, rᵢ, rᵢ₊₁)Returns the "T-value" as described in Hormann's presentation [2] on how to calculate the mean-value coordinate.
Here, sᵢ is the vector from vertex vᵢ to the point, and rᵢ is the norm (length) of sᵢ. s must be Point and r must be real numbers.
<Badge type="info" class="source-link" text="source"><a href="https://github.com/JuliaGeo/GeometryOps.jl/blob/a2a8b8283322586a2cb4913a213b973e9108e257/src/methods/barycentric.jl#L211-L227" target="_blank" rel="noreferrer">source</a></Badge>
</details>
<details class='jldocstring custom-block' open>
<summary><a id='GeometryOps.to_edgelist-Union{Tuple{Any}, Tuple{T}, Tuple{Any, Type{T}}} where T' href='#GeometryOps.to_edgelist-Union{Tuple{Any}, Tuple{T}, Tuple{Any, Type{T}}} where T'><span class="jlbinding">GeometryOps.to_edgelist</span></a> <Badge type="info" class="jlObjectType jlMethod" text="Method" /></summary>
```julia
to_edgelist(geom, [::Type{T}])
to_edgelist(m::Manifold, geom, [::Type{T}])Convert a geometry into a vector of GI.Line objects with attached extents.
On Spherical() — or whenever the geometry's points are already UnitSphericalPoints — each edge carries the 3D UnitSpherical.spherical_arc_extent of its great-circle arc.
GeometryOps.to_edgelist Method
to_edgelist(ext::E, geom, [::Type{T}])::(::Vector{GI.Line}, ::Vector{Int})Filter the edges of geom for those that intersect ext, and return:
a vector of
GI.Lineobjects with attached extents,a vector of indices into the original geometry.
GeometryOps.to_edges Method
to_edges()Convert any geometry or collection of geometries into a flat vector of Tuple{Tuple{Float64,Float64},Tuple{Float64,Float64}} edges.
GeometryOps.touches Method
touches([manifold::Manifold], geom1, geom2)::BoolReturn true if the first geometry touches the second geometry. In other words, the two interiors cannot interact, but one of the geometries must have a boundary point that interacts with either the other geometry's interior or boundary.
Examples
import GeometryOps as GO, GeoInterface as GI
l1 = GI.Line([(0.0, 0.0), (1.0, 0.0)])
l2 = GI.Line([(1.0, 1.0), (1.0, -1.0)])
GO.touches(l1, l2)
# output
trueGeometryOps.touches Method
touches(g1)Return a function that checks if its input touches g1. This is equivalent to x -> touches(x, g1).
GeometryOps.transform Method
transform(f, obj)Apply a function f to all the points in obj.
Points will be passed to f as an SVector to allow using CoordinateTransformations.jl and Rotations.jl without hassle.
SVector is also a valid GeoInterface.jl point, so will work in all GeoInterface.jl methods.
Example
julia> import GeoInterface as GI
julia> import GeometryOps as GO
julia> geom = GI.Polygon([GI.LinearRing([(1, 2), (3, 4), (5, 6), (1, 2)]), GI.LinearRing([(3, 4), (5, 6), (6, 7), (3, 4)])]);
julia> f = CoordinateTransformations.Translation(3.5, 1.5)
Translation(3.5, 1.5)
julia> GO.transform(f, geom)
GeoInterface.Wrappers.Polygon{false, false, Vector{GeoInterface.Wrappers.LinearRing{false, false, Vector{StaticArraysCore.SVector{2, Float64}}, Nothing, Nothing}}, Nothing, Nothing}(GeoInterface.Wrappers.Linea
rRing{false, false, Vector{StaticArraysCore.SVector{2, Float64}}, Nothing, Nothing}[GeoInterface.Wrappers.LinearRing{false, false, Vector{StaticArraysCore.SVector{2, Float64}}, Nothing, Nothing}(StaticArraysCo
re.SVector{2, Float64}[[4.5, 3.5], [6.5, 5.5], [8.5, 7.5], [4.5, 3.5]], nothing, nothing), GeoInterface.Wrappers.LinearRing{false, false, Vector{StaticArraysCore.SVector{2, Float64}}, Nothing, Nothing}(StaticA
rraysCore.SVector{2, Float64}[[6.5, 5.5], [8.5, 7.5], [9.5, 8.5], [6.5, 5.5]], nothing, nothing)], nothing, nothing)With Rotations.jl you need to actually multiply the Rotation by the SVector point, which is easy using an anonymous function.
julia> using Rotations
julia> GO.transform(p -> one(RotMatrix{2}) * p, geom)
GeoInterface.Wrappers.Polygon{false, false, Vector{GeoInterface.Wrappers.LinearRing{false, false, Vector{StaticArraysCore.SVector{2, Int64}}, Nothing, Nothing}}, Nothing, Nothing}(GeoInterface.Wrappers.LinearR
ing{false, false, Vector{StaticArraysCore.SVector{2, Int64}}, Nothing, Nothing}[GeoInterface.Wrappers.LinearRing{false, false, Vector{StaticArraysCore.SVector{2, Int64}}, Nothing, Nothing}(StaticArraysCore.SVe
ctor{2, Int64}[[2, 1], [4, 3], [6, 5], [2, 1]], nothing, nothing), GeoInterface.Wrappers.LinearRing{false, false, Vector{StaticArraysCore.SVector{2, Int64}}, Nothing, Nothing}(StaticArraysCore.SVector{2, Int64
}[[4, 3], [6, 5], [7, 6], [4, 3]], nothing, nothing)], nothing, nothing)GeometryOps.tuples Method
tuples(obj)Convert all points in obj to Tuples, wherever the are nested.
Returns a similar object or collection of objects using GeoInterface.jl geometries wrapping Tuple points.
Keywords
threaded:trueorfalse. Whether to use multithreading. Defaults tofalse.crs: The CRS to attach to geometries. Defaults tonothing.calc_extent:trueorfalse. Whether to calculate the extent. Defaults tofalse.
GeometryOps.union Method
union(geom_a, geom_b, [::Type{T}]; target::Type, fix_multipoly = UnionIntersectingPolygons())Return the union between two geometries as a list of geometries. Return an empty list if none are found. The type of the list will be constrained as much as possible given the input geometries. Furthermore, the user can provide a taget type as a keyword argument and a list of target geometries found in the difference will be returned. The user can also provide a float type 'T' that they would like the points of returned geometries to be. If the user is taking a intersection involving one or more multipolygons, and the multipolygon might be comprised of polygons that intersect, if fix_multipoly is set to an IntersectingPolygons correction (the default is UnionIntersectingPolygons()), then the needed multipolygons will be fixed to be valid before performing the intersection to ensure a correct answer. Only set fix_multipoly to false if you know that the multipolygons are valid, as it will avoid unneeded computation.
Calculates the union between two polygons.
Example
import GeoInterface as GI, GeometryOps as GO
p1 = GI.Polygon([[(0.0, 0.0), (5.0, 5.0), (10.0, 0.0), (5.0, -5.0), (0.0, 0.0)]])
p2 = GI.Polygon([[(3.0, 0.0), (8.0, 5.0), (13.0, 0.0), (8.0, -5.0), (3.0, 0.0)]])
union_poly = GO.union(p1, p2; target = GI.PolygonTrait())
GI.coordinates.(union_poly)
# output
1-element Vector{Vector{Vector{Vector{Float64}}}}:
[[[6.5, 3.5], [5.0, 5.0], [0.0, 0.0], [5.0, -5.0], [6.5, -3.5], [8.0, -5.0], [13.0, 0.0], [8.0, 5.0], [6.5, 3.5]]]GeometryOps.voronoi Method
voronoi(geometries, [T = Float64]; clip_polygon = nothing, kwargs...)Compute the Voronoi tessellation of the points in geometries. Returns a vector of GI.Polygon objects representing the Voronoi cells, in the same order as the input points.
Arguments
geometries: Any GeoInterface-compatible geometry or collection of geometries that can be decomposed into pointsT: Float-type for returned polygons points (default: Float64)
Keyword Arguments
clip_polygon: what bounding shape should the Voronoi cells be clipped to? (default: nothing -> clipped to the convex hull) clip_polygon can of several types: (1) a GeoInterface polygon, (2) a two-element tuple where the first element is a list of tuple points and the second element is a list of integer indices to indicate the order of the provided points, or (3) a a two-element tuple where the first element is a tuple of tuple points and the second element is a tuple of integer indices to indicate the order of the provided pointscrs: The CRS to attach to geometries. Defaults tonothing.
rng: random number generator to generating the voronoi tesselation
Warning
This interface only computes the 2-dimensional Voronoi tessellation! Only clipped voronoi tesselations can be created! Only T = Float64 or Float32 are guaranteed good results by the underlying package DelaunayTriangulation.
Note
The polygons are returned in the same order as the input points after flattening. Each polygon corresponds to the Voronoi cell of the point at the same index.
Examples
An example with default clipping to the convex hull.
import GeometryOps as GO
import GeoInterface as GI
using Random
rng = Xoshiro(0)
points = [(rand(rng), rand(rng)) .* 5 for i in range(1, 3)]
GO.voronoi(points; rng = rng)
# output
3-element Vector{GeoInterface.Wrappers.Polygon{false, false, Vector{GeoInterface.Wrappers.LinearRing{false, false, Vector{Tuple{Float64, Float64}}, Nothing, Nothing}}, Nothing, Nothing}}:
GeoInterface.Wrappers.Polygon{false, false}([GeoInterface.Wrappers.LinearRing([(4.310704285977424, 0.42985432929210976), … (2) … , (4.310704285977424, 0.42985432929210976)])])
GeoInterface.Wrappers.Polygon{false, false}([GeoInterface.Wrappers.LinearRing([(3.7949144210695653, 0.4101636087384888), … (4) … , (3.7949144210695653, 0.4101636087384888)])])
GeoInterface.Wrappers.Polygon{false, false}([GeoInterface.Wrappers.LinearRing([(2.685897788908803, 0.3678259474564151), … (2) … , (2.685897788908803, 0.3678259474564151)])])An example with clipping to a GeoInterface polygon.
clip_points = ((0.0,0.0), (5.0,0.0), (5.0,5.0), (0.0,5.0), (0.0,0.0))
clip_order = (1, 2, 3, 4, 1)
clip_poly1 = GI.Polygon([collect(clip_points)])
GO.voronoi(points; clip_polygon = clip_poly1, rng = rng)
# output
3-element Vector{GeoInterface.Wrappers.Polygon{false, false, Vector{GeoInterface.Wrappers.LinearRing{false, false, Vector{Tuple{Float64, Float64}}, Nothing, Nothing}}, Nothing, Nothing}}:
GeoInterface.Wrappers.Polygon{false, false}([GeoInterface.Wrappers.LinearRing([(5.0, 0.0), … (3) … , (5.0, 0.0)])])
GeoInterface.Wrappers.Polygon{false, false}([GeoInterface.Wrappers.LinearRing([(3.7328227614527916, 0.0), … (3) … , (3.7328227614527916, 0.0)])])
GeoInterface.Wrappers.Polygon{false, false}([GeoInterface.Wrappers.LinearRing([(0.0, 5.0), … (3) … , (0.0, 5.0)])])An example with clipping to a tuple of tuples.
clip_poly2 = (clip_points, clip_order) # tuples
GO.voronoi(points; clip_polygon = clip_poly2, rng = rng)
# output
3-element Vector{GeoInterface.Wrappers.Polygon{false, false, Vector{GeoInterface.Wrappers.LinearRing{false, false, Vector{Tuple{Float64, Float64}}, Nothing, Nothing}}, Nothing, Nothing}}:
GeoInterface.Wrappers.Polygon{false, false}([GeoInterface.Wrappers.LinearRing([(5.0, 0.0), … (3) … , (5.0, 0.0)])])
GeoInterface.Wrappers.Polygon{false, false}([GeoInterface.Wrappers.LinearRing([(3.7328227614527916, 0.0), … (3) … , (3.7328227614527916, 0.0)])])
GeoInterface.Wrappers.Polygon{false, false}([GeoInterface.Wrappers.LinearRing([(0.0, 5.0), … (3) … , (0.0, 5.0)])])An example with clipping to a tuple of vectors.
clip_poly3 = (collect(clip_points), collect(clip_order)) # vectors
GO.voronoi(points; clip_polygon = clip_poly3, rng = rng)
# output
3-element Vector{GeoInterface.Wrappers.Polygon{false, false, Vector{GeoInterface.Wrappers.LinearRing{false, false, Vector{Tuple{Float64, Float64}}, Nothing, Nothing}}, Nothing, Nothing}}:
GeoInterface.Wrappers.Polygon{false, false}([GeoInterface.Wrappers.LinearRing([(5.0, 0.0), … (3) … , (5.0, 0.0)])])
GeoInterface.Wrappers.Polygon{false, false}([GeoInterface.Wrappers.LinearRing([(3.7328227614527916, 0.0), … (3) … , (3.7328227614527916, 0.0)])])
GeoInterface.Wrappers.Polygon{false, false}([GeoInterface.Wrappers.LinearRing([(0.0, 5.0), … (3) … , (0.0, 5.0)])])GeometryOps.weighted_mean Method
weighted_mean(weight::Real, x1, x2)Returns the weighted mean of x1 and x2, where weight is the weight of x1.
Specifically, calculates x1 * weight + x2 * (1 - weight).
Note
The idea for this method is that you can override this for custom types, like Color types, in extension modules.
GeometryOps.within Method
within(g1)Return a function that checks if its input is within g1. This is equivalent to x -> within(x, g1).
Core types
GeometryOpsCore.WGS84_EARTH_INV_FLATTENING Constant
The inverse flattening of the WGS84 ellipsoid
sourceGeometryOpsCore.WGS84_EARTH_MEAN_RADIUS Constant
The mean radius of the WGS84 ellipsoid, used for spherical manifold default
sourceGeometryOpsCore.WGS84_EARTH_SEMI_MAJOR_RADIUS Constant
The semi-major axis of the WGS84 ellipsoid
sourceGeometryOpsCore.Algorithm Type
abstract type Algorithm{M <: Manifold}The abstract supertype for all GeometryOps algorithms. These define how to perform a particular Operation.
An algorithm may be associated with one or many Manifolds. It may either have the manifold as a field, or have it as a static parameter (e.g. struct GEOS <: Algorithm{Planar}).
Interface
All Algorithms must implement the following methods:
rebuild(alg, manifold::Manifold)Rebuild algorithmalgwith a new manifold as passed in the second argument. This may error and throw aWrongManifoldExceptionif the manifold is not compatible with that algorithm.manifold(alg::Algorithm)Return the manifold associated with the algorithm.best_manifold(alg::Algorithm, input): Return the best manifold for that algorithm, in the absence of any other context. WARNING: this may change in future and is not stable!
The actual implementation is left to the implementation of that particular Operation.
Notable subtypes
AutoAlgorithm: Tells theOperationreceiving it to automatically select the best algorithm for its input data.ManifoldIndependentAlgorithm: An abstract supertype for an algorithm that works on any manifold. The manifold must be stored in the algorithm for aManifoldIndependentAlgorithm, and accessed viamanifold(alg).SingleManifoldAlgorithm: An abstract supertype for an algorithm that only works on a single manifold, specified in its type parameter.SingleManifoldAlgorithm{Planar}is a special case that does not have to store its manifold, since that doesn't contain any information. All otherSingleManifoldAlgorithms must store their manifold, since they do contain information.NoAlgorithm: A type that indicates no algorithm is to be used, essentially the equivalent ofnothing.
GeometryOpsCore.Applicator Type
abstract type Applicator{F,T}An abstract type for applicators that apply a function to a target object.
The type parameter F is the type of the function to apply, and T is the type of the target object.
A common dispatch pattern is to dispatch on F which may also be e.g. a ThreadFunctor.
Interface
All applicators must be callable by an index integer, and define the following methods:
rebuild(a::Applicator, f)- swap out the function and return a new applicator.
The calling convention is my_applicator(i::Int), so applicators must define this method.
GeometryOpsCore.ApplyToArray Type
ApplyToArray(f, target, arr, kw)Create an Applicator that applies a function to all elements of arr.
GeometryOpsCore.ApplyToFeatures Type
ApplyToFeatures(f, target, fc, kw)Create an Applicator that applies a function to all features of fc.
GeometryOpsCore.ApplyToGeom Type
ApplyToGeom(f, target, geom, kw)Create an Applicator that applies a function to all sub-geometries of geom.
GeometryOpsCore.AutoAlgorithm Type
AutoAlgorithm{T, M <: Manifold}(manifold::M, x::T)Indicates that the Operation should automatically select the best algorithm for its input data, based on the passed in manifold (may be an AutoManifold) and data x.
The actual implementation is left to the implementation of that particular Operation.
GeometryOpsCore.AutoManifold Type
AutoManifold()The AutoManifold is a special manifold that automatically selects the best manifold for the operation. It does not carry any parameters, nor does it indicate anything about the nature of the space.
This gets resolved to a specific manifold when an operation is applied, using the format method.
GeometryOpsCore.Geodesic Type
Geodesic(; semimajor_axis, inv_flattening)A geodesic manifold means that the geometry is on a 3-dimensional ellipsoid, parameterized by semimajor_axis (inv_flattening (
Usually, this is only relevant for area and segmentization calculations. It becomes more relevant as one grows closer to the poles (or equator).
sourceGeometryOpsCore.Manifold Type
abstract type ManifoldA manifold is mathematically defined as a topological space that resembles Euclidean space locally.
We use the manifold definition to define the space in which an operation should be performed, or where a geometry lies.
Currently we have Planar, Spherical, and Geodesic manifolds.
GeometryOpsCore.ManifoldIndependentAlgorithm Type
abstract type ManifoldIndependentAlgorithm{M <: Manifold} <: Algorithm{M}The abstract supertype for a manifold-independent algorithm, i.e., one which may work on any manifold.
The manifold is stored in the algorithm for a ManifoldIndependentAlgorithm, and accessed via manifold(alg).
GeometryOpsCore.MissingKeywordInAlgorithmException Type
MissingKeywordInAlgorithmException{Alg, F} <: ExceptionAn error type which is thrown when a keyword argument is missing from an algorithm.
The alg argument is the algorithm struct, and the keyword argument is the keyword that was missing.
This error message is used in the enforce method.
Usage
This is of course not how you would actually use this error type, but it is how you construct and throw it.
throw(MissingKeywordInAlgorithmException(GEOS(; tokl = 1.0), my_function, :tol))Real world usage will often look like this:
function my_function(alg::CLibraryPlanarAlgorithm, args...)
mykwarg = enforce(alg, :mykwarg, my_function) # this will throw an error if :mykwarg is not present in alg
endGeometryOpsCore.NoAlgorithm Type
NoAlgorithm(manifold)A type that indicates no algorithm is to be used, essentially the equivalent of nothing.
Stores a manifold within itself.
sourceGeometryOpsCore.Operation Type
abstract type Operation{Alg <: Algorithm} endOperations are callable structs, that contain the entire specification for what the algorithm will do.
Sometimes they may be underspecified and only materialized fully when you see the geometry, so you can extract the best manifold for those geometries.
sourceGeometryOpsCore.Planar Type
Planar()A planar manifold refers to the 2D Euclidean plane.
Z coordinates may be accepted but will not influence geometry calculations, which are done purely on 2D geometry. This is the standard "2.5D" model used by e.g. GEOS.
sourceGeometryOpsCore.SingleManifoldAlgorithm Type
abstract type SingleManifoldAlgorithm{M <: Manifold} <: Algorithm{M}The abstract supertype for a single-manifold algorithm, i.e., one which is known to only work on a single manifold.
The manifold may be accessed via manifold(alg).
GeometryOpsCore.Spherical Type
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'ss2/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'sS2Polygon::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, 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 -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.
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).
GeometryOpsCore.TaskFunctors Type
TaskFunctors(functors, tasks_per_thread)A struct to hold the functors and tasks_per_thread, for internal use with _maptasks where functions have state that cannot be shared accross threads, such as Proj.Transformation.
functors must be an array or tuple of functors, one per thread, and tasks_per_thread must be an integer. This also allows you to control the number of tasks per thread that _maptasks launches, useful for tuning performance if you like.
GeometryOpsCore.TraitTarget Type
TraitTarget{T}This struct holds a trait parameter or a union of trait parameters.
It is primarily used for dispatch into methods which select trait levels, like apply, or as a parameter to target.
Constructors
TraitTarget(GI.PointTrait())
TraitTarget(GI.LineStringTrait(), GI.LinearRingTrait()) # and other traits as you may like
TraitTarget(TraitTarget(...))
# There are also type based constructors available, but that's not advised.
TraitTarget(GI.PointTrait)
TraitTarget(Union{GI.LineStringTrait, GI.LinearRingTrait})
# etc.GeometryOpsCore.WithTrait Type
WithTrait(f)WithTrait is a functor that applies a function to a trait and an object.
Specifically, the calling convention is for f is changed from f(geom) to f(trait, geom; kw...).
This is useful to keep the trait materialized through the call stack, which can improve inferrability and performance.
sourceGeometryOpsCore.WrongManifoldException Type
WrongManifoldException{InputManifold, DesiredManifold, Algorithm} <: ExceptionThis error is thrown when an Algorithm is called with a manifold that it was not designed for.
It's mainly thrown when constructing SingleManifoldAlgorithm types.
GeometryOpsCore._InitialValue Type
_InitialValue()Sentinel value for "no init provided". This is the same way Base mapreduce does this. It's a singleton struct so e.g. similar to Nothing.
This is meant to be the default value for applyreduce's init keyword argument.
GeometryOpsCore.apply Method
apply(f, target::Union{TraitTarget, GI.AbstractTrait}, obj; kw...)Reconstruct a geometry, feature, feature collection, or nested vectors of either using the function f on the target trait.
f(target_geom) => x where x also has the target trait, or a trait that can be substituted. For example, swapping PolgonTrait to MultiPointTrait will fail if the outer object has MultiPolygonTrait, but should work if it has FeatureTrait.
Objects "shallower" than the target trait are always completely rebuilt, like a Vector of FeatureCollectionTrait of FeatureTrait when the target has PolygonTrait and is held in the features. These will always be GeoInterface geometries/feature/feature collections. But "deeper" objects may remain unchanged or be whatever GeoInterface compatible objects f returns.
The result is a functionally similar geometry with values depending on f.
threaded:trueorfalse. Whether to use multithreading. Defaults tofalse.crs: The CRS to attach to geometries. Defaults tonothing.calc_extent:trueorfalse. Whether to calculate the extent. Defaults tofalse.
Example
Flipped point the order in any feature or geometry, or iterables of either:
import GeoInterface as GI
import GeometryOps as GO
geom = GI.Polygon([GI.LinearRing([(1, 2), (3, 4), (5, 6), (1, 2)]),
GI.LinearRing([(3, 4), (5, 6), (6, 7), (3, 4)])])
flipped_geom = GO.apply(GI.PointTrait, geom) do p
(GI.y(p), GI.x(p))
endGeometryOpsCore.applyreduce Method
applyreduce(f, op, target::Union{TraitTarget, GI.AbstractTrait}, obj; threaded, init, kw...)Apply function f to all objects with the target trait, and reduce the result with an op like +.
The order and grouping of application of op is not guaranteed.
If threaded==true threads will be used over arrays and iterables, feature collections and nested geometries.
init specifies the initial value for the reduction. If not provided, the reduction uses the first result as the starting point (like reduce without init). For operations like vcat, you typically don't need to provide init. For numeric reductions like +, you may want to provide init=zero(T) to ensure type stability.
GeometryOpsCore.best_manifold Function
best_manifold(alg::Algorithm, input)::ManifoldReturn the best Manifold for the algorithm alg based on the given input.
May be any subtype of Manifold.
GeometryOpsCore.booltype Function
booltype(x)Returns a BoolsAsTypes from x, whether it's a boolean or a BoolsAsTypes.
GeometryOpsCore.flatten Method
flatten(target::Type{<:GI.AbstractTrait}, obj)
flatten(f, target::Type{<:GI.AbstractTrait}, obj)Lazily flatten any AbstractArray, iterator, FeatureCollectionTrait, FeatureTrait or AbstractGeometryTrait object obj, so that objects with the target trait are returned by the iterator.
If f is passed in it will be applied to the target geometries.
GeometryOpsCore.manifold Function
manifold(alg::Algorithm)::ManifoldReturn the manifold associated with the algorithm.
May be any subtype of Manifold.
GeometryOpsCore.rebuild Method
rebuild(geom, child_geoms)Rebuild a geometry from child geometries.
By default geometries will be rebuilt as a GeoInterface.Wrappers geometry, but rebuild can have methods added to it to dispatch on geometries from other packages and specify how to rebuild them.
(Maybe it should go into GeoInterface.jl)
sourceGeometryOpsCore.reconstruct Method
reconstruct(geom, components)Reconstruct geom from an iterable of component objects that match its structure.
All objects in components must have the same GeoInterface.trait.
Usually used in combination with flatten.
GeometryOpsCore.reconstruct_table Method
reconstruct_table(input, geometry_column_names, geometry_columns, other_column_names, args...; kwargs...)Reconstruct a table from the given input, geometry column names, geometry columns, and other column names.
Any function that defines reconstruct_table must also define used_reconstruct_table_kwargs.
The input must be a table.
The function should return a best-effort attempt at a table of the same type as the input, with the new geometry column(s) and other columns.
The fallback implementation invokes Tables.materializer. But if you want to be efficient and pass e.g. arbitrary kwargs to the materializer, or materialize in a different way, you can do so by overloading this function for your desired input type.
This is "semi-public" API and while it may add optional arguments, it will not add new required positional arguments. All implementations must allow arbitrary kwargs to pass through and harvest what they need.
sourceGeometryOpsCore.unwrap Function
unwrap(target::Type{<:AbstractTrait}, obj)
unwrap(f, target::Type{<:AbstractTrait}, obj)Unwrap the object to vectors, down to the target trait.
If f is passed in it will be applied to the target geometries as they are found.
GeometryOpsCore.used_reconstruct_table_kwargs Method
used_reconstruct_table_kwargs(input)Return a tuple of the kwargs that should be passed to reconstruct_table for the given input.
This is "semi-public" API, and required for any input type that defines reconstruct_table.
Submodules
These are internal submodules of GeometryOps. They are not part of the public API, so anything here may change or disappear in a patch release, but they are documented for reference (and because GeometryOps' own source code links to them).
UnitSpherical
GeometryOps.UnitSpherical.ArcIntersectionResult Type
ArcIntersectionResult{T}Result of computing the intersection between two great circle arcs.
Fields:
type::ArcIntersectionType: The type of intersectionpoints::Vector{UnitSphericalPoint{T}}: The intersection point(s)fracs::Vector{Tuple{T,T}}: For each intersection point, the fractional positions (α, β) where α is the position along the first arc and β is the position along the second arc. Both are in [0, 1], where 0 is the start point and 1 is the end point.
GeometryOps.UnitSpherical.ArcIntersectionType Type
ArcIntersectionTypeEnumeration of the types of arc intersections:
arc_cross: The arcs cross at a single point in their interiorsarc_hinge: The arcs share exactly one endpointarc_overlap: The arcs are collinear and overlap in a segmentarc_disjoint: The arcs do not intersect
GeometryOps.UnitSpherical.GeographicFromUnitSphere Type
GeographicFromUnitSphere()A transformation that converts a UnitSphericalPoint in ℝ³ to a 2-tuple geographic point (longitude, latitude), in degrees.
Accepts any 3-element vector, but the input is assumed to be on the unit sphere.
Examples
julia> using GeometryOps.UnitSpherical
julia> GeographicFromUnitSphere()(UnitSphericalPoint(0.5, 0.5, 1/√(2)))
(45.0, 44.99999999999999)(the inaccuracy is due to the precision of the atan function)
GeometryOps.UnitSpherical.SphericalCap Type
SphericalCap{T}
SphericalCap(point::UnitSphericalPoint{T}, radius::T)A spherical cap represents a section of a unit sphere about some point, bounded by a radius. It is defined by a center point on the unit sphere and a radius (in radians).
sourceGeometryOps.UnitSpherical.SphericalCap Method
Get the circumcenter of the triangle (a, b, c) on the unit sphere. Returns a normalized 3-vector.
sourceGeometryOps.UnitSpherical.UnitSphereFromGeographic Type
UnitSphereFromGeographic()A transformation that converts a geographic point (latitude, longitude) to a [UnitSphericalPoint] in ℝ³.
Accepts any GeoInterface-compatible point.
Examples
julia> import GeoInterface as GI; using GeometryOps.UnitSpherical
julia> UnitSphereFromGeographic()(GI.Point(45, 45))
3-element UnitSphericalPoint{Float64} with indices SOneTo(3):
0.5000000000000001
0.5000000000000001
0.7071067811865476julia> using GeometryOps.UnitSpherical
julia> UnitSphereFromGeographic()((45, 45))
3-element UnitSphericalPoint{Float64} with indices SOneTo(3):
0.5000000000000001
0.5000000000000001
0.7071067811865476GeometryOps.UnitSpherical.UnitSphericalPoint Type
UnitSphericalPoint(v)A unit spherical point, i.e., point living on the 2-sphere (𝕊²), represented as Cartesian coordinates in ℝ³.
This currently has no support for heights, only going from lat long to spherical and back again.
Examples
julia> using GeometryOps.UnitSpherical
julia> UnitSphericalPoint(1, 0, 0)
3-element UnitSphericalPoint{Int64} with indices SOneTo(3):
1
0
0Base.convert Method
convert(::Type{<:Extents.Extent}, cap::SphericalCap) -> Extents.Extent{(:X, :Y, :Z)}Convert cap to an outward-rounded Cartesian bounding box containing every unit-sphere point in the cap. The result is intended for Cartesian spatial indexes over unit-spherical data.
Extent extraction conventionally means that an object has a Cartesian extent. A SphericalCap is instead a spherical query object, so conversion makes this boundary crossing explicit.
Valid caps have radii in [0, π]. A radius at least π, or a non-finite or negative radius, conservatively returns the whole unit-sphere box.
The abstract target Extents.Extent and compatible concrete targets are accepted. Incompatible concrete targets throw an ArgumentError.
Extents.contains Method
Extents.contains(big::SphericalCap, small::SphericalCap; strict=false)Whether the closed cap big contains all of small.
Extents.grow Method
Extents.grow(cap::SphericalCap, factor::Real) -> SphericalCapGrow the cap's angular diameter by factor on each side.
Extents.intersects Method
Extents.intersects(cap::SphericalCap, ext::Extents.Extent{(:X, :Y, :Z)})
Extents.intersects(ext::Extents.Extent{(:X, :Y, :Z)}, cap::SphericalCap)Whether cap intersects the part of the unit sphere covered by the 3D Cartesian bounding box ext (also in unit-spherical space).
A point on the sphere lies in the cap iff its Euclidean (chord) distance to the cap's center is at most
This is a fail-safe comparison, so may have false positives but never false negatives.
sourceExtents.intersects Method
Extents.intersects(x::SphericalCap, y::SphericalCap)Whether the two closed caps intersect, including at tangency.
sourceExtents.union Method
Extents.union(x::SphericalCap, y::SphericalCap; strict=false) -> SphericalCapReturn a cap containing both x and y.
If either input already contains the other it is returned unchanged. Otherwise, the centre lies between the input caps' centres, and the radius is rounded outward to preserve coverage.
strict is accepted for consistency with the Extents API and has no effect because both inputs use the same domain.
Extents.within Method
Extents.within(small::SphericalCap, big::SphericalCap; strict=false)Whether the closed cap small is within big.
GeometryOps.UnitSpherical._arc_fraction Method
_arc_fraction(p, a, b) -> TCompute the fractional position of point p along the arc from a to b.
Returns a value in [0, 1] where 0 corresponds to a and 1 corresponds to b. Uses spherical distance to compute the fraction.
GeometryOps.UnitSpherical._find_collinear_arc_intersection Method
_find_collinear_arc_intersection(a1, b1, a2, b2, T) -> ArcIntersectionResult{T}Find the intersection of two collinear arcs (arcs on the same great circle).
Returns an ArcIntersectionResult with type arc_overlap if the arcs overlap, or arc_disjoint if they don't.
GeometryOps.UnitSpherical._make_hinge_result Method
_make_hinge_result(T, point, α, β) -> ArcIntersectionResult{T}Create an ArcIntersectionResult for a hinge intersection.
GeometryOps.UnitSpherical._points_equal Method
_points_equal(a::UnitSphericalPoint, b::UnitSphericalPoint, tol) -> BoolCheck if two points are equal within a tolerance.
sourceGeometryOps.UnitSpherical.circumcenter_on_unit_sphere Method
circumcenter_on_unit_sphere(a, b, c)Return the center of the circle passing through the three UnitSphericalPoints a, b and c, as a UnitSphericalPoint.
Of the two antipodal circumcenters, this returns the one on the same hemisphere as the input points, i.e. the center of the smaller of the two circles they bound.
sourceGeometryOps.UnitSpherical.point_on_spherical_arc Method
point_on_spherical_arc(p::UnitSphericalPoint, a::UnitSphericalPoint, b::UnitSphericalPoint) -> BoolCheck if point p lies on the great circle arc from a to b.
The arc is the shorter path along the great circle connecting a and b. Returns true if p is on the arc (including endpoints), false otherwise.
Examples
using GeometryOps.UnitSpherical: UnitSphericalPoint, point_on_spherical_arc
a = UnitSphericalPoint(1.0, 0.0, 0.0)
b = UnitSphericalPoint(0.0, 1.0, 0.0)
mid = UnitSphericalPoint(1/√2, 1/√2, 0.0)
point_on_spherical_arc(mid, a, b)
# output
trueGeometryOps.UnitSpherical.slerp Method
slerp(a::UnitSphericalPoint, b::UnitSphericalPoint, i01::Number)Interpolate between a and b, at a proportion i01 between 0 and 1 along the path from a to b.
Uses the tangent-vector form cos(r)·a + sin(r)·dir — where r = i01 · spherical_distance(a, b) and dir = normalize(robust_cross_product(a, b) × a) is the unit tangent at a pointing toward b. This avoids the 1/sin(Ω) divisor of the classic sin((1-t)Ω)/sin(Ω) · a + sin(tΩ)/sin(Ω) · b formulation, which collapses for near- and exactly-antipodal inputs. Adapted from Google's S2 geometry library (see S2::Interpolate and S2::GetPointOnLine).
For exactly antipodal a and b the great circle is mathematically ambiguous; robust_cross_product returns a deterministic perpendicular via its symbolic-perturbation branch, so the result is still a well-defined unit vector on some great circle through both points.
Examples
julia> using GeometryOps.UnitSpherical
julia> slerp(UnitSphericalPoint(1, 0, 0), UnitSphericalPoint(0, 1, 0), 0.5)
3-element UnitSphericalPoint{Float64} with indices SOneTo(3):
0.7071067811865476
0.7071067811865475
0.0GeometryOps.UnitSpherical.spherical_arc_extent Method
spherical_arc_extent(a, b)::Extents.Extent{(:X, :Y, :Z)}The 3D Cartesian extent of the shorter great-circle arc between a and b on the unit sphere. Accepts UnitSphericalPoints, or any GeoInterface point (interpreted geographically, as longitude/latitude, like the UnitSphericalPoint constructor itself).
The extent is exact up to floating point error, padded by a few ulps so it always contains the arc. For antipodal endpoints the arc's plane is ambiguous; the one chosen by robust_cross_product is used.
Example
julia> using GeometryOps.UnitSpherical
julia> ext = spherical_arc_extent(UnitSphericalPoint(1, 0, 0), UnitSphericalPoint(0, 1, 0));
julia> ext.X[2] ≈ 1 && ext.Y[2] ≈ 1
trueGeometryOps.UnitSpherical.spherical_arc_intersection Method
spherical_arc_intersection(a1, b1, a2, b2) -> ArcIntersectionResultCompute the intersection between two great circle arcs on the unit sphere.
The first arc goes from a1 to b1, and the second arc goes from a2 to b2. All points should be UnitSphericalPoint instances.
Returns an ArcIntersectionResult containing:
The type of intersection (cross, hinge, overlap, or disjoint)
The intersection point(s) if they exist
The fractional positions along each arc for each intersection point
Examples
# Two arcs that cross
a1 = UnitSphereFromGeographic()((-45.0, 0.0))
b1 = UnitSphereFromGeographic()((45.0, 0.0))
a2 = UnitSphereFromGeographic()((0.0, -45.0))
b2 = UnitSphereFromGeographic()((0.0, 45.0))
result = spherical_arc_intersection(a1, b1, a2, b2)
# result.type == arc_cross, with one intersection point at (0°, 0°)GeometryOps.UnitSpherical.spherical_distance Method
spherical_distance(x::UnitSphericalPoint, y::UnitSphericalPoint)Compute the great-circle distance (central angle, in radians) between two points on the unit sphere. Returns a Number whose type follows from the inputs.
Uses the atan2(‖x × y‖, x · y) form, which is numerically stable across the full [0, π] range — unlike acos(x · y), which loses precision for nearly-identical points. Adapted from Google's S2 geometry library (see Vector3::Angle).
Extended help
Doctests
julia> using GeometryOps.UnitSpherical
julia> spherical_distance(UnitSphericalPoint(1, 0, 0), UnitSphericalPoint(0, 1, 0))
1.5707963267948966julia> using GeometryOps.UnitSpherical
julia> spherical_distance(UnitSphericalPoint(1, 0, 0), UnitSphericalPoint(1, 0, 0))
0.0GeometryOps.UnitSpherical.spherical_exterior_anchor Method
spherical_exterior_anchor(pts, n) -> Union{UnitSphericalPoint{Float64}, Nothing}A reference point exterior BY DEFINITION of the enclosed-region semantics of the ring pts[1:n]: the antipode of the ring's normalized vertex mass (the sum of the unit vertex directions). For any ring whose enclosed region is meaningfully smaller than a hemisphere, the vertex mass points into the cap the vertices bound, so its antipode lies in the larger — exterior — region.
Returns nothing when the mass norm is tiny (below 1e-6 per vertex): near-hemisphere or vertex-symmetric rings, whose vertices spread over a near-great circle. There the enclosed/complement distinction is itself near-degenerate (the turning-angle winding tolerance already treats exact hemispheres permissively — see _ring_is_ccw), so callers fall back to the winding-consistent wedge bootstrap of spherical_ring_contains.
GeometryOps.UnitSpherical.spherical_orient Method
spherical_orient(a::UnitSphericalPoint, b::UnitSphericalPoint, c::UnitSphericalPoint) -> IntDetermine the orientation of point c with respect to the great circle arc from a to b.
Returns:
1ifcis to the left of the arc (counter-clockwise)-1ifcis to the right of the arc (clockwise)0ifcis on the great circle (collinear)
Uses robust_cross_product for numerical stability with nearly identical or antipodal points.
Examples
using GeometryOps.UnitSpherical: UnitSphericalPoint, spherical_orient
a = UnitSphericalPoint(1.0, 0.0, 0.0)
b = UnitSphericalPoint(0.0, 1.0, 0.0)
c = UnitSphericalPoint(0.0, 0.0, 1.0)
spherical_orient(a, b, c)
# output
1Extended help
Why this does not simply call robust_cross_product
The common path uses the unnormalized cross(a - b, a + b): orientation needs only the sign of its dot product with c. The squared degeneracy test avoids normalizing the cross product or taking a square root. Rounding at the boundary may change 0 to a sign, but cannot flip +1 to -1.
For nearly equal or antipodal a and b, the cross-product direction becomes unstable. Those cases fall back to robust_cross_product.
GeometryOps.UnitSpherical.spherical_ring_contains Method
spherical_ring_contains(pts, n, q; orient, on_arc, proper_crossing) -> Union{Bool, Nothing}Whether q lies in the closed region on the left of the ring pts[1:n] (S2 loop convention: counterclockwise winding, interior on the left, so a clockwise ring contains the complement). The closing edge pts[n] → pts[1] is implied; boundary points count as contained. Returns nothing when every anchor edge is degenerate with respect to q — callers must treat that conservatively.
Containment is decided by crossing parity, the way S2Loop::Contains / InitBound decide pole containment: which side of an anchor edge q falls on, flipped once per transversal crossing of the arc from the anchor's midpoint to q with the other edges; degenerate anchors are skipped and the next edge tried.
The geometric predicates are injectable, for callers with stricter requirements. They receive the input points untouched (which may be non-unit for scale-invariant predicates — the defaults assume unit input); only the constructed reference midpoint is normalized.
orient(a, b, c): sign-valued orientation ofcagainst the oriented great circle througha, b; defaultspherical_orient.on_arc(q, a, b)::Bool: boundary membership; defaultpoint_on_spherical_arc. PassReturns(false)when boundary points are already classified.proper_crossing(q, m, a, b)::Int:1if the minor arcs(q, m)and(a, b)cross transversally in both interiors,0if not,-1for too close to call; consulted onceorientplaces both endpoint pairs strictly transversally. The default usesrobust_cross_productwith a small tolerance band.
GeometryOps.UnitSpherical.spherical_ring_encloses Method
spherical_ring_encloses(pts, n, q;
anchor, orient, on_arc, proper_crossing) -> Union{Bool, Nothing}Whether q lies in the region ENCLOSED by the ring pts[1:n] (the closing edge pts[n] → pts[1] is implied; boundary points count as enclosed): even-odd crossing parity of the arc from q to a reference point that is exterior by definition of the enclosed-region semantics — anchor, by default the antipode of the normalized vertex mass (spherical_exterior_anchor).
Winding-independent, like spherical_ring_contains composed with a winding test — but where that composition bootstraps the interior from a local wedge at one edge and a global turning-angle sum, both of which a ring that self-intersects on the sphere defeats (a figure-eight's lobes cancel the turning angle, and the wedge answer is anchored to whichever lobe hosts the edge — S2's forced-through behavior, globally inverted on real data), parity from a definitionally exterior point degrades to even-odd semantics: both lobes enclosed, the far side out.
Returns nothing — callers fall back conservatively — when:
anchor === nothing(degenerate vertex mass, seespherical_exterior_anchor);qis (nearly) antipodal to the anchor (the test arc is ill-defined:qsits at the center of the vertex mass);the anchor lies exactly ON a ring edge (the test arc ends on the ring); or
proper_crossingreports a crossing as too close to call (-1; never with exact injected predicates).
The orient/on_arc/proper_crossing predicates are injectable exactly as in spherical_ring_contains; on_test_arc(v, a, b) decides whether a point already known to lie on the great circle of (a, b) lies on the closed minor arc (the vertex-grazing resolution below — exact callers inject their span test).
GeometryOps.UnitSpherical.to_unit_spherical_points Method
to_unit_spherical_points(ring) -> Vector{<:UnitSphericalPoint}Convert a ring (linear ring or any GeoInterface point iterator) to a vector of UnitSphericalPoints, treating geographic input as (longitude, latitude). UnitSphericalPoints pass through unchanged.
RobustCrossProduct
GeometryOps.UnitSpherical.RobustCrossProduct.ensureNormalizable Method
ensureNormalizable(p::AbstractVector)Scales a 3-vector as necessary to ensure that the result can be normalized without loss of precision due to floating-point underflow.
This matches S2's EnsureNormalizable function.
sourceGeometryOps.UnitSpherical.RobustCrossProduct.exact_cross_product Method
exact_cross_product(a::AbstractVector, b::AbstractVector)Compute the cross product using arbitrary precision arithmetic. This is used when standard floating-point arithmetic is not accurate enough.
This matches S2's ExactCrossProd function, first trying higher precision if available, then exact arithmetic, then symbolic perturbation.
sourceGeometryOps.UnitSpherical.RobustCrossProduct.isNormalizable Method
isNormalizable(v::AbstractVector)Returns true if the given vector's magnitude is large enough such that the angle to another vector of the same magnitude can be measured using angle calculations without loss of precision due to floating-point underflow.
This matches S2's IsNormalizable function.
sourceGeometryOps.UnitSpherical.RobustCrossProduct.isUnitLength Method
isUnitLength(v::AbstractVector)Check if a vector has unit length within a small tolerance.
Returns true if the vector's magnitude is approximately 1.0.
The tolerance adapts to the element type of the vector to handle both Float64 (high precision) and Float32 (lower precision, e.g., from GeoJSON) inputs. Uses 16 * eps(T) which provides adequate margin for unit vectors constructed from trigonometric functions while still being tight enough to catch errors.
GeometryOps.UnitSpherical.RobustCrossProduct.isless_vector Method
isless_vector(a::AbstractVector, b::AbstractVector)Lexicographic comparison of vectors. This is used to establish a consistent order for symbolic perturbations.
Returns true if a comes before b in lexicographic order.
sourceGeometryOps.UnitSpherical.RobustCrossProduct.min_stable_norm Method
min_stable_norm(::Type{T})Return the smallest stable-cross-product norm that meets ROBUST_CROSS_PROD_ERROR in precision T.
This is S2's kMinNorm: s2edge_crossings.cc#L129-L131.
GeometryOps.UnitSpherical.RobustCrossProduct.normalizableFromExact Method
normalizableFromExact(xf::Vector{BigFloat})Converts a BigFloat vector to a double-precision vector, scaling the result as necessary to ensure that the result can be normalized without loss of precision due to floating-point underflow.
This matches S2's NormalizableFromExact function.
sourceGeometryOps.UnitSpherical.RobustCrossProduct.normalization_needed Method
normalization_needed(v::AbstractVector)Determines if a vector's magnitude is too small for reliable normalization. Returns true if the vector needs special handling to avoid numerical issues.
This is essentially the opposite of isNormalizable.
sourceGeometryOps.UnitSpherical.RobustCrossProduct.robust_cross_product Method
robust_cross_product(a::AbstractVector, b::AbstractVector)Compute a robust version of a × b (cross product) for unit vectors.
This method handles the case where a and b are very close together or antipodal by computing a stable perpendicular to both points.
The implementation follows Google's S2 Geometry Library to ensure numerical stability even in difficult cases.
Returns a unit-length vector that is perpendicular to both input vectors.
Examples
using GeometryOps.UnitSpherical: UnitSphericalPoint, robust_cross_product
a = UnitSphericalPoint(1, 0, 0);
b = UnitSphericalPoint(0, 1, 0);
result = robust_cross_product(a, b)
isapprox(result, UnitSphericalPoint(0, 0, 1))
# output
trueGeometryOps.UnitSpherical.RobustCrossProduct.stable_cross_product Method
getStableCrossProd(a::AbstractVector, b::AbstractVector)Computes a numerically stable cross product between unit vectors.
This implements the algorithm from S2's GetStableCrossProd function, computing (a-b)×(a+b) which yields better numerical stability when the vectors are nearly identical.
Returns a tuple of (result, success) where:
result is the computed cross product vector (not normalized)
success is a boolean indicating if the computation was sufficiently accurate
GeometryOps.UnitSpherical.RobustCrossProduct.symbolic_cross_product Method
symbolic_cross_product(a::AbstractVector, b::AbstractVector)Compute a symbolic cross product when exact arithmetic yields zero. This implements the symbolic perturbation model used in S2 geometry.
Returns a vector that is the symbolic cross product.
sourceGeometryOps.UnitSpherical.RobustCrossProduct.symbolic_cross_product_sorted Method
symbolic_cross_product_sorted(a::AbstractVector, b::AbstractVector)Helper function to compute the symbolic cross product when points are collinear. Assumes that a < b lexicographically.
This implements the symbolic perturbation model described in S2 geometry.
sourceSpatialTreeInterface
GeometryOps.SpatialTreeInterface.FlatNoTree Type
FlatNoTree(iterable_of_geoms_or_extents)Represents a flat collection with no tree structure, i.e., a brute force search. This is cost free, so particularly useful when you don't want to build a tree!
sourceGeometryOps.SpatialTreeInterface.child_indices_extents Method
child_indices_extents(node)Return an iterator over the indices and extents of the children of a node.
Each value of the iterator should take the form (i, extent).
This can only be invoked on leaf nodes!
sourceGeometryOps.SpatialTreeInterface.depth_first_search Method
depth_first_search(f, predicate, tree)Call f(i) for each index i in the tree that satisfies predicate(extent(i)).
This is generic to anything that implements the SpatialTreeInterface, particularly the methods isleaf, getchild, and child_indices_extents.
Example
using Extents, GeometryOps.SpatialTreeInterface, SortTileRecursiveTree
# Construct a tree of extents (in this case, just a grid)
xs, ys = 1:10, 1:10
extents_vec = vec([Extents.Extent(X = (x, x+1), Y = (y, y+1)) for x in xs, y in ys])
# Construct a tree of extents - this is an STRtree,
# but works on any SpatialTreeInterface-compatible tree.
extents_tree = STRtree(extents_vec)
# Count the number of extents that intersect the extent (1,1) x (2,2)
target_extent = Extents.Extent(X = (1,2), Y = (1,2))
count = 0
SpatialTreeInterface.depth_first_search(
(i::Int -> global count += 1), # `f`
Base.Fix1(Extents.intersects, target_extent),
extents_tree
)
count
# output
4GeometryOps.SpatialTreeInterface.dual_depth_first_search Method
dual_depth_first_search(f, predicate, tree1, tree2)Executes a dual depth-first search over two trees, descending into the children of nodes i and j when predicate(node_extent(i), node_extent(j)) is true, and pruning that branch when predicate(node_extent(i), node_extent(j)) is false.
Finally, calls f(i1, i2) for each leaf-level index i1::Int in tree1 and i2::Int in tree2 that satisfies predicate(extent(i1), extent(i2)).
Here, f(i1::Int, i2::Int) may be any function that takes two integers as arguments.
It may optionally return an Action to alter the control flow of the Action(:full_return, true). Return Action(:full_return, true) from this function and break out of the recursion.
This is generic to anything that implements the SpatialTreeInterface, particularly the methods isleaf, getchild, node_extent and child_indices_extents.
Each visited node's extent is computed once and carried into the recursion. Trees that derive their extents rather than storing them should also define node_extent_is_expensive.
GeometryOps.SpatialTreeInterface.getchild Function
getchild(node)
getchild(node, i)Accessor function to get the children of a node.
If invoked as getchild(node), return an iterator over all the children of a node. This may be lazy, like a Base.Generator, or it may be materialized.
If invoked as getchild(node, i), return the i-th child of a node.
GeometryOps.SpatialTreeInterface.getchild Method
getchild(node, i)Return the i-th child of a node.
GeometryOps.SpatialTreeInterface.isleaf Method
isleaf(node)Return true if the node is a leaf node, i.e., there are no "children" below it. getchild should still work on leaf nodes, though, returning an iterator over the extents stored in the node - and similarly for getnodes.
GeometryOps.SpatialTreeInterface.isspatialtree Method
isspatialtree(tree)::BoolReturn true if the object is a spatial tree, false otherwise.
Implementation notes
For type stability, if your spatial tree type is MyTree, you should define isspatialtree(::Type{MyTree}) = true, and isspatialtree(::MyTree) will forward to that method automatically.
GeometryOps.SpatialTreeInterface.nchild Method
nchild(node)Return the number of children of a node.
sourceGeometryOps.SpatialTreeInterface.node_extent Method
node_extent(node)Return the extent like object of the node. Falls back to GI.extent by default, which falls back to Extents.extent.
Generally, defining Extents.extent(node) is sufficient here, and you won't need to define this
The reason we don't use that directly is to give users of this interface a way to define bounding boxes that are not extents, like spherical caps and other such things.
sourceGeometryOps.SpatialTreeInterface.node_extent_is_expensive Method
node_extent_is_expensive(node)::BoolReturn true if node_extent computes the node's extent instead of reading one the node already stores. Defaults to false.
When true, dual_depth_first_search caches a node's child extents rather than re-deriving them once per opposing child, at the cost of a small vector per visited node.
Implementation notes
Define this on the type - node_extent_is_expensive(::Type{MyNode}) = true - so that it is known at compile time; node_extent_is_expensive(::MyNode) forwards there automatically.
GeometryOps.SpatialTreeInterface.query Method
query(tree, predicate)Return a sorted list of indices of the tree that satisfy the predicate.
sourceGeometryOps.SpatialTreeInterface.sanitize_predicate Method
sanitize_predicate(pred)Convert a predicate to a function that returns a Boolean.
If pred is an Extent, convert it to a function that returns a Boolean by intersecting with the extent. If pred is a geometry, convert it to an extent first, then wrap in Extents.intersects.
Otherwise, return the predicate unchanged.
Users and developers may overload this function to provide custom behaviour when something is passed in.
sourceGeometryOps.SpatialTreeInterface.spatialtree Method
spatialtree([manifold::Manifold], geometries)Build a default packed STR RTree from an iterable of geometries, over their extents on manifold (Planar() if not given).
missing and nothing entries are skipped, as are empty geometries. Queries return indices into geometries regardless, so the skipped entries do not shift the answers. If no valid geometries remain, return nothing.
FlexibleRTrees
GeometryOps.FlexibleRTrees.BulkLoadAlgorithm Type
BulkLoadAlgorithmSupertype for the algorithms that decide the leaf order of an RTree. Packing is always "union consecutive runs of nodecapacity, bottom-up"; the algorithm only chooses the order, via a loadorder method.
GeometryOps.FlexibleRTrees.HPR Type
HPR()Hilbert-packed ordering, as in JTS's HPRtree: sort by the Hilbert-curve index of each extent's center. Hilbert order is spatially local at every scale, which suits this tree's consecutive-run packing particularly well.
GeometryOps.FlexibleRTrees.RTree Type
RTree(algorithm::BulkLoadAlgorithm, data; nodecapacity = 16, extents = nothing, indices = nothing)A packed R-tree over the extents of data (anything GI.extent accepts — geometries, or Extents.Extents themselves), of any dimensionality, bulk loaded in the order chosen by algorithm.
Pass a vector as indices to index only data[indices], leaving the rest of data out of the tree — for a collection only part of which can be indexed, say one with missing entries. data stays whole, so queries still report positions in it.
Pass a vector as extents to index by precomputed extents instead of GI.extent — for payload elements that carry no extent of their own, or extents computed in another coordinate space. One per indexed element, in order: per element of data normally, per element of indices alongside indices. The tree takes ownership of the vector (Unsorted aliases it as the leaf level rather than copying).
The tree is flat and fully concrete: levels[1] is the coarsest level and levels[end] holds the leaf extents in packed order, with indices mapping each leaf slot back to its position in data. Queries through SpatialTreeInterface therefore return indices into data, which the tree keeps as tree.data so hits map straight back to elements wherever the tree travels.
GeometryOps.FlexibleRTrees.RTree Method
RTree(m::Manifold, algorithm::BulkLoadAlgorithm, data; nodecapacity = 16, indices = nothing)Build the tree over each element's extent on the manifold m, via Extents.extent(m, x). On Spherical() the leaves are the 3D Cartesian boxes of the elements as regions on the unit sphere, covering the arc bulge and enclosed poles that vertex extents miss.
GeometryOps.FlexibleRTrees.RTreeNode Type
RTreeNode{T, E}A cursor into one node of an RTree: the tree, the node's level (0-based; the children of a level-l node live in levels[l + 1]), its position within that level, and its extent. All SpatialTreeInterface methods traverse the tree through these cursors. The children of one node occupy one contiguous run of the next level's extent vector, which each per-child method resolves once per node and then indexes into. At the leaf level, child_indices_extents maps leaf slots through tree.indices, so queries return indices into the original collection despite the packed reordering.
GeometryOps.FlexibleRTrees.STR Type
STR()Sort-tile-recursive ordering (Leutenegger et al., 1997), generalized to any dimensionality: sort by center along the first dimension, cut into slabs, recurse within each slab on the remaining dimensions.
sourceGeometryOps.FlexibleRTrees.Unsorted Type
Unsorted()Keep the input order (no sort). Equivalent to natural indexing — good when the input is already spatially coherent (e.g. the edges of a ring), and the baseline the sorting algorithms have to beat.
sourceGeometryOps.FlexibleRTrees.hilbert_key Method
hilbert_key(coords::NTuple{N, UInt32}, bits::Int) -> UInt64The Hilbert-curve index of a point on the N-dimensional 2^bits grid, as a sortable integer. Requires N * bits <= 64; each coordinate must be < 2^bits.
GeometryOps.FlexibleRTrees.loadorder Method
loadorder(algorithm, extents::Vector{<:Extents.Extent}, nodecapacity)The permutation (an AbstractVector{Int}) in which algorithm packs extents into leaves. Implement this for a new BulkLoadAlgorithm subtype to plug in another ordering. Return Base.OneTo for the identity — the constructor then skips the reorder copy and aliases extents as the leaf level.
GeometryOps.FlexibleRTrees.query Method
query(tree::RTree, extent_or_geom)Indices (into the collection the tree was built from) of every leaf whose extent intersects the given extent — or the extent of the given geometry — in ascending order.
sourceNaturalIndexing
GeometryOps.NaturalIndexing.NaturalIndex Type
NaturalIndex{E <: Extents.Extent}A natural tree index. Stored in a vector in NaturalIndex.
nodecapacityis the "spread", number of children per nodeextentis the extent of the treelevelsis a vector ofNaturalLevels
GeometryOps.NaturalIndexing.NaturalIndex Method
NaturalIndex(m::Manifold, geoms; nodecapacity = 32)Index each geometry's extent on the manifold m, via Extents.extent(m, geom). On Spherical() the leaf extents are the 3D Cartesian boxes of the geometries as regions on the unit sphere.
GeometryOps.NaturalIndexing.NaturalIndexNode Type
NaturalIndexNode{E <: Extents.Extent}A reference to a node in the natural tree. Kind of like a tree cursor.
parent_indexis a pointer to the parent indexlevelis the level of the node in the treeindexis the index of the node in the levelextentis the extent of the node
GeometryOps.NaturalIndexing.NaturalLevel Type
NaturalLevel{E <: Extents.Extent}A level in the natural tree. Stored in a vector in NaturalIndex.
extentsis a vector of extents of the children of the node
GeometryOps.NaturalIndexing.NaturallyIndexedRing Type
NaturallyIndexedRing(points; nodecapacity = 32)A linear ring that contains a natural index.
Warning
This will be removed in favour of prepared geometry - the idea here is just to test what interface works best to store things in.
GeometryOps.NaturalIndexing._number_of_levels Method
_number_of_levels(nodecapacity::Int, ngeoms::Int)Calculate the number of levels in a natural tree for a given number of geometries and node capacity.
How this works
The number of keys in a level is given by ngeoms / nodecapacity ^ level.
The number of levels is the smallest integer such that the number of keys in the last level is 1. So it goes - if that makes sense.
sourceLoopStateMachine
GeometryOps.LoopStateMachine Module
LoopStateMachineUtilities for returning state from functions that run inside a loop.
This is used in e.g clipping, where we may need to break or transition states.
The main entry point is to return an Action from a function that is wrapped in a @controlflow f(...) macro in a loop. When a known Action (currently, :continue, :break, :return, or :full_return actions) is returned, it is processed by the @controlflow macro, which allows the function to break out of the loop early, continue to the next iteration, or return a value, basically a way to provoke syntactic behaviour from a function called from a inside a loop, where you do not have access to that loop.
Example
GeometryOps.LoopStateMachine.Action Type
Action(name::Symbol, [x])Create an Action with the name name and optional contents x.
Actions are returned from functions wrapped in a @controlflow macro, which does something based on the return value of that function if it is an Action.
Available actions
:continue: continue to the next iteration of the loop. This is thecontinuekeyword in Julia. The contents of the action are not used.:break: break out of the loop. This is thebreakkeyword in Julia. The contents of the action are not used.:return: cause the function executing the loop to return with the wrapped value.:full_return: cause the function executing the loop to returnAction(:full_return, x). This is very useful to terminate recursive funtions, like tree queries terminating after you have found a single intersecting segment.
GeometryOps.LoopStateMachine.@controlflow Macro
@controlflow f(...)Process the result of f(...) and return the result if it's not an Action(@ref LoopStateMachine.Action).
If it is an Action, then process it according to the following rules, and throw an error if it's not recognized. :continue, :break, :return, or :full_return are valid actions.
:continue: continue to the next iteration of the loop. This is thecontinuekeyword in Julia. The contents of the action are not used.:break: break out of the loop. This is thebreakkeyword in Julia. The contents of the action are not used.:return: cause the function executing the loop to return with the wrapped value.:full_return: cause the function executing the loop to returnAction(:full_return, x). This is very useful to terminate recursive funtions, like tree queries terminating after you have found a single intersecting segment.
Warning
Only use this inside a loop, otherwise you'll get a syntax error, especially if you use :continue or :break.
Examples
source