diff --git a/dev/index.html b/dev/index.html index f1846cfb1..896249eae 100644 --- a/dev/index.html +++ b/dev/index.html @@ -1,171 +1,161 @@ -Home · GeometryOps.jl

GeometryOps

Documentation for GeometryOps.

GeometryOps.AbstractBarycentricCoordinateMethodType
abstract type AbstractBarycentricCoordinateMethod

Abstract 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})::V
  • barycentric_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.

source
GeometryOps.DouglasPeuckerType
DouglasPeucker <: SimplifyAlg
+Home · GeometryOps.jl

GeometryOps

Documentation for GeometryOps.

GeometryOps.AbstractBarycentricCoordinateMethodType
abstract type AbstractBarycentricCoordinateMethod

Abstract 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})::V
  • barycentric_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.

source
GeometryOps.DouglasPeuckerType
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 after simplify. Useful as it will generalise for large collections of objects.

  • number: the number of points that should remain after simplify. Less useful for large collections of mixed size objects.

  • tol: the minimum distance a point will be from the line joining its neighboring points.

source
GeometryOps.MeanValueType
MeanValue() <: AbstractBarycentricCoordinateMethod

This method calculates barycentric coordinates using the mean value method.

References

source
GeometryOps.RadialDistanceType
RadialDistance <: SimplifyAlg

Simplifies geometries by removing points less than tol distance from the line between its neighboring points.

Keywords

  • ratio: the fraction of points that should remain after simplify. Useful as it will generalise for large collections of objects.

  • number: the number of points that should remain after simplify. Less useful for large collections of mixed size objects.

  • tol: the minimum distance between points.

source
GeometryOps.SimplifyAlgType
abstract type SimplifyAlg

Abstract 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.

source
GeometryOps.VisvalingamWhyattType
VisvalingamWhyatt <: 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 after simplify. Useful as it will generalise for large collections of objects.

  • number: the number of points that should remain after simplify. Less useful for large collections of mixed size objects.

  • tol: the minimum distance a point will be from the line joining its neighboring points.

source
GeometryOps.MeanValueType
MeanValue() <: AbstractBarycentricCoordinateMethod

This method calculates barycentric coordinates using the mean value method.

References

source
GeometryOps.RadialDistanceType
RadialDistance <: SimplifyAlg

Simplifies geometries by removing points less than tol distance from the line between its neighboring points.

Keywords

  • ratio: the fraction of points that should remain after simplify. Useful as it will generalise for large collections of objects.

  • number: the number of points that should remain after simplify. Less useful for large collections of mixed size objects.

  • tol: the minimum distance between points.

source
GeometryOps.SimplifyAlgType
abstract type SimplifyAlg

Abstract 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.

source
GeometryOps.VisvalingamWhyattType
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 after simplify. Useful as it will generalise for large collections of objects.

  • number: the number of points that should remain after simplify. Less useful for large collections of mixed size objects.

  • tol: the minimum area of a triangle made with a point and its neighboring points.

source
GeometryOps._detMethod
_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]
source
GeometryOps._equals_curvesMethod
_equals_curves(c1, c2, closed_type1, closed_type2)::Bool

Two 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 closedtype1::Bool true if c1 is closed by definition (polygon, linear ring) closedtype2::Bool true if c2 is closed by definition (polygon, linear ring)

source
GeometryOps._intersection_pointMethod
_intersection_point(
+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 after simplify. Useful as it will generalise for large collections of objects.

  • number: the number of points that should remain after simplify. Less useful for large collections of mixed size objects.

  • tol: the minimum area of a triangle made with a point and its neighboring points.

source
GeometryOps._detMethod
_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]
source
GeometryOps._equals_curvesMethod
_equals_curves(c1, c2, closed_type1, closed_type2)::Bool

Two 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 closedtype1::Bool true if c1 is closed by definition (polygon, linear ring) closedtype2::Bool true if c2 is closed by definition (polygon, linear ring)

source
GeometryOps._intersection_pointMethod
_intersection_point(
     (a1, a2)::Tuple,
     (b1, b2)::Tuple,
-)

Calculates the intersection point between two lines if it exists, and as if the line extended to infinity, and the fractional component of each line from the initial end point to the intersection point. Inputs: (a1, a2)::Tuple{Tuple{::Real, ::Real}, Tuple{::Real, ::Real}} first line (b1, b2)::Tuple{Tuple{::Real, ::Real}, Tuple{::Real, ::Real}} second line Outputs: (x, y)::Tuple{::Real, ::Real} intersection point (t, u)::Tuple{::Real, ::Real} fractional length of lines to intersection Both are ::Nothing if point doesn't exist!

Calculation derivation can be found here: https://stackoverflow.com/questions/563198/

source
GeometryOps._line_intersectsMethod
_line_intersects(
-    edge_a::Edge,
-    edge_b::Edge,
-)::Bool

Returns true if there is at least one intersection between two edges.

source
GeometryOps._line_intersectsMethod
_line_intersects(
-    edges_a::Vector{Edge},
-    edges_b::Vector{Edge}
-)::Bool

Returns true if there is at least one intersection between edges within the two lists of edges.

source
GeometryOps._overlapsMethod
_overlaps(
-    (a1, a2)::Edge,
-    (b1, b2)::Edge
-)::Bool

If the edges overlap, meaning that they are colinear but each have one endpoint outside of the other edge, return true. Else false.

source
GeometryOps.applyMethod
apply(f, target::Type{<: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. But "deeper" objects may remain unchanged - such as points and linear rings if the target is the same PolygonTrait.

The result is a functionally similar geometry with values depending on f

  • threaded: true or false. Whether to use multithreading. Defaults to false.
  • crs: The CRS to attach to geometries. Defaults to nothing.
  • calc_extent: true or false. Whether to calculate the extent. Defaults to false.

Example

Flipped point the order in any feature or geometry, or iterables of either:

```juia 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)) end

source
GeometryOps.areaMethod
area(geom, ::Type{T} = Float64)::T

Returns the area of the geometry. This is computed slighly 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 is the sum of the areas of all of the sub-geometries.

Result will be of type T, where T is an optional argument with a default value of Float64.

source
GeometryOps.centroidMethod
centroid(trait, geom)::Tuple{T, T}

Returns the centroid of a polygon or multipolygon, which is calculated by weighting edges by their area component by convention.

source
GeometryOps.centroidMethod
centroid(geom)::Tuple{T, T}

Returns the centroid of a given line segment, linear ring, polygon, or mutlipolygon.

source
GeometryOps.centroidMethod
centroid(
+)

Calculates the intersection point between two lines if it exists, and as if the line extended to infinity, and the fractional component of each line from the initial end point to the intersection point. Inputs: (a1, a2)::Tuple{Tuple{::Real, ::Real}, Tuple{::Real, ::Real}} first line (b1, b2)::Tuple{Tuple{::Real, ::Real}, Tuple{::Real, ::Real}} second line Outputs: (x, y)::Tuple{::Real, ::Real} intersection point (t, u)::Tuple{::Real, ::Real} fractional length of lines to intersection Both are ::Nothing if point doesn't exist!

Calculation derivation can be found here: https://stackoverflow.com/questions/563198/

source
GeometryOps.applyMethod
apply(f, target::Type{<: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. But "deeper" objects may remain unchanged - such as points and linear rings if the target is the same PolygonTrait.

The result is a functionally similar geometry with values depending on f

  • threaded: true or false. Whether to use multithreading. Defaults to false.
  • crs: The CRS to attach to geometries. Defaults to nothing.
  • calc_extent: true or false. Whether to calculate the extent. Defaults to false.

Example

Flipped point the order in any feature or geometry, or iterables of either:

```juia 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)) end

source
GeometryOps.areaMethod
area(geom, ::Type{T} = Float64)::T

Returns the area of the geometry. This is computed slighly 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 is the sum of the areas of all of the sub-geometries.

Result will be of type T, where T is an optional argument with a default value of Float64.

source
GeometryOps.centroidMethod
centroid(trait, geom)::Tuple{T, T}

Returns the centroid of a polygon or multipolygon, which is calculated by weighting edges by their area component by convention.

source
GeometryOps.centroidMethod
centroid(geom)::Tuple{T, T}

Returns the centroid of a given line segment, linear ring, polygon, or mutlipolygon.

source
GeometryOps.centroidMethod
centroid(
     trait::Union{GI.LineStringTrait, GI.LinearRingTrait},
     geom,
-)::Tuple{T, T}

Returns the centroid of a line string or linear ring, which is calculated by weighting line segments by their length by convention.

source
GeometryOps.centroid_and_areaMethod
centroid_and_area(
+)::Tuple{T, T}

Returns the centroid of a line string or linear ring, which is calculated by weighting line segments by their length by convention.

source
GeometryOps.centroid_and_areaMethod
centroid_and_area(
     ::Union{GI.LineStringTrait, GI.LinearRingTrait}, 
     geom,
-)::(::Tuple{T, T}, ::Real)

Returns the centroid and area of a given geom.

source
GeometryOps.centroid_and_areaMethod
centroid_and_area(
     ::Union{GI.LineStringTrait, GI.LinearRingTrait},
     geom,
-)::(::Tuple{T, T}, ::Real)

Returns the centroid and area of a given a line string or a linear ring. Note that this is only valid if the line segment or linear ring is closed.

source
GeometryOps.centroid_and_lengthMethod
centroid_and_length(geom)::(::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.

source
GeometryOps.centroid_and_lengthMethod
centroid_and_length(geom)::(::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.

source
GeometryOps.containsMethod
contains(ft1::AbstractGeometry, ft2::AbstractGeometry)::Bool

Return 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 (geometry b) must not intersect the exterior of the primary (geometry a). contains returns the exact opposite result of within.

Examples

import GeometryOps as GO, GeoInterface as GI
+)::(::Tuple{T, T}, ::Real)

Returns the centroid and area of a given a line string or a linear ring. Note that this is only valid if the line segment or linear ring is closed.

source
GeometryOps.centroid_and_lengthMethod
centroid_and_length(geom)::(::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.

source
GeometryOps.centroid_and_lengthMethod
centroid_and_length(geom)::(::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.

source
GeometryOps.containsMethod
contains(g1::AbstractGeometry, g2::AbstractGeometry)::Bool

Return 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 = (1, 2)
+point = GI.Point((1, 2))
 
 GO.contains(line, point)
 # output
-true
source
GeometryOps.crossesMethod
 crosses(geom1, geom2)::Bool

Return 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
+true
source
GeometryOps.coveredbyMethod
coveredby(g1, g2)::Bool

Return 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
+true
source
GeometryOps.coversMethod
covers(g1::AbstractGeometry, g2::AbstractGeometry)::Bool

Return 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, 1), (1, 2), (1, 3), (1, 4)])
+l2 = GI.LineString([(1, 1), (1, 2)])
+
+GO.covers(l1, l2)
+# output
+true
source
GeometryOps.crossesMethod
 crosses(geom1, geom2)::Bool

Return 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
 
 line1 = GI.LineString([(1, 1), (1, 2), (1, 3), (1, 4)])
 line2 = GI.LineString([(-2, 2), (4, 2)])
 
 GO.crosses(line1, line2)
 # output
-true
source
GeometryOps.disjointMethod
disjoint(geom1, geom2)::Bool

Return true if the intersection of the two geometries is an empty set.

Examples

import GeometryOps as GO, GeoInterface as GI
+true
source
GeometryOps.disjointMethod
disjoint(geom1, geom2)::Bool

Return 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
 
-poly = GI.Polygon([[(-1, 2), (3, 2), (3, 3), (-1, 3), (-1, 2)]])
-point = (1, 1)
-GO.disjoint(poly, point)
+line = GI.LineString([(1, 1), (1, 2), (1, 3), (1, 4)])
+point = (2, 2)
+GO.disjoint(point, line)
 
 # output
-true
source
GeometryOps.distanceMethod
distance(point, geom, ::Type{T} = Float64)::T

Calculates the ditance 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.

source
GeometryOps.embed_extentMethod
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: true or false. Whether to use multithreading. Defaults to false.
  • crs: The CRS to attach to geometries. Defaults to nothing.
source
GeometryOps.equalsMethod
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.

source
GeometryOps.equalsMethod
equals(geom1, geom2)::Bool

Compare two Geometries return true if they are the same geometry.

Examples

import GeometryOps as GO, GeoInterface as GI
+true
source
GeometryOps.distanceMethod
distance(point, geom, ::Type{T} = Float64)::T

Calculates the ditance 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.

source
GeometryOps.embed_extentMethod
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: true or false. Whether to use multithreading. Defaults to false.
  • crs: The CRS to attach to geometries. Defaults to nothing.
source
GeometryOps.equalsMethod
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.

source
GeometryOps.equalsMethod
equals(geom1, geom2)::Bool

Compare 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
-true
source
GeometryOps.equalsMethod
equals(
     ::GI.LinearRingTrait, l1,
     ::GI.LinearRingTrait, l2,
-)::Bool

Two 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.

source
GeometryOps.equalsMethod
equals(
+)::Bool

Two 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.

source
GeometryOps.equalsMethod
equals(
     ::GI.LinearRingTrait, l1,
     ::Union{GI.LineTrait, GI.LineStringTrait}, l2,
-)::Bool

A 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 defintion, but rings are, so the line must have a repeated last point to be equal

source
GeometryOps.equalsMethod
equals(::GI.MultiPointTrait, mp1, ::GI.MultiPointTrait, mp2)::Bool

Two multipoints are equal if they share the same set of points.

source
GeometryOps.equalsMethod
equals(::GI.MultiPointTrait, mp1, ::GI.PointTrait, p2)::Bool

A point and a multipoint are equal if the multipoint is composed of a single point that is equivalent to the given point.

source
GeometryOps.equalsMethod
equals(::GI.PolygonTrait, geom_a, ::GI.PolygonTrait, geom_b)::Bool

Two multipolygons are equal if they share the same set of polygons.

source
GeometryOps.equalsMethod
equals(::GI.MultiPolygonTrait, geom_a, ::GI.PolygonTrait, geom_b)::Bool

A polygon and a multipolygon are equal if the multipolygon is composed of a single polygon that is equivalent to the given polygon.

source
GeometryOps.equalsMethod
equals(::GI.PointTrait, p1, ::GI.MultiPointTrait, mp2)::Bool

A point and a multipoint are equal if the multipoint is composed of a single point that is equivalent to the given point.

source
GeometryOps.equalsMethod
equals(::GI.PointTrait, p1, ::GI.PointTrait, p2)::Bool

Two points are the same if they have the same x and y (and z if 3D) coordinates.

source
GeometryOps.equalsMethod
equals(::GI.PolygonTrait, geom_a, ::GI.MultiPolygonTrait, geom_b)::Bool

A polygon and a multipolygon are equal if the multipolygon is composed of a single polygon that is equivalent to the given polygon.

source
GeometryOps.equalsMethod
equals(::GI.PolygonTrait, geom_a, ::GI.PolygonTrait, geom_b)::Bool

Two polygons are equal if they share the same exterior edge and holes.

source
GeometryOps.equalsMethod
equals(
+)::Bool

A 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 defintion, but rings are, so the line must have a repeated last point to be equal

source
GeometryOps.equalsMethod
equals(::GI.MultiPointTrait, mp1, ::GI.MultiPointTrait, mp2)::Bool

Two multipoints are equal if they share the same set of points.

source
GeometryOps.equalsMethod
equals(::GI.MultiPointTrait, mp1, ::GI.PointTrait, p2)::Bool

A point and a multipoint are equal if the multipoint is composed of a single point that is equivalent to the given point.

source
GeometryOps.equalsMethod
equals(::GI.PolygonTrait, geom_a, ::GI.PolygonTrait, geom_b)::Bool

Two multipolygons are equal if they share the same set of polygons.

source
GeometryOps.equalsMethod
equals(::GI.MultiPolygonTrait, geom_a, ::GI.PolygonTrait, geom_b)::Bool

A polygon and a multipolygon are equal if the multipolygon is composed of a single polygon that is equivalent to the given polygon.

source
GeometryOps.equalsMethod
equals(::GI.PointTrait, p1, ::GI.MultiPointTrait, mp2)::Bool

A point and a multipoint are equal if the multipoint is composed of a single point that is equivalent to the given point.

source
GeometryOps.equalsMethod
equals(::GI.PointTrait, p1, ::GI.PointTrait, p2)::Bool

Two points are the same if they have the same x and y (and z if 3D) coordinates.

source
GeometryOps.equalsMethod
equals(::GI.PolygonTrait, geom_a, ::GI.MultiPolygonTrait, geom_b)::Bool

A polygon and a multipolygon are equal if the multipolygon is composed of a single polygon that is equivalent to the given polygon.

source
GeometryOps.equalsMethod
equals(::GI.PolygonTrait, geom_a, ::GI.PolygonTrait, geom_b)::Bool

Two polygons are equal if they share the same exterior edge and holes.

source
GeometryOps.equalsMethod
equals(
     ::Union{GI.LineTrait, GI.LineStringTrait}, l1,
     ::GI.LinearRingTrait, l2,
-)::Bool

A 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 defintion, but rings are, so the line must have a repeated last point to be equal

source
GeometryOps.equalsMethod
equals(
+)::Bool

A 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 defintion, but rings are, so the line must have a repeated last point to be equal

source
GeometryOps.equalsMethod
equals(
     ::Union{GI.LineTrait, GI.LineStringTrait}, l1,
     ::Union{GI.LineTrait, GI.LineStringTrait}, l2,
-)::Bool

Two lines/linestrings are equal if they share the same set of points going along the curve. Note that lines/linestrings aren't closed by defintion.

source
GeometryOps.equalsMethod
equals(::T, geom_a, ::T, geom_b)::Bool

Two geometries of the same type, which don't have a equals function to dispatch off of should throw an error.

source
GeometryOps.flattenMethod
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.

source
GeometryOps.flipMethod
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: true or false. Whether to use multithreading. Defaults to false.
  • crs: The CRS to attach to geometries. Defaults to nothing.
  • calc_extent: true or false. Whether to calculate the extent. Defaults to false.
source
GeometryOps.intersectionMethod
intersection(geom_a, geom_b)::Union{Tuple{::Real, ::Real}, ::Nothing}

Return an intersection point between two geometries. Return nothing if none are found. Else, the return type depends on the input. It will be a union between: a point, a line, a linear ring, a polygon, or a multipolygon

Example

import GeoInterface as GI, GeometryOps as GO
+)::Bool

Two lines/linestrings are equal if they share the same set of points going along the curve. Note that lines/linestrings aren't closed by defintion.

source
GeometryOps.equalsMethod
equals(::T, geom_a, ::T, geom_b)::Bool

Two geometries of the same type, which don't have a equals function to dispatch off of should throw an error.

source
GeometryOps.flattenMethod
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.

source
GeometryOps.flipMethod
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: true or false. Whether to use multithreading. Defaults to false.
  • crs: The CRS to attach to geometries. Defaults to nothing.
  • calc_extent: true or false. Whether to calculate the extent. Defaults to false.
source
GeometryOps.intersectionMethod
intersection(geom_a, geom_b)::Union{Tuple{::Real, ::Real}, ::Nothing}

Return an intersection point between two geometries. Return nothing if none are found. Else, the return type depends on the input. It will be a union between: a point, a line, a linear ring, a polygon, or a multipolygon

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.intersection(line1, line2)
 
 # output
-(125.58375366067547, -14.83572303404496)
source
GeometryOps.intersectionMethod
intersection(
     ::GI.AbstractTrait, geom_a,
     ::GI.AbstractTrait, geom_b,
 )::Union{
     ::Vector{Vector{Tuple{::Real, ::Real}}}, # is this a good return type?
     ::Nothing
-}

Calculates the intersection between two line segments. Return nothing if there isn't one.

source
GeometryOps.intersectionMethod
intersection(
     ::GI.LineTrait, line_a,
     ::GI.LineTrait, line_b,
 )::Union{
     ::Tuple{::Real, ::Real},
     ::Nothing
-}

Calculates the intersection between two line segments. Return nothing if there isn't one.

source
GeometryOps.intersectionMethod
intersection(
     ::GI.PolygonTrait, poly_a,
     ::GI.PolygonTrait, poly_b,
 )::Union{
     ::Vector{Vector{Tuple{::Real, ::Real}}}, # is this a good return type?
     ::Nothing
-}

Calculates the intersection between two line segments. Return nothing if there isn't one.

source
GeometryOps.intersection_pointsMethod
intersection_points(
     geom_a,
     geom_b,
 )::Union{
     ::Vector{::Tuple{::Real, ::Real}},
     ::Nothing,
-}

Return a list of intersection points between two geometries. If no intersection point was possible given geometry extents, return nothing. If none are found, return an empty list.

source
GeometryOps.intersection_pointsMethod
intersection_points(
+}

Return a list of intersection points between two geometries. If no intersection point was possible given geometry extents, return nothing. If none are found, return an empty list.

source
GeometryOps.intersection_pointsMethod
intersection_points(
     ::GI.AbstractTrait, geom_a,
     ::GI.AbstractTrait, geom_b,
 )::Union{
     ::Vector{::Tuple{::Real, ::Real}},
     ::Nothing,
-}

Calculates the list of intersection points between two geometries, inlcuding line segments, line strings, linear rings, polygons, and multipolygons. If no intersection points were possible given geometry extents, return nothing. If none are found, return an empty list.

source
GeometryOps.intersectsMethod
intersects(geom1, geom2)::Bool

Check if two geometries intersect, returning true if so and false otherwise.

Example

import GeoInterface as GI, GeometryOps as GO
+}

Calculates the list of intersection points between two geometries, inlcuding line segments, line strings, linear rings, polygons, and multipolygons. If no intersection points were possible given geometry extents, return nothing. If none are found, return an empty list.

source
GeometryOps.intersectsMethod
intersects(geom1, geom2)::Bool

Return 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
-true
source
GeometryOps.intersectsMethod
intersects(::GI.AbstractTrait, a, ::GI.AbstractTrait, b)::Bool

Returns true if two geometries intersect with one another and false otherwise. For all geometries but lines, convert the geometry to a list of edges and cross compare the edges for intersections.

source
GeometryOps.intersectsMethod
intersects(::GI.LineTrait, a, ::GI.LineTrait, b)::Bool

Returns true if two line segments intersect and false otherwise.

source
GeometryOps.isclockwiseMethod
isclockwise(line::Union{LineString, Vector{Position}})::Bool

Take a ring and return true or false whether or not the ring is clockwise or counter-clockwise.

Example

import GeoInterface as GI, GeometryOps as GO
+true
source
GeometryOps.isclockwiseMethod
isclockwise(line::Union{LineString, Vector{Position}})::Bool

Take a ring and return true or false whether or not the ring is clockwise or counter-clockwise.

Example

import GeoInterface as GI, GeometryOps as GO
 
 ring = GI.LinearRing([(0, 0), (1, 1), (1, 0), (0, 0)])
 GO.isclockwise(ring)
 
 # output
-true
source
GeometryOps.isconcaveMethod
isconcave(poly::Polygon)::Bool

Take a polygon and return true or false as to whether it is concave or not.

Examples

import GeoInterface as GI, GeometryOps as GO
+true
source
GeometryOps.isconcaveMethod
isconcave(poly::Polygon)::Bool

Take 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
-false
source
GeometryOps.overlapsMethod
overlaps(geom1, geom2)::Bool

Compare 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
+false
source
GeometryOps.overlapsMethod
overlaps(geom1, geom2)::Bool

Compare 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
-true
source
GeometryOps.overlapsMethod
overlaps(::GI.AbstractTrait, geom1, ::GI.AbstractTrait, geom2)::Bool

For any non-specified pair, all have non-matching dimensions, return false.

source
GeometryOps.overlapsMethod
overlaps(::GI.LineTrait, line1, ::GI.LineTrait, line)::Bool

If the lines overlap, meaning that they are colinear but each have one endpoint outside of the other line, return true. Else false.

source
GeometryOps.overlapsMethod
overlaps(::GI.AbstractTrait, geom1, ::GI.AbstractTrait, geom2)::Bool

For any non-specified pair, all have non-matching dimensions, return false.

source
GeometryOps.overlapsMethod
overlaps(::GI.LineTrait, line1, ::GI.LineTrait, line)::Bool

If the lines overlap, meaning that they are colinear but each have one endpoint outside of the other line, return true. Else false.

source
GeometryOps.overlapsMethod
overlaps(
     ::GI.MultiPointTrait, points1,
     ::GI.MultiPointTrait, points2,
-)::Bool

If the multipoints overlap, meaning some, but not all, of the points within the multipoints are shared, return true.

source
GeometryOps.overlapsMethod
overlaps(
+)::Bool

If the multipoints overlap, meaning some, but not all, of the points within the multipoints are shared, return true.

source
GeometryOps.overlapsMethod
overlaps(
     ::GI.MultiPolygonTrait, polys1,
     ::GI.MultiPolygonTrait, polys2,
-)::Bool

Return true if at least one pair of polygons from multipolygons overlap. Else false.

source
GeometryOps.overlapsMethod
overlaps(
+)::Bool

Return true if at least one pair of polygons from multipolygons overlap. Else false.

source
GeometryOps.overlapsMethod
overlaps(
     ::GI.MultiPolygonTrait, polys1,
     ::GI.PolygonTrait, poly2,
-)::Bool

Return true if polygon overlaps with at least one of the polygons within the multipolygon. Else false.

source
GeometryOps.overlapsMethod
overlaps(
+)::Bool

Return true if polygon overlaps with at least one of the polygons within the multipolygon. Else false.

source
GeometryOps.overlapsMethod
overlaps(
     ::GI.PolygonTrait, poly1,
     ::GI.MultiPolygonTrait, polys2,
-)::Bool

Return true if polygon overlaps with at least one of the polygons within the multipolygon. Else false.

source
GeometryOps.overlapsMethod
overlaps(
+)::Bool

Return true if polygon overlaps with at least one of the polygons within the multipolygon. Else false.

source
GeometryOps.overlapsMethod
overlaps(
     trait_a::GI.PolygonTrait, poly_a,
     trait_b::GI.PolygonTrait, poly_b,
-)::Bool

If the two polygons intersect with one another, but are not equal, return true. Else false.

source
GeometryOps.overlapsMethod
overlaps(
+)::Bool

If the two polygons intersect with one another, but are not equal, return true. Else false.

source
GeometryOps.overlapsMethod
overlaps(
     ::Union{GI.LineStringTrait, GI.LinearRing}, line1,
     ::Union{GI.LineStringTrait, GI.LinearRing}, line2,
-)::Bool

If the curves overlap, meaning that at least one edge of each curve overlaps, return true. Else false.

source
GeometryOps.point_in_polygonMethod
point_in_polygon(point::Point, polygon::Union{Polygon, MultiPolygon}, ignore_boundary::Bool=false)::Bool

Take a Point and a Polygon and determine if the point resides inside the polygon. The polygon can be convex or concave. The function accounts for holes.

Examples

import GeoInterface as GI, GeometryOps as GO
-
-point = (-77.0, 44.0)
-poly = GI.Polygon([[(-81, 41), (-81, 47), (-72, 47), (-72, 41), (-81, 41)]])
-GO.point_in_polygon(point, poly)
-
-# output
-true
source
GeometryOps.point_on_lineMethod
point_on_line(point::Point, line::LineString; ignore_end_vertices::Bool=false)::Bool

Return true if a point is on a line. Accept a optional parameter to ignore the start and end vertices of the linestring.

Examples

import GeoInterface as GI, GeometryOps as GO
-
-point = (1, 1)
-line = GI.LineString([(0, 0), (3, 3), (4, 4)])
-GO.point_on_line(point, line)
-
-# output
-true
source
GeometryOps.polygon_to_lineMethod
polygon_to_line(poly::Polygon)

Converts a Polygon to LineString or MultiLineString

Examples

import GeometryOps as GO, GeoInterface as GI
+)::Bool

If the curves overlap, meaning that at least one edge of each curve overlaps, return true. Else false.

source
GeometryOps.polygon_to_lineMethod
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, Vector{Tuple{Float64, Float64}}, Nothing, Nothing}([(-2.275543, 53.464547), (-2.275543, 53.489271), (-2.215118, 53.489271), (-2.215118, 53.464547), (-2.275543, 53.464547)], nothing, nothing)
source
GeometryOps.polygonizeMethod
polygonize(A; minpoints=10)
-polygonize(xs, ys, A; minpoints=10)

Convert matrix A to polygons.

If xs and ys are passed in they are used as the pixel center points.

Keywords

  • minpoints: ignore polygons with less than minpoints points.
source
GeometryOps.rebuildMethod
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)

source
GeometryOps.reconstructMethod
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.

Ususally used in combination with flatten.

source
GeometryOps.reprojectMethod
reproject(geometry; source_crs, target_crs, transform, always_xy, time)
+GeoInterface.Wrappers.LineString{false, false, Vector{Tuple{Float64, Float64}}, Nothing, Nothing}([(-2.275543, 53.464547), (-2.275543, 53.489271), (-2.215118, 53.489271), (-2.215118, 53.464547), (-2.275543, 53.464547)], nothing, nothing)
source
GeometryOps.polygonizeMethod
polygonize(A; minpoints=10)
+polygonize(xs, ys, A; minpoints=10)

Convert matrix A to polygons.

If xs and ys are passed in they are used as the pixel center points.

Keywords

  • minpoints: ignore polygons with less than minpoints points.
source
GeometryOps.rebuildMethod
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)

source
GeometryOps.reconstructMethod
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.

Ususally used in combination with flatten.

source
GeometryOps.reprojectMethod
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.WrapperGeometry geometries, wrapping views of a Vector{Proj.Point{D}}, where D is the dimension.

Arguments

  • geometry: Any GeoInterface.jl compatible geometries.
  • source_crs: the source coordinate referece system, as a GeoFormatTypes.jl object or a string.
  • target_crs: the target coordinate referece 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 retreivable from the geometry with GeoInterface.crs(geometry).

Keywords

  • always_xy: force x, y coordinate order, true by default. false will expect and return points in the crs coordinate order.
  • time: the time for the coordinates. Inf by default.
  • threaded: true or false. Whether to use multithreading. Defaults to false.
  • crs: The CRS to attach to geometries. Defaults to nothing.
  • calc_extent: true or false. Whether to calculate the extent. Defaults to false.
source
GeometryOps.signed_areaMethod
signed_area(geom, ::Type{T} = Float64)::T

Returns the signed area of the geometry, based on winding order. This is computed slighly 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.

source
GeometryOps.signed_distanceMethod
signed_distance(point, geom, ::Type{T} = Float64)::T

Calculates 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.

source
GeometryOps.simplifyMethod
simplify(obj; kw...)
+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.WrapperGeometry geometries, wrapping views of a Vector{Proj.Point{D}}, where D is the dimension.

Arguments

  • geometry: Any GeoInterface.jl compatible geometries.
  • source_crs: the source coordinate referece system, as a GeoFormatTypes.jl object or a string.
  • target_crs: the target coordinate referece 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 retreivable from the geometry with GeoInterface.crs(geometry).

Keywords

  • always_xy: force x, y coordinate order, true by default. false will expect and return points in the crs coordinate order.
  • time: the time for the coordinates. Inf by default.
  • threaded: true or false. Whether to use multithreading. Defaults to false.
  • crs: The CRS to attach to geometries. Defaults to nothing.
  • calc_extent: true or false. Whether to calculate the extent. Defaults to false.
source
GeometryOps.signed_areaMethod
signed_area(geom, ::Type{T} = Float64)::T

Returns the signed area of the geometry, based on winding order. This is computed slighly 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.

source
GeometryOps.signed_distanceMethod
signed_distance(point, geom, ::Type{T} = Float64)::T

Calculates 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.

source
GeometryOps.simplifyMethod
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 decreaseing performance.

PoinTrait and MultiPointTrait are returned unchanged.

The default behaviour is simplify(DouglasPeucker(; kw...), obj). Pass in other SimplifyAlg to use other algorithms.

Keywords

  • threaded: true or false. Whether to use multithreading. Defaults to false.
  • crs: The CRS to attach to geometries. Defaults to nothing.
  • calc_extent: true or false. Whether to calculate the extent. Defaults to false.

Keywords for DouglasPeucker are allowed when no algorithm is specified:

Keywords

  • ratio: the fraction of points that should remain after simplify. Useful as it will generalise for large collections of objects.
  • number: the number of points that should remain after simplify. Less useful for large collections of mixed size objects.

Example

Simplify a polygon to have six points:

import GeoInterface as GI
 import GeometryOps as GO
 
@@ -195,7 +185,14 @@
 GI.npoint(simple)
 
 # output
-6
source
GeometryOps.t_valueMethod
t_value(sᵢ, sᵢ₊₁, rᵢ, rᵢ₊₁)

Returns the "T-value" as described in Hormann's presentation [HormannPresentation] 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.

\[tᵢ = \frac{\mathrm{det}\left(sᵢ, sᵢ₊₁\right)}{rᵢ * rᵢ₊₁ + sᵢ ⋅ sᵢ₊₁}\]

```

source
GeometryOps.to_edgesMethod
to_edges()

Convert any geometry or collection of geometries into a flat vector of Tuple{Tuple{Float64,Float64},Tuple{Float64,Float64}} edges.

source
GeometryOps.transformMethod
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
+6
source
GeometryOps.t_valueMethod
t_value(sᵢ, sᵢ₊₁, rᵢ, rᵢ₊₁)

Returns the "T-value" as described in Hormann's presentation [HormannPresentation] 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.

\[tᵢ = \frac{\mathrm{det}\left(sᵢ, sᵢ₊₁\right)}{rᵢ * rᵢ₊₁ + sᵢ ⋅ sᵢ₊₁}\]

```

source
GeometryOps.to_edgesMethod
to_edges()

Convert any geometry or collection of geometries into a flat vector of Tuple{Tuple{Float64,Float64},Tuple{Float64,Float64}} edges.

source
GeometryOps.touchesMethod
touches(geom1, geom2)::Bool

Return 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 geometies 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
+true
source
GeometryOps.transformMethod
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
 
@@ -214,5 +211,12 @@
 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)
source
GeometryOps.tuplesMethod
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: true or false. Whether to use multithreading. Defaults to false.
  • crs: The CRS to attach to geometries. Defaults to nothing.
  • calc_extent: true or false. Whether to calculate the extent. Defaults to false.
source
GeometryOps.unwrapFunction
unwrap(target::Type{<:AbstractTrait}, obj)
-unwrap(f, target::Type{<:AbstractTrait}, obj)

Unwrap the object newst to vectors, down to the target trait.

If f is passed in it will be applied to the target geometries as they are found.

source
GeometryOps.weighted_meanMethod
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.

source
  • HormannPresentationK. Hormann and N. Sukumar. Generalized Barycentric Coordinates in Computer Graphics and Computational Mechanics. Taylor & Fancis, CRC Press, 2017.
+}[[4, 3], [6, 5], [7, 6], [4, 3]], nothing, nothing)], nothing, nothing)
source
GeometryOps.tuplesMethod
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: true or false. Whether to use multithreading. Defaults to false.
  • crs: The CRS to attach to geometries. Defaults to nothing.
  • calc_extent: true or false. Whether to calculate the extent. Defaults to false.
source
GeometryOps.unwrapFunction
unwrap(target::Type{<:AbstractTrait}, obj)
+unwrap(f, target::Type{<:AbstractTrait}, obj)

Unwrap the object newst to vectors, down to the target trait.

If f is passed in it will be applied to the target geometries as they are found.

source
GeometryOps.weighted_meanMethod
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.

source
GeometryOps.withinMethod
within(geom1, geom2)::Bool

Return true if the first geometry is completely within the second geometry. The interiors of both geometries must intersect and the interior and boundary of the primary geometry (geom1) must not intersect the exterior of the secondary geometry (geom2).

Furthermore, within returns the exact opposite result of contains.

Examples

import GeometryOps as GO, GeoInterface as GI
+
+line = GI.LineString([(1, 1), (1, 2), (1, 3), (1, 4)])
+point = (1, 2)
+GO.within(point, line)
+
+# output
+true
source
  • HormannPresentationK. Hormann and N. Sukumar. Generalized Barycentric Coordinates in Computer Graphics and Computational Mechanics. Taylor & Fancis, CRC Press, 2017.
diff --git a/dev/search/index.html b/dev/search/index.html index ef522acbd..05e5ad063 100644 --- a/dev/search/index.html +++ b/dev/search/index.html @@ -1,2 +1,2 @@ -Search · GeometryOps.jl

Loading search...

    +Search · GeometryOps.jl

    Loading search...

      diff --git a/dev/search_index.js b/dev/search_index.js index 1de0bc5ee..36d10b69a 100644 --- a/dev/search_index.js +++ b/dev/search_index.js @@ -1,3 +1,3 @@ var documenterSearchIndex = {"docs": -[{"location":"source/GeometryOps/#GeometryOps.jl","page":"GeometryOps.jl","title":"GeometryOps.jl","text":"","category":"section"},{"location":"source/GeometryOps/","page":"GeometryOps.jl","title":"GeometryOps.jl","text":"module GeometryOps\n\nusing GeoInterface\nusing GeometryBasics\nimport Proj\nusing LinearAlgebra\nimport ExactPredicates\nimport Proj.CoordinateTransformations.StaticArrays\n\nusing GeoInterface.Extents: Extents\n\nconst GI = GeoInterface\nconst GB = GeometryBasics\n\nconst TuplePoint = Tuple{Float64,Float64}\nconst Edge = Tuple{TuplePoint,TuplePoint}\n\ninclude(\"primitives.jl\")\ninclude(\"utils.jl\")\n\ninclude(\"methods/area.jl\")\ninclude(\"methods/barycentric.jl\")\ninclude(\"methods/bools.jl\")\ninclude(\"methods/centroid.jl\")\ninclude(\"methods/distance.jl\")\ninclude(\"methods/equals.jl\")\ninclude(\"methods/geom_relations/intersects.jl\")\ninclude(\"methods/geom_relations/contains.jl\")\ninclude(\"methods/geom_relations/crosses.jl\")\ninclude(\"methods/geom_relations/disjoint.jl\")\ninclude(\"methods/geom_relations/overlaps.jl\")\ninclude(\"methods/geom_relations/within.jl\")\ninclude(\"methods/polygonize.jl\")\n\ninclude(\"transformations/extent.jl\")\ninclude(\"transformations/flip.jl\")\ninclude(\"transformations/reproject.jl\")\ninclude(\"transformations/simplify.jl\")\ninclude(\"transformations/tuples.jl\")\ninclude(\"transformations/transform.jl\")\n\nend","category":"page"},{"location":"source/GeometryOps/","page":"GeometryOps.jl","title":"GeometryOps.jl","text":"","category":"page"},{"location":"source/GeometryOps/","page":"GeometryOps.jl","title":"GeometryOps.jl","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"const THREADED_KEYWORD = \"- `threaded`: `true` or `false`. Whether to use multithreading. Defaults to `false`.\"\nconst CRS_KEYWORD = \"- `crs`: The CRS to attach to geometries. Defaults to `nothing`.\"\nconst CALC_EXTENT_KEYWORD = \"- `calc_extent`: `true` or `false`. Whether to calculate the extent. Defaults to `false`.\"\n\nconst APPLY_KEYWORDS = \"\"\"\n$THREADED_KEYWORD\n$CRS_KEYWORD\n$CALC_EXTENT_KEYWORD\n\"\"\"","category":"page"},{"location":"source/primitives/#Primitive-functions","page":"Primitive functions","title":"Primitive functions","text":"","category":"section"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"This file mainly defines the apply function.","category":"page"},{"location":"source/primitives/#What-is-apply?","page":"Primitive functions","title":"What is apply?","text":"","category":"section"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"apply applies some function to every geometry matching the Target GeoInterface trait, in some arbitrarily nested object made up of:","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"AbstractArrays (we also try to iterate other non-GeoInteface compatible object)\nFeatureCollectionTrait objects\nFeatureTrait objects\nAbstractGeometryTrait objects","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"apply recursively calls itself through these nested layers until it reaches objects with the Target GeoInterface trait. When found apply applies the function f, and stops.","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"The outer recursive functions then progressively rebuild the object using GeoInterface objects matching the original traits.","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"If PointTrait is found but it is not the Target, an error is thrown. This likely means the object contains a different geometry trait to the target, such as MultiPointTrait when LineStringTrait was specified.","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"To handle this possibility it may be necessary to make Target a Union of traits found at the same level of nesting, and define methods of f to handle all cases.","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Be careful making a union across \"levels\" of nesting, e.g. Union{FeatureTrait,PolygonTrait}, as _apply will just never reach PolygonTrait when all the polygons are wrapped in a FeatureTrait object.","category":"page"},{"location":"source/primitives/#Embedding:","page":"Primitive functions","title":"Embedding:","text":"","category":"section"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"extent and crs can be embedded in all geometries, features, and feature collections as part of apply. Geometries deeper than Target will of course not have new extent or crs embedded.","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"calc_extent signals to recalculate an Extent and embed it.\ncrs will be embedded as-is","category":"page"},{"location":"source/primitives/#Threading","page":"Primitive functions","title":"Threading","text":"","category":"section"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Threading is used at the outermost level possible - over an array, feature collection, or e.g. a MultiPolygonTrait where each PolygonTrait sub-geometry may be calculated on a different thread.","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"\"\"\"\n apply(f, target::Type{<:AbstractTrait}, obj; kw...)\n\nReconstruct a geometry, feature, feature collection, or nested vectors of\neither using the function `f` on the `target` trait.\n\n`f(target_geom) => x` where `x` also has the `target` trait, or a trait that can\nbe substituted. For example, swapping `PolgonTrait` to `MultiPointTrait` will fail\nif the outer object has `MultiPolygonTrait`, but should work if it has `FeatureTrait`.\n\nObjects \"shallower\" than the target trait are always completely rebuilt, like\na `Vector` of `FeatureCollectionTrait` of `FeatureTrait` when the target\nhas `PolygonTrait` and is held in the features. But \"deeper\" objects may remain\nunchanged - such as points and linear rings if the target is the same `PolygonTrait`.\n\nThe result is a functionally similar geometry with values depending on `f`\n\n$APPLY_KEYWORDS","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Example","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Flipped point the order in any feature or geometry, or iterables of either:\n\n```juia\nimport GeoInterface as GI\nimport GeometryOps as GO\ngeom = GI.Polygon([GI.LinearRing([(1, 2), (3, 4), (5, 6), (1, 2)]),\n GI.LinearRing([(3, 4), (5, 6), (6, 7), (3, 4)])])\n\nflipped_geom = GO.apply(GI.PointTrait, geom) do p\n (GI.y(p), GI.x(p))\nend\n\"\"\"\napply(f, ::Type{Target}, geom; kw...) where Target = _apply(f, Target, geom; kw...)","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Call _apply again with the trait of geom","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"_apply(f, ::Type{Target}, geom; kw...) where Target =\n _apply(f, Target, GI.trait(geom), geom; kw...)","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"There is no trait and this is an AbstractArray - so just iterate over it calling _apply on the contents","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"function _apply(f, ::Type{Target}, ::Nothing, A::AbstractArray; threaded=false, kw...) where Target","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"For an Array there is nothing else to do but map _apply over all values maptasks may run this level threaded if threaded==true, but deeper `apply` called in the closure will not be threaded","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":" _maptasks(eachindex(A); threaded) do i\n _apply(f, Target, A[i]; threaded=false, kw...)\n end\nend","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"There is no trait and this is not an AbstractArray. Try to call _apply over it. We can't use threading as we don't know if we can can index into it. So just map. (TODO: maybe collect first if threaded=true so we can thread?)","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"_apply(f, ::Type{Target}, ::Nothing, iterable; kw...) where Target =\n map(x -> _apply(f, Target, x; kw...), iterable)","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Rewrap all FeatureCollectionTrait feature collections as GI.FeatureCollection","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"function _apply(f, ::Type{Target}, ::GI.FeatureCollectionTrait, fc;\n crs=GI.crs(fc), calc_extent=false, threaded=false\n) where Target","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Run _apply on all features in the feature collection, possibly threaded","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":" features = _maptasks(1:GI.nfeature(fc); threaded) do i\n feature = GI.getfeature(fc, i)\n _apply(f, Target, feature; crs, calc_extent, threaded=false)::GI.Feature\n end\n if calc_extent","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Calculate the extent of the features","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":" extent = mapreduce(GI.extent, Extents.union, features)","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Return a FeatureCollection with features, crs and caculated extent","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":" return GI.FeatureCollection(features; crs, extent)\n else","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Return a FeatureCollection with features and crs","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":" return GI.FeatureCollection(features; crs)\n end\nend","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Rewrap all FeatureTrait features as GI.Feature, keeping the properties","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"function _apply(f, ::Type{Target}, ::GI.FeatureTrait, feature;\n crs=GI.crs(feature), calc_extent=false, threaded=false\n) where Target","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Run _apply on the contained geometry","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":" geometry = _apply(f, Target, GI.geometry(feature); crs, calc_extent, threaded)","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Get the feature properties","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":" properties = GI.properties(feature)\n if calc_extent","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Calculate the extent of the geometry","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":" extent = GI.extent(geometry)","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Return a new Feature with the new geometry and calculated extent, but the oroginal properties and crs","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":" return GI.Feature(geometry; properties, crs, extent)\n else","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Return a new Feature with the new geometry, but the oroginal properties and crs","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":" return GI.Feature(geometry; properties, crs)\n end\nend","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Reconstruct nested geometries","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"function _apply(f, ::Type{Target}, trait, geom;\n crs=GI.crs(geom), calc_extent=false, threaded=false\n)::(GI.geointerface_geomtype(trait)) where Target","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Map _apply over all sub geometries of geom to create a new vector of geometries TODO handle zero length","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":" geoms = _maptasks(1:GI.ngeom(geom); threaded) do i\n _apply(f, Target, GI.getgeom(geom, i); crs, calc_extent, threaded=false)\n end\n if calc_extent","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Calculate the extent of the sub geometries","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":" extent = mapreduce(GI.extent, Extents.union, geoms)","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Return a new geometry of the same trait as geom, holding tnew geoms with crs and calcualted extent","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":" return rebuild(geom, geoms; crs, extent)\n else","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Return a new geometryof the same trait as geom, holding the new geoms with crs","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":" return rebuild(geom, geoms; crs)\n end\nend","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Fail loudly if we hit PointTrait without running f (after PointTrait there is no further to dig with _apply)","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"_apply(f, ::Type{Target}, trait::GI.PointTrait, geom; crs=nothing, kw...) where Target =\n throw(ArgumentError(\"target $Target not found, but reached a `PointTrait` leaf\"))","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Finally, these short methods are the main purpose of apply. The Trait is a subtype of the Target (or identical to it) So the Target is found. We apply f to geom and return it to previous _apply calls to be wrapped with the outer geometries/feature/featurecollection/array.","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"_apply(f, ::Type{Target}, ::Trait, geom; crs=GI.crs(geom), kw...) where {Target,Trait<:Target} = f(geom)","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Define some specific cases of this match to avoid method ambiguity","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"_apply(f, ::Type{GI.PointTrait}, trait::GI.PointTrait, geom; kw...) = f(geom)\n_apply(f, ::Type{GI.FeatureTrait}, ::GI.FeatureTrait, feature; kw...) = f(feature)\n_apply(f, ::Type{GI.FeatureCollectionTrait}, ::GI.FeatureCollectionTrait, fc; kw...) = f(fc)\n\n\"\"\"\n unwrap(target::Type{<:AbstractTrait}, obj)\n unwrap(f, target::Type{<:AbstractTrait}, obj)\n\nUnwrap the object newst to vectors, down to the target trait.\n\nIf `f` is passed in it will be applied to the target geometries\nas they are found.\n\"\"\"\nfunction unwrap end\nunwrap(target::Type, geom) = unwrap(identity, target, geom)","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Add dispatch argument for trait","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"unwrap(f, target::Type, geom) = unwrap(f, target, GI.trait(geom), geom)","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Try to unwrap over iterables","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"unwrap(f, target::Type, ::Nothing, iterable) =\n map(x -> unwrap(f, target, x), iterable)","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Rewrap feature collections","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"unwrap(f, target::Type, ::GI.FeatureCollectionTrait, fc) =\n map(x -> unwrap(f, target, x), GI.getfeature(fc))\nunwrap(f, target::Type, ::GI.FeatureTrait, feature) = unwrap(f, target, GI.geometry(feature))\nunwrap(f, target::Type, trait, geom) = map(g -> unwrap(f, target, g), GI.getgeom(geom))","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Apply f to the target geometry","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"unwrap(f, ::Type{Target}, ::Trait, geom) where {Target,Trait<:Target} = f(geom)","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Fail if we hit PointTrait","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"unwrap(f, target::Type, trait::GI.PointTrait, geom) =\n throw(ArgumentError(\"target $target not found, but reached a `PointTrait` leaf\"))","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Specific cases to avoid method ambiguity","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"unwrap(f, target::Type{GI.PointTrait}, trait::GI.PointTrait, geom) = f(geom)\nunwrap(f, target::Type{GI.FeatureTrait}, ::GI.FeatureTrait, feature) = f(feature)\nunwrap(f, target::Type{GI.FeatureCollectionTrait}, ::GI.FeatureCollectionTrait, fc) = f(fc)\n\n\"\"\"\n flatten(target::Type{<:GI.AbstractTrait}, obj)\n flatten(f, target::Type{<:GI.AbstractTrait}, obj)\n\nLazily flatten any `AbstractArray`, iterator, `FeatureCollectionTrait`,\n`FeatureTrait` or `AbstractGeometryTrait` object `obj`, so that objects\nwith the `target` trait are returned by the iterator.\n\nIf `f` is passed in it will be applied to the target geometries.\n\"\"\"\nflatten(::Type{Target}, geom) where {Target<:GI.AbstractTrait} = flatten(identity, Target, geom)\nflatten(f, ::Type{Target}, geom) where {Target<:GI.AbstractTrait} = _flatten(f, Target, geom)\n\n_flatten(f, ::Type{Target}, geom) where Target = _flatten(f, Target, GI.trait(geom), geom)","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Try to flatten over iterables","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"_flatten(f, ::Type{Target}, ::Nothing, iterable) where Target =\n Iterators.flatten(Iterators.map(x -> _flatten(f, Target, x), iterable))","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Flatten feature collections","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"function _flatten(f, ::Type{Target}, ::GI.FeatureCollectionTrait, fc) where Target\n Iterators.map(GI.getfeature(fc)) do feature\n _flatten(f, Target, feature)\n end |> Iterators.flatten\nend\n_flatten(f, ::Type{Target}, ::GI.FeatureTrait, feature) where Target =\n _flatten(f, Target, GI.geometry(feature))","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Apply f to the target geometry","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"_flatten(f, ::Type{Target}, ::Trait, geom) where {Target,Trait<:Target} = (f(geom),)\n_flatten(f, ::Type{Target}, trait, geom) where Target =\n Iterators.flatten(Iterators.map(g -> _flatten(f, Target, g), GI.getgeom(geom)))","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Fail if we hit PointTrait without running f","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"_flatten(f, ::Type{Target}, trait::GI.PointTrait, geom) where Target =\n throw(ArgumentError(\"target $Target not found, but reached a `PointTrait` leaf\"))","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Specific cases to avoid method ambiguity","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"_flatten(f, ::Type{<:GI.PointTrait}, ::GI.PointTrait, geom) = (f(geom),)\n_flatten(f, ::Type{<:GI.FeatureTrait}, ::GI.FeatureTrait, feature) = (f(feature),)\n_flatten(f, ::Type{<:GI.FeatureCollectionTrait}, ::GI.FeatureCollectionTrait, fc) = (f(fc),)\n\n\n\"\"\"\n reconstruct(geom, components)\n\nReconstruct `geom` from an iterable of component objects that match its structure.\n\nAll objects in `components` must have the same `GeoInterface.trait`.\n\nUsusally used in combination with `flatten`.\n\"\"\"\nfunction reconstruct(geom, components)\n obj, iter = _reconstruct(geom, components)\n return obj\nend\n\n_reconstruct(geom, components) =\n _reconstruct(typeof(GI.trait(first(components))), geom, components, 1)\n_reconstruct(::Type{Target}, geom, components, iter) where Target =\n _reconstruct(Target, GI.trait(geom), geom, components, iter)","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Try to reconstruct over iterables","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"function _reconstruct(::Type{Target}, ::Nothing, iterable, components, iter) where Target\n vect = map(iterable) do x","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"iter is updated by _reconstruct here","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":" obj, iter = _reconstruct(Target, x, components, iter)\n obj\n end\n return vect, iter\nend","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Reconstruct feature collections","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"function _reconstruct(::Type{Target}, ::GI.FeatureCollectionTrait, fc, components, iter) where Target\n features = map(GI.getfeature(fc)) do feature","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"iter is updated by _reconstruct here","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":" newfeature, iter = _reconstruct(Target, feature, components, iter)\n newfeature\n end\n return GI.FeatureCollection(features; crs=GI.crs(fc)), iter\nend\nfunction _reconstruct(::Type{Target}, ::GI.FeatureTrait, feature, components, iter) where Target\n geom, iter = _reconstruct(Target, GI.geometry(feature), components, iter)\n return GI.Feature(geom; properties=GI.properties(feature), crs=GI.crs(feature)), iter\nend\nfunction _reconstruct(::Type{Target}, trait, geom, components, iter) where Target\n geoms = map(GI.getgeom(geom)) do subgeom","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"iter is updated by _reconstruct here","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":" subgeom1, iter = _reconstruct(Target, GI.trait(subgeom), subgeom, components, iter)\n subgeom1\n end\n return rebuild(geom, geoms), iter\nend","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Apply f to the target geometry","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"_reconstruct(::Type{Target}, ::Trait, geom, components, iter) where {Target,Trait<:Target} =\n iterate(components, iter)","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Specific cases to avoid method ambiguity","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"_reconstruct(::Type{<:GI.PointTrait}, ::GI.PointTrait, geom, components, iter) = iterate(components, iter)\n_reconstruct(::Type{<:GI.FeatureTrait}, ::GI.FeatureTrait, feature, components, iter) = iterate(feature, iter)\n_reconstruct(::Type{<:GI.FeatureCollectionTrait}, ::GI.FeatureCollectionTrait, fc, components, iter) = iterate(fc, iter)","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Fail if we hit PointTrait without running f","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"_reconstruct(::Type{Target}, trait::GI.PointTrait, geom, components, iter) where Target =\n throw(ArgumentError(\"target $Target not found, but reached a `PointTrait` leaf\"))\n\n\nconst BasicsGeoms = Union{GB.AbstractGeometry,GB.AbstractFace,GB.AbstractPoint,GB.AbstractMesh,\n GB.AbstractPolygon,GB.LineString,GB.MultiPoint,GB.MultiLineString,GB.MultiPolygon,GB.Mesh}\n\n\"\"\"\n rebuild(geom, child_geoms)\n\nRebuild a geometry from child geometries.\n\nBy default geometries will be rebuilt as a `GeoInterface.Wrappers`\ngeometry, but `rebuild` can have methods added to it to dispatch\non geometries from other packages and specify how to rebuild them.\n\n(Maybe it should go into GeoInterface.jl)\n\"\"\"\nrebuild(geom, child_geoms; kw...) = rebuild(GI.trait(geom), geom, child_geoms; kw...)\nfunction rebuild(trait::GI.AbstractTrait, geom, child_geoms; crs=GI.crs(geom), extent=nothing)\n T = GI.geointerface_geomtype(trait)\n if GI.is3d(geom)","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"The Boolean type parameters here indicate 3d-ness and measure coordinate presence respectively.","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":" return T{true,false}(child_geoms; crs, extent)\n else\n return T{false,false}(child_geoms; crs, extent)\n end\nend","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"So that GeometryBasics geoms rebuild as themselves","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"function rebuild(trait::GI.AbstractTrait, geom::BasicsGeoms, child_geoms; crs=nothing)\n GB.geointerface_geomtype(trait)(child_geoms)\nend\nfunction rebuild(trait::GI.AbstractTrait, geom::Union{GB.LineString,GB.MultiPoint}, child_geoms; crs=nothing)\n GB.geointerface_geomtype(trait)(GI.convert.(GB.Point, child_geoms))\nend\nfunction rebuild(trait::GI.PolygonTrait, geom::GB.Polygon, child_geoms; crs=nothing)\n Polygon(child_geoms[1], child_geoms[2:end])\nend\n\nusing Base.Threads: nthreads, @threads, @spawn","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Threading utility, modified Mason Protters threading PSA run f over ntasks, where f recieves an AbstractArray/range of linear indices","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"function _maptasks(f, taskrange; threaded=false)\n if threaded\n ntasks = length(taskrange)","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Customize this as needed. More tasks have more overhead, but better load balancing","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":" tasks_per_thread = 2\n chunk_size = max(1, ntasks ÷ (tasks_per_thread * nthreads()))","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"partition the range into chunks","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":" task_chunks = Iterators.partition(taskrange, chunk_size)","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Map over the chunks","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":" tasks = map(task_chunks) do chunk","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Spawn a task to process this chunk","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":" @spawn begin","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Where we map f over the chunk indices","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":" map(f, chunk)\n end\n end","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Finally we join the results into a new vector","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":" return mapreduce(fetch, vcat, tasks)\n else\n return map(f, taskrange)\n end\nend","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/methods/polygonize/#Polygonizing-raster-data","page":"Polygonizing raster data","title":"Polygonizing raster data","text":"","category":"section"},{"location":"source/methods/polygonize/","page":"Polygonizing raster data","title":"Polygonizing raster data","text":"export polygonize","category":"page"},{"location":"source/methods/polygonize/","page":"Polygonizing raster data","title":"Polygonizing raster data","text":"The methods in this file are able to convert a raster image into a set of polygons, by contour detection using a clockwise Moore neighborhood method.","category":"page"},{"location":"source/methods/polygonize/","page":"Polygonizing raster data","title":"Polygonizing raster data","text":"The main entry point is the polygonize function.","category":"page"},{"location":"source/methods/polygonize/","page":"Polygonizing raster data","title":"Polygonizing raster data","text":"polygonize","category":"page"},{"location":"source/methods/polygonize/#Example","page":"Polygonizing raster data","title":"Example","text":"","category":"section"},{"location":"source/methods/polygonize/","page":"Polygonizing raster data","title":"Polygonizing raster data","text":"Here's a basic implementation, using the Makie.peaks() function. First, let's investigate the nature of the function:","category":"page"},{"location":"source/methods/polygonize/","page":"Polygonizing raster data","title":"Polygonizing raster data","text":"using Makie, GeometryOps\nn = 49\nxs, ys = LinRange(-3, 3, n), LinRange(-3, 3, n)\nzs = Makie.peaks(n)\nz_max_value = maximum(abs.(extrema(zs)))\nf, a, p = heatmap(\n xs, ys, zs;\n axis = (; aspect = DataAspect(), title = \"Exact function\")\n)\ncb = Colorbar(f[1, 2], p; label = \"Z-value\")\nf","category":"page"},{"location":"source/methods/polygonize/","page":"Polygonizing raster data","title":"Polygonizing raster data","text":"Now, we can use the polygonize function to convert the raster data into polygons.","category":"page"},{"location":"source/methods/polygonize/","page":"Polygonizing raster data","title":"Polygonizing raster data","text":"For this particular example, we chose a range of z-values between 0.8 and 3.2, which would provide two distinct polyogns with holes.","category":"page"},{"location":"source/methods/polygonize/","page":"Polygonizing raster data","title":"Polygonizing raster data","text":"polygons = polygonize(xs, ys, 0.8 .< zs .< 3.2)","category":"page"},{"location":"source/methods/polygonize/","page":"Polygonizing raster data","title":"Polygonizing raster data","text":"This returns a list of GeometryBasics.Polygon, which can be plotted immediately, or wrapped directly in a GeometryBasics.MultiPolygon. Let's see how these look:","category":"page"},{"location":"source/methods/polygonize/","page":"Polygonizing raster data","title":"Polygonizing raster data","text":"f, a, p = poly(polygons; label = \"Polygonized polygons\", axis = (; aspect = DataAspect()))","category":"page"},{"location":"source/methods/polygonize/","page":"Polygonizing raster data","title":"Polygonizing raster data","text":"Finally, let's plot the Makie contour lines on top, to see how well the polygonization worked:","category":"page"},{"location":"source/methods/polygonize/","page":"Polygonizing raster data","title":"Polygonizing raster data","text":"contour!(a, zs; labels = true, levels = [0.8, 3.2], label = \"Contour lines\")\nf","category":"page"},{"location":"source/methods/polygonize/#Implementation","page":"Polygonizing raster data","title":"Implementation","text":"","category":"section"},{"location":"source/methods/polygonize/","page":"Polygonizing raster data","title":"Polygonizing raster data","text":"The implementation follows:","category":"page"},{"location":"source/methods/polygonize/","page":"Polygonizing raster data","title":"Polygonizing raster data","text":"\"\"\"\n polygonize(A; minpoints=10)\n polygonize(xs, ys, A; minpoints=10)\n\nConvert matrix `A` to polygons.\n\nIf `xs` and `ys` are passed in they are used as the pixel center points.","category":"page"},{"location":"source/methods/polygonize/","page":"Polygonizing raster data","title":"Polygonizing raster data","text":"Keywords","category":"page"},{"location":"source/methods/polygonize/","page":"Polygonizing raster data","title":"Polygonizing raster data","text":"- `minpoints`: ignore polygons with less than `minpoints` points.\n\"\"\"\npolygonize(A::AbstractMatrix; kw...) = polygonize(axes(A)..., A; kw...)\n\nfunction polygonize(xs, ys, A::AbstractMatrix; minpoints=10)\n # This function uses a lazy map to get contours.\n contours = Iterators.map(get_contours(A)) do contour\n poly = map(contour) do xy\n x, y = Tuple(xy)\n Point2f(x + first(xs) - 1, y + first(ys) - 1)\n end\n end\n # If we filter off the minimum points, then it's a hair more efficient\n # not to convert contours with length < missingpoints to polygons.\n if minpoints > 1\n contours = Iterators.filter(contours) do contour\n length(contour) > minpoints\n end\n return map(Polygon, contours)\n else\n return map(Polygon, contours)\n end\nend\n\n# rotate direction clockwise\nrot_clockwise(dir) = (dir) % 8 + 1\n# rotate direction counterclockwise\nrot_counterclockwise(dir) = (dir + 6) % 8 + 1\n\n# move from current pixel to next in given direction\nfunction move(pixel, image, dir, dir_delta)\n newp = pixel + dir_delta[dir]\n height, width = size(image)\n if (0 < newp[1] <= height) && (0 < newp[2] <= width)\n if image[newp] != 0\n return newp\n end\n end\n return CartesianIndex(0, 0)\nend\n\n# finds direction between two given pixels\nfunction from_to(from, to, dir_delta)\n delta = to - from\n return findall(x -> x == delta, dir_delta)[1]\nend\n\nfunction detect_move(image, p0, p2, nbd, border, done, dir_delta)\n dir = from_to(p0, p2, dir_delta)\n moved = rot_clockwise(dir)\n p1 = CartesianIndex(0, 0)\n while moved != dir ## 3.1\n newp = move(p0, image, moved, dir_delta)\n if newp[1] != 0\n p1 = newp\n break\n end\n moved = rot_clockwise(moved)\n end\n\n if p1 == CartesianIndex(0, 0)\n return\n end\n\n p2 = p1 ## 3.2\n p3 = p0 ## 3.2\n done .= false\n while true\n dir = from_to(p3, p2, dir_delta)\n moved = rot_counterclockwise(dir)\n p4 = CartesianIndex(0, 0)\n done .= false\n while true ## 3.3\n p4 = move(p3, image, moved, dir_delta)\n if p4[1] != 0\n break\n end\n done[moved] = true\n moved = rot_counterclockwise(moved)\n end\n push!(border, p3) ## 3.4\n if p3[1] == size(image, 1) || done[3]\n image[p3] = -nbd\n elseif image[p3] == 1\n image[p3] = nbd\n end\n\n if (p4 == p0 && p3 == p1) ## 3.5\n break\n end\n p2 = p3\n p3 = p4\n end\nend\n\n\"\"\"\n get_contours(A::AbstractMatrix)\n\nReturns contours as vectors of `CartesianIndex`.\n\"\"\"\nfunction get_contours(image::AbstractMatrix)\n nbd = 1\n lnbd = 1\n image = Float64.(image)\n contour_list = Vector{typeof(CartesianIndex[])}()\n done = [false, false, false, false, false, false, false, false]\n\n # Clockwise Moore neighborhood.\n dir_delta = (CartesianIndex(-1, 0), CartesianIndex(-1, 1), CartesianIndex(0, 1), CartesianIndex(1, 1),\n CartesianIndex(1, 0), CartesianIndex(1, -1), CartesianIndex(0, -1), CartesianIndex(-1, -1))\n\n height, width = size(image)\n\n for i = 1:height\n lnbd = 1\n for j = 1:width\n fji = image[i, j]\n is_outer = (image[i, j] == 1 && (j == 1 || image[i, j-1] == 0)) ## 1 (a)\n is_hole = (image[i, j] >= 1 && (j == width || image[i, j+1] == 0))\n\n if is_outer || is_hole\n # 2\n border = CartesianIndex[]\n from = CartesianIndex(i, j)\n\n if is_outer\n nbd += 1\n from -= CartesianIndex(0, 1)\n\n else\n nbd += 1\n if fji > 1\n lnbd = fji\n end\n from += CartesianIndex(0, 1)\n end\n\n p0 = CartesianIndex(i, j)\n detect_move(image, p0, from, nbd, border, done, dir_delta) ## 3\n if isempty(border) ##TODO\n push!(border, p0)\n image[p0] = -nbd\n end\n push!(contour_list, border)\n end\n if fji != 0 && fji != 1\n lnbd = abs(fji)\n end\n\n end\n end\n\n return contour_list\nend","category":"page"},{"location":"source/methods/polygonize/","page":"Polygonizing raster data","title":"Polygonizing raster data","text":"","category":"page"},{"location":"source/methods/polygonize/","page":"Polygonizing raster data","title":"Polygonizing raster data","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/transformations/reproject/#Geometry-reprojection","page":"Geometry reprojection","title":"Geometry reprojection","text":"","category":"section"},{"location":"source/transformations/reproject/","page":"Geometry reprojection","title":"Geometry reprojection","text":"export reproject","category":"page"},{"location":"source/transformations/reproject/","page":"Geometry reprojection","title":"Geometry reprojection","text":"This file is pretty simple - it simply reprojects a geometry pointwise from one CRS to another. It uses the Proj package for the transformation, but this could be moved to an extension if needed.","category":"page"},{"location":"source/transformations/reproject/","page":"Geometry reprojection","title":"Geometry reprojection","text":"This works using the apply functionality.","category":"page"},{"location":"source/transformations/reproject/","page":"Geometry reprojection","title":"Geometry reprojection","text":"\"\"\"\n reproject(geometry; source_crs, target_crs, transform, always_xy, time)\n reproject(geometry, source_crs, target_crs; always_xy, time)\n reproject(geometry, transform; always_xy, time)\n\nReproject any GeoInterface.jl compatible `geometry` from `source_crs` to `target_crs`.\n\nThe returned object will be constructed from `GeoInterface.WrapperGeometry`\ngeometries, wrapping views of a `Vector{Proj.Point{D}}`, where `D` is the dimension.\n\n# Arguments\n\n- `geometry`: Any GeoInterface.jl compatible geometries.\n- `source_crs`: the source coordinate referece system, as a GeoFormatTypes.jl object or a string.\n- `target_crs`: the target coordinate referece system, as a GeoFormatTypes.jl object or a string.\n\nIf these a passed as keywords, `transform` will take priority.\nWithout it `target_crs` is always needed, and `source_crs` is\nneeded if it is not retreivable from the geometry with `GeoInterface.crs(geometry)`.\n\n# Keywords\n\n- `always_xy`: force x, y coordinate order, `true` by default.\n `false` will expect and return points in the crs coordinate order.\n- `time`: the time for the coordinates. `Inf` by default.\n$APPLY_KEYWORDS\n\"\"\"\nfunction reproject(geom;\n source_crs=nothing, target_crs=nothing, transform=nothing, kw...\n)\n if isnothing(transform)\n if isnothing(source_crs)\n source_crs = if GI.trait(geom) isa Nothing && geom isa AbstractArray\n GeoInterface.crs(first(geom))\n else\n GeoInterface.crs(geom)\n end\n end","category":"page"},{"location":"source/transformations/reproject/","page":"Geometry reprojection","title":"Geometry reprojection","text":"If its still nothing, error","category":"page"},{"location":"source/transformations/reproject/","page":"Geometry reprojection","title":"Geometry reprojection","text":" isnothing(source_crs) && throw(ArgumentError(\"geom has no crs attatched. Pass a `source_crs` keyword\"))","category":"page"},{"location":"source/transformations/reproject/","page":"Geometry reprojection","title":"Geometry reprojection","text":"Otherwise reproject","category":"page"},{"location":"source/transformations/reproject/","page":"Geometry reprojection","title":"Geometry reprojection","text":" reproject(geom, source_crs, target_crs; kw...)\n else\n reproject(geom, transform; kw...)\n end\nend\nfunction reproject(geom, source_crs, target_crs;\n time=Inf,\n always_xy=true,\n transform=Proj.Transformation(Proj.CRS(source_crs), Proj.CRS(target_crs); always_xy),\n kw...\n)\n reproject(geom, transform; time, target_crs, kw...)\nend\nfunction reproject(geom, transform::Proj.Transformation; time=Inf, target_crs=nothing, kw...)\n if _is3d(geom)\n return apply(PointTrait, geom; crs=target_crs, kw...) do p\n transform(GI.x(p), GI.y(p), GI.z(p))\n end\n else\n return apply(PointTrait, geom; crs=target_crs, kw...) do p\n transform(GI.x(p), GI.y(p))\n end\n end\nend","category":"page"},{"location":"source/transformations/reproject/","page":"Geometry reprojection","title":"Geometry reprojection","text":"","category":"page"},{"location":"source/transformations/reproject/","page":"Geometry reprojection","title":"Geometry reprojection","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/methods/geom_relations/crosses/#Crossing-checks","page":"Crossing checks","title":"Crossing checks","text":"","category":"section"},{"location":"source/methods/geom_relations/crosses/","page":"Crossing checks","title":"Crossing checks","text":"\"\"\"\n crosses(geom1, geom2)::Bool\n\nReturn `true` if the intersection results in a geometry whose dimension is one less than\nthe maximum dimension of the two source geometries and the intersection set is interior to\nboth source geometries.\n\nTODO: broken\n\n# Examples\n```julia\nimport GeoInterface as GI, GeometryOps as GO\n\nline1 = GI.LineString([(1, 1), (1, 2), (1, 3), (1, 4)])\nline2 = GI.LineString([(-2, 2), (4, 2)])\n\nGO.crosses(line1, line2)","category":"page"},{"location":"source/methods/geom_relations/crosses/","page":"Crossing checks","title":"Crossing checks","text":"output","category":"page"},{"location":"source/methods/geom_relations/crosses/","page":"Crossing checks","title":"Crossing checks","text":"true\n```\n\"\"\"\ncrosses(g1, g2)::Bool = crosses(trait(g1), g1, trait(g2), g2)::Bool\ncrosses(t1::FeatureTrait, g1, t2, g2)::Bool = crosses(GI.geometry(g1), g2)\ncrosses(t1, g1, t2::FeatureTrait, g2)::Bool = crosses(g1, geometry(g2))\ncrosses(::MultiPointTrait, g1, ::LineStringTrait, g2)::Bool = multipoint_crosses_line(g1, g2)\ncrosses(::MultiPointTrait, g1, ::PolygonTrait, g2)::Bool = multipoint_crosses_poly(g1, g2)\ncrosses(::LineStringTrait, g1, ::MultiPointTrait, g2)::Bool = multipoint_crosses_lines(g2, g1)\ncrosses(::LineStringTrait, g1, ::PolygonTrait, g2)::Bool = line_crosses_poly(g1, g2)\ncrosses(::LineStringTrait, g1, ::LineStringTrait, g2)::Bool = line_crosses_line(g1, g2)\ncrosses(::PolygonTrait, g1, ::MultiPointTrait, g2)::Bool = multipoint_crosses_poly(g2, g1)\ncrosses(::PolygonTrait, g1, ::LineStringTrait, g2)::Bool = line_crosses_poly(g2, g1)\n\nfunction multipoint_crosses_line(geom1, geom2)\n int_point = false\n ext_point = false\n i = 1\n np2 = GI.npoint(geom2)\n\n while i < GI.npoint(geom1) && !int_point && !ext_point\n for j in 1:GI.npoint(geom2) - 1\n exclude_boundary = (j === 1 || j === np2 - 2) ? :none : :both\n if point_on_segment(GI.getpoint(geom1, i), (GI.getpoint(geom2, j), GI.getpoint(geom2, j + 1)); exclude_boundary)\n int_point = true\n else\n ext_point = true\n end\n end\n i += 1\n end\n\n return int_point && ext_point\nend\n\nfunction line_crosses_line(line1, line2)\n np2 = GI.npoint(line2)\n if intersects(line1, line2)\n for i in 1:GI.npoint(line1) - 1\n for j in 1:GI.npoint(line2) - 1\n exclude_boundary = (j === 1 || j === np2 - 2) ? :none : :both\n pa = GI.getpoint(line1, i)\n pb = GI.getpoint(line1, i + 1)\n p = GI.getpoint(line2, j)\n point_on_segment(p, (pa, pb); exclude_boundary) && return true\n end\n end\n end\n return false\nend\n\nfunction line_crosses_poly(line, poly)\n for l in flatten(AbstractCurveTrait, poly)\n intersects(line, l) && return true\n end\n return false\nend\n\nfunction multipoint_crosses_poly(mp, poly)\n int_point = false\n ext_point = false\n\n for p in GI.getpoint(mp)\n if point_in_polygon(p, poly)\n int_point = true\n else\n ext_point = true\n end\n int_point && ext_point && return true\n end\n return false\nend","category":"page"},{"location":"source/methods/geom_relations/crosses/","page":"Crossing checks","title":"Crossing checks","text":"","category":"page"},{"location":"source/methods/geom_relations/crosses/","page":"Crossing checks","title":"Crossing checks","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/methods/barycentric/#Barycentric-coordinates","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"","category":"section"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"export barycentric_coordinates, barycentric_coordinates!, barycentric_interpolate\nexport MeanValue","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"Generalized barycentric coordinates are a generalization of barycentric coordinates, which are typically used in triangles, to arbitrary polygons.","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"They provide a way to express a point within a polygon as a weighted average of the polygon's vertices.","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"In the case of a triangle, barycentric coordinates are a set of three numbers (λ_1 λ_2 λ_3), each associated with a vertex of the triangle. Any point within the triangle can be expressed as a weighted average of the vertices, where the weights are the barycentric coordinates. The weights sum to 1, and each is non-negative.","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"For a polygon with n vertices, generalized barycentric coordinates are a set of n numbers (λ_1 λ_2 λ_n), each associated with a vertex of the polygon. Any point within the polygon can be expressed as a weighted average of the vertices, where the weights are the generalized barycentric coordinates.","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"As with the triangle case, the weights sum to 1, and each is non-negative.","category":"page"},{"location":"source/methods/barycentric/#Example","page":"Barycentric coordinates","title":"Example","text":"","category":"section"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"This example was taken from this page of CGAL's documentation.","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"using GeometryOps, Makie\nusing GeometryOps.GeometryBasics\n# Define a polygon\npolygon_points = Point3f[\n(0.03, 0.05, 0.00), (0.07, 0.04, 0.02), (0.10, 0.04, 0.04),\n(0.14, 0.04, 0.06), (0.17, 0.07, 0.08), (0.20, 0.09, 0.10),\n(0.22, 0.11, 0.12), (0.25, 0.11, 0.14), (0.27, 0.10, 0.16),\n(0.30, 0.07, 0.18), (0.31, 0.04, 0.20), (0.34, 0.03, 0.22),\n(0.37, 0.02, 0.24), (0.40, 0.03, 0.26), (0.42, 0.04, 0.28),\n(0.44, 0.07, 0.30), (0.45, 0.10, 0.32), (0.46, 0.13, 0.34),\n(0.46, 0.19, 0.36), (0.47, 0.26, 0.38), (0.47, 0.31, 0.40),\n(0.47, 0.35, 0.42), (0.45, 0.37, 0.44), (0.41, 0.38, 0.46),\n(0.38, 0.37, 0.48), (0.35, 0.36, 0.50), (0.32, 0.35, 0.52),\n(0.30, 0.37, 0.54), (0.28, 0.39, 0.56), (0.25, 0.40, 0.58),\n(0.23, 0.39, 0.60), (0.21, 0.37, 0.62), (0.21, 0.34, 0.64),\n(0.23, 0.32, 0.66), (0.24, 0.29, 0.68), (0.27, 0.24, 0.70),\n(0.29, 0.21, 0.72), (0.29, 0.18, 0.74), (0.26, 0.16, 0.76),\n(0.24, 0.17, 0.78), (0.23, 0.19, 0.80), (0.24, 0.22, 0.82),\n(0.24, 0.25, 0.84), (0.21, 0.26, 0.86), (0.17, 0.26, 0.88),\n(0.12, 0.24, 0.90), (0.07, 0.20, 0.92), (0.03, 0.15, 0.94),\n(0.01, 0.10, 0.97), (0.02, 0.07, 1.00)]\n# Plot it!\n# First, we'll plot the polygon using Makie's rendering:\nf, a1, p1 = poly(\n polygon_points;\n color = last.(polygon_points), colormap = cgrad(:jet, 18; categorical = true),\n axis = (;\n aspect = DataAspect(), title = \"Makie mesh based polygon rendering\", subtitle = \"CairoMakie\"\n ),\n figure = (; resolution = (800, 400),)\n)\n\nMakie.update_state_before_display!(f) # We have to call this explicitly, to get the axis limits correct\n# Now that we've plotted the first polygon,\n# we can render it using barycentric coordinates.\na1_bbox = a1.finallimits[] # First we get the extent of the axis\next = GeometryOps.GI.Extent(NamedTuple{(:X, :Y)}(zip(minimum(a1_bbox), maximum(a1_bbox))))\n\na2, p2box = poly( # Now, we plot a cropping rectangle around the axis so we only show the polygon\n f[1, 2],\n GeometryOps.GeometryBasics.Polygon( # This is a rectangle with an internal hole shaped like the polygon.\n Point2f[(ext.X[1], ext.Y[1]), (ext.X[2], ext.Y[1]), (ext.X[2], ext.Y[2]), (ext.X[1], ext.Y[2]), (ext.X[1], ext.Y[1])],\n [reverse(Point2f.(polygon_points))]\n );\n color = :white, xautolimits = false, yautolimits = false,\n axis = (;\n aspect = DataAspect(), title = \"Barycentric coordinate based polygon rendering\", subtitle = \"GeometryOps\",\n limits = (ext.X, ext.Y),\n )\n)\nhidedecorations!(a1)\nhidedecorations!(a2)\ncb = Colorbar(f[2, :], p1.plots[1]; vertical = false, flipaxis = true)\n# Finally, we perform barycentric interpolation on a grid,\nxrange = LinRange(ext.X..., widths(a2.scene.px_area[])[1] * 4) # 2 rendered pixels per \"physical\" pixel\nyrange = LinRange(ext.Y..., widths(a2.scene.px_area[])[2] * 4) # 2 rendered pixels per \"physical\" pixel\n@time mean_values = barycentric_interpolate.(\n (MeanValue(),), # The barycentric coordinate algorithm (MeanValue is the only one for now)\n (Point2f.(polygon_points),), # The polygon points as `Point2f`\n (last.(polygon_points,),), # The values per polygon point - can be anything which supports addition and division\n Point2f.(xrange, yrange') # The points at which to interpolate\n)\n# and render!\nhm = heatmap!(\n a2, xrange, yrange, mean_values;\n colormap = p1.colormap, # Use the same colormap as the original polygon plot\n colorrange = p1.plots[1].colorrange[], # Access the rendered mesh plot's colorrange directly\n transformation = (; translation = Vec3f(0,0,-1)), # This gets the heatmap to render \"behind\" the previously plotted polygon\n xautolimits = false, yautolimits = false\n)\nf","category":"page"},{"location":"source/methods/barycentric/#Barycentric-coordinate-API","page":"Barycentric coordinates","title":"Barycentric-coordinate API","text":"","category":"section"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"In some cases, we actually want barycentric interpolation, and have no interest in the coordinates themselves.","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"However, the coordinates can be useful for debugging, and when performing 3D rendering, multiple barycentric values (depth, uv) are needed for depth buffering.","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"const _VecTypes = Union{Tuple{Vararg{T, N}}, GeometryBasics.StaticArraysCore.StaticArray{Tuple{N}, T, 1}} where {N, T}\n\n\"\"\"\n abstract type AbstractBarycentricCoordinateMethod\n\nAbstract supertype for barycentric coordinate methods.\nThe subtypes may serve as dispatch types, or may cache\nsome information about the target polygon.\n\n# API\nThe following methods must be implemented for all subtypes:\n- `barycentric_coordinates!(λs::Vector{<: Real}, method::AbstractBarycentricCoordinateMethod, exterior::Vector{<: Point{2, T1}}, point::Point{2, T2})`\n- `barycentric_interpolate(method::AbstractBarycentricCoordinateMethod, exterior::Vector{<: Point{2, T1}}, values::Vector{V}, point::Point{2, T2})::V`\n- `barycentric_interpolate(method::AbstractBarycentricCoordinateMethod, exterior::Vector{<: Point{2, T1}}, interiors::Vector{<: Vector{<: Point{2, T1}}} values::Vector{V}, point::Point{2, T2})::V`\nThe rest of the methods will be implemented in terms of these, and have efficient dispatches for broadcasting.\n\"\"\"\nabstract type AbstractBarycentricCoordinateMethod end\n\n\nBase.@propagate_inbounds function barycentric_coordinates!(λs::Vector{<: Real}, method::AbstractBarycentricCoordinateMethod, polypoints::AbstractVector{<: Point{N1, T1}}, point::Point{N2, T2}) where {N1, N2, T1 <: Real, T2 <: Real}\n @boundscheck @assert length(λs) == length(polypoints)\n @boundscheck @assert length(polypoints) >= 3\n\n @error(\"Not implemented yet for method $(method).\")\nend\nBase.@propagate_inbounds barycentric_coordinates!(λs::Vector{<: Real}, polypoints::AbstractVector{<: Point{N1, T1}}, point::Point{N2, T2}) where {N1, N2, T1 <: Real, T2 <: Real} = barycentric_coordinates!(λs, MeanValue(), polypoints, point)\n\nBase.@propagate_inbounds function barycentric_coordinates(method::AbstractBarycentricCoordinateMethod, polypoints::AbstractVector{<: Point{N1, T1}}, point::Point{N2, T2}) where {N1, N2, T1 <: Real, T2 <: Real}\n λs = zeros(promote_type(T1, T2), length(polypoints))\n barycentric_coordinates!(λs, method, polypoints, point)\n return λs\nend\nBase.@propagate_inbounds barycentric_coordinates(polypoints::AbstractVector{<: Point{N1, T1}}, point::Point{N2, T2}) where {N1, N2, T1 <: Real, T2 <: Real} = barycentric_coordinates(MeanValue(), polypoints, point)\n\nBase.@propagate_inbounds function barycentric_interpolate(method::AbstractBarycentricCoordinateMethod, polypoints::AbstractVector{<: Point{N, T1}}, values::AbstractVector{V}, point::Point{N, T2}) where {N, T1 <: Real, T2 <: Real, V}\n @boundscheck @assert length(values) == length(polypoints)\n @boundscheck @assert length(polypoints) >= 3\n λs = barycentric_coordinates(method, polypoints, point)\n return sum(λs .* values)\nend\nBase.@propagate_inbounds barycentric_interpolate(polypoints::AbstractVector{<: Point{N, T1}}, values::AbstractVector{V}, point::Point{N, T2}) where {N, T1 <: Real, T2 <: Real, V} = barycentric_interpolate(MeanValue(), polypoints, values, point)\n\nBase.@propagate_inbounds function barycentric_interpolate(method::AbstractBarycentricCoordinateMethod, exterior::AbstractVector{<: Point{N, T1}}, interiors::AbstractVector{<: Point{N, T1}}, values::AbstractVector{V}, point::Point{N, T2}) where {N, T1 <: Real, T2 <: Real, V}\n @boundscheck @assert length(values) == length(exterior) + isempty(interiors) ? 0 : sum(length.(interiors))\n @boundscheck @assert length(exterior) >= 3\n λs = barycentric_coordinates(method, exterior, interiors, point)\n return sum(λs .* values)\nend\nBase.@propagate_inbounds barycentric_interpolate(exterior::AbstractVector{<: Point{N, T1}}, interiors::AbstractVector{<: Point{N, T1}}, values::AbstractVector{V}, point::Point{N, T2}) where {N, T1 <: Real, T2 <: Real, V} = barycentric_interpolate(MeanValue(), exterior, interiors, values, point)\n\nBase.@propagate_inbounds function barycentric_interpolate(method::AbstractBarycentricCoordinateMethod, polygon::Polygon{2, T1}, values::AbstractVector{V}, point::Point{2, T2}) where {T1 <: Real, T2 <: Real, V}\n exterior = decompose(Point{2, promote_type(T1, T2)}, polygon.exterior)\n if isempty(polygon.interiors)\n @boundscheck @assert length(values) == length(exterior)\n return barycentric_interpolate(method, exterior, values, point)\n else # the poly has interiors\n interiors = reverse.(decompose.((Point{2, promote_type(T1, T2)},), polygon.interiors))\n @boundscheck @assert length(values) == length(exterior) + sum(length.(interiors))\n return barycentric_interpolate(method, exterior, interiors, values, point)\n end\nend\nBase.@propagate_inbounds barycentric_interpolate(polygon::Polygon{2, T1}, values::AbstractVector{V}, point::Point{2, T2}) where {T1 <: Real, T2 <: Real, V} = barycentric_interpolate(MeanValue(), polygon, values, point)","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"3D polygons are considered to have their vertices in the XY plane, and the Z coordinate must represent some value. This is to say that the Z coordinate is interpreted as an M coordinate.","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"Base.@propagate_inbounds function barycentric_interpolate(method::AbstractBarycentricCoordinateMethod, polygon::Polygon{3, T1}, point::Point{2, T2}) where {T1 <: Real, T2 <: Real}\n exterior_point3s = decompose(Point{3, promote_type(T1, T2)}, polygon.exterior)\n exterior_values = getindex.(exterior_point3s, 3)\n exterior_points = Point2f.(exterior_point3s)\n if isempty(polygon.interiors)\n return barycentric_interpolate(method, exterior_points, exterior_values, point)\n else # the poly has interiors\n interior_point3s = decompose.((Point{3, promote_type(T1, T2)},), polygon.interiors)\n interior_values = collect(Iterators.flatten((getindex.(point3s, 3) for point3s in interior_point3s)))\n interior_points = map(point3s -> Point2f.(point3s), interior_point3s)\n return barycentric_interpolate(method, exterior_points, interior_points, vcat(exterior_values, interior_values), point)\n end\nend\nBase.@propagate_inbounds barycentric_interpolate(polygon::Polygon{3, T1}, point::Point{2, T2}) where {T1 <: Real, T2 <: Real} = barycentric_interpolate(MeanValue(), polygon, point)","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"This method is the one which supports GeoInterface.","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"Base.@propagate_inbounds function barycentric_interpolate(method::AbstractBarycentricCoordinateMethod, polygon, values::AbstractVector{V}, point) where V\n @assert GeoInterface.trait(polygon) isa GeoInterface.PolygonTrait\n @assert GeoInterface.trait(point) isa GeoInterface.PointTrait\n passable_polygon = GeoInterface.convert(GeometryBasics, polygon)\n @assert passable_polygon isa GeometryBasics.Polygon \"The polygon was converted to a $(typeof(passable_polygon)), which is not a `GeometryBasics.Polygon`.\"\n # first_poly_point = GeoInterface.getpoint(GeoInterface.getexterior(polygon))\n passable_point = GeoInterface.convert(GeometryBasics, point)\n return barycentric_interpolate(method, passable_polygon, Point2(passable_point))\nend\nBase.@propagate_inbounds barycentric_interpolate(polygon, values::AbstractVector{V}, point) where V = barycentric_interpolate(MeanValue(), polygon, values, point)\n\n\"\"\"\n weighted_mean(weight::Real, x1, x2)\n\nReturns the weighted mean of `x1` and `x2`, where `weight` is the weight of `x1`.\n\nSpecifically, calculates `x1 * weight + x2 * (1 - weight)`.\n\n!!! note\n The idea for this method is that you can override this for custom types, like Color types, in extension modules.\n\"\"\"\nfunction weighted_mean(weight::WT, x1, x2) where {WT <: Real}\n return muladd(x1, weight, x2 * (oneunit(WT) - weight))\nend\n\n\n\"\"\"\n MeanValue() <: AbstractBarycentricCoordinateMethod\n\nThis method calculates barycentric coordinates using the mean value method.\n\n# References\n\n\"\"\"\nstruct MeanValue <: AbstractBarycentricCoordinateMethod\nend","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"Before we go to the actual implementation, there are some quick and simple utility functions that we need to implement. These are mainly for convenience and code brevity.","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"\"\"\"\n _det(s1::Point2{T1}, s2::Point2{T2}) where {T1 <: Real, T2 <: Real}\n\nReturns the determinant of the matrix formed by `hcat`'ing two points `s1` and `s2`.\n\nSpecifically, this is:\n```julia\ns1[1] * s2[2] - s1[2] * s2[1]\n```\n\"\"\"\nfunction _det(s1::_VecTypes{2, T1}, s2::_VecTypes{2, T2}) where {T1 <: Real, T2 <: Real}\n return s1[1] * s2[2] - s1[2] * s2[1]\nend\n\n\"\"\"\n t_value(sᵢ, sᵢ₊₁, rᵢ, rᵢ₊₁)\n\nReturns the \"T-value\" as described in Hormann's presentation [^HormannPresentation] on how to calculate\nthe mean-value coordinate.\n\nHere, `sᵢ` is the vector from vertex `vᵢ` to the point, and `rᵢ` is the norm (length) of `sᵢ`.\n`s` must be `Point` and `r` must be real numbers.\n\n```math\ntᵢ = \\\\frac{\\\\mathrm{det}\\\\left(sᵢ, sᵢ₊₁\\\\right)}{rᵢ * rᵢ₊₁ + sᵢ ⋅ sᵢ₊₁}\n```\n\n[^HormannPresentation]: K. Hormann and N. Sukumar. Generalized Barycentric Coordinates in Computer Graphics and Computational Mechanics. Taylor & Fancis, CRC Press, 2017.\n```\n\n\"\"\"\nfunction t_value(sᵢ::_VecTypes{N, T1}, sᵢ₊₁::_VecTypes{N, T1}, rᵢ::T2, rᵢ₊₁::T2) where {N, T1 <: Real, T2 <: Real}\n return _det(sᵢ, sᵢ₊₁) / muladd(rᵢ, rᵢ₊₁, dot(sᵢ, sᵢ₊₁))\nend\n\n\nfunction barycentric_coordinates!(λs::Vector{<: Real}, ::MeanValue, polypoints::AbstractVector{<: Point{2, T1}}, point::Point{2, T2}) where {T1 <: Real, T2 <: Real}\n @boundscheck @assert length(λs) == length(polypoints)\n @boundscheck @assert length(polypoints) >= 3\n n_points = length(polypoints)\n # Initialize counters and register variables\n # Points - these are actually vectors from point to vertices\n # polypoints[i-1], polypoints[i], polypoints[i+1]\n sᵢ₋₁ = polypoints[end] - point\n sᵢ = polypoints[begin] - point\n sᵢ₊₁ = polypoints[begin+1] - point\n # radius / Euclidean distance between points.\n rᵢ₋₁ = norm(sᵢ₋₁)\n rᵢ = norm(sᵢ )\n rᵢ₊₁ = norm(sᵢ₊₁)\n # Perform the first computation explicitly, so we can cut down on\n # a mod in the loop.\n λs[1] = (t_value(sᵢ₋₁, sᵢ, rᵢ₋₁, rᵢ) + t_value(sᵢ, sᵢ₊₁, rᵢ, rᵢ₊₁)) / rᵢ\n # Loop through the rest of the vertices, compute, store in λs\n for i in 2:n_points\n # Increment counters + set variables\n sᵢ₋₁ = sᵢ\n sᵢ = sᵢ₊₁\n sᵢ₊₁ = polypoints[mod1(i+1, n_points)] - point\n rᵢ₋₁ = rᵢ\n rᵢ = rᵢ₊₁\n rᵢ₊₁ = norm(sᵢ₊₁) # radius / Euclidean distance between points.\n λs[i] = (t_value(sᵢ₋₁, sᵢ, rᵢ₋₁, rᵢ) + t_value(sᵢ, sᵢ₊₁, rᵢ, rᵢ₊₁)) / rᵢ\n end\n # Normalize λs to the 1-norm (sum=1)\n λs ./= sum(λs)\n return λs\nend","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"function barycentric_coordinates(::MeanValue, polypoints::NTuple{N, Point{2, T2}}, point::Point{2, T1},) where {N, T1, T2}\n ## Initialize counters and register variables\n ## Points - these are actually vectors from point to vertices\n ## polypoints[i-1], polypoints[i], polypoints[i+1]\n sᵢ₋₁ = polypoints[end] - point\n sᵢ = polypoints[begin] - point\n sᵢ₊₁ = polypoints[begin+1] - point\n ## radius / Euclidean distance between points.\n rᵢ₋₁ = norm(sᵢ₋₁)\n rᵢ = norm(sᵢ )\n rᵢ₊₁ = norm(sᵢ₊₁)\n λ₁ = (t_value(sᵢ₋₁, sᵢ, rᵢ₋₁, rᵢ) + t_value(sᵢ, sᵢ₊₁, rᵢ, rᵢ₊₁)) / rᵢ\n λs = ntuple(N) do i\n if i == 1\n return λ₁\n end\n ## Increment counters + set variables\n sᵢ₋₁ = sᵢ\n sᵢ = sᵢ₊₁\n sᵢ₊₁ = polypoints[mod1(i+1, N)] - point\n rᵢ₋₁ = rᵢ\n rᵢ = rᵢ₊₁\n rᵢ₊₁ = norm(sᵢ₊₁) # radius / Euclidean distance between points.\n return (t_value(sᵢ₋₁, sᵢ, rᵢ₋₁, rᵢ) + t_value(sᵢ, sᵢ₊₁, rᵢ, rᵢ₊₁)) / rᵢ\n end\n\n ∑λ = sum(λs)\n\n return ntuple(N) do i\n λs[i] / ∑λ\n end\nend","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"This performs an inplace accumulation, using less memory and is faster. That's particularly good if you are using a polygon with a large number of points...","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"function barycentric_interpolate(::MeanValue, polypoints::AbstractVector{<: Point{2, T1}}, values::AbstractVector{V}, point::Point{2, T2}) where {T1 <: Real, T2 <: Real, V}\n @boundscheck @assert length(values) == length(polypoints)\n @boundscheck @assert length(polypoints) >= 3\n\n n_points = length(polypoints)\n # Initialize counters and register variables\n # Points - these are actually vectors from point to vertices\n # polypoints[i-1], polypoints[i], polypoints[i+1]\n sᵢ₋₁ = polypoints[end] - point\n sᵢ = polypoints[begin] - point\n sᵢ₊₁ = polypoints[begin+1] - point\n # radius / Euclidean distance between points.\n rᵢ₋₁ = norm(sᵢ₋₁)\n rᵢ = norm(sᵢ )\n rᵢ₊₁ = norm(sᵢ₊₁)\n # Now, we set the interpolated value to the first point's value, multiplied\n # by the weight computed relative to the first point in the polygon.\n wᵢ = (t_value(sᵢ₋₁, sᵢ, rᵢ₋₁, rᵢ) + t_value(sᵢ, sᵢ₊₁, rᵢ, rᵢ₊₁)) / rᵢ\n wₜₒₜ = wᵢ\n interpolated_value = values[begin] * wᵢ\n for i in 2:n_points\n # Increment counters + set variables\n sᵢ₋₁ = sᵢ\n sᵢ = sᵢ₊₁\n sᵢ₊₁ = polypoints[mod1(i+1, n_points)] - point\n rᵢ₋₁ = rᵢ\n rᵢ = rᵢ₊₁\n rᵢ₊₁ = norm(sᵢ₊₁)\n # Now, we calculate the weight:\n wᵢ = (t_value(sᵢ₋₁, sᵢ, rᵢ₋₁, rᵢ) + t_value(sᵢ, sᵢ₊₁, rᵢ, rᵢ₊₁)) / rᵢ\n # perform a weighted sum with the interpolated value:\n interpolated_value += values[i] * wᵢ\n # and add the weight to the total weight accumulator.\n wₜₒₜ += wᵢ\n end\n # Return the normalized interpolated value.\n return interpolated_value / wₜₒₜ\nend","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"When you have holes, then you have to be careful about the order you iterate around points.","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"Specifically, you have to iterate around each linear ring separately and ensure there are no degenerate/repeated points at the start and end!","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"function barycentric_interpolate(::MeanValue, exterior::AbstractVector{<: Point{N, T1}}, interiors::AbstractVector{<: AbstractVector{<: Point{N, T1}}}, values::AbstractVector{V}, point::Point{N, T2}) where {N, T1 <: Real, T2 <: Real, V}\n # @boundscheck @assert length(values) == (length(exterior) + isempty(interiors) ? 0 : sum(length.(interiors)))\n # @boundscheck @assert length(exterior) >= 3\n\n current_index = 1\n l_exterior = length(exterior)\n\n sᵢ₋₁ = exterior[end] - point\n sᵢ = exterior[begin] - point\n sᵢ₊₁ = exterior[begin+1] - point\n rᵢ₋₁ = norm(sᵢ₋₁) # radius / Euclidean distance between points.\n rᵢ = norm(sᵢ ) # radius / Euclidean distance between points.\n rᵢ₊₁ = norm(sᵢ₊₁) # radius / Euclidean distance between points.","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"Now, we set the interpolated value to the first point's value, multiplied by the weight computed relative to the first point in the polygon.","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":" wᵢ = (t_value(sᵢ₋₁, sᵢ, rᵢ₋₁, rᵢ) + t_value(sᵢ, sᵢ₊₁, rᵢ, rᵢ₊₁)) / rᵢ\n wₜₒₜ = wᵢ\n interpolated_value = values[begin] * wᵢ\n\n for i in 2:l_exterior","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"Increment counters + set variables","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":" sᵢ₋₁ = sᵢ\n sᵢ = sᵢ₊₁\n sᵢ₊₁ = exterior[mod1(i+1, l_exterior)] - point\n rᵢ₋₁ = rᵢ\n rᵢ = rᵢ₊₁\n rᵢ₊₁ = norm(sᵢ₊₁) # radius / Euclidean distance between points.\n wᵢ = (t_value(sᵢ₋₁, sᵢ, rᵢ₋₁, rᵢ) + t_value(sᵢ, sᵢ₊₁, rᵢ, rᵢ₊₁)) / rᵢ","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"Updates - first the interpolated value,","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":" interpolated_value += values[current_index] * wᵢ","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"then the accumulators for total weight and current index.","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":" wₜₒₜ += wᵢ\n current_index += 1\n\n end\n for hole in interiors\n l_hole = length(hole)\n sᵢ₋₁ = hole[end] - point\n sᵢ = hole[begin] - point\n sᵢ₊₁ = hole[begin+1] - point\n rᵢ₋₁ = norm(sᵢ₋₁) # radius / Euclidean distance between points.\n rᵢ = norm(sᵢ ) # radius / Euclidean distance between points.\n rᵢ₊₁ = norm(sᵢ₊₁) # radius / Euclidean distance between points.\n # Now, we set the interpolated value to the first point's value, multiplied\n # by the weight computed relative to the first point in the polygon.\n wᵢ = (t_value(sᵢ₋₁, sᵢ, rᵢ₋₁, rᵢ) + t_value(sᵢ, sᵢ₊₁, rᵢ, rᵢ₊₁)) / rᵢ\n\n interpolated_value += values[current_index] * wᵢ\n\n wₜₒₜ += wᵢ\n current_index += 1\n\n for i in 2:l_hole\n # Increment counters + set variables\n sᵢ₋₁ = sᵢ\n sᵢ = sᵢ₊₁\n sᵢ₊₁ = hole[mod1(i+1, l_hole)] - point\n rᵢ₋₁ = rᵢ\n rᵢ = rᵢ₊₁\n rᵢ₊₁ = norm(sᵢ₊₁) ## radius / Euclidean distance between points.\n wᵢ = (t_value(sᵢ₋₁, sᵢ, rᵢ₋₁, rᵢ) + t_value(sᵢ, sᵢ₊₁, rᵢ, rᵢ₊₁)) / rᵢ\n interpolated_value += values[current_index] * wᵢ\n wₜₒₜ += wᵢ\n current_index += 1\n end\n end\n return interpolated_value / wₜₒₜ\n\nend\n\nstruct Wachspress <: AbstractBarycentricCoordinateMethod\nend","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/methods/geom_relations/disjoint/#Disjointness-checks","page":"Disjointness checks","title":"Disjointness checks","text":"","category":"section"},{"location":"source/methods/geom_relations/disjoint/","page":"Disjointness checks","title":"Disjointness checks","text":"\"\"\"\n disjoint(geom1, geom2)::Bool\n\nReturn `true` if the intersection of the two geometries is an empty set.","category":"page"},{"location":"source/methods/geom_relations/disjoint/","page":"Disjointness checks","title":"Disjointness checks","text":"Examples","category":"page"},{"location":"source/methods/geom_relations/disjoint/","page":"Disjointness checks","title":"Disjointness checks","text":"```jldoctest\nimport GeometryOps as GO, GeoInterface as GI\n\npoly = GI.Polygon([[(-1, 2), (3, 2), (3, 3), (-1, 3), (-1, 2)]])\npoint = (1, 1)\nGO.disjoint(poly, point)","category":"page"},{"location":"source/methods/geom_relations/disjoint/","page":"Disjointness checks","title":"Disjointness checks","text":"output","category":"page"},{"location":"source/methods/geom_relations/disjoint/","page":"Disjointness checks","title":"Disjointness checks","text":"true\n```\n\"\"\"\ndisjoint(g1, g2)::Bool = disjoint(trait(g1), g1, trait(g2), g2)\ndisjoint(::FeatureTrait, g1, ::Any, g2)::Bool = disjoint(GI.geometry(g1), g2)\ndisjoint(::Any, g1, t2::FeatureTrait, g2)::Bool = disjoint(g1, geometry(g2))\ndisjoint(::PointTrait, g1, ::PointTrait, g2)::Bool = !point_equals_point(g1, g2)\ndisjoint(::PointTrait, g1, ::LineStringTrait, g2)::Bool = !point_on_line(g1, g2)\ndisjoint(::PointTrait, g1, ::PolygonTrait, g2)::Bool = !point_in_polygon(g1, g2)\ndisjoint(::LineStringTrait, g1, ::PointTrait, g2)::Bool = !point_on_line(g2, g1)\ndisjoint(::LineStringTrait, g1, ::LineStringTrait, g2)::Bool = !line_on_line(g1, g2)\ndisjoint(::LineStringTrait, g1, ::PolygonTrait, g2)::Bool = !line_in_polygon(g2, g1)\ndisjoint(::PolygonTrait, g1, ::PointTrait, g2)::Bool = !point_in_polygon(g2, g1)\ndisjoint(::PolygonTrait, g1, ::LineStringTrait, g2)::Bool = !line_in_polygon(g2, g1)\ndisjoint(::PolygonTrait, g1, ::PolygonTrait, g2)::Bool = polygon_disjoint(g2, g1)\n\nfunction polygon_disjoint(poly1, poly2)\n for point in GI.getpoint(poly1)\n point_in_polygon(point, poly2) && return false\n end\n for point in GI.getpoint(poly2)\n point_in_polygon(point, poly1) && return false\n end\n return !intersects(poly1, poly2)\nend","category":"page"},{"location":"source/methods/geom_relations/disjoint/","page":"Disjointness checks","title":"Disjointness checks","text":"","category":"page"},{"location":"source/methods/geom_relations/disjoint/","page":"Disjointness checks","title":"Disjointness checks","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/methods/geom_relations/overlaps/#Overlaps","page":"Overlaps","title":"Overlaps","text":"","category":"section"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":"export overlaps","category":"page"},{"location":"source/methods/geom_relations/overlaps/#What-is-overlaps?","page":"Overlaps","title":"What is overlaps?","text":"","category":"section"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":"The overlaps function checks if two geometries overlap. Two geometries can only overlap if they have the same dimension, and if they overlap, but one is not contained, within, or equal to the other.","category":"page"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":"Note that this means it is impossible for a single point to overlap with a single point and a line only overlaps with another line if only a section of each line is colinear.","category":"page"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":"To provide an example, consider these two lines:","category":"page"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":"using GeometryOps\nusing GeometryOps.GeometryBasics\nusing Makie\nusing CairoMakie\n\nl1 = GI.LineString([(0.0, 0.0), (0.0, 10.0)])\nl2 = GI.LineString([(0.0, -10.0), (0.0, 3.0)])\nf, a, p = lines(GI.getpoint(l1), color = :blue)\nscatter!(GI.getpoint(l1), color = :blue)\nlines!(GI.getpoint(l2), color = :orange)\nscatter!(GI.getpoint(l2), color = :orange)","category":"page"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":"We can see that the two lines overlap in the plot:","category":"page"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":"overlap(l1, l2)","category":"page"},{"location":"source/methods/geom_relations/overlaps/#Implementation","page":"Overlaps","title":"Implementation","text":"","category":"section"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":"This is the GeoInterface-compatible implementation.","category":"page"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":"First, we implement a wrapper method that dispatches to the correct implementation based on the geometry trait. This is also used in the implementation, since it's a lot less work!","category":"page"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":"Note that that since only elements of the same dimension can overlap, any two geometries with traits that are of different dimensions autmoatically can return false.","category":"page"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":"For geometries with the same trait dimension, we must make sure that they share a point, an edge, or area for points, lines, and polygons/multipolygons respectivly, without being contained.","category":"page"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":"\"\"\"\n overlaps(geom1, geom2)::Bool\n\nCompare two Geometries of the same dimension and return true if their\nintersection set results in a geometry different from both but of the same\ndimension. This means one geometry cannot be within or contain the other and\nthey cannot be equal\n\n# Examples\n```jldoctest\nimport GeometryOps as GO, GeoInterface as GI\npoly1 = GI.Polygon([[(0,0), (0,5), (5,5), (5,0), (0,0)]])\npoly2 = GI.Polygon([[(1,1), (1,6), (6,6), (6,1), (1,1)]])\n\nGO.overlaps(poly1, poly2)","category":"page"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":"output","category":"page"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":"true\n```\n\"\"\"\noverlaps(geom1, geom2)::Bool = overlaps(\n GI.trait(geom1),\n geom1,\n GI.trait(geom2),\n geom2,\n)\n\n\"\"\"\n overlaps(::GI.AbstractTrait, geom1, ::GI.AbstractTrait, geom2)::Bool\n\nFor any non-specified pair, all have non-matching dimensions, return false.\n\"\"\"\noverlaps(::GI.AbstractTrait, geom1, ::GI.AbstractTrait, geom2) = false\n\n\"\"\"\n overlaps(\n ::GI.MultiPointTrait, points1,\n ::GI.MultiPointTrait, points2,\n )::Bool\n\nIf the multipoints overlap, meaning some, but not all, of the points within the\nmultipoints are shared, return true.\n\"\"\"\nfunction overlaps(\n ::GI.MultiPointTrait, points1,\n ::GI.MultiPointTrait, points2,\n)\n one_diff = false # assume that all the points are the same\n one_same = false # assume that all points are different\n for p1 in GI.getpoint(points1)\n match_point = false\n for p2 in GI.getpoint(points2)\n if equals(p1, p2) # Point is shared\n one_same = true\n match_point = true\n break\n end\n end\n one_diff |= !match_point # Point isn't shared\n one_same && one_diff && return true\n end\n return false\nend\n\n\"\"\"\n overlaps(::GI.LineTrait, line1, ::GI.LineTrait, line)::Bool\n\nIf the lines overlap, meaning that they are colinear but each have one endpoint\noutside of the other line, return true. Else false.\n\"\"\"\noverlaps(::GI.LineTrait, line1, ::GI.LineTrait, line) =\n _overlaps((a1, a2), (b1, b2))\n\n\"\"\"\n overlaps(\n ::Union{GI.LineStringTrait, GI.LinearRing}, line1,\n ::Union{GI.LineStringTrait, GI.LinearRing}, line2,\n )::Bool\n\nIf the curves overlap, meaning that at least one edge of each curve overlaps,\nreturn true. Else false.\n\"\"\"\nfunction overlaps(\n ::Union{GI.LineStringTrait, GI.LinearRing}, line1,\n ::Union{GI.LineStringTrait, GI.LinearRing}, line2,\n)\n edges_a, edges_b = map(sort! ∘ to_edges, (line1, line2))\n for edge_a in edges_a\n for edge_b in edges_b\n _overlaps(edge_a, edge_b) && return true\n end\n end\n return false\nend\n\n\"\"\"\n overlaps(\n trait_a::GI.PolygonTrait, poly_a,\n trait_b::GI.PolygonTrait, poly_b,\n )::Bool\n\nIf the two polygons intersect with one another, but are not equal, return true.\nElse false.\n\"\"\"\nfunction overlaps(\n trait_a::GI.PolygonTrait, poly_a,\n trait_b::GI.PolygonTrait, poly_b,\n)\n edges_a, edges_b = map(sort! ∘ to_edges, (poly_a, poly_b))\n return _line_intersects(edges_a, edges_b) &&\n !equals(trait_a, poly_a, trait_b, poly_b)\nend\n\n\"\"\"\n overlaps(\n ::GI.PolygonTrait, poly1,\n ::GI.MultiPolygonTrait, polys2,\n )::Bool\n\nReturn true if polygon overlaps with at least one of the polygons within the\nmultipolygon. Else false.\n\"\"\"\nfunction overlaps(\n ::GI.PolygonTrait, poly1,\n ::GI.MultiPolygonTrait, polys2,\n)\n for poly2 in GI.getgeom(polys2)\n overlaps(poly1, poly2) && return true\n end\n return false\nend\n\n\"\"\"\n overlaps(\n ::GI.MultiPolygonTrait, polys1,\n ::GI.PolygonTrait, poly2,\n )::Bool\n\nReturn true if polygon overlaps with at least one of the polygons within the\nmultipolygon. Else false.\n\"\"\"\noverlaps(trait1::GI.MultiPolygonTrait, polys1, trait2::GI.PolygonTrait, poly2) =\n overlaps(trait2, poly2, trait1, polys1)\n\n\"\"\"\n overlaps(\n ::GI.MultiPolygonTrait, polys1,\n ::GI.MultiPolygonTrait, polys2,\n )::Bool\n\nReturn true if at least one pair of polygons from multipolygons overlap. Else\nfalse.\n\"\"\"\nfunction overlaps(\n ::GI.MultiPolygonTrait, polys1,\n ::GI.MultiPolygonTrait, polys2,\n)\n for poly1 in GI.getgeom(polys1)\n overlaps(poly1, polys2) && return true\n end\n return false\nend\n\n\"\"\"\n _overlaps(\n (a1, a2)::Edge,\n (b1, b2)::Edge\n )::Bool\n\nIf the edges overlap, meaning that they are colinear but each have one endpoint\noutside of the other edge, return true. Else false.\n\"\"\"\nfunction _overlaps(\n (a1, a2)::Edge,\n (b1, b2)::Edge\n)","category":"page"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":"meets in more than one point","category":"page"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":" on_top = ExactPredicates.meet(a1, a2, b1, b2) == 0","category":"page"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":"one end point is outside of other segment","category":"page"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":" a_fully_within = point_on_seg(a1, b1, b2) && point_on_seg(a2, b1, b2)\n b_fully_within = point_on_seg(b1, a1, a2) && point_on_seg(b2, a1, a2)\n return on_top && (!a_fully_within && !b_fully_within)\nend","category":"page"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":"","category":"page"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/methods/centroid/#Centroid","page":"Centroid","title":"Centroid","text":"","category":"section"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"export centroid, centroid_and_length, centroid_and_area","category":"page"},{"location":"source/methods/centroid/#What-is-the-centroid?","page":"Centroid","title":"What is the centroid?","text":"","category":"section"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"The centroid is the geometric center of a line string or area(s). Note that the centroid does not need to be inside of a concave area.","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"Further note that by convention a line, or linear ring, is calculated by weighting the line segments by their length, while polygons and multipolygon centroids are calculated by weighting edge's by their 'area components'.","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"To provide an example, consider this concave polygon in the shape of a 'C':","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"using GeometryOps\nusing GeometryOps.GeometryBasics\nusing Makie\nusing CairoMakie\n\ncshape = Polygon([\n Point(0,0), Point(0,3), Point(3,3), Point(3,2), Point(1,2),\n Point(1,1), Point(3,1), Point(3,0), Point(0,0),\n])\nf, a, p = poly(cshape; axis = (; aspect = DataAspect()))","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"Let's see what the centroid looks like (plotted in red):","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"cent = centroid(cshape)\nscatter!(a, GI.x(cent), GI.y(cent), color = :red)\nf","category":"page"},{"location":"source/methods/centroid/#Implementation","page":"Centroid","title":"Implementation","text":"","category":"section"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"This is the GeoInterface-compatible implementation.","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"First, we implement a wrapper method that dispatches to the correct implementation based on the geometry trait. This is also used in the implementation, since it's a lot less work!","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"Note that if you call centroid on a LineString or LinearRing, the centroidandlength function will be called due to the weighting scheme described above, while centroidandarea is called for polygons and multipolygons. However, centroidandarea can still be called on a LineString or LinearRing when they are closed, for example as the interior hole of a polygon.","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"The helper functions centroidandlength and centroidandarea are made availible just in case the user also needs the area or length to decrease repeat computation.","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"\"\"\"\n centroid(geom)::Tuple{T, T}\n\nReturns the centroid of a given line segment, linear ring, polygon, or\nmutlipolygon.\n\"\"\"\ncentroid(geom) = centroid(GI.trait(geom), geom)\n\n\"\"\"\n centroid(\n trait::Union{GI.LineStringTrait, GI.LinearRingTrait},\n geom,\n )::Tuple{T, T}\n\nReturns the centroid of a line string or linear ring, which is calculated by\nweighting line segments by their length by convention.\n\"\"\"\ncentroid(\n trait::Union{GI.LineStringTrait, GI.LinearRingTrait},\n geom,\n) = centroid_and_length(trait, geom)[1]\n\n\"\"\"\n centroid(trait, geom)::Tuple{T, T}\n\nReturns the centroid of a polygon or multipolygon, which is calculated by\nweighting edges by their `area component` by convention.\n\"\"\"\ncentroid(trait, geom) = centroid_and_area(trait, geom)[1]\n\n\"\"\"\n centroid_and_length(geom)::(::Tuple{T, T}, ::Real)\n\nReturns the centroid and length of a given line/ring. Note this is only valid\nfor line strings and linear rings.\n\"\"\"\ncentroid_and_length(geom) = centroid_and_length(GI.trait(geom), geom)\n\n\"\"\"\n centroid_and_area(\n ::Union{GI.LineStringTrait, GI.LinearRingTrait},\n geom,\n )::(::Tuple{T, T}, ::Real)\n\nReturns the centroid and area of a given geom.\n\"\"\"\ncentroid_and_area(geom) = centroid_and_area(GI.trait(geom), geom)\n\n\"\"\"\n centroid_and_length(geom)::(::Tuple{T, T}, ::Real)\n\nReturns the centroid and length of a given line/ring. Note this is only valid\nfor line strings and linear rings.\n\"\"\"\nfunction centroid_and_length(\n ::Union{GI.LineStringTrait, GI.LinearRingTrait},\n geom,\n)\n T = typeof(GI.x(GI.getpoint(geom, 1)))","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"Initialize starting values","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":" xcentroid = T(0)\n ycentroid = T(0)\n length = T(0)\n point₁ = GI.getpoint(geom, 1)","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"Loop over line segments of line string","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":" for point₂ in GI.getpoint(geom)","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"Calculate length of line segment","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":" length_component = sqrt(\n (GI.x(point₂) - GI.x(point₁))^2 +\n (GI.y(point₂) - GI.y(point₁))^2\n )","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"Accumulate the line segment length into length","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":" length += length_component","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"Weighted average of line segment centroids","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":" xcentroid += (GI.x(point₁) + GI.x(point₂)) * (length_component / 2)\n ycentroid += (GI.y(point₁) + GI.y(point₂)) * (length_component / 2)\n #centroid = centroid .+ ((point₁ .+ point₂) .* (length_component / 2))","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"Advance the point buffer by 1 point to move to next line segment","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":" point₁ = point₂\n end\n xcentroid /= length\n ycentroid /= length\n return (xcentroid, ycentroid), length\nend\n\n\"\"\"\n centroid_and_area(\n ::Union{GI.LineStringTrait, GI.LinearRingTrait},\n geom,\n )::(::Tuple{T, T}, ::Real)\n\nReturns the centroid and area of a given a line string or a linear ring.\nNote that this is only valid if the line segment or linear ring is closed.\n\"\"\"\nfunction centroid_and_area(\n ::Union{GI.LineStringTrait, GI.LinearRingTrait},\n geom,\n)\n T = typeof(GI.x(GI.getpoint(geom, 1)))","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"Check that the geometry is closed","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":" @assert(\n GI.getpoint(geom, 1) == GI.getpoint(geom, GI.ngeom(geom)),\n \"centroid_and_area should only be used with closed geometries\"\n )","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"Initialize starting values","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":" xcentroid = T(0)\n ycentroid = T(0)\n area = T(0)\n point₁ = GI.getpoint(geom, 1)","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"Loop over line segments of linear ring","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":" for point₂ in GI.getpoint(geom)\n area_component = GI.x(point₁) * GI.y(point₂) -\n GI.x(point₂) * GI.y(point₁)","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"Accumulate the area component into area","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":" area += area_component","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"Weighted average of centroid components","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":" xcentroid += (GI.x(point₁) + GI.x(point₂)) * area_component\n ycentroid += (GI.y(point₁) + GI.y(point₂)) * area_component","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"Advance the point buffer by 1 point","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":" point₁ = point₂\n end\n area /= 2\n xcentroid /= 6area\n ycentroid /= 6area\n return (xcentroid, ycentroid), abs(area)\nend\n\n\"\"\"\n centroid_and_area(::GI.PolygonTrait, geom)::(::Tuple{T, T}, ::Real)\n\nReturns the centroid and area of a given polygon.\n\"\"\"\nfunction centroid_and_area(::GI.PolygonTrait, geom)","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"Exterior ring's centroid and area","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":" (xcentroid, ycentroid), area = centroid_and_area(GI.getexterior(geom))","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"Weight exterior centroid by area","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":" xcentroid *= area\n ycentroid *= area","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"Loop over any holes within the polygon","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":" for hole in GI.gethole(geom)","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"Hole polygon's centroid and area","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":" (xinterior, yinterior), interior_area = centroid_and_area(hole)","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"Accumulate the area component into area","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":" area -= interior_area","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"Weighted average of centroid components","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":" xcentroid -= xinterior * interior_area\n ycentroid -= yinterior * interior_area\n end\n xcentroid /= area\n ycentroid /= area\n return (xcentroid, ycentroid), area\nend\n\n\"\"\"\n centroid_and_area(::GI.MultiPolygonTrait, geom)::(::Tuple{T, T}, ::Real)\n\nReturns the centroid and area of a given multipolygon.\n\"\"\"\nfunction centroid_and_area(::GI.MultiPolygonTrait, geom)","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"First polygon's centroid and area","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":" (xcentroid, ycentroid), area = centroid_and_area(GI.getpolygon(geom, 1))","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"Weight first polygon's centroid by area","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":" xcentroid *= area\n ycentroid *= area","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"Loop over any polygons within the multipolygon","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":" for i in 2:GI.ngeom(geom)","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"Polygon centroid and area","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":" (xpoly, ypoly), poly_area = centroid_and_area(GI.getpolygon(geom, i))","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"Accumulate the area component into area","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":" area += poly_area","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"Weighted average of centroid components","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":" xcentroid += xpoly * poly_area\n ycentroid += ypoly * poly_area\n end\n xcentroid /= area\n ycentroid /= area\n return (xcentroid, ycentroid), area\nend","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/transformations/extent/","page":"-","title":"-","text":"\"\"\"\n embed_extent(obj)\n\nRecursively wrap the object with a GeoInterface.jl geometry,\ncalculating and adding an `Extents.Extent` to all objects.\n\nThis can improve performance when extents need to be checked multiple times,\nsuch when needing to check if many points are in geometries, and using their extents\nas a quick filter for obviously exterior points.","category":"page"},{"location":"source/transformations/extent/","page":"-","title":"-","text":"Keywords","category":"page"},{"location":"source/transformations/extent/","page":"-","title":"-","text":"$THREADED_KEYWORD\n$CRS_KEYWORD\n\"\"\"\nembed_extent(x; threaded=false, crs=nothing) =\n apply(identity, GI.PointTrait, x; calc_extent=true, threaded, crs)","category":"page"},{"location":"source/transformations/extent/","page":"-","title":"-","text":"","category":"page"},{"location":"source/transformations/extent/","page":"-","title":"-","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/methods/geom_relations/intersects/#Intersection-checks","page":"Intersection checks","title":"Intersection checks","text":"","category":"section"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"export intersects, intersection, intersection_points","category":"page"},{"location":"source/methods/geom_relations/intersects/#What-is-intersects-vs-intersection-vs-intersection_points?","page":"Intersection checks","title":"What is intersects vs intersection vs intersection_points?","text":"","category":"section"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"The intersects methods check whether two geometries intersect with each other. The intersection methods return the geometry intersection between the two input geometries. The intersection_points method returns a list of intersection points between two geometries.","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"The intersects methods will always return a Boolean. However, note that the intersection methods will not all return the same type. For example, the intersection of two lines will be a point in most cases, unless the lines are parallel. On the other hand, the intersection of two polygons will be another polygon in most cases. Finally, the intersection_points method returns a list of tuple points.","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"To provide an example, consider these two lines:","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"using GeometryOps\nusing GeometryOps.GeometryBasics\nusing Makie\nusing CairoMakie\npoint1, point2 = Point(124.584961,-12.768946), Point(126.738281,-17.224758)\npoint3, point4 = Point(123.354492,-15.961329), Point(127.22168,-14.008696)\nline1 = Line(point1, point2)\nline2 = Line(point3, point4)\nf, a, p = lines([point1, point2])\nlines!([point3, point4])","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"We can see that they intersect, so we expect intersects to return true, and we can visualize the intersection point in red.","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"int_bool = GO.intersects(line1, line2)\nprintln(int_bool)\nint_point = GO.intersection(line1, line2)\nscatter!(int_point, color = :red)\nf","category":"page"},{"location":"source/methods/geom_relations/intersects/#Implementation","page":"Intersection checks","title":"Implementation","text":"","category":"section"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"This is the GeoInterface-compatible implementation.","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"First, we implement a wrapper method for intersects, intersection, and intersectionpoints that dispatches to the correct implementation based on the geometry trait. The two underlying helper functions that are widely used in all geometry dispatches are _lineintersects, which determines if two line segments intersect and intersectionpoint which determines the intersection point between two line segments.","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"\"\"\"\n intersects(geom1, geom2)::Bool\n\nCheck if two geometries intersect, returning true if so and false otherwise.\n\n# Example\n\n```jldoctest\nimport GeoInterface as GI, GeometryOps as GO\n\nline1 = GI.Line([(124.584961,-12.768946), (126.738281,-17.224758)])\nline2 = GI.Line([(123.354492,-15.961329), (127.22168,-14.008696)])\nGO.intersects(line1, line2)","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"output","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"true\n```\n\"\"\"\nintersects(geom1, geom2) = intersects(\n GI.trait(geom1),\n geom1,\n GI.trait(geom2),\n geom2\n)\n\n\"\"\"\n intersects(::GI.LineTrait, a, ::GI.LineTrait, b)::Bool\n\nReturns true if two line segments intersect and false otherwise.\n\"\"\"\nfunction intersects(::GI.LineTrait, a, ::GI.LineTrait, b)\n a1 = _tuple_point(GI.getpoint(a, 1))\n a2 = _tuple_point(GI.getpoint(a, 2))\n b1 = _tuple_point(GI.getpoint(b, 1))\n b2 = _tuple_point(GI.getpoint(b, 2))\n meet_type = ExactPredicates.meet(a1, a2, b1, b2)\n return meet_type == 0 || meet_type == 1\nend\n\n\"\"\"\n intersects(::GI.AbstractTrait, a, ::GI.AbstractTrait, b)::Bool\n\nReturns true if two geometries intersect with one another and false\notherwise. For all geometries but lines, convert the geometry to a list of edges\nand cross compare the edges for intersections.\n\"\"\"\nfunction intersects(\n trait_a::GI.AbstractTrait, a_geom,\n trait_b::GI.AbstractTrait, b_geom,\n) edges_a, edges_b = map(sort! ∘ to_edges, (a_geom, b_geom))\n return _line_intersects(edges_a, edges_b) ||\n within(trait_a, a_geom, trait_b, b_geom) ||\n within(trait_b, b_geom, trait_a, a_geom)\nend\n\n\"\"\"\n _line_intersects(\n edges_a::Vector{Edge},\n edges_b::Vector{Edge}\n )::Bool\n\nReturns true if there is at least one intersection between edges within the\ntwo lists of edges.\n\"\"\"\nfunction _line_intersects(\n edges_a::Vector{Edge},\n edges_b::Vector{Edge}\n)","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"Extents.intersects(toextent(edgesa), toextent(edgesb)) || return false","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":" for edge_a in edges_a\n for edge_b in edges_b\n _line_intersects(edge_a, edge_b) && return true\n end\n end\n return false\nend\n\n\"\"\"\n _line_intersects(\n edge_a::Edge,\n edge_b::Edge,\n )::Bool\n\nReturns true if there is at least one intersection between two edges.\n\"\"\"\nfunction _line_intersects(edge_a::Edge, edge_b::Edge)\n meet_type = ExactPredicates.meet(edge_a..., edge_b...)\n return meet_type == 0 || meet_type == 1\nend\n\n\"\"\"\n intersection(geom_a, geom_b)::Union{Tuple{::Real, ::Real}, ::Nothing}\n\nReturn an intersection point between two geometries. Return nothing if none are\nfound. Else, the return type depends on the input. It will be a union between:\na point, a line, a linear ring, a polygon, or a multipolygon\n\n# Example\n\n```jldoctest\nimport GeoInterface as GI, GeometryOps as GO\n\nline1 = GI.Line([(124.584961,-12.768946), (126.738281,-17.224758)])\nline2 = GI.Line([(123.354492,-15.961329), (127.22168,-14.008696)])\nGO.intersection(line1, line2)","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"output","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"(125.58375366067547, -14.83572303404496)\n```\n\"\"\"\nintersection(geom_a, geom_b) =\n intersection(GI.trait(geom_a), geom_a, GI.trait(geom_b), geom_b)\n\n\"\"\"\n intersection(\n ::GI.LineTrait, line_a,\n ::GI.LineTrait, line_b,\n )::Union{\n ::Tuple{::Real, ::Real},\n ::Nothing\n }\n\nCalculates the intersection between two line segments. Return nothing if\nthere isn't one.\n\"\"\"\nfunction intersection(::GI.LineTrait, line_a, ::GI.LineTrait, line_b)","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"Get start and end points for both lines","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":" a1 = GI.getpoint(line_a, 1)\n a2 = GI.getpoint(line_a, 2)\n b1 = GI.getpoint(line_b, 1)\n b2 = GI.getpoint(line_b, 2)","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"Determine the intersection point","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":" point, fracs = _intersection_point((a1, a2), (b1, b2))","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"Determine if intersection point is on line segments","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":" if !isnothing(point) && 0 <= fracs[1] <= 1 && 0 <= fracs[2] <= 1\n return point\n end\n return nothing\nend\n\nintersection(\n trait_a::Union{GI.LineStringTrait, GI.LinearRingTrait},\n geom_a,\n trait_b::Union{GI.LineStringTrait, GI.LinearRingTrait},\n geom_b,\n) = intersection_points(trait_a, geom_a, trait_b, geom_b)\n\n\"\"\"\n intersection(\n ::GI.PolygonTrait, poly_a,\n ::GI.PolygonTrait, poly_b,\n )::Union{\n ::Vector{Vector{Tuple{::Real, ::Real}}}, # is this a good return type?\n ::Nothing\n }\n\nCalculates the intersection between two line segments. Return nothing if\nthere isn't one.\n\"\"\"\nfunction intersection(::GI.PolygonTrait, poly_a, ::GI.PolygonTrait, poly_b)\n @assert false \"Polygon intersection isn't implemented yet.\"\n return nothing\nend\n\n\"\"\"\n intersection(\n ::GI.AbstractTrait, geom_a,\n ::GI.AbstractTrait, geom_b,\n )::Union{\n ::Vector{Vector{Tuple{::Real, ::Real}}}, # is this a good return type?\n ::Nothing\n }\n\nCalculates the intersection between two line segments. Return nothing if\nthere isn't one.\n\"\"\"\nfunction intersection(\n trait_a::GI.AbstractTrait, geom_a,\n trait_b::GI.AbstractTrait, geom_b,\n)\n @assert(\n false,\n \"Intersection between $trait_a and $trait_b isn't implemented yet.\",\n )\n return nothing\nend\n\n\"\"\"\n intersection_points(\n geom_a,\n geom_b,\n )::Union{\n ::Vector{::Tuple{::Real, ::Real}},\n ::Nothing,\n }\n\nReturn a list of intersection points between two geometries. If no intersection\npoint was possible given geometry extents, return nothing. If none are found,\nreturn an empty list.\n\"\"\"\nintersection_points(geom_a, geom_b) =\n intersection_points(GI.trait(geom_a), geom_a, GI.trait(geom_b), geom_b)\n\n\"\"\"\n intersection_points(\n ::GI.AbstractTrait, geom_a,\n ::GI.AbstractTrait, geom_b,\n )::Union{\n ::Vector{::Tuple{::Real, ::Real}},\n ::Nothing,\n }\n\nCalculates the list of intersection points between two geometries, inlcuding\nline segments, line strings, linear rings, polygons, and multipolygons. If no\nintersection points were possible given geometry extents, return nothing. If\nnone are found, return an empty list.\n\"\"\"\nfunction intersection_points(::GI.AbstractTrait, a, ::GI.AbstractTrait, b)","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"Check if the geometries extents even overlap","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":" Extents.intersects(GI.extent(a), GI.extent(b)) || return nothing","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"Create a list of edges from the two input geometries","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":" edges_a, edges_b = map(sort! ∘ to_edges, (a, b))\n npoints_a, npoints_b = length(edges_a), length(edges_b)\n a_closed = npoints_a > 1 && edges_a[1][1] == edges_a[end][1]\n b_closed = npoints_b > 1 && edges_b[1][1] == edges_b[end][1]\n if npoints_a > 0 && npoints_b > 0","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"Initialize an empty list of points","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":" T = typeof(edges_a[1][1][1]) # x-coordinate of first point in first edge\n result = Tuple{T,T}[]","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"Loop over pairs of edges and add any intersection points to results","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":" for i in eachindex(edges_a)\n for j in eachindex(edges_b)\n point, fracs = _intersection_point(edges_a[i], edges_b[j])\n if !isnothing(point)\n #=\n Determine if point is on edge (all edge endpoints excluded\n except for the last edge for an open geometry)\n =#\n α, β = fracs\n on_a_edge = (!a_closed && i == npoints_a && 0 <= α <= 1) ||\n (0 <= α < 1)\n on_b_edge = (!b_closed && j == npoints_b && 0 <= β <= 1) ||\n (0 <= β < 1)\n if on_a_edge && on_b_edge\n push!(result, point)\n end\n end\n end\n end\n return result\n end\n return nothing\nend\n\n\"\"\"\n _intersection_point(\n (a1, a2)::Tuple,\n (b1, b2)::Tuple,\n )\n\nCalculates the intersection point between two lines if it exists, and as if the\nline extended to infinity, and the fractional component of each line from the\ninitial end point to the intersection point.\nInputs:\n (a1, a2)::Tuple{Tuple{::Real, ::Real}, Tuple{::Real, ::Real}} first line\n (b1, b2)::Tuple{Tuple{::Real, ::Real}, Tuple{::Real, ::Real}} second line\nOutputs:\n (x, y)::Tuple{::Real, ::Real} intersection point\n (t, u)::Tuple{::Real, ::Real} fractional length of lines to intersection\n Both are ::Nothing if point doesn't exist!\n\nCalculation derivation can be found here:\n https://stackoverflow.com/questions/563198/\n\"\"\"\nfunction _intersection_point((a1, a2)::Tuple, (b1, b2)::Tuple)","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"First line runs from p to p + r","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":" px, py = GI.x(a1), GI.y(a1)\n rx, ry = GI.x(a2) - px, GI.y(a2) - py","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"Second line runs from q to q + s","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":" qx, qy = GI.x(b1), GI.y(b1)\n sx, sy = GI.x(b2) - qx, GI.y(b2) - qy","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"Intersection will be where p + tr = q + us where 0 < t, u < 1 and","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":" r_cross_s = rx * sy - ry * sx\n if r_cross_s != 0\n Δqp_x = qx - px\n Δqp_y = qy - py\n t = (Δqp_x * sy - Δqp_y * sx) / r_cross_s\n u = (Δqp_x * ry - Δqp_y * rx) / r_cross_s\n x = px + t * rx\n y = py + t * ry\n return (x, y), (t, u)\n end\n return nothing, nothing\nend","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/transformations/flip/#Coordinate-flipping","page":"Coordinate flipping","title":"Coordinate flipping","text":"","category":"section"},{"location":"source/transformations/flip/","page":"Coordinate flipping","title":"Coordinate flipping","text":"This is a simple example of how to use the apply functionality in a function, by flipping the x and y coordinates of a geometry.","category":"page"},{"location":"source/transformations/flip/","page":"Coordinate flipping","title":"Coordinate flipping","text":"\"\"\"\n flip(obj)\n\nSwap all of the x and y coordinates in obj, otherwise\nkeeping the original structure (but not necessarily the\noriginal type).\n\n# Keywords\n\n$APPLY_KEYWORDS\n\"\"\"\nfunction flip(geom; kw...)\n if _is3d(geom)\n return apply(PointTrait, geom; kw...) do p\n (GI.y(p), GI.x(p), GI.z(p))\n end\n else\n return apply(PointTrait, geom; kw...) do p\n (GI.y(p), GI.x(p))\n end\n end\nend","category":"page"},{"location":"source/transformations/flip/","page":"Coordinate flipping","title":"Coordinate flipping","text":"","category":"page"},{"location":"source/transformations/flip/","page":"Coordinate flipping","title":"Coordinate flipping","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/transformations/transform/","page":"-","title":"-","text":"\"\"\"\n transform(f, obj)\n\nApply a function `f` to all the points in `obj`.\n\nPoints will be passed to `f` as an `SVector` to allow\nusing CoordinateTransformations.jl and Rotations.jl\nwithout hassle.\n\n`SVector` is also a valid GeoInterface.jl point, so will\nwork in all GeoInterface.jl methods.\n\n# Example\n\n```julia\njulia> import GeoInterface as GI\n\njulia> import GeometryOps as GO\n\njulia> geom = GI.Polygon([GI.LinearRing([(1, 2), (3, 4), (5, 6), (1, 2)]), GI.LinearRing([(3, 4), (5, 6), (6, 7), (3, 4)])]);\n\njulia> f = CoordinateTransformations.Translation(3.5, 1.5)\nTranslation(3.5, 1.5)\n\njulia> GO.transform(f, geom)\nGeoInterface.Wrappers.Polygon{false, false, Vector{GeoInterface.Wrappers.LinearRing{false, false, Vector{StaticArraysCore.SVector{2, Float64}}, Nothing, Nothing}}, Nothing, Nothing}(GeoInterface.Wrappers.Linea\nrRing{false, false, Vector{StaticArraysCore.SVector{2, Float64}}, Nothing, Nothing}[GeoInterface.Wrappers.LinearRing{false, false, Vector{StaticArraysCore.SVector{2, Float64}}, Nothing, Nothing}(StaticArraysCo\nre.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\nrraysCore.SVector{2, Float64}[[6.5, 5.5], [8.5, 7.5], [9.5, 8.5], [6.5, 5.5]], nothing, nothing)], nothing, nothing)\n```\n\nWith Rotations.jl you need to actuall multiply the Rotation\nby the `SVector` point, which is easy using an anonymous function.\n\n```julia\njulia> using Rotations\n\njulia> GO.transform(p -> one(RotMatrix{2}) * p, geom)\nGeoInterface.Wrappers.Polygon{false, false, Vector{GeoInterface.Wrappers.LinearRing{false, false, Vector{StaticArraysCore.SVector{2, Int64}}, Nothing, Nothing}}, Nothing, Nothing}(GeoInterface.Wrappers.LinearR\ning{false, false, Vector{StaticArraysCore.SVector{2, Int64}}, Nothing, Nothing}[GeoInterface.Wrappers.LinearRing{false, false, Vector{StaticArraysCore.SVector{2, Int64}}, Nothing, Nothing}(StaticArraysCore.SVe\nctor{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\n}[[4, 3], [6, 5], [7, 6], [4, 3]], nothing, nothing)], nothing, nothing)\n```\n\"\"\"\nfunction transform(f, geom; kw...)\n if _is3d(geom)\n return apply(PointTrait, geom; kw...) do p\n f(StaticArrays.SVector{3}((GI.x(p), GI.y(p), GI.z(p))))\n end\n else\n return apply(PointTrait, geom; kw...) do p\n f(StaticArrays.SVector{2}((GI.x(p), GI.y(p))))\n end\n end\nend","category":"page"},{"location":"source/transformations/transform/","page":"-","title":"-","text":"","category":"page"},{"location":"source/transformations/transform/","page":"-","title":"-","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/transformations/simplify/#Geometry-simplification","page":"Geometry simplification","title":"Geometry simplification","text":"","category":"section"},{"location":"source/transformations/simplify/","page":"Geometry simplification","title":"Geometry simplification","text":"This file holds implementations for the Douglas-Peucker and Visvalingam-Whyatt algorithms for simplifying geometries (specifically polygons and lines).","category":"page"},{"location":"source/transformations/simplify/","page":"Geometry simplification","title":"Geometry simplification","text":"export simplify, VisvalingamWhyatt, DouglasPeucker, RadialDistance\n\n\n\"\"\"\n abstract type SimplifyAlg\n\nAbstract type for simplification algorithms.\n\n# API\n\nFor now, the algorithm must hold the `number`, `ratio` and `tol` properties.\n\nSimplification algorithm types can hook into the interface by implementing\nthe `_simplify(trait, alg, geom)` methods for whichever traits are necessary.\n\"\"\"\nabstract type SimplifyAlg end\n\nconst SIMPLIFY_ALG_KEYWORDS = \"\"\"\n# Keywords\n\n- `ratio`: the fraction of points that should remain after `simplify`.\n Useful as it will generalise for large collections of objects.\n- `number`: the number of points that should remain after `simplify`.\n Less useful for large collections of mixed size objects.\n\"\"\"\n\nconst MIN_POINTS = 3\n\nfunction checkargs(number, ratio, tol)\n count(isnothing, (number, ratio, tol)) == 2 ||\n error(\"Must provide one of `number`, `ratio` or `tol` keywords\")\n if !isnothing(ratio)\n if ratio <= 0 || ratio > 1\n error(\"`ratio` must be 0 < ratio <= 1. Got $ratio\")\n end\n end\n if !isnothing(number)\n if number < MIN_POINTS\n error(\"`number` must be $MIN_POINTS or larger. Got $number\")\n end\n end\n return nothing\nend\n\n\"\"\"\n simplify(obj; kw...)\n simplify(::SimplifyAlg, obj; kw...)\n\nSimplify a geometry, feature, feature collection,\nor nested vectors or a table of these.\n\n`RadialDistance`, `DouglasPeucker`, or\n`VisvalingamWhyatt` algorithms are available,\nlisted in order of increasing quality but decreaseing performance.\n\n`PoinTrait` and `MultiPointTrait` are returned unchanged.\n\nThe default behaviour is `simplify(DouglasPeucker(; kw...), obj)`.\nPass in other `SimplifyAlg` to use other algorithms.","category":"page"},{"location":"source/transformations/simplify/","page":"Geometry simplification","title":"Geometry simplification","text":"Keywords","category":"page"},{"location":"source/transformations/simplify/","page":"Geometry simplification","title":"Geometry simplification","text":"$APPLY_KEYWORDS\n\nKeywords for DouglasPeucker are allowed when no algorithm is specified:\n\n$SIMPLIFY_ALG_KEYWORDS","category":"page"},{"location":"source/transformations/simplify/","page":"Geometry simplification","title":"Geometry simplification","text":"Example","category":"page"},{"location":"source/transformations/simplify/","page":"Geometry simplification","title":"Geometry simplification","text":"Simplify a polygon to have six points:\n\n```jldoctest\nimport GeoInterface as GI\nimport GeometryOps as GO\n\npoly = GI.Polygon([[\n [-70.603637, -33.399918],\n [-70.614624, -33.395332],\n [-70.639343, -33.392466],\n [-70.659942, -33.394759],\n [-70.683975, -33.404504],\n [-70.697021, -33.419406],\n [-70.701141, -33.434306],\n [-70.700454, -33.446339],\n [-70.694274, -33.458369],\n [-70.682601, -33.465816],\n [-70.668869, -33.472117],\n [-70.646209, -33.473835],\n [-70.624923, -33.472117],\n [-70.609817, -33.468107],\n [-70.595397, -33.458369],\n [-70.587158, -33.442901],\n [-70.587158, -33.426283],\n [-70.590591, -33.414248],\n [-70.594711, -33.406224],\n [-70.603637, -33.399918]]])\n\nsimple = GO.simplify(poly; number=6)\nGI.npoint(simple)","category":"page"},{"location":"source/transformations/simplify/","page":"Geometry simplification","title":"Geometry simplification","text":"output","category":"page"},{"location":"source/transformations/simplify/","page":"Geometry simplification","title":"Geometry simplification","text":"6\n```\n\"\"\"\nsimplify(data; calc_extent=false, threaded=false, crs=nothing, kw...) =\n _simplify(DouglasPeucker(; kw...), data; calc_extent, threaded, crs)\nsimplify(alg::SimplifyAlg, data; kw...) = _simplify(alg, data; kw...)\n\nfunction _simplify(alg::SimplifyAlg, data; kw...)\n # Apply simplication to all curves, multipoints, and points,\n # reconstructing everything else around them.\n simplifier(geom) = _simplify(trait(geom), alg, geom)\n apply(simplifier, Union{PolygonTrait,AbstractCurveTrait,MultiPoint,PointTrait}, data; kw...)\nend\n# For Point and MultiPoint traits we do nothing\n_simplify(::PointTrait, alg, geom) = geom\n_simplify(::MultiPointTrait, alg, geom) = geom\nfunction _simplify(::PolygonTrait, alg, geom)\n # Force treating children as LinearRing\n rebuilder(g) = rebuild(g, _simplify(LinearRingTrait(), alg, g))\n lrs = map(rebuilder, GI.getgeom(geom))\n return rebuild(geom, lrs)\nend\n# For curves and rings we simplify\n_simplify(::AbstractCurveTrait, alg, geom) = rebuild(geom, simplify(alg, tuple_points(geom)))\n\n\"\"\"\n RadialDistance <: SimplifyAlg\n\nSimplifies geometries by removing points less than\n`tol` distance from the line between its neighboring points.\n\n$SIMPLIFY_ALG_KEYWORDS\n- `tol`: the minimum distance between points.\n\"\"\"\nstruct RadialDistance <: SimplifyAlg\n number::Union{Int64,Nothing}\n ratio::Union{Float64,Nothing}\n tol::Union{Float64,Nothing}\nend\nfunction RadialDistance(; number=nothing, ratio=nothing, tol=nothing)\n checkargs(number, ratio, tol)\n return RadialDistance(number, ratio, tol)\nend\n\nsettol(alg::RadialDistance, tol) = RadialDistance(alg.number, alg.ratio, tol)\n\nfunction _simplify(alg::RadialDistance, points::Vector)\n previous = first(points)\n distances = Array{Float64}(undef, length(points))\n for i in eachindex(points)\n point = points[i]\n distances[i] = _squared_dist(point, previous)\n previous = point\n end\n # Never remove the end points\n distances[begin] = distances[end] = Inf\n # This avoids taking the square root of each distance above\n if !isnothing(alg.tol)\n alg = settol(alg, (alg.tol::Float64)^2)\n end\n return _get_points(alg, points, distances)\nend\n\nfunction _squared_dist(p1, p2)\n dx = GI.x(p1) - GI.x(p2)\n dy = GI.y(p1) - GI.y(p2)\n return dx^2 + dy^2\nend\n\n\"\"\"\n DouglasPeucker <: SimplifyAlg\n\n DouglasPeucker(; number, ratio, tol)\n\nSimplifies geometries by removing points below `tol`\ndistance from the line between its neighboring points.\n\n$SIMPLIFY_ALG_KEYWORDS\n- `tol`: the minimum distance a point will be from the line\n joining its neighboring points.\n\"\"\"\nstruct DouglasPeucker <: SimplifyAlg\n number::Union{Int64,Nothing}\n ratio::Union{Float64,Nothing}\n tol::Union{Float64,Nothing}\n prefilter::Bool\nend\nfunction DouglasPeucker(; number=nothing, ratio=nothing, tol=nothing, prefilter=false)\n checkargs(number, ratio, tol)\n return DouglasPeucker(number, ratio, tol, prefilter)\nend\n\nsettol(alg::DouglasPeucker, tol) = DouglasPeucker(alg.number, alg.ratio, tol, alg.prefilter)\n\nfunction _simplify(alg::DouglasPeucker, points::Vector)\n length(points) <= MIN_POINTS && return points\n # TODO do we need this?\n # points = alg.prefilter ? simplify(RadialDistance(alg.tol), points) : points\n\n distances = _build_tolerances(_squared_segdist, points)\n return _get_points(alg, points, distances)\nend\n\nfunction _squared_segdist(l1, p, l2)\n x, y = GI.x(l1), GI.y(l1)\n dx = GI.x(l2) - x\n dy = GI.y(l2) - y\n\n if !iszero(dx) || !iszero(dy)\n t = ((GI.x(p) - x) * dx + (GI.y(p) - y) * dy) / (dx * dx + dy * dy)\n if t > 1\n x = GI.x(l2)\n y = GI.y(l2)\n elseif t > 0\n x += dx * t\n y += dy * t\n end\n end\n\n dx = GI.x(p) - x\n dy = GI.y(p) - y\n\n return dx^2 + dy^2\nend\n\n\n\"\"\"\n VisvalingamWhyatt <: SimplifyAlg\n\n VisvalingamWhyatt(; kw...)\n\nSimplifies geometries by removing points below `tol`\ndistance from the line between its neighboring points.\n\n$SIMPLIFY_ALG_KEYWORDS\n- `tol`: the minimum area of a triangle made with a point and\n its neighboring points.\n\"\"\"\nstruct VisvalingamWhyatt <: SimplifyAlg\n number::Union{Int,Nothing}\n ratio::Union{Float64,Nothing}\n tol::Union{Float64,Nothing}\n prefilter::Bool\nend\nfunction VisvalingamWhyatt(; number=nothing, ratio=nothing, tol=nothing, prefilter=false)\n checkargs(number, ratio, tol)\n return VisvalingamWhyatt(number, ratio, tol, prefilter)\nend\n\nsettol(alg::VisvalingamWhyatt, tol) = VisvalingamWhyatt(alg.number, alg.ratio, tol, alg.prefilter)\n\nfunction _simplify(alg::VisvalingamWhyatt, points::Vector)\n length(points) <= MIN_POINTS && return points\n areas = _build_tolerances(_triangle_double_area, points)\n\n # This avoids diving everything by two\n if !isnothing(alg.tol)\n alg = settol(alg, (alg.tol::Float64)*2)\n end\n return _get_points(alg, points, areas)\nend\n\n# calculates the area of a triangle given its vertices\n_triangle_double_area(p1, p2, p3) =\n abs(p1[1] * (p2[2] - p3[2]) + p2[1] * (p3[2] - p1[2]) + p3[1] * (p1[2] - p2[2]))","category":"page"},{"location":"source/transformations/simplify/#Shared-utils","page":"Geometry simplification","title":"Shared utils","text":"","category":"section"},{"location":"source/transformations/simplify/","page":"Geometry simplification","title":"Geometry simplification","text":"function _build_tolerances(f, points)\n nmax = length(points)\n real_tolerances = _flat_tolerances(f, points)\n\n tolerances = copy(real_tolerances)\n i = collect(1:nmax)\n\n min_vert = argmin(tolerances)\n this_tolerance = tolerances[min_vert]\n _remove!(tolerances, min_vert)\n deleteat!(i, min_vert)\n\n while this_tolerance < Inf\n skip = false\n\n if min_vert < length(i)\n right_tolerance = f(\n points[i[min_vert - 1]],\n points[i[min_vert]],\n points[i[min_vert + 1]],\n )\n if right_tolerance <= this_tolerance\n right_tolerance = this_tolerance\n skip = min_vert == 1\n end\n\n real_tolerances[i[min_vert]] = right_tolerance\n tolerances[min_vert] = right_tolerance\n end\n\n if min_vert > 2\n left_tolerance = f(\n points[i[min_vert - 2]],\n points[i[min_vert - 1]],\n points[i[min_vert]],\n )\n if left_tolerance <= this_tolerance\n left_tolerance = this_tolerance\n skip = min_vert == 2\n end\n real_tolerances[i[min_vert - 1]] = left_tolerance\n tolerances[min_vert - 1] = left_tolerance\n end\n\n if !skip\n min_vert = argmin(tolerances)\n end\n deleteat!(i, min_vert)\n this_tolerance = tolerances[min_vert]\n _remove!(tolerances, min_vert)\n end\n\n return real_tolerances\nend\n\nfunction tuple_points(geom)\n points = Array{Tuple{Float64,Float64}}(undef, GI.ngeom(geom))\n for (i, p) in enumerate(GI.getpoint(geom))\n points[i] = (GI.x(p), GI.y(p))\n end\n return points\nend\n\nfunction _get_points(alg, points, tolerances)\n # This assumes that `alg` has the properties\n # `tol`, `number`, and `ratio` available...\n tol = alg.tol\n number = alg.number\n ratio = alg.ratio\n bit_indices = if !isnothing(tol)\n _tol_indices(alg.tol::Float64, points, tolerances)\n elseif !isnothing(number)\n _number_indices(alg.number::Int64, points, tolerances)\n else\n _ratio_indices(alg.ratio::Float64, points, tolerances)\n end\n return points[bit_indices]\nend\n\nfunction _tol_indices(tol, points, tolerances)\n tolerances .>= tol\nend\n\nfunction _number_indices(n, points, tolerances)\n tol = partialsort(tolerances, length(points) - n + 1)\n bit_indices = _tol_indices(tol, points, tolerances)\n nselected = sum(bit_indices)\n # If there are multiple values exactly at `tol` we will get\n # the wrong output length. So we need to remove some.\n while nselected > n\n min_tol = Inf\n min_i = 0\n for i in eachindex(bit_indices)\n bit_indices[i] || continue\n if tolerances[i] < min_tol\n min_tol = tolerances[i]\n min_i = i\n end\n end\n nselected -= 1\n bit_indices[min_i] = false\n end\n return bit_indices\nend\n\nfunction _ratio_indices(r, points, tolerances)\n n = max(3, round(Int, r * length(points)))\n return _number_indices(n, points, tolerances)\nend\n\nfunction _flat_tolerances(f, points)\n result = Array{Float64}(undef, length(points))\n result[1] = result[end] = Inf\n\n for i in 2:length(result) - 1\n result[i] = f(points[i-1], points[i], points[i+1])\n end\n return result\nend\n\n_remove!(s, i) = s[i:end-1] .= s[i+1:end]","category":"page"},{"location":"source/transformations/simplify/","page":"Geometry simplification","title":"Geometry simplification","text":"","category":"page"},{"location":"source/transformations/simplify/","page":"Geometry simplification","title":"Geometry simplification","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/methods/geom_relations/within/#Containment/withinness","page":"Containment/withinness","title":"Containment/withinness","text":"","category":"section"},{"location":"source/methods/geom_relations/within/","page":"Containment/withinness","title":"Containment/withinness","text":"export within\n\n\n\"\"\"\n within(geom1, geom)::Bool\n\nReturn `true` if the first geometry is completely within the second geometry.\nThe interiors of both geometries must intersect and, the interior and boundary of the primary (geometry a)\nmust not intersect the exterior of the secondary (geometry b).\n`within` returns the exact opposite result of `contains`.\n\n# Examples\n```jldoctest setup=:(using GeometryOps, GeometryBasics)\nimport GeometryOps as GO, GeoInterface as GI\n\nline = GI.LineString([(1, 1), (1, 2), (1, 3), (1, 4)])\npoint = (1, 2)\nGO.within(point, line)","category":"page"},{"location":"source/methods/geom_relations/within/","page":"Containment/withinness","title":"Containment/withinness","text":"output","category":"page"},{"location":"source/methods/geom_relations/within/","page":"Containment/withinness","title":"Containment/withinness","text":"true\n```\n\"\"\"","category":"page"},{"location":"source/methods/geom_relations/within/","page":"Containment/withinness","title":"Containment/withinness","text":"Syntactic sugar","category":"page"},{"location":"source/methods/geom_relations/within/","page":"Containment/withinness","title":"Containment/withinness","text":"within(g1, g2)::Bool = within(trait(g1), g1, trait(g2), g2)::Bool\nwithin(::GI.FeatureTrait, g1, ::Any, g2)::Bool = within(GI.geometry(g1), g2)\nwithin(::Any, g1, t2::GI.FeatureTrait, g2)::Bool = within(g1, GI.geometry(g2))","category":"page"},{"location":"source/methods/geom_relations/within/","page":"Containment/withinness","title":"Containment/withinness","text":"Points in geometries","category":"page"},{"location":"source/methods/geom_relations/within/","page":"Containment/withinness","title":"Containment/withinness","text":"within(::GI.PointTrait, g1, ::GI.LineStringTrait, g2)::Bool = point_on_line(g1, g2; ignore_end_vertices=true)\nwithin(::GI.PointTrait, g1, ::GI.LinearRingTrait, g2)::Bool = point_on_line(g1, g2; ignore_end_vertices=true)\nwithin(::GI.PointTrait, g1, ::GI.PolygonTrait, g2)::Bool = point_in_polygon(g1, g2; ignore_boundary=true)","category":"page"},{"location":"source/methods/geom_relations/within/","page":"Containment/withinness","title":"Containment/withinness","text":"Lines in geometries","category":"page"},{"location":"source/methods/geom_relations/within/","page":"Containment/withinness","title":"Containment/withinness","text":"within(::GI.LineStringTrait, g1, ::GI.LineStringTrait, g2)::Bool = line_on_line(g1, g2)\nwithin(::GI.LineStringTrait, g1, ::GI.LinearRingTrait, g2)::Bool = line_on_line(g1, g2)\nwithin(::GI.LineStringTrait, g1, ::GI.PolygonTrait, g2)::Bool = line_in_polygon(g1, g2)","category":"page"},{"location":"source/methods/geom_relations/within/","page":"Containment/withinness","title":"Containment/withinness","text":"Polygons within geometries","category":"page"},{"location":"source/methods/geom_relations/within/","page":"Containment/withinness","title":"Containment/withinness","text":"within(::GI.PolygonTrait, g1, ::GI.PolygonTrait, g2)::Bool = polygon_in_polygon(g1, g2)","category":"page"},{"location":"source/methods/geom_relations/within/","page":"Containment/withinness","title":"Containment/withinness","text":"Everything not specified TODO: Add multipolygons","category":"page"},{"location":"source/methods/geom_relations/within/","page":"Containment/withinness","title":"Containment/withinness","text":"within(::GI.AbstractTrait, g1, ::GI.AbstractCurveTrait, g2)::Bool = false","category":"page"},{"location":"source/methods/geom_relations/within/","page":"Containment/withinness","title":"Containment/withinness","text":"","category":"page"},{"location":"source/methods/geom_relations/within/","page":"Containment/withinness","title":"Containment/withinness","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/methods/distance/#Distance-and-signed-distance","page":"Distance and signed distance","title":"Distance and signed distance","text":"","category":"section"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"export distance, signed_distance","category":"page"},{"location":"source/methods/distance/#What-is-distance?-What-is-signed-distance?","page":"Distance and signed distance","title":"What is distance? What is signed distance?","text":"","category":"section"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"Distance is the distance of a point to another geometry. This is always a positive number. If a point is inside of geometry, so on a curve or inside of a polygon, the distance will be zero. Signed distance is mainly used for polygons and multipolygons. If a point is outside of a geometry, signed distance has the same value as distance. However, points within the geometry have a negative distance representing the distance of a point to the closest boundary. Therefore, for all \"non-filled\" geometries, like curves, the distance will either be postitive or 0.","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"To provide an example, consider this rectangle:","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"using GeometryOps\nusing GeometryOps.GeometryBasics\nusing Makie\n\nrect = Polygon([Point(0,0), Point(0,1), Point(1,1), Point(1,0), Point(0, 0)])\npoint_in = Point(0.5, 0.5)\npoint_out = Point(0.5, 1.5)\nf, a, p = poly(rect; axis = (; aspect = DataAspect()))\nscatter!(f, point_in)\nscatter!(f, point_out)\nf","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"This is clearly a rectangle with one point inside and one point outside. The points are both an equal distance to the polygon. The distance to pointin is negative while the distance to pointout is positive.","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"distance(point_in, poly) # == 0\nsigned_distance(point_in, poly) # < 0\nsigned_distance(point_out, poly) # > 0","category":"page"},{"location":"source/methods/distance/#Implementation","page":"Distance and signed distance","title":"Implementation","text":"","category":"section"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"This is the GeoInterface-compatible implementation. First, we implement a wrapper method that dispatches to the correct implementation based on the geometry trait. This is also used in the implementation, since it's a lot less work!","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"Distance and signed distance are only implemented for points to other geometries right now. This could be extended to include distance from other geometries in the future.","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"The distance calculated is the Euclidean distance using the Pythagorean theorem. Also note that singed_distance only makes sense for \"filled-in\" shapes, like polygons, so it isn't implemented for curves.","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"\"\"\"\n distance(point, geom, ::Type{T} = Float64)::T\n\nCalculates the ditance from the geometry `g1` to the `point`. The distance\nwill always be positive or zero.\n\nThe method will differ based on the type of the geometry provided:\n - The distance from a point to a point is just the Euclidean distance\n between the points.\n - The distance from a point to a line is the minimum distance from the point\n to the closest point on the given line.\n - The distance from a point to a linestring is the minimum distance from the\n point to the closest segment of the linestring.\n - The distance from a point to a linear ring is the minimum distance from\n the point to the closest segment of the linear ring.\n - The distance from a point to a polygon is zero if the point is within the\n polygon and otherwise is the minimum distance from the point to an edge of\n the polygon. This includes edges created by holes.\n - The distance from a point to a multigeometry or a geometry collection is\n the minimum distance between the point and any of the sub-geometries.\n\nResult will be of type T, where T is an optional argument with a default value\nof Float64.\n\"\"\"\ndistance(point, geom, ::Type{T} = Float64) where T <: AbstractFloat =\n _distance(T, GI.trait(point), point, GI.trait(geom), geom)\n\n\"\"\"\n signed_distance(point, geom, ::Type{T} = Float64)::T\n\nCalculates the signed distance from the geometry `geom` to the given point.\nPoints within `geom` have a negative signed distance, and points outside of\n`geom` have a positive signed distance.\n - The signed distance from a point to a point, line, linestring, or linear\n ring is equal to the distance between the two.\n - The signed distance from a point to a polygon is negative if the point is\n within the polygon and is positive otherwise. The value of the distance is\n the minimum distance from the point to an edge of the polygon. This includes\n edges created by holes.\n - The signed distance from a point to a multigeometry or a geometry\n collection is the minimum signed distance between the point and any of the\n sub-geometries.\n\nResult will be of type T, where T is an optional argument with a default value\nof Float64.\n\"\"\"\nsigned_distance(point, geom, ::Type{T} = Float64) where T<:AbstractFloat =\n _signed_distance(T, GI.trait(point), point, GI.trait(geom), geom)","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"Swap argument order to point as first argument","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"_distance(\n ::Type{T},\n gtrait::GI.AbstractTrait, geom,\n ptrait::GI.PointTrait, point,\n) where T = _distance(T, ptrait, point, gtrait, geom)\n\n_signed_distance(\n ::Type{T},\n gtrait::GI.AbstractTrait, geom,\n ptrait::GI.PointTrait, point,\n) where T = _signed_distance(T, ptrait, point, gtrait, geom)","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"Point-Point, Point-Line, Point-LineString, Point-LinearRing","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"_distance(::Type{T}, ::GI.PointTrait, point, ::GI.PointTrait, geom) where T =\n _euclid_distance(T, point, geom)\n\n_distance(::Type{T}, ::GI.PointTrait, point, ::GI.LineTrait, geom) where T =\n _distance_line(T, point, GI.getpoint(geom, 1), GI.getpoint(geom, 2))\n\n_distance(::Type{T}, ::GI.PointTrait, point, ::GI.LineStringTrait, geom) where T =\n _distance_curve(T, point, geom, close_curve = false)\n\n_distance(::Type{T}, ::GI.PointTrait, point, ::GI.LinearRingTrait, geom) where T =\n _distance_curve(T, point, geom, close_curve = true)\n\n_signed_distance(::Type{T}, ptrait::GI.PointTrait, point, gtrait::GI.AbstractTrait, geom) where T =\n _distance(T, ptrait, point, gtrait, geom)","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"Point-Polygon","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"function _distance(::Type{T}, ::GI.PointTrait, point, ::GI.PolygonTrait, geom) where T\n GI.within(point, geom) && return zero(T)\n return _distance_polygon(T, point, geom)\nend\n\nfunction _signed_distance(::Type{T}, ::GI.PointTrait, point, ::GI.PolygonTrait, geom) where T\n min_dist = _distance_polygon(T, point, geom)","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"negative if point is inside polygon","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":" return GI.within(point, geom) ? -min_dist : min_dist\nend","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"Point-MultiGeometries / Point-GeometryCollections","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"function _distance(\n ::Type{T},\n ::GI.PointTrait,\n point,\n ::Union{\n GI.MultiPointTrait, GI.MultiCurveTrait,\n GI.MultiPolygonTrait, GI.GeometryCollectionTrait,\n },\n geoms,\n) where T\n min_dist = typemax(T)\n for g in GI.getgeom(geoms)\n dist = distance(point, g, T)\n min_dist = dist < min_dist ? dist : min_dist\n end\n return min_dist\nend\n\nfunction _signed_distance(\n ::Type{T},\n ::GI.PointTrait,\n point,\n ::Union{\n GI.MultiPointTrait, GI.MultiCurveTrait,\n GI.MultiPolygonTrait, GI.GeometryCollectionTrait,\n },\n geoms,\n) where T\n min_dist = typemax(T)\n for g in GI.getgeom(geoms)\n dist = signed_distance(point, g, T)\n min_dist = dist < min_dist ? dist : min_dist\n end\n return min_dist\nend","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"Returns the Euclidean distance between two points.","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"Base.@propagate_inbounds _euclid_distance(::Type{T}, p1, p2) where T =\n _euclid_distance(\n T,\n GeoInterface.x(p1), GeoInterface.y(p1),\n GeoInterface.x(p2), GeoInterface.y(p2),\n )","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"Returns the Euclidean distance between two points given their x and y values.","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"Base.@propagate_inbounds _euclid_distance(::Type{T}, x1, y1, x2, y2) where T =\n T(sqrt((x2 - x1)^2 + (y2 - y1)^2))","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"Returns the minimum distance from point p0 to the line defined by endpoints p1 and p2.","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"function _distance_line(::Type{T}, p0, p1, p2) where T\n x0, y0 = GeoInterface.x(p0), GeoInterface.y(p0)\n x1, y1 = GeoInterface.x(p1), GeoInterface.y(p1)\n x2, y2 = GeoInterface.x(p2), GeoInterface.y(p2)\n\n xfirst, yfirst, xlast, ylast = x1 < x2 ?\n (x1, y1, x2, y2) : (x2, y2, x1, y1)\n\n #=\n Vectors from first point to last point (v) and from first point to point of\n interest (w) to find the projection of w onto v to find closest point\n =#\n v = (xlast - xfirst, ylast - yfirst)\n w = (x0 - xfirst, y0 - yfirst)\n\n c1 = sum(w .* v)\n if c1 <= 0 # p0 is closest to first endpoint\n return _euclid_distance(T, x0, y0, xfirst, yfirst)\n end\n\n c2 = sum(v .* v)\n if c2 <= c1 # p0 is closest to last endpoint\n return _euclid_distance(T, x0, y0, xlast, ylast)\n end\n\n b2 = c1 / c2 # projection fraction\n return _euclid_distance(T, x0, y0, xfirst + (b2 * v[1]), yfirst + (b2 * v[2]))\nend","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"Returns the minimum distance from the given point to the given curve. If close_curve is true, make sure to include the edge from the first to last point of the curve, even if it isn't explicitly repeated.","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"function _distance_curve(::Type{T}, point, curve; close_curve = false) where T","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"see if linear ring has explicitly repeated last point in coordinates","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":" np = GI.npoint(curve)\n first_last_equal = equals(GI.getpoint(curve, 1), GI.getpoint(curve, np))\n close_curve &= first_last_equal\n np -= first_last_equal ? 1 : 0","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"find minimum distance","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":" min_dist = typemax(T)\n p1 = GI.getpoint(curve, close_curve ? np : 1)\n for i in (close_curve ? 1 : 2):np\n p2 = GI.getpoint(curve, i)\n dist = _distance_line(T, point, p1, p2)\n min_dist = dist < min_dist ? dist : min_dist\n p1 = p2\n end\n return min_dist\nend","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"Returns the minimum distance from the given point to an edge of the given polygon, including from edges created by holes. Assumes polygon isn't filled and treats the exterior and each hole as a linear ring.","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"function _distance_polygon(::Type{T}, point, poly) where T\n min_dist = _distance_curve(T, point, GI.getexterior(poly); close_curve = true)\n @inbounds for hole in GI.gethole(poly)\n dist = _distance_curve(T, point, hole; close_curve = true)\n min_dist = dist < min_dist ? dist : min_dist\n end\n return min_dist\nend","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/utils/#Utility-functions","page":"Utility functions","title":"Utility functions","text":"","category":"section"},{"location":"source/utils/","page":"Utility functions","title":"Utility functions","text":"_is3d(geom) = _is3d(GI.trait(geom), geom)\n_is3d(::GI.AbstractGeometryTrait, geom) = GI.is3d(geom)\n_is3d(::GI.FeatureTrait, feature) = _is3d(GI.geometry(feature))\n_is3d(::GI.FeatureCollectionTrait, fc) = _is3d(GI.getfeature(fc, 1))\n_is3d(::Nothing, geom) = _is3d(first(geom)) # Otherwise step into an itererable\n\n_npoint(x) = _npoint(trait(x), x)\n_npoint(::Nothing, xs::AbstractArray) = sum(_npoint, xs)\n_npoint(::GI.FeatureCollectionTrait, fc) = sum(_npoint, GI.getfeature(fc))\n_npoint(::GI.FeatureTrait, f) = _npoint(GI.geometry(f))\n_npoint(::GI.AbstractGeometryTrait, x) = GI.npoint(trait(x), x)\n\n_nedge(x) = _nedge(trait(x), x)\n_nedge(::Nothing, xs::AbstractArray) = sum(_nedge, xs)\n_nedge(::GI.FeatureCollectionTrait, fc) = sum(_nedge, GI.getfeature(fc))\n_nedge(::GI.FeatureTrait, f) = _nedge(GI.geometry(f))\nfunction _nedge(::GI.AbstractGeometryTrait, x)\n n = 0\n for g in GI.getgeom(x)\n n += _nedge(g)\n end\n return n\nend\n_nedge(::GI.AbstractCurveTrait, x) = GI.npoint(x) - 1\n_nedge(::GI.PointTrait, x) = error(\"Cant get edges from points\")\n\n\n\"\"\"\n polygon_to_line(poly::Polygon)\n\nConverts a Polygon to LineString or MultiLineString","category":"page"},{"location":"source/utils/","page":"Utility functions","title":"Utility functions","text":"Examples","category":"page"},{"location":"source/utils/","page":"Utility functions","title":"Utility functions","text":"```jldoctest\nimport GeometryOps as GO, GeoInterface as GI\n\npoly = GI.Polygon([[(-2.275543, 53.464547), (-2.275543, 53.489271), (-2.215118, 53.489271), (-2.215118, 53.464547), (-2.275543, 53.464547)]])\nGO.polygon_to_line(poly)","category":"page"},{"location":"source/utils/","page":"Utility functions","title":"Utility functions","text":"output","category":"page"},{"location":"source/utils/","page":"Utility functions","title":"Utility functions","text":"GeoInterface.Wrappers.LineString{false, false, Vector{Tuple{Float64, Float64}}, Nothing, Nothing}([(-2.275543, 53.464547), (-2.275543, 53.489271), (-2.215118, 53.489271), (-2.215118, 53.464547), (-2.275543, 53.464547)], nothing, nothing)\n```\n\"\"\"\nfunction polygon_to_line(poly)\n @assert GI.trait(poly) isa PolygonTrait\n GI.ngeom(poly) > 1 && return GI.MultiLineString(collect(GI.getgeom(poly)))\n return GI.LineString(collect(GI.getgeom(GI.getgeom(poly, 1))))\nend\n\n\n\"\"\"\n to_edges()\n\nConvert any geometry or collection of geometries into a flat\nvector of `Tuple{Tuple{Float64,Float64},Tuple{Float64,Float64}}` edges.\n\"\"\"\nfunction to_edges(x)\n edges = Vector{Edge}(undef, _nedge(x))\n _to_edges!(edges, x, 1)\n return edges\nend\n\n_to_edges!(edges::Vector, x, n) = _to_edges!(edges, trait(x), x, n)\nfunction _to_edges!(edges::Vector, ::GI.FeatureCollectionTrait, fc, n)\n for f in GI.getfeature(fc)\n n = _to_edges!(edges, f, n)\n end\nend\n_to_edges!(edges::Vector, ::GI.FeatureTrait, f, n) = _to_edges!(edges, GI.geometry(f), n)\nfunction _to_edges!(edges::Vector, ::GI.AbstractGeometryTrait, fc, n)\n for f in GI.getgeom(fc)\n n = _to_edges!(edges, f, n)\n end\nend\nfunction _to_edges!(edges::Vector, ::GI.AbstractCurveTrait, geom, n)\n p1 = GI.getpoint(geom, 1)\n p1x, p1y = GI.x(p1), GI.y(p1)\n for i in 2:GI.npoint(geom)\n p2 = GI.getpoint(geom, i)\n p2x, p2y = GI.x(p2), GI.y(p2)\n edges[n] = (p1x, p1y), (p2x, p2y)\n p1x, p1y = p2x, p2y\n n += 1\n end\n return n\nend\n\n_tuple_point(p) = GI.x(p), GI.y(p)\n\nfunction to_extent(edges::Vector{Edge})\n x, y = extrema(first, edges)\n Extents.Extent(X=x, Y=y)\nend\n\nfunction to_points(xs)\n points = Vector{TuplePoint}(undef, _npoint(x))\n _to_points!(points, x, 1)\n return points\nend\n\n_to_points!(points::Vector, x, n) = _to_points!(points, trait(x), x, n)\nfunction _to_points!(points::Vector, ::FeatureCollectionTrait, fc, n)\n for f in GI.getfeature(fc)\n n = _to_points!(points, f, n)\n end\nend\n_to_points!(points::Vector, ::FeatureTrait, f, n) = _to_points!(points, GI.geometry(f), n)\nfunction _to_points!(points::Vector, ::AbstractGeometryTrait, fc, n)\n for f in GI.getgeom(fc)\n n = _to_points!(points, f, n)\n end\nend\nfunction _to_points!(points::Vector, ::Union{AbstractCurveTrait,MultiPointTrait}, geom, n)\n p1 = GI.getpoint(geom, 1)\n p1x, p1y = GI.x(p1), GI.y(p1)\n for i in 2:GI.npoint(geom)\n p2 = GI.getpoint(geom, i)\n p2x, p2y = GI.x(p2), GI.y(p2)\n points[n] = (p1x, p1y), (p2x, p2y)\n p1 = p2\n n += 1\n end\n return n\nend","category":"page"},{"location":"source/utils/","page":"Utility functions","title":"Utility functions","text":"","category":"page"},{"location":"source/utils/","page":"Utility functions","title":"Utility functions","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/methods/bools/#Boolean-conditions","page":"Boolean conditions","title":"Boolean conditions","text":"","category":"section"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"export isclockwise, isconcave\nexport point_on_line, point_in_polygon, point_in_ring\nexport line_on_line, line_in_polygon, polygon_in_polygon","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"These are all adapted from Turf.jl.","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"The may not necessarily be what want in the end but work for now!","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"\"\"\"\n isclockwise(line::Union{LineString, Vector{Position}})::Bool\n\nTake a ring and return true or false whether or not the ring is clockwise or\ncounter-clockwise.\n\n# Example\n\n```jldoctest\nimport GeoInterface as GI, GeometryOps as GO\n\nring = GI.LinearRing([(0, 0), (1, 1), (1, 0), (0, 0)])\nGO.isclockwise(ring)","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"output","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"true\n```\n\"\"\"\nisclockwise(geom)::Bool = isclockwise(GI.trait(geom), geom)\n\nfunction isclockwise(::AbstractCurveTrait, line)::Bool\n sum = 0.0\n prev = GI.getpoint(line, 1)\n for p in GI.getpoint(line)","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"sum will be zero for the first point as x is subtracted from itself","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":" sum += (GI.x(p) - GI.x(prev)) * (GI.y(p) + GI.y(prev))\n prev = p\n end\n\n return sum > 0.0\nend\n\n\"\"\"\n isconcave(poly::Polygon)::Bool\n\nTake a polygon and return true or false as to whether it is concave or not.\n\n# Examples\n```jldoctest\nimport GeoInterface as GI, GeometryOps as GO\n\npoly = GI.Polygon([[(0, 0), (0, 1), (1, 1), (1, 0), (0, 0)]])\nGO.isconcave(poly)","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"output","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"false\n```\n\"\"\"\nfunction isconcave(poly)::Bool\n sign = false\n\n exterior = GI.getexterior(poly)","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"FIXME handle not closed polygons","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":" GI.npoint(exterior) <= 4 && return false\n n = GI.npoint(exterior) - 1\n\n for i in 1:n\n j = ((i + 1) % n) === 0 ? 1 : (i + 1) % n\n m = ((i + 2) % n) === 0 ? 1 : (i + 2) % n\n\n pti = GI.getpoint(exterior, i)\n ptj = GI.getpoint(exterior, j)\n ptm = GI.getpoint(exterior, m)\n\n dx1 = GI.x(ptm) - GI.x(ptj)\n dy1 = GI.y(ptm) - GI.y(ptj)\n dx2 = GI.x(pti) - GI.x(ptj)\n dy2 = GI.y(pti) - GI.y(ptj)\n\n cross = (dx1 * dy2) - (dy1 * dx2)\n\n if i === 0\n sign = cross > 0\n elseif sign !== (cross > 0)\n return true\n end\n end\n\n return false\nend","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"\"\"\" isparallel(line1::LineString, line2::LineString)::Bool","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"Return true if each segment of line1 is parallel to the correspondent segment of line2","category":"page"},{"location":"source/methods/bools/#Examples","page":"Boolean conditions","title":"Examples","text":"","category":"section"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"import GeoInterface as GI, GeometryOps as GO\njulia> line1 = GI.LineString([(9.170356, 45.477985), (9.164434, 45.482551), (9.166644, 45.484003)])\nGeoInterface.Wrappers.LineString{false, false, Vector{Tuple{Float64, Float64}}, Nothing, Nothing}([(9.170356, 45.477985), (9.164434, 45.482551), (9.166644, 45.484003)], nothing, nothing)\n\njulia> line2 = GI.LineString([(9.169356, 45.477985), (9.163434, 45.482551), (9.165644, 45.484003)])\nGeoInterface.Wrappers.LineString{false, false, Vector{Tuple{Float64, Float64}}, Nothing, Nothing}([(9.169356, 45.477985), (9.163434, 45.482551), (9.165644, 45.484003)], nothing, nothing)\n\njulia>\nGO.isparallel(line1, line2)\ntrue","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"\"\"\" function isparallel(line1, line2)::Bool seg1 = linesegment(line1) seg2 = linesegment(line2)","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"for i in eachindex(seg1)\n coors2 = nothing\n coors1 = seg1[i]\n coors2 = seg2[i]\n _isparallel(coors1, coors2) == false && return false\nend\nreturn true","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"end","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"@inline function isparallel(p1, p2) slope1 = bearingtoazimuth(rhumbbearing(GI.x(p1), GI.x(p2))) slope2 = bearingtoazimuth(rhumb_bearing(GI.y(p1), GI.y(p2)))","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"return slope1 === slope2","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"end","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"\"\"\"\n point_on_line(point::Point, line::LineString; ignore_end_vertices::Bool=false)::Bool\n\nReturn true if a point is on a line. Accept a optional parameter to ignore the\nstart and end vertices of the linestring.\n\n# Examples\n\n```jldoctest\nimport GeoInterface as GI, GeometryOps as GO\n\npoint = (1, 1)\nline = GI.LineString([(0, 0), (3, 3), (4, 4)])\nGO.point_on_line(point, line)","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"output","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"true\n```\n\"\"\"\nfunction point_on_line(point, line; ignore_end_vertices::Bool=false)::Bool\n line_points = tuple_points(line)\n n = length(line_points)\n\n exclude_boundary = :none\n for i in 1:n - 1\n if ignore_end_vertices\n if i === 1\n exclude_boundary = :start\n elseif i === n - 2\n exclude_boundary = :end\n elseif (i === 1 && i + 1 === n - 1)\n exclude_boundary = :both\n end\n end\n if point_on_segment(point, (line_points[i], line_points[i + 1]); exclude_boundary)\n return true\n end\n end\n return false\nend\n\nfunction point_on_seg(point, start, stop)","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"Parse out points","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":" x, y = GI.x(point), GI.y(point)\n x1, y1 = GI.x(start), GI.y(start)\n x2, y2 = GI.x(stop), GI.y(stop)\n Δxl = x2 - x1\n Δyl = y2 - y1","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"Determine if point is on segment","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":" cross = (x - x1) * Δyl - (y - y1) * Δxl\n if cross == 0 # point is on line extending to infinity","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"is line between endpoints","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":" if abs(Δxl) >= abs(Δyl) # is line between endpoints\n return Δxl > 0 ? x1 <= x <= x2 : x2 <= x <= x1\n else\n return Δyl > 0 ? y1 <= y <= y2 : y2 <= y <= y1\n end\n end\n return false\nend\n\nfunction point_on_segment(point, (start, stop); exclude_boundary::Symbol=:none)::Bool\n x, y = GI.x(point), GI.y(point)\n x1, y1 = GI.x(start), GI.y(start)\n x2, y2 = GI.x(stop), GI.y(stop)\n\n dxc = x - x1\n dyc = y - y1\n dx1 = x2 - x1\n dy1 = y2 - y1","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"TODO use better predicate for crossing here","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":" cross = dxc * dy1 - dyc * dx1\n cross != 0 && return false","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"Will constprop optimise these away?","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":" if exclude_boundary === :none\n if abs(dx1) >= abs(dy1)\n return dx1 > 0 ? x1 <= x && x <= x2 : x2 <= x && x <= x1\n end\n return dy1 > 0 ? y1 <= y && y <= y2 : y2 <= y && y <= y1\n elseif exclude_boundary === :start\n if abs(dx1) >= abs(dy1)\n return dx1 > 0 ? x1 < x && x <= x2 : x2 <= x && x < x1\n end\n return dy1 > 0 ? y1 < y && y <= y2 : y2 <= y && y < y1\n elseif exclude_boundary === :end\n if abs(dx1) >= abs(dy1)\n return dx1 > 0 ? x1 <= x && x < x2 : x2 < x && x <= x1\n end\n return dy1 > 0 ? y1 <= y && y < y2 : y2 < y && y <= y1\n elseif exclude_boundary === :both\n if abs(dx1) >= abs(dy1)\n return dx1 > 0 ? x1 < x && x < x2 : x2 < x && x < x1\n end\n return dy1 > 0 ? y1 < y && y < y2 : y2 < y && y < y1\n end\n return false\nend\n\n\"\"\"\n point_in_polygon(point::Point, polygon::Union{Polygon, MultiPolygon}, ignore_boundary::Bool=false)::Bool\n\nTake a Point and a Polygon and determine if the point\nresides inside the polygon. The polygon can be convex or concave. The function accounts for holes.\n\n# Examples\n\n```jldoctest\nimport GeoInterface as GI, GeometryOps as GO\n\npoint = (-77.0, 44.0)\npoly = GI.Polygon([[(-81, 41), (-81, 47), (-72, 47), (-72, 41), (-81, 41)]])\nGO.point_in_polygon(point, poly)","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"output","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"true\n```\n\"\"\"\npoint_in_polygon(point, polygon; kw...)::Bool =\n point_in_polygon(GI.trait(point), point, GI.trait(polygon), polygon; kw...)\nfunction point_in_polygon(\n ::PointTrait, point,\n ::PolygonTrait, poly;\n ignore_boundary::Bool=false,\n check_extent::Bool=false,\n)::Bool","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"Cheaply check that the point is inside the polygon extent","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":" if check_extent\n point_in_extent(point, GI.extent(poly)) || return false\n end","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"Then check the point is inside the exterior ring","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":" point_in_polygon(\n point,GI.getexterior(poly);\n ignore_boundary, check_extent=false,\n ) || return false","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"Finally make sure the point is not in any of the holes, flipping the boundary condition","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":" for ring in GI.gethole(poly)\n point_in_polygon(\n point, ring;\n ignore_boundary=!ignore_boundary,\n ) && return false\n end\n return true\nend\n\nfunction point_in_polygon(\n ::PointTrait, pt,\n ::Union{LineStringTrait,LinearRingTrait}, ring;\n ignore_boundary::Bool=false,\n check_extent::Bool=false,\n)::Bool\n x, y = GI.x(pt), GI.y(pt)","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"Cheaply check that the point is inside the ring extent","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":" if check_extent\n point_in_extent(point, GI.extent(ring)) || return false\n end","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"Then check the point is inside the ring","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":" inside = false\n n = GI.npoint(ring)\n p_start = GI.getpoint(ring, 1)\n p_end = GI.getpoint(ring, n)","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"Handle closed vs opne rings","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":" if GI.x(p_start) == GI.x(p_end) && GI.y(p_start) == GI.y(p_end)\n n -= 1\n end","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"Loop over all points in the ring","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":" for i in 1:(n - 1)","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"First point on edge","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":" p_i = GI.getpoint(ring, i)\n xi, yi = GI.x(p_i), GI.y(p_i)","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"Second point on edge (j = i + 1)","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":" p_j = GI.getpoint(ring, i + 1)\n xj, yj = GI.x(p_j), GI.y(p_j)","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"Check if point is on the ring boundary","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":" on_boundary = ( # vertex to point has same slope as edge\n yi * (xj - x) + yj * (x - xi) == y * (xj - xi) &&\n (xi - x) * (xj - x) <= 0 && # x is between xi and xj\n (yi - y) * (yj - y) <= 0 # y is between yi and yj\n )\n on_boundary && return !ignore_boundary","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"Check if ray from point passes through edge","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":" intersects = (\n (yi > y) !== (yj > y) &&\n (x < (xj - xi) * (y - yi) / (yj - yi) + xi)\n )\n if intersects\n inside = !inside\n end\n end\n return inside\nend\n\nfunction point_in_extent(p, extent::Extents.Extent)\n (x1, x2), (y1, y1) = extent.X, extent.Y\n return x1 <= GI.x(p) && y1 <= GI.y(p) && x2 >= GI.x(p) && y2 >= GI.y(p)\nend\n\nline_on_line(line1, line2) = line_on_line(trait(line1), line1, trait(line2), line2)\nfunction line_on_line(t1::GI.AbstractCurveTrait, line1, t2::AbstractCurveTrait, line2)\n for p in GI.getpoint(line1)","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"FIXME: all points being on the line doesn't actually mean the whole line is on the line...","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":" point_on_line(p, line2) || return false\n end\n return true\nend\n\nline_in_polygon(line, poly) = line_in_polygon(trait(line), line, trait(poly), poly)\n\nfunction line_in_polygon(\n ::AbstractCurveTrait, line,\n ::Union{AbstractPolygonTrait,LinearRingTrait}, poly\n)\n Extents.intersects(GI.extent(poly), GI.extent(line)) || return false\n\n inside = false\n for i in 1:GI.npoint(line) - 1\n p = GI.getpoint(line, i)\n p2 = GI.getpoint(line, i + 1)\n point_in_polygon(p, poly) || return false\n if !inside\n inside = point_in_polygon(p, poly; ignore_boundary=true)\n end","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"FIXME This seems like a hack, we should check for intersections rather than midpoint??","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":" if !inside\n mid = ((GI.x(p) + GI.x(p2)) / 2, (GI.y(p) + GI.y(p2)) / 2)\n inside = point_in_polygon(mid, poly; ignore_boundary=true)\n end\n end\n return inside\nend\n\nfunction polygon_in_polygon(poly1, poly2)","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"edges1, edges2 = toedges(poly1), toedges(poly2) extent1, extent2 = toextent(edges1), toextent(edges2) Check the extents intersect","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":" Extents.intersects(GI.extent(poly1), GI.extent(poly2)) || return false","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"Check all points in poly1 are in poly2","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":" for point in GI.getpoint(poly1)\n point_in_polygon(point, poly2) || return false\n end","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"Check the line of poly1 does not intersect the line of poly2","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":" #intersects(poly1, poly2) && return false","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"poly1 must be in poly2","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":" return true\n end","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/methods/equals/#Equals","page":"Equals","title":"Equals","text":"","category":"section"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"export equals","category":"page"},{"location":"source/methods/equals/#What-is-equals?","page":"Equals","title":"What is equals?","text":"","category":"section"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"The equals function checks if two geometries are equal. They are equal if they share the same set of points and edges to define the same shape.","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"To provide an example, consider these two lines:","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"using GeometryOps\nusing GeometryOps.GeometryBasics\nusing Makie\nusing CairoMakie\n\nl1 = GI.LineString([(0.0, 0.0), (0.0, 10.0)])\nl2 = GI.LineString([(0.0, -10.0), (0.0, 3.0)])\nf, a, p = lines(GI.getpoint(l1), color = :blue)\nscatter!(GI.getpoint(l1), color = :blue)\nlines!(GI.getpoint(l2), color = :orange)\nscatter!(GI.getpoint(l2), color = :orange)","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"We can see that the two lines do not share a commen set of points and edges in the plot, so they are not equal:","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"equals(l1, l2) # returns false","category":"page"},{"location":"source/methods/equals/#Implementation","page":"Equals","title":"Implementation","text":"","category":"section"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"This is the GeoInterface-compatible implementation.","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"First, we implement a wrapper method that dispatches to the correct implementation based on the geometry trait. This is also used in the implementation, since it's a lot less work!","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"Note that while we need the same set of points and edges, they don't need to be provided in the same order for polygons. For for example, we need the same set points for two multipoints to be equal, but they don't have to be saved in the same order. The winding order also doesn't have to be the same to represent the same geometry. This requires checking every point against every other point in the two geometries we are comparing. Also, some geometries must be \"closed\" like polygons and linear rings. These will be assumed to be closed, even if they don't have a repeated last point explicity written in the coordinates. Additionally, geometries and multi-geometries can be equal if the multi-geometry only includes that single geometry.","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"\"\"\"\n equals(geom1, geom2)::Bool\n\nCompare two Geometries return true if they are the same geometry.\n\n# Examples\n```jldoctest\nimport GeometryOps as GO, GeoInterface as GI\npoly1 = GI.Polygon([[(0,0), (0,5), (5,5), (5,0), (0,0)]])\npoly2 = GI.Polygon([[(0,0), (0,5), (5,5), (5,0), (0,0)]])\n\nGO.equals(poly1, poly2)","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"output","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"true\n```\n\"\"\"\nequals(geom_a, geom_b) = equals(\n GI.trait(geom_a), geom_a,\n GI.trait(geom_b), geom_b,\n)\n\n\"\"\"\n equals(::T, geom_a, ::T, geom_b)::Bool\n\nTwo geometries of the same type, which don't have a equals function to dispatch\noff of should throw an error.\n\"\"\"\nequals(::T, geom_a, ::T, geom_b) where T = error(\"Cant compare $T yet\")\n\n\"\"\"\n equals(trait_a, geom_a, trait_b, geom_b)\n\nTwo geometries which are not of the same type cannot be equal so they always\nreturn false.\n\"\"\"\nequals(trait_a, geom_a, trait_b, geom_b) = false\n\n\"\"\"\n equals(::GI.PointTrait, p1, ::GI.PointTrait, p2)::Bool\n\nTwo points are the same if they have the same x and y (and z if 3D) coordinates.\n\"\"\"\nfunction equals(::GI.PointTrait, p1, ::GI.PointTrait, p2)\n GI.ncoord(p1) == GI.ncoord(p2) || return false\n GI.x(p1) == GI.x(p2) || return false\n GI.y(p1) == GI.y(p2) || return false\n if GI.is3d(p1)\n GI.z(p1) == GI.z(p2) || return false\n end\n return true\nend\n\n\"\"\"\n equals(::GI.PointTrait, p1, ::GI.MultiPointTrait, mp2)::Bool\n\nA point and a multipoint are equal if the multipoint is composed of a single\npoint that is equivalent to the given point.\n\"\"\"\nfunction equals(::GI.PointTrait, p1, ::GI.MultiPointTrait, mp2)\n GI.npoint(mp2) == 1 || return false\n return equals(p1, GI.getpoint(mp2, 1))\nend\n\n\"\"\"\n equals(::GI.MultiPointTrait, mp1, ::GI.PointTrait, p2)::Bool\n\nA point and a multipoint are equal if the multipoint is composed of a single\npoint that is equivalent to the given point.\n\"\"\"\nequals(trait1::GI.MultiPointTrait, mp1, trait2::GI.PointTrait, p2) =\n equals(trait2, p2, trait1, mp1)\n\n\"\"\"\n equals(::GI.MultiPointTrait, mp1, ::GI.MultiPointTrait, mp2)::Bool\n\nTwo multipoints are equal if they share the same set of points.\n\"\"\"\nfunction equals(::GI.MultiPointTrait, mp1, ::GI.MultiPointTrait, mp2)\n GI.npoint(mp1) == GI.npoint(mp2) || return false\n for p1 in GI.getpoint(mp1)\n has_match = false # if point has a matching point in other multipoint\n for p2 in GI.getpoint(mp2)\n if equals(p1, p2)\n has_match = true\n break\n end\n end\n has_match || return false # if no matching point, can't be equal\n end\n return true # all points had a match\nend\n\n\"\"\"\n _equals_curves(c1, c2, closed_type1, closed_type2)::Bool\n\nTwo curves are equal if they share the same set of point, representing the same\ngeometry. Both curves must must be composed of the same set of points, however,\nthey do not have to wind in the same direction, or start on the same point to be\nequivalent.\nInputs:\n c1 first geometry\n c2 second geometry\n closed_type1::Bool true if c1 is closed by definition (polygon, linear ring)\n closed_type2::Bool true if c2 is closed by definition (polygon, linear ring)\n\"\"\"\nfunction _equals_curves(c1, c2, closed_type1, closed_type2)","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"Check if both curves are closed or not","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":" n1 = GI.npoint(c1)\n n2 = GI.npoint(c2)\n c1_repeat_point = GI.getpoint(c1, 1) == GI.getpoint(c1, n1)\n n2 = GI.npoint(c2)\n c2_repeat_point = GI.getpoint(c2, 1) == GI.getpoint(c2, n2)\n closed1 = closed_type1 || c1_repeat_point\n closed2 = closed_type2 || c2_repeat_point\n closed1 == closed2 || return false","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"How many points in each curve","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":" n1 -= c1_repeat_point ? 1 : 0\n n2 -= c2_repeat_point ? 1 : 0\n n1 == n2 || return false\n n1 == 0 && return true","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"Find offset between curves","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":" jstart = nothing\n p1 = GI.getpoint(c1, 1)\n for i in 1:n2\n if equals(p1, GI.getpoint(c2, i))\n jstart = i\n break\n end\n end","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"no point matches the first point","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":" isnothing(jstart) && return false","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"found match for only point","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":" n1 == 1 && return true","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"if isn't closed and first or last point don't match, not same curve","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":" !closed_type1 && (jstart != 1 && jstart != n1) && return false","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"Check if curves are going in same direction","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":" i = 2\n j = jstart + 1\n j -= j > n2 ? n2 : 0\n same_direction = equals(GI.getpoint(c1, i), GI.getpoint(c2, j))","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"if only 2 points, we have already compared both","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":" n1 == 2 && return same_direction","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"Check all remaining points are the same wrapping around line","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":" jstep = same_direction ? 1 : -1\n for i in 2:n1\n ip = GI.getpoint(c1, i)\n j = jstart + (i - 1) * jstep\n j += (0 < j <= n2) ? 0 : (n2 * -jstep)\n jp = GI.getpoint(c2, j)\n equals(ip, jp) || return false\n end\n return true\nend\n\n\"\"\"\n equals(\n ::Union{GI.LineTrait, GI.LineStringTrait}, l1,\n ::Union{GI.LineTrait, GI.LineStringTrait}, l2,\n )::Bool\n\nTwo lines/linestrings are equal if they share the same set of points going\nalong the curve. Note that lines/linestrings aren't closed by defintion.\n\"\"\"\nequals(\n ::Union{GI.LineTrait, GI.LineStringTrait}, l1,\n ::Union{GI.LineTrait, GI.LineStringTrait}, l2,\n) = _equals_curves(l1, l2, false, false)\n\n\"\"\"\n equals(\n ::Union{GI.LineTrait, GI.LineStringTrait}, l1,\n ::GI.LinearRingTrait, l2,\n )::Bool\n\nA line/linestring and a linear ring are equal if they share the same set of\npoints going along the curve. Note that lines aren't closed by defintion, but\nrings are, so the line must have a repeated last point to be equal\n\"\"\"\nequals(\n ::Union{GI.LineTrait, GI.LineStringTrait}, l1,\n ::GI.LinearRingTrait, l2,\n) = _equals_curves(l1, l2, false, true)\n\n\"\"\"\n equals(\n ::GI.LinearRingTrait, l1,\n ::Union{GI.LineTrait, GI.LineStringTrait}, l2,\n )::Bool\n\nA linear ring and a line/linestring are equal if they share the same set of\npoints going along the curve. Note that lines aren't closed by defintion, but\nrings are, so the line must have a repeated last point to be equal\n\"\"\"\nequals(\n ::GI.LinearRingTrait, l1,\n ::Union{GI.LineTrait, GI.LineStringTrait}, l2,\n) = _equals_curves(l1, l2, true, false)\n\n\"\"\"\n equals(\n ::GI.LinearRingTrait, l1,\n ::GI.LinearRingTrait, l2,\n )::Bool\n\nTwo linear rings are equal if they share the same set of points going along the\ncurve. Note that rings are closed by definition, so they can have, but don't\nneed, a repeated last point to be equal.\n\"\"\"\nequals(\n ::GI.LinearRingTrait, l1,\n ::GI.LinearRingTrait, l2,\n) = _equals_curves(l1, l2, true, true)\n\n\"\"\"\n equals(::GI.PolygonTrait, geom_a, ::GI.PolygonTrait, geom_b)::Bool\n\nTwo polygons are equal if they share the same exterior edge and holes.\n\"\"\"\nfunction equals(::GI.PolygonTrait, geom_a, ::GI.PolygonTrait, geom_b)","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"Check if exterior is equal","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":" _equals_curves(\n GI.getexterior(geom_a), GI.getexterior(geom_b),\n true, true, # linear rings are closed by definition\n ) || return false","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"Check if number of holes are equal","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":" GI.nhole(geom_a) == GI.nhole(geom_b) || return false","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"Check if holes are equal","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":" for ihole in GI.gethole(geom_a)\n has_match = false\n for jhole in GI.gethole(geom_b)\n if _equals_curves(\n ihole, jhole,\n true, true, # linear rings are closed by definition\n )\n has_match = true\n break\n end\n end\n has_match || return false\n end\n return true\nend\n\n\"\"\"\n equals(::GI.PolygonTrait, geom_a, ::GI.MultiPolygonTrait, geom_b)::Bool\n\nA polygon and a multipolygon are equal if the multipolygon is composed of a\nsingle polygon that is equivalent to the given polygon.\n\"\"\"\nfunction equals(::GI.PolygonTrait, geom_a, ::MultiPolygonTrait, geom_b)\n GI.npolygon(geom_b) == 1 || return false\n return equals(geom_a, GI.getpolygon(geom_b, 1))\nend\n\n\"\"\"\n equals(::GI.MultiPolygonTrait, geom_a, ::GI.PolygonTrait, geom_b)::Bool\n\nA polygon and a multipolygon are equal if the multipolygon is composed of a\nsingle polygon that is equivalent to the given polygon.\n\"\"\"\nequals(trait_a::GI.MultiPolygonTrait, geom_a, trait_b::PolygonTrait, geom_b) =\n equals(trait_b, geom_b, trait_a, geom_a)\n\n\"\"\"\n equals(::GI.PolygonTrait, geom_a, ::GI.PolygonTrait, geom_b)::Bool\n\nTwo multipolygons are equal if they share the same set of polygons.\n\"\"\"\nfunction equals(::GI.MultiPolygonTrait, geom_a, ::GI.MultiPolygonTrait, geom_b)","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"Check if same number of polygons","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":" GI.npolygon(geom_a) == GI.npolygon(geom_b) || return false","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"Check if each polygon has a matching polygon","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":" for poly_a in GI.getpolygon(geom_a)\n has_match = false\n for poly_b in GI.getpolygon(geom_b)\n if equals(poly_a, poly_b)\n has_match = true\n break\n end\n end\n has_match || return false\n end\n return true\nend","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/transformations/tuples/#Tuple-conversion","page":"Tuple conversion","title":"Tuple conversion","text":"","category":"section"},{"location":"source/transformations/tuples/","page":"Tuple conversion","title":"Tuple conversion","text":"\"\"\"\n tuples(obj)\n\nConvert all points in `obj` to `Tuple`s, wherever the are nested.\n\nReturns a similar object or collection of objects using GeoInterface.jl\ngeometries wrapping `Tuple` points.","category":"page"},{"location":"source/transformations/tuples/","page":"Tuple conversion","title":"Tuple conversion","text":"Keywords","category":"page"},{"location":"source/transformations/tuples/","page":"Tuple conversion","title":"Tuple conversion","text":"$APPLY_KEYWORDS\n\"\"\"\nfunction tuples(geom; kw...)\n if _is3d(geom)\n return apply(PointTrait, geom; kw...) do p\n (Float64(GI.x(p)), Float64(GI.y(p)), Float64(GI.z(p)))\n end\n else\n return apply(PointTrait, geom; kw...) do p\n (Float64(GI.x(p)), Float64(GI.y(p)))\n end\n end\nend","category":"page"},{"location":"source/transformations/tuples/","page":"Tuple conversion","title":"Tuple conversion","text":"","category":"page"},{"location":"source/transformations/tuples/","page":"Tuple conversion","title":"Tuple conversion","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/methods/geom_relations/contains/#Containment","page":"Containment","title":"Containment","text":"","category":"section"},{"location":"source/methods/geom_relations/contains/","page":"Containment","title":"Containment","text":"export contains\n\n\"\"\"\n contains(ft1::AbstractGeometry, ft2::AbstractGeometry)::Bool\n\nReturn true if the second geometry is completely contained by the first geometry.\nThe interiors of both geometries must intersect and, the interior and boundary of the secondary (geometry b)\nmust not intersect the exterior of the primary (geometry a).\n`contains` returns the exact opposite result of `within`.\n\n# Examples\n\n```jldoctest\nimport GeometryOps as GO, GeoInterface as GI\nline = GI.LineString([(1, 1), (1, 2), (1, 3), (1, 4)])\npoint = (1, 2)\n\nGO.contains(line, point)","category":"page"},{"location":"source/methods/geom_relations/contains/","page":"Containment","title":"Containment","text":"output","category":"page"},{"location":"source/methods/geom_relations/contains/","page":"Containment","title":"Containment","text":"true\n```\n\"\"\"\ncontains(g1, g2)::Bool = within(g2, g1)","category":"page"},{"location":"source/methods/geom_relations/contains/","page":"Containment","title":"Containment","text":"","category":"page"},{"location":"source/methods/geom_relations/contains/","page":"Containment","title":"Containment","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/methods/area/#Area-and-signed-area","page":"Area and signed area","title":"Area and signed area","text":"","category":"section"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":"export area, signed_area","category":"page"},{"location":"source/methods/area/#What-is-area?-What-is-signed-area?","page":"Area and signed area","title":"What is area? What is signed area?","text":"","category":"section"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":"Area is the amount of space occupied by a two-dimensional figure. It is always a positive value. Signed area is simply the integral over the exterior path of a polygon, minus the sum of integrals over its interior holes. It is signed such that a clockwise path has a positive area, and a counterclockwise path has a negative area. The area is the absolute value of the signed area.","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":"To provide an example, consider this rectangle:","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":"using GeometryOps\nusing GeometryOps.GeometryBasics\nusing Makie\n\nrect = Polygon([Point(0,0), Point(0,1), Point(1,1), Point(1,0), Point(0, 0)])\nf, a, p = poly(rect; axis = (; aspect = DataAspect()))","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":"This is clearly a rectangle, etc. But now let's look at how the points look:","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":"lines!(a, rect; color = 1:length(coordinates(rect))+1)\nf","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":"The points are ordered in a clockwise fashion, which means that the signed area is negative. If we reverse the order of the points, we get a postive area.","category":"page"},{"location":"source/methods/area/#Implementation","page":"Area and signed area","title":"Implementation","text":"","category":"section"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":"This is the GeoInterface-compatible implementation. First, we implement a wrapper method that dispatches to the correct implementation based on the geometry trait. This is also used in the implementation, since it's a lot less work!","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":"Note that area (and signed area) are zero for all points and curves, even if the curves are closed like with a linear ring. Also note that signed area really only makes sense for polygons, given with a multipolygon can have several polygons each with a different orientation and thus the absolute value of the signed area might not be the area. This is why signed area is only implemented for polygons.","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":"\"\"\"\n area(geom, ::Type{T} = Float64)::T\n\nReturns the area of the geometry. This is computed slighly differently for\ndifferent geometries:\n - The area of a point/multipoint is always zero.\n - The area of a curve/multicurve is always zero.\n - The area of a polygon is the absolute value of the signed area.\n - The area multi-polygon is the sum of the areas of all of the sub-polygons.\n - The area of a geometry collection is the sum of the areas of all of the\n sub-geometries.\n\nResult will be of type T, where T is an optional argument with a default value\nof Float64.\n\"\"\"\narea(geom, ::Type{T} = Float64) where T <: AbstractFloat =\n _area(T, GI.trait(geom), geom)\n\n\"\"\"\n signed_area(geom, ::Type{T} = Float64)::T\n\nReturns the signed area of the geometry, based on winding order. This is\ncomputed slighly differently for different geometries:\n - The signed area of a point is always zero.\n - The signed area of a curve is always zero.\n - The signed area of a polygon is computed with the shoelace formula and is\n positive if the polygon coordinates wind clockwise and negative if\n counterclockwise.\n - You cannot compute the signed area of a multipolygon as it doesn't have a\n meaning as each sub-polygon could have a different winding order.\n\nResult will be of type T, where T is an optional argument with a default value\nof Float64.\n\"\"\"\nsigned_area(geom, ::Type{T} = Float64) where T <: AbstractFloat =\n _signed_area(T, GI.trait(geom), geom)","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":"Points, MultiPoints, Curves, MultiCurves","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":"_area(::Type{T}, ::GI.AbstractGeometryTrait, geom) where T = zero(T)\n\n_signed_area(::Type{T}, ::GI.AbstractGeometryTrait, geom) where T = zero(T)","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":"Polygons","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":"_area(::Type{T}, trait::GI.PolygonTrait, poly) where T =\n abs(_signed_area(T, trait, poly))\n\nfunction _signed_area(::Type{T}, ::GI.PolygonTrait, poly) where T\n GI.isempty(poly) && return zero(T)\n s_area = _signed_area(T, GI.getexterior(poly))\n area = abs(s_area)\n area == 0 && return area","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":"Remove hole areas from total","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":" for hole in GI.gethole(poly)\n area -= abs(_signed_area(T, hole))\n end","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":"Winding of exterior ring determines sign","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":" return area * sign(s_area)\nend","category":"page"},{"location":"source/methods/area/#MultiPolygons-and-GeometryCollections","page":"Area and signed area","title":"MultiPolygons and GeometryCollections","text":"","category":"section"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":"_area(\n ::Type{T},\n ::Union{GI.MultiPolygonTrait, GI.GeometryCollectionTrait},\n geoms,\n) where T =\n sum((area(geom, T) for geom in GI.getgeom(geoms)), init = zero(T))","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":"Helper function:","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":"Calculates the signed area of a given curve. This is equivalent to integrating to find the area under the curve. Even if curve isn't explicitly closed by repeating the first point at the end of the coordinates, curve is still assumed to be closed.","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":"function _signed_area(::Type{T}, geom) where T\n area = zero(T)\n np = GI.npoint(geom)\n np == 0 && return area\n\n first = true\n local pfirst, p1","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":"Integrate the area under the curve","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":" for p2 in GI.getpoint(geom)","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":"Skip the first and do it later This lets us work within one iteration over geom, which means on C call when using points from external libraries.","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":" if first\n p1 = pfirst = p2\n first = false\n continue\n end","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":"Accumulate the area into area","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":" area += GI.x(p1) * GI.y(p2) - GI.y(p1) * GI.x(p2)\n p1 = p2\n end","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":"Complete the last edge. If the first and last where the same this will be zero","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":" p2 = pfirst\n area += GI.x(p1) * GI.y(p2) - GI.y(p1) * GI.x(p2)\n return T(area / 2)\nend","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":"","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":"This page was generated using Literate.jl.","category":"page"},{"location":"","page":"Home","title":"Home","text":"CurrentModule = GeometryOps","category":"page"},{"location":"#GeometryOps","page":"Home","title":"GeometryOps","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Documentation for GeometryOps.","category":"page"},{"location":"","page":"Home","title":"Home","text":"","category":"page"},{"location":"","page":"Home","title":"Home","text":"Modules = [GeometryOps]","category":"page"},{"location":"#GeometryOps.AbstractBarycentricCoordinateMethod","page":"Home","title":"GeometryOps.AbstractBarycentricCoordinateMethod","text":"abstract type AbstractBarycentricCoordinateMethod\n\nAbstract supertype for barycentric coordinate methods. The subtypes may serve as dispatch types, or may cache some information about the target polygon. \n\nAPI\n\nThe following methods must be implemented for all subtypes:\n\nbarycentric_coordinates!(λs::Vector{<: Real}, method::AbstractBarycentricCoordinateMethod, exterior::Vector{<: Point{2, T1}}, point::Point{2, T2})\nbarycentric_interpolate(method::AbstractBarycentricCoordinateMethod, exterior::Vector{<: Point{2, T1}}, values::Vector{V}, point::Point{2, T2})::V\nbarycentric_interpolate(method::AbstractBarycentricCoordinateMethod, exterior::Vector{<: Point{2, T1}}, interiors::Vector{<: Vector{<: Point{2, T1}}} values::Vector{V}, point::Point{2, T2})::V\n\nThe rest of the methods will be implemented in terms of these, and have efficient dispatches for broadcasting.\n\n\n\n\n\n","category":"type"},{"location":"#GeometryOps.DouglasPeucker","page":"Home","title":"GeometryOps.DouglasPeucker","text":"DouglasPeucker <: SimplifyAlg\n\nDouglasPeucker(; number, ratio, tol)\n\nSimplifies geometries by removing points below tol distance from the line between its neighboring points.\n\nKeywords\n\nratio: the fraction of points that should remain after simplify. Useful as it will generalise for large collections of objects.\nnumber: the number of points that should remain after simplify. Less useful for large collections of mixed size objects.\ntol: the minimum distance a point will be from the line joining its neighboring points.\n\n\n\n\n\n","category":"type"},{"location":"#GeometryOps.MeanValue","page":"Home","title":"GeometryOps.MeanValue","text":"MeanValue() <: AbstractBarycentricCoordinateMethod\n\nThis method calculates barycentric coordinates using the mean value method.\n\nReferences\n\n\n\n\n\n","category":"type"},{"location":"#GeometryOps.RadialDistance","page":"Home","title":"GeometryOps.RadialDistance","text":"RadialDistance <: SimplifyAlg\n\nSimplifies geometries by removing points less than tol distance from the line between its neighboring points.\n\nKeywords\n\nratio: the fraction of points that should remain after simplify. Useful as it will generalise for large collections of objects.\nnumber: the number of points that should remain after simplify. Less useful for large collections of mixed size objects.\ntol: the minimum distance between points.\n\n\n\n\n\n","category":"type"},{"location":"#GeometryOps.SimplifyAlg","page":"Home","title":"GeometryOps.SimplifyAlg","text":"abstract type SimplifyAlg\n\nAbstract type for simplification algorithms.\n\nAPI\n\nFor now, the algorithm must hold the number, ratio and tol properties. \n\nSimplification algorithm types can hook into the interface by implementing the _simplify(trait, alg, geom) methods for whichever traits are necessary.\n\n\n\n\n\n","category":"type"},{"location":"#GeometryOps.VisvalingamWhyatt","page":"Home","title":"GeometryOps.VisvalingamWhyatt","text":"VisvalingamWhyatt <: SimplifyAlg\n\nVisvalingamWhyatt(; kw...)\n\nSimplifies geometries by removing points below tol distance from the line between its neighboring points.\n\nKeywords\n\nratio: the fraction of points that should remain after simplify. Useful as it will generalise for large collections of objects.\nnumber: the number of points that should remain after simplify. Less useful for large collections of mixed size objects.\ntol: the minimum area of a triangle made with a point and its neighboring points.\n\n\n\n\n\n","category":"type"},{"location":"#GeometryOps._det-Union{Tuple{T2}, Tuple{T1}, Tuple{Union{Tuple{T1, T1}, StaticArraysCore.StaticArray{Tuple{2}, T1, 1}}, Union{Tuple{T2, T2}, StaticArraysCore.StaticArray{Tuple{2}, T2, 1}}}} where {T1<:Real, T2<:Real}","page":"Home","title":"GeometryOps._det","text":"_det(s1::Point2{T1}, s2::Point2{T2}) where {T1 <: Real, T2 <: Real}\n\nReturns the determinant of the matrix formed by hcat'ing two points s1 and s2.\n\nSpecifically, this is: \n\ns1[1] * s2[2] - s1[2] * s2[1]\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps._equals_curves-NTuple{4, Any}","page":"Home","title":"GeometryOps._equals_curves","text":"_equals_curves(c1, c2, closed_type1, closed_type2)::Bool\n\nTwo 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 closedtype1::Bool true if c1 is closed by definition (polygon, linear ring) closedtype2::Bool true if c2 is closed by definition (polygon, linear ring)\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps._intersection_point-Tuple{Tuple, Tuple}","page":"Home","title":"GeometryOps._intersection_point","text":"_intersection_point(\n (a1, a2)::Tuple,\n (b1, b2)::Tuple,\n)\n\nCalculates the intersection point between two lines if it exists, and as if the line extended to infinity, and the fractional component of each line from the initial end point to the intersection point. Inputs: (a1, a2)::Tuple{Tuple{::Real, ::Real}, Tuple{::Real, ::Real}} first line (b1, b2)::Tuple{Tuple{::Real, ::Real}, Tuple{::Real, ::Real}} second line Outputs: (x, y)::Tuple{::Real, ::Real} intersection point (t, u)::Tuple{::Real, ::Real} fractional length of lines to intersection Both are ::Nothing if point doesn't exist!\n\nCalculation derivation can be found here: https://stackoverflow.com/questions/563198/\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps._line_intersects-Tuple{Tuple{Tuple{Float64, Float64}, Tuple{Float64, Float64}}, Tuple{Tuple{Float64, Float64}, Tuple{Float64, Float64}}}","page":"Home","title":"GeometryOps._line_intersects","text":"_line_intersects(\n edge_a::Edge,\n edge_b::Edge,\n)::Bool\n\nReturns true if there is at least one intersection between two edges.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps._line_intersects-Tuple{Vector{Tuple{Tuple{Float64, Float64}, Tuple{Float64, Float64}}}, Vector{Tuple{Tuple{Float64, Float64}, Tuple{Float64, Float64}}}}","page":"Home","title":"GeometryOps._line_intersects","text":"_line_intersects(\n edges_a::Vector{Edge},\n edges_b::Vector{Edge}\n)::Bool\n\nReturns true if there is at least one intersection between edges within the two lists of edges.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps._overlaps-Tuple{Tuple{Tuple{Float64, Float64}, Tuple{Float64, Float64}}, Tuple{Tuple{Float64, Float64}, Tuple{Float64, Float64}}}","page":"Home","title":"GeometryOps._overlaps","text":"_overlaps(\n (a1, a2)::Edge,\n (b1, b2)::Edge\n)::Bool\n\nIf the edges overlap, meaning that they are colinear but each have one endpoint outside of the other edge, return true. Else false. \n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.apply-Union{Tuple{Target}, Tuple{Any, Type{Target}, Any}} where Target","page":"Home","title":"GeometryOps.apply","text":"apply(f, target::Type{<:AbstractTrait}, obj; kw...)\n\nReconstruct a geometry, feature, feature collection, or nested vectors of either using the function f on the target trait.\n\nf(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.\n\nObjects \"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. But \"deeper\" objects may remain unchanged - such as points and linear rings if the target is the same PolygonTrait.\n\nThe result is a functionally similar geometry with values depending on f\n\nthreaded: true or false. Whether to use multithreading. Defaults to false.\ncrs: The CRS to attach to geometries. Defaults to nothing.\ncalc_extent: true or false. Whether to calculate the extent. Defaults to false.\n\nExample\n\nFlipped point the order in any feature or geometry, or iterables of either:\n\n```juia 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)])])\n\nflipped_geom = GO.apply(GI.PointTrait, geom) do p (GI.y(p), GI.x(p)) end\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.area-Union{Tuple{Any}, Tuple{T}, Tuple{Any, Type{T}}} where T<:AbstractFloat","page":"Home","title":"GeometryOps.area","text":"area(geom, ::Type{T} = Float64)::T\n\nReturns the area of the geometry. This is computed slighly 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 is the sum of the areas of all of the sub-geometries. \n\nResult will be of type T, where T is an optional argument with a default value of Float64.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.centroid-Tuple{Any, Any}","page":"Home","title":"GeometryOps.centroid","text":"centroid(trait, geom)::Tuple{T, T}\n\nReturns the centroid of a polygon or multipolygon, which is calculated by weighting edges by their area component by convention.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.centroid-Tuple{Any}","page":"Home","title":"GeometryOps.centroid","text":"centroid(geom)::Tuple{T, T}\n\nReturns the centroid of a given line segment, linear ring, polygon, or mutlipolygon.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.centroid-Tuple{Union{GeoInterface.LineStringTrait, GeoInterface.LinearRingTrait}, Any}","page":"Home","title":"GeometryOps.centroid","text":"centroid(\n trait::Union{GI.LineStringTrait, GI.LinearRingTrait},\n geom,\n)::Tuple{T, T}\n\nReturns the centroid of a line string or linear ring, which is calculated by weighting line segments by their length by convention.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.centroid_and_area-Tuple{Any}","page":"Home","title":"GeometryOps.centroid_and_area","text":"centroid_and_area(\n ::Union{GI.LineStringTrait, GI.LinearRingTrait}, \n geom,\n)::(::Tuple{T, T}, ::Real)\n\nReturns the centroid and area of a given geom.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.centroid_and_area-Tuple{GeoInterface.MultiPolygonTrait, Any}","page":"Home","title":"GeometryOps.centroid_and_area","text":"centroid_and_area(::GI.MultiPolygonTrait, geom)::(::Tuple{T, T}, ::Real)\n\nReturns the centroid and area of a given multipolygon.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.centroid_and_area-Tuple{GeoInterface.PolygonTrait, Any}","page":"Home","title":"GeometryOps.centroid_and_area","text":"centroid_and_area(::GI.PolygonTrait, geom)::(::Tuple{T, T}, ::Real)\n\nReturns the centroid and area of a given polygon.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.centroid_and_area-Tuple{Union{GeoInterface.LineStringTrait, GeoInterface.LinearRingTrait}, Any}","page":"Home","title":"GeometryOps.centroid_and_area","text":"centroid_and_area(\n ::Union{GI.LineStringTrait, GI.LinearRingTrait},\n geom,\n)::(::Tuple{T, T}, ::Real)\n\nReturns the centroid and area of a given a line string or a linear ring. Note that this is only valid if the line segment or linear ring is closed. \n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.centroid_and_length-Tuple{Any}","page":"Home","title":"GeometryOps.centroid_and_length","text":"centroid_and_length(geom)::(::Tuple{T, T}, ::Real)\n\nReturns the centroid and length of a given line/ring. Note this is only valid for line strings and linear rings.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.centroid_and_length-Tuple{Union{GeoInterface.LineStringTrait, GeoInterface.LinearRingTrait}, Any}","page":"Home","title":"GeometryOps.centroid_and_length","text":"centroid_and_length(geom)::(::Tuple{T, T}, ::Real)\n\nReturns the centroid and length of a given line/ring. Note this is only valid for line strings and linear rings.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.contains-Tuple{Any, Any}","page":"Home","title":"GeometryOps.contains","text":"contains(ft1::AbstractGeometry, ft2::AbstractGeometry)::Bool\n\nReturn 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 (geometry b) must not intersect the exterior of the primary (geometry a). contains returns the exact opposite result of within.\n\nExamples\n\nimport GeometryOps as GO, GeoInterface as GI\nline = GI.LineString([(1, 1), (1, 2), (1, 3), (1, 4)])\npoint = (1, 2)\n\nGO.contains(line, point)\n# output\ntrue\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.crosses-Tuple{Any, Any}","page":"Home","title":"GeometryOps.crosses","text":" crosses(geom1, geom2)::Bool\n\nReturn 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.\n\nTODO: broken\n\nExamples\n\nimport GeoInterface as GI, GeometryOps as GO\n\nline1 = GI.LineString([(1, 1), (1, 2), (1, 3), (1, 4)])\nline2 = GI.LineString([(-2, 2), (4, 2)])\n\nGO.crosses(line1, line2)\n# output\ntrue\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.disjoint-Tuple{Any, Any}","page":"Home","title":"GeometryOps.disjoint","text":"disjoint(geom1, geom2)::Bool\n\nReturn true if the intersection of the two geometries is an empty set.\n\nExamples\n\nimport GeometryOps as GO, GeoInterface as GI\n\npoly = GI.Polygon([[(-1, 2), (3, 2), (3, 3), (-1, 3), (-1, 2)]])\npoint = (1, 1)\nGO.disjoint(poly, point)\n\n# output\ntrue\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.distance-Union{Tuple{T}, Tuple{Any, Any}, Tuple{Any, Any, Type{T}}} where T<:AbstractFloat","page":"Home","title":"GeometryOps.distance","text":"distance(point, geom, ::Type{T} = Float64)::T\n\nCalculates the ditance from the geometry g1 to the point. The distance will always be positive or zero.\n\nThe 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.\n\nResult will be of type T, where T is an optional argument with a default value of Float64.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.embed_extent-Tuple{Any}","page":"Home","title":"GeometryOps.embed_extent","text":"embed_extent(obj)\n\nRecursively wrap the object with a GeoInterface.jl geometry, calculating and adding an Extents.Extent to all objects.\n\nThis 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.\n\nKeywords\n\nthreaded: true or false. Whether to use multithreading. Defaults to false.\ncrs: The CRS to attach to geometries. Defaults to nothing.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.equals-NTuple{4, Any}","page":"Home","title":"GeometryOps.equals","text":"equals(trait_a, geom_a, trait_b, geom_b)\n\nTwo geometries which are not of the same type cannot be equal so they always return false.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.equals-Tuple{Any, Any}","page":"Home","title":"GeometryOps.equals","text":"equals(geom1, geom2)::Bool\n\nCompare two Geometries return true if they are the same geometry.\n\nExamples\n\nimport GeometryOps as GO, GeoInterface as GI\npoly1 = GI.Polygon([[(0,0), (0,5), (5,5), (5,0), (0,0)]])\npoly2 = GI.Polygon([[(0,0), (0,5), (5,5), (5,0), (0,0)]])\n\nGO.equals(poly1, poly2)\n# output\ntrue\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.equals-Tuple{GeoInterface.LinearRingTrait, Any, GeoInterface.LinearRingTrait, Any}","page":"Home","title":"GeometryOps.equals","text":"equals(\n ::GI.LinearRingTrait, l1,\n ::GI.LinearRingTrait, l2,\n)::Bool\n\nTwo 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.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.equals-Tuple{GeoInterface.LinearRingTrait, Any, Union{GeoInterface.LineStringTrait, GeoInterface.LineTrait}, Any}","page":"Home","title":"GeometryOps.equals","text":"equals(\n ::GI.LinearRingTrait, l1,\n ::Union{GI.LineTrait, GI.LineStringTrait}, l2,\n)::Bool\n\nA 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 defintion, but rings are, so the line must have a repeated last point to be equal\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.equals-Tuple{GeoInterface.MultiPointTrait, Any, GeoInterface.MultiPointTrait, Any}","page":"Home","title":"GeometryOps.equals","text":"equals(::GI.MultiPointTrait, mp1, ::GI.MultiPointTrait, mp2)::Bool\n\nTwo multipoints are equal if they share the same set of points.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.equals-Tuple{GeoInterface.MultiPointTrait, Any, GeoInterface.PointTrait, Any}","page":"Home","title":"GeometryOps.equals","text":"equals(::GI.MultiPointTrait, mp1, ::GI.PointTrait, p2)::Bool\n\nA point and a multipoint are equal if the multipoint is composed of a single point that is equivalent to the given point.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.equals-Tuple{GeoInterface.MultiPolygonTrait, Any, GeoInterface.MultiPolygonTrait, Any}","page":"Home","title":"GeometryOps.equals","text":"equals(::GI.PolygonTrait, geom_a, ::GI.PolygonTrait, geom_b)::Bool\n\nTwo multipolygons are equal if they share the same set of polygons.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.equals-Tuple{GeoInterface.MultiPolygonTrait, Any, GeoInterface.PolygonTrait, Any}","page":"Home","title":"GeometryOps.equals","text":"equals(::GI.MultiPolygonTrait, geom_a, ::GI.PolygonTrait, geom_b)::Bool\n\nA polygon and a multipolygon are equal if the multipolygon is composed of a single polygon that is equivalent to the given polygon.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.equals-Tuple{GeoInterface.PointTrait, Any, GeoInterface.MultiPointTrait, Any}","page":"Home","title":"GeometryOps.equals","text":"equals(::GI.PointTrait, p1, ::GI.MultiPointTrait, mp2)::Bool\n\nA point and a multipoint are equal if the multipoint is composed of a single point that is equivalent to the given point.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.equals-Tuple{GeoInterface.PointTrait, Any, GeoInterface.PointTrait, Any}","page":"Home","title":"GeometryOps.equals","text":"equals(::GI.PointTrait, p1, ::GI.PointTrait, p2)::Bool\n\nTwo points are the same if they have the same x and y (and z if 3D) coordinates.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.equals-Tuple{GeoInterface.PolygonTrait, Any, GeoInterface.MultiPolygonTrait, Any}","page":"Home","title":"GeometryOps.equals","text":"equals(::GI.PolygonTrait, geom_a, ::GI.MultiPolygonTrait, geom_b)::Bool\n\nA polygon and a multipolygon are equal if the multipolygon is composed of a single polygon that is equivalent to the given polygon.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.equals-Tuple{GeoInterface.PolygonTrait, Any, GeoInterface.PolygonTrait, Any}","page":"Home","title":"GeometryOps.equals","text":"equals(::GI.PolygonTrait, geom_a, ::GI.PolygonTrait, geom_b)::Bool\n\nTwo polygons are equal if they share the same exterior edge and holes.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.equals-Tuple{Union{GeoInterface.LineStringTrait, GeoInterface.LineTrait}, Any, GeoInterface.LinearRingTrait, Any}","page":"Home","title":"GeometryOps.equals","text":"equals(\n ::Union{GI.LineTrait, GI.LineStringTrait}, l1,\n ::GI.LinearRingTrait, l2,\n)::Bool\n\nA 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 defintion, but rings are, so the line must have a repeated last point to be equal\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.equals-Tuple{Union{GeoInterface.LineStringTrait, GeoInterface.LineTrait}, Any, Union{GeoInterface.LineStringTrait, GeoInterface.LineTrait}, Any}","page":"Home","title":"GeometryOps.equals","text":"equals(\n ::Union{GI.LineTrait, GI.LineStringTrait}, l1,\n ::Union{GI.LineTrait, GI.LineStringTrait}, l2,\n)::Bool\n\nTwo lines/linestrings are equal if they share the same set of points going along the curve. Note that lines/linestrings aren't closed by defintion.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.equals-Union{Tuple{T}, Tuple{T, Any, T, Any}} where T","page":"Home","title":"GeometryOps.equals","text":"equals(::T, geom_a, ::T, geom_b)::Bool\n\nTwo geometries of the same type, which don't have a equals function to dispatch off of should throw an error.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.flatten-Union{Tuple{Target}, Tuple{Type{Target}, Any}} where Target<:GeoInterface.AbstractTrait","page":"Home","title":"GeometryOps.flatten","text":"flatten(target::Type{<:GI.AbstractTrait}, obj)\nflatten(f, target::Type{<:GI.AbstractTrait}, obj)\n\nLazily flatten any AbstractArray, iterator, FeatureCollectionTrait, FeatureTrait or AbstractGeometryTrait object obj, so that objects with the target trait are returned by the iterator.\n\nIf f is passed in it will be applied to the target geometries.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.flip-Tuple{Any}","page":"Home","title":"GeometryOps.flip","text":"flip(obj)\n\nSwap all of the x and y coordinates in obj, otherwise keeping the original structure (but not necessarily the original type).\n\nKeywords\n\nthreaded: true or false. Whether to use multithreading. Defaults to false.\ncrs: The CRS to attach to geometries. Defaults to nothing.\ncalc_extent: true or false. Whether to calculate the extent. Defaults to false.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.get_contours-Tuple{AbstractMatrix}","page":"Home","title":"GeometryOps.get_contours","text":"get_contours(A::AbstractMatrix)\n\nReturns contours as vectors of CartesianIndex.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.intersection-Tuple{Any, Any}","page":"Home","title":"GeometryOps.intersection","text":"intersection(geom_a, geom_b)::Union{Tuple{::Real, ::Real}, ::Nothing}\n\nReturn an intersection point between two geometries. Return nothing if none are found. Else, the return type depends on the input. It will be a union between: a point, a line, a linear ring, a polygon, or a multipolygon\n\nExample\n\nimport GeoInterface as GI, GeometryOps as GO\n\nline1 = GI.Line([(124.584961,-12.768946), (126.738281,-17.224758)])\nline2 = GI.Line([(123.354492,-15.961329), (127.22168,-14.008696)])\nGO.intersection(line1, line2)\n\n# output\n(125.58375366067547, -14.83572303404496)\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.intersection-Tuple{GeoInterface.AbstractTrait, Any, GeoInterface.AbstractTrait, Any}","page":"Home","title":"GeometryOps.intersection","text":"intersection(\n ::GI.AbstractTrait, geom_a,\n ::GI.AbstractTrait, geom_b,\n)::Union{\n ::Vector{Vector{Tuple{::Real, ::Real}}}, # is this a good return type?\n ::Nothing\n}\n\nCalculates the intersection between two line segments. Return nothing if there isn't one.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.intersection-Tuple{GeoInterface.LineTrait, Any, GeoInterface.LineTrait, Any}","page":"Home","title":"GeometryOps.intersection","text":"intersection(\n ::GI.LineTrait, line_a,\n ::GI.LineTrait, line_b,\n)::Union{\n ::Tuple{::Real, ::Real},\n ::Nothing\n}\n\nCalculates the intersection between two line segments. Return nothing if there isn't one.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.intersection-Tuple{GeoInterface.PolygonTrait, Any, GeoInterface.PolygonTrait, Any}","page":"Home","title":"GeometryOps.intersection","text":"intersection(\n ::GI.PolygonTrait, poly_a,\n ::GI.PolygonTrait, poly_b,\n)::Union{\n ::Vector{Vector{Tuple{::Real, ::Real}}}, # is this a good return type?\n ::Nothing\n}\n\nCalculates the intersection between two line segments. Return nothing if there isn't one.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.intersection_points-Tuple{Any, Any}","page":"Home","title":"GeometryOps.intersection_points","text":"intersection_points(\n geom_a,\n geom_b,\n)::Union{\n ::Vector{::Tuple{::Real, ::Real}},\n ::Nothing,\n}\n\nReturn a list of intersection points between two geometries. If no intersection point was possible given geometry extents, return nothing. If none are found, return an empty list.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.intersection_points-Tuple{GeoInterface.AbstractTrait, Any, GeoInterface.AbstractTrait, Any}","page":"Home","title":"GeometryOps.intersection_points","text":"intersection_points(\n ::GI.AbstractTrait, geom_a,\n ::GI.AbstractTrait, geom_b,\n)::Union{\n ::Vector{::Tuple{::Real, ::Real}},\n ::Nothing,\n}\n\nCalculates the list of intersection points between two geometries, inlcuding line segments, line strings, linear rings, polygons, and multipolygons. If no intersection points were possible given geometry extents, return nothing. If none are found, return an empty list.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.intersects-Tuple{Any, Any}","page":"Home","title":"GeometryOps.intersects","text":"intersects(geom1, geom2)::Bool\n\nCheck if two geometries intersect, returning true if so and false otherwise.\n\nExample\n\nimport GeoInterface as GI, GeometryOps as GO\n\nline1 = GI.Line([(124.584961,-12.768946), (126.738281,-17.224758)])\nline2 = GI.Line([(123.354492,-15.961329), (127.22168,-14.008696)])\nGO.intersects(line1, line2)\n\n# output\ntrue\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.intersects-Tuple{GeoInterface.AbstractTrait, Any, GeoInterface.AbstractTrait, Any}","page":"Home","title":"GeometryOps.intersects","text":"intersects(::GI.AbstractTrait, a, ::GI.AbstractTrait, b)::Bool\n\nReturns true if two geometries intersect with one another and false otherwise. For all geometries but lines, convert the geometry to a list of edges and cross compare the edges for intersections.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.intersects-Tuple{GeoInterface.LineTrait, Any, GeoInterface.LineTrait, Any}","page":"Home","title":"GeometryOps.intersects","text":"intersects(::GI.LineTrait, a, ::GI.LineTrait, b)::Bool\n\nReturns true if two line segments intersect and false otherwise.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.isclockwise-Tuple{Any}","page":"Home","title":"GeometryOps.isclockwise","text":"isclockwise(line::Union{LineString, Vector{Position}})::Bool\n\nTake a ring and return true or false whether or not the ring is clockwise or counter-clockwise.\n\nExample\n\nimport GeoInterface as GI, GeometryOps as GO\n\nring = GI.LinearRing([(0, 0), (1, 1), (1, 0), (0, 0)])\nGO.isclockwise(ring)\n\n# output\ntrue\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.isconcave-Tuple{Any}","page":"Home","title":"GeometryOps.isconcave","text":"isconcave(poly::Polygon)::Bool\n\nTake a polygon and return true or false as to whether it is concave or not.\n\nExamples\n\nimport GeoInterface as GI, GeometryOps as GO\n\npoly = GI.Polygon([[(0, 0), (0, 1), (1, 1), (1, 0), (0, 0)]])\nGO.isconcave(poly)\n\n# output\nfalse\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.overlaps-Tuple{Any, Any}","page":"Home","title":"GeometryOps.overlaps","text":"overlaps(geom1, geom2)::Bool\n\nCompare 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\n\nExamples\n\nimport GeometryOps as GO, GeoInterface as GI\npoly1 = GI.Polygon([[(0,0), (0,5), (5,5), (5,0), (0,0)]])\npoly2 = GI.Polygon([[(1,1), (1,6), (6,6), (6,1), (1,1)]])\n\nGO.overlaps(poly1, poly2)\n# output\ntrue\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.overlaps-Tuple{GeoInterface.AbstractTrait, Any, GeoInterface.AbstractTrait, Any}","page":"Home","title":"GeometryOps.overlaps","text":"overlaps(::GI.AbstractTrait, geom1, ::GI.AbstractTrait, geom2)::Bool\n\nFor any non-specified pair, all have non-matching dimensions, return false.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.overlaps-Tuple{GeoInterface.LineTrait, Any, GeoInterface.LineTrait, Any}","page":"Home","title":"GeometryOps.overlaps","text":"overlaps(::GI.LineTrait, line1, ::GI.LineTrait, line)::Bool\n\nIf the lines overlap, meaning that they are colinear but each have one endpoint outside of the other line, return true. Else false.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.overlaps-Tuple{GeoInterface.MultiPointTrait, Any, GeoInterface.MultiPointTrait, Any}","page":"Home","title":"GeometryOps.overlaps","text":"overlaps(\n ::GI.MultiPointTrait, points1,\n ::GI.MultiPointTrait, points2,\n)::Bool\n\nIf the multipoints overlap, meaning some, but not all, of the points within the multipoints are shared, return true.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.overlaps-Tuple{GeoInterface.MultiPolygonTrait, Any, GeoInterface.MultiPolygonTrait, Any}","page":"Home","title":"GeometryOps.overlaps","text":"overlaps(\n ::GI.MultiPolygonTrait, polys1,\n ::GI.MultiPolygonTrait, polys2,\n)::Bool\n\nReturn true if at least one pair of polygons from multipolygons overlap. Else false.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.overlaps-Tuple{GeoInterface.MultiPolygonTrait, Any, GeoInterface.PolygonTrait, Any}","page":"Home","title":"GeometryOps.overlaps","text":"overlaps(\n ::GI.MultiPolygonTrait, polys1,\n ::GI.PolygonTrait, poly2,\n)::Bool\n\nReturn true if polygon overlaps with at least one of the polygons within the multipolygon. Else false.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.overlaps-Tuple{GeoInterface.PolygonTrait, Any, GeoInterface.MultiPolygonTrait, Any}","page":"Home","title":"GeometryOps.overlaps","text":"overlaps(\n ::GI.PolygonTrait, poly1,\n ::GI.MultiPolygonTrait, polys2,\n)::Bool\n\nReturn true if polygon overlaps with at least one of the polygons within the multipolygon. Else false.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.overlaps-Tuple{GeoInterface.PolygonTrait, Any, GeoInterface.PolygonTrait, Any}","page":"Home","title":"GeometryOps.overlaps","text":"overlaps(\n trait_a::GI.PolygonTrait, poly_a,\n trait_b::GI.PolygonTrait, poly_b,\n)::Bool\n\nIf the two polygons intersect with one another, but are not equal, return true. Else false.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.overlaps-Tuple{Union{GeoInterface.LineStringTrait, GeoInterface.Wrappers.LinearRing}, Any, Union{GeoInterface.LineStringTrait, GeoInterface.Wrappers.LinearRing}, Any}","page":"Home","title":"GeometryOps.overlaps","text":"overlaps(\n ::Union{GI.LineStringTrait, GI.LinearRing}, line1,\n ::Union{GI.LineStringTrait, GI.LinearRing}, line2,\n)::Bool\n\nIf the curves overlap, meaning that at least one edge of each curve overlaps, return true. Else false.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.point_in_polygon-Tuple{Any, Any}","page":"Home","title":"GeometryOps.point_in_polygon","text":"point_in_polygon(point::Point, polygon::Union{Polygon, MultiPolygon}, ignore_boundary::Bool=false)::Bool\n\nTake a Point and a Polygon and determine if the point resides inside the polygon. The polygon can be convex or concave. The function accounts for holes.\n\nExamples\n\nimport GeoInterface as GI, GeometryOps as GO\n\npoint = (-77.0, 44.0)\npoly = GI.Polygon([[(-81, 41), (-81, 47), (-72, 47), (-72, 41), (-81, 41)]])\nGO.point_in_polygon(point, poly)\n\n# output\ntrue\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.point_on_line-Tuple{Any, Any}","page":"Home","title":"GeometryOps.point_on_line","text":"point_on_line(point::Point, line::LineString; ignore_end_vertices::Bool=false)::Bool\n\nReturn true if a point is on a line. Accept a optional parameter to ignore the start and end vertices of the linestring.\n\nExamples\n\nimport GeoInterface as GI, GeometryOps as GO\n\npoint = (1, 1)\nline = GI.LineString([(0, 0), (3, 3), (4, 4)])\nGO.point_on_line(point, line)\n\n# output\ntrue\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.polygon_to_line-Tuple{Any}","page":"Home","title":"GeometryOps.polygon_to_line","text":"polygon_to_line(poly::Polygon)\n\nConverts a Polygon to LineString or MultiLineString\n\nExamples\n\nimport GeometryOps as GO, GeoInterface as GI\n\npoly = GI.Polygon([[(-2.275543, 53.464547), (-2.275543, 53.489271), (-2.215118, 53.489271), (-2.215118, 53.464547), (-2.275543, 53.464547)]])\nGO.polygon_to_line(poly)\n# output\nGeoInterface.Wrappers.LineString{false, false, Vector{Tuple{Float64, Float64}}, Nothing, Nothing}([(-2.275543, 53.464547), (-2.275543, 53.489271), (-2.215118, 53.489271), (-2.215118, 53.464547), (-2.275543, 53.464547)], nothing, nothing)\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.polygonize-Tuple{AbstractMatrix}","page":"Home","title":"GeometryOps.polygonize","text":"polygonize(A; minpoints=10)\npolygonize(xs, ys, A; minpoints=10)\n\nConvert matrix A to polygons.\n\nIf xs and ys are passed in they are used as the pixel center points.\n\nKeywords\n\nminpoints: ignore polygons with less than minpoints points. \n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.rebuild-Tuple{Any, Any}","page":"Home","title":"GeometryOps.rebuild","text":"rebuild(geom, child_geoms)\n\nRebuild a geometry from child geometries.\n\nBy 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.\n\n(Maybe it should go into GeoInterface.jl)\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.reconstruct-Tuple{Any, Any}","page":"Home","title":"GeometryOps.reconstruct","text":"reconstruct(geom, components)\n\nReconstruct geom from an iterable of component objects that match its structure.\n\nAll objects in components must have the same GeoInterface.trait.\n\nUsusally used in combination with flatten.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.reproject-Tuple{Any}","page":"Home","title":"GeometryOps.reproject","text":"reproject(geometry; source_crs, target_crs, transform, always_xy, time)\nreproject(geometry, source_crs, target_crs; always_xy, time)\nreproject(geometry, transform; always_xy, time)\n\nReproject any GeoInterface.jl compatible geometry from source_crs to target_crs.\n\nThe returned object will be constructed from GeoInterface.WrapperGeometry geometries, wrapping views of a Vector{Proj.Point{D}}, where D is the dimension.\n\nArguments\n\ngeometry: Any GeoInterface.jl compatible geometries.\nsource_crs: the source coordinate referece system, as a GeoFormatTypes.jl object or a string.\ntarget_crs: the target coordinate referece system, as a GeoFormatTypes.jl object or a string.\n\nIf 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 retreivable from the geometry with GeoInterface.crs(geometry).\n\nKeywords\n\nalways_xy: force x, y coordinate order, true by default. false will expect and return points in the crs coordinate order.\ntime: the time for the coordinates. Inf by default.\nthreaded: true or false. Whether to use multithreading. Defaults to false.\ncrs: The CRS to attach to geometries. Defaults to nothing.\ncalc_extent: true or false. Whether to calculate the extent. Defaults to false.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.signed_area-Union{Tuple{Any}, Tuple{T}, Tuple{Any, Type{T}}} where T<:AbstractFloat","page":"Home","title":"GeometryOps.signed_area","text":"signed_area(geom, ::Type{T} = Float64)::T\n\nReturns the signed area of the geometry, based on winding order. This is computed slighly 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.\n\nResult will be of type T, where T is an optional argument with a default value of Float64.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.signed_distance-Union{Tuple{T}, Tuple{Any, Any}, Tuple{Any, Any, Type{T}}} where T<:AbstractFloat","page":"Home","title":"GeometryOps.signed_distance","text":"signed_distance(point, geom, ::Type{T} = Float64)::T\n\nCalculates 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.\n\nResult will be of type T, where T is an optional argument with a default value of Float64.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.simplify-Tuple{Any}","page":"Home","title":"GeometryOps.simplify","text":"simplify(obj; kw...)\nsimplify(::SimplifyAlg, obj; kw...)\n\nSimplify a geometry, feature, feature collection, or nested vectors or a table of these.\n\nRadialDistance, DouglasPeucker, or VisvalingamWhyatt algorithms are available, listed in order of increasing quality but decreaseing performance.\n\nPoinTrait and MultiPointTrait are returned unchanged.\n\nThe default behaviour is simplify(DouglasPeucker(; kw...), obj). Pass in other SimplifyAlg to use other algorithms.\n\nKeywords\n\nthreaded: true or false. Whether to use multithreading. Defaults to false.\ncrs: The CRS to attach to geometries. Defaults to nothing.\ncalc_extent: true or false. Whether to calculate the extent. Defaults to false.\n\nKeywords for DouglasPeucker are allowed when no algorithm is specified:\n\nKeywords\n\nratio: the fraction of points that should remain after simplify. Useful as it will generalise for large collections of objects.\nnumber: the number of points that should remain after simplify. Less useful for large collections of mixed size objects.\n\nExample\n\nSimplify a polygon to have six points:\n\nimport GeoInterface as GI\nimport GeometryOps as GO\n\npoly = GI.Polygon([[\n [-70.603637, -33.399918],\n [-70.614624, -33.395332],\n [-70.639343, -33.392466],\n [-70.659942, -33.394759],\n [-70.683975, -33.404504],\n [-70.697021, -33.419406],\n [-70.701141, -33.434306],\n [-70.700454, -33.446339],\n [-70.694274, -33.458369],\n [-70.682601, -33.465816],\n [-70.668869, -33.472117],\n [-70.646209, -33.473835],\n [-70.624923, -33.472117],\n [-70.609817, -33.468107],\n [-70.595397, -33.458369],\n [-70.587158, -33.442901],\n [-70.587158, -33.426283],\n [-70.590591, -33.414248],\n [-70.594711, -33.406224],\n [-70.603637, -33.399918]]])\n\nsimple = GO.simplify(poly; number=6)\nGI.npoint(simple)\n\n# output\n6\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.t_value-Union{Tuple{T2}, Tuple{T1}, Tuple{N}, Tuple{Union{Tuple{Vararg{T1, N}}, StaticArraysCore.StaticArray{Tuple{N}, T1, 1}}, Union{Tuple{Vararg{T1, N}}, StaticArraysCore.StaticArray{Tuple{N}, T1, 1}}, T2, T2}} where {N, T1<:Real, T2<:Real}","page":"Home","title":"GeometryOps.t_value","text":"t_value(sᵢ, sᵢ₊₁, rᵢ, rᵢ₊₁)\n\nReturns the \"T-value\" as described in Hormann's presentation [HormannPresentation] on how to calculate the mean-value coordinate. \n\nHere, 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.\n\ntᵢ = fracmathrmdetleft(sᵢ sᵢ₁right)rᵢ * rᵢ₁ + sᵢ sᵢ₁\n\n[HormannPresentation]: K. Hormann and N. Sukumar. Generalized Barycentric Coordinates in Computer Graphics and Computational Mechanics. Taylor & Fancis, CRC Press, 2017.\n\n```\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.to_edges-Tuple{Any}","page":"Home","title":"GeometryOps.to_edges","text":"to_edges()\n\nConvert any geometry or collection of geometries into a flat vector of Tuple{Tuple{Float64,Float64},Tuple{Float64,Float64}} edges.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.transform-Tuple{Any, Any}","page":"Home","title":"GeometryOps.transform","text":"transform(f, obj)\n\nApply a function f to all the points in obj.\n\nPoints will be passed to f as an SVector to allow using CoordinateTransformations.jl and Rotations.jl without hassle.\n\nSVector is also a valid GeoInterface.jl point, so will work in all GeoInterface.jl methods.\n\nExample\n\njulia> import GeoInterface as GI\n\njulia> import GeometryOps as GO\n\njulia> geom = GI.Polygon([GI.LinearRing([(1, 2), (3, 4), (5, 6), (1, 2)]), GI.LinearRing([(3, 4), (5, 6), (6, 7), (3, 4)])]);\n\njulia> f = CoordinateTransformations.Translation(3.5, 1.5)\nTranslation(3.5, 1.5)\n\njulia> GO.transform(f, geom)\nGeoInterface.Wrappers.Polygon{false, false, Vector{GeoInterface.Wrappers.LinearRing{false, false, Vector{StaticArraysCore.SVector{2, Float64}}, Nothing, Nothing}}, Nothing, Nothing}(GeoInterface.Wrappers.Linea\nrRing{false, false, Vector{StaticArraysCore.SVector{2, Float64}}, Nothing, Nothing}[GeoInterface.Wrappers.LinearRing{false, false, Vector{StaticArraysCore.SVector{2, Float64}}, Nothing, Nothing}(StaticArraysCo\nre.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\nrraysCore.SVector{2, Float64}[[6.5, 5.5], [8.5, 7.5], [9.5, 8.5], [6.5, 5.5]], nothing, nothing)], nothing, nothing)\n\nWith Rotations.jl you need to actuall multiply the Rotation by the SVector point, which is easy using an anonymous function.\n\njulia> using Rotations\n\njulia> GO.transform(p -> one(RotMatrix{2}) * p, geom)\nGeoInterface.Wrappers.Polygon{false, false, Vector{GeoInterface.Wrappers.LinearRing{false, false, Vector{StaticArraysCore.SVector{2, Int64}}, Nothing, Nothing}}, Nothing, Nothing}(GeoInterface.Wrappers.LinearR\ning{false, false, Vector{StaticArraysCore.SVector{2, Int64}}, Nothing, Nothing}[GeoInterface.Wrappers.LinearRing{false, false, Vector{StaticArraysCore.SVector{2, Int64}}, Nothing, Nothing}(StaticArraysCore.SVe\nctor{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\n}[[4, 3], [6, 5], [7, 6], [4, 3]], nothing, nothing)], nothing, nothing)\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.tuples-Tuple{Any}","page":"Home","title":"GeometryOps.tuples","text":"tuples(obj)\n\nConvert all points in obj to Tuples, wherever the are nested.\n\nReturns a similar object or collection of objects using GeoInterface.jl geometries wrapping Tuple points.\n\nKeywords\n\nthreaded: true or false. Whether to use multithreading. Defaults to false.\ncrs: The CRS to attach to geometries. Defaults to nothing.\ncalc_extent: true or false. Whether to calculate the extent. Defaults to false.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.unwrap","page":"Home","title":"GeometryOps.unwrap","text":"unwrap(target::Type{<:AbstractTrait}, obj)\nunwrap(f, target::Type{<:AbstractTrait}, obj)\n\nUnwrap the object newst to vectors, down to the target trait.\n\nIf f is passed in it will be applied to the target geometries as they are found.\n\n\n\n\n\n","category":"function"},{"location":"#GeometryOps.weighted_mean-Union{Tuple{WT}, Tuple{WT, Any, Any}} where WT<:Real","page":"Home","title":"GeometryOps.weighted_mean","text":"weighted_mean(weight::Real, x1, x2)\n\nReturns the weighted mean of x1 and x2, where weight is the weight of x1.\n\nSpecifically, calculates x1 * weight + x2 * (1 - weight).\n\nnote: Note\nThe idea for this method is that you can override this for custom types, like Color types, in extension modules.\n\n\n\n\n\n","category":"method"}] +[{"location":"source/GeometryOps/#GeometryOps.jl","page":"GeometryOps.jl","title":"GeometryOps.jl","text":"","category":"section"},{"location":"source/GeometryOps/","page":"GeometryOps.jl","title":"GeometryOps.jl","text":"module GeometryOps\n\nusing GeoInterface\nusing GeometryBasics\nimport Proj\nusing LinearAlgebra\nimport ExactPredicates\nimport Proj.CoordinateTransformations.StaticArrays\n\nusing GeoInterface.Extents: Extents\n\nconst GI = GeoInterface\nconst GB = GeometryBasics\n\nconst TuplePoint = Tuple{Float64,Float64}\nconst Edge = Tuple{TuplePoint,TuplePoint}\n\ninclude(\"primitives.jl\")\ninclude(\"utils.jl\")\n\ninclude(\"methods/area.jl\")\ninclude(\"methods/barycentric.jl\")\ninclude(\"methods/bools.jl\")\ninclude(\"methods/centroid.jl\")\ninclude(\"methods/distance.jl\")\ninclude(\"methods/equals.jl\")\ninclude(\"methods/geom_relations/contains.jl\")\ninclude(\"methods/geom_relations/coveredby.jl\")\ninclude(\"methods/geom_relations/covers.jl\")\ninclude(\"methods/geom_relations/crosses.jl\")\ninclude(\"methods/geom_relations/disjoint.jl\")\ninclude(\"methods/geom_relations/geom_geom_processors.jl\")\ninclude(\"methods/geom_relations/intersects.jl\")\ninclude(\"methods/geom_relations/overlaps.jl\")\ninclude(\"methods/geom_relations/touches.jl\")\ninclude(\"methods/geom_relations/within.jl\")\ninclude(\"methods/polygonize.jl\")\n\ninclude(\"transformations/extent.jl\")\ninclude(\"transformations/flip.jl\")\ninclude(\"transformations/reproject.jl\")\ninclude(\"transformations/simplify.jl\")\ninclude(\"transformations/tuples.jl\")\ninclude(\"transformations/transform.jl\")\n\nend","category":"page"},{"location":"source/GeometryOps/","page":"GeometryOps.jl","title":"GeometryOps.jl","text":"","category":"page"},{"location":"source/GeometryOps/","page":"GeometryOps.jl","title":"GeometryOps.jl","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"const THREADED_KEYWORD = \"- `threaded`: `true` or `false`. Whether to use multithreading. Defaults to `false`.\"\nconst CRS_KEYWORD = \"- `crs`: The CRS to attach to geometries. Defaults to `nothing`.\"\nconst CALC_EXTENT_KEYWORD = \"- `calc_extent`: `true` or `false`. Whether to calculate the extent. Defaults to `false`.\"\n\nconst APPLY_KEYWORDS = \"\"\"\n$THREADED_KEYWORD\n$CRS_KEYWORD\n$CALC_EXTENT_KEYWORD\n\"\"\"","category":"page"},{"location":"source/primitives/#Primitive-functions","page":"Primitive functions","title":"Primitive functions","text":"","category":"section"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"This file mainly defines the apply function.","category":"page"},{"location":"source/primitives/#What-is-apply?","page":"Primitive functions","title":"What is apply?","text":"","category":"section"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"apply applies some function to every geometry matching the Target GeoInterface trait, in some arbitrarily nested object made up of:","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"AbstractArrays (we also try to iterate other non-GeoInteface compatible object)\nFeatureCollectionTrait objects\nFeatureTrait objects\nAbstractGeometryTrait objects","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"apply recursively calls itself through these nested layers until it reaches objects with the Target GeoInterface trait. When found apply applies the function f, and stops.","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"The outer recursive functions then progressively rebuild the object using GeoInterface objects matching the original traits.","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"If PointTrait is found but it is not the Target, an error is thrown. This likely means the object contains a different geometry trait to the target, such as MultiPointTrait when LineStringTrait was specified.","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"To handle this possibility it may be necessary to make Target a Union of traits found at the same level of nesting, and define methods of f to handle all cases.","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Be careful making a union across \"levels\" of nesting, e.g. Union{FeatureTrait,PolygonTrait}, as _apply will just never reach PolygonTrait when all the polygons are wrapped in a FeatureTrait object.","category":"page"},{"location":"source/primitives/#Embedding:","page":"Primitive functions","title":"Embedding:","text":"","category":"section"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"extent and crs can be embedded in all geometries, features, and feature collections as part of apply. Geometries deeper than Target will of course not have new extent or crs embedded.","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"calc_extent signals to recalculate an Extent and embed it.\ncrs will be embedded as-is","category":"page"},{"location":"source/primitives/#Threading","page":"Primitive functions","title":"Threading","text":"","category":"section"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Threading is used at the outermost level possible - over an array, feature collection, or e.g. a MultiPolygonTrait where each PolygonTrait sub-geometry may be calculated on a different thread.","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"\"\"\"\n apply(f, target::Type{<:AbstractTrait}, obj; kw...)\n\nReconstruct a geometry, feature, feature collection, or nested vectors of\neither using the function `f` on the `target` trait.\n\n`f(target_geom) => x` where `x` also has the `target` trait, or a trait that can\nbe substituted. For example, swapping `PolgonTrait` to `MultiPointTrait` will fail\nif the outer object has `MultiPolygonTrait`, but should work if it has `FeatureTrait`.\n\nObjects \"shallower\" than the target trait are always completely rebuilt, like\na `Vector` of `FeatureCollectionTrait` of `FeatureTrait` when the target\nhas `PolygonTrait` and is held in the features. But \"deeper\" objects may remain\nunchanged - such as points and linear rings if the target is the same `PolygonTrait`.\n\nThe result is a functionally similar geometry with values depending on `f`\n\n$APPLY_KEYWORDS","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Example","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Flipped point the order in any feature or geometry, or iterables of either:\n\n```juia\nimport GeoInterface as GI\nimport GeometryOps as GO\ngeom = GI.Polygon([GI.LinearRing([(1, 2), (3, 4), (5, 6), (1, 2)]),\n GI.LinearRing([(3, 4), (5, 6), (6, 7), (3, 4)])])\n\nflipped_geom = GO.apply(GI.PointTrait, geom) do p\n (GI.y(p), GI.x(p))\nend\n\"\"\"\napply(f, ::Type{Target}, geom; kw...) where Target = _apply(f, Target, geom; kw...)","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Call _apply again with the trait of geom","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"_apply(f, ::Type{Target}, geom; kw...) where Target =\n _apply(f, Target, GI.trait(geom), geom; kw...)","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"There is no trait and this is an AbstractArray - so just iterate over it calling _apply on the contents","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"function _apply(f, ::Type{Target}, ::Nothing, A::AbstractArray; threaded=false, kw...) where Target","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"For an Array there is nothing else to do but map _apply over all values maptasks may run this level threaded if threaded==true, but deeper `apply` called in the closure will not be threaded","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":" _maptasks(eachindex(A); threaded) do i\n _apply(f, Target, A[i]; threaded=false, kw...)\n end\nend","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"There is no trait and this is not an AbstractArray. Try to call _apply over it. We can't use threading as we don't know if we can can index into it. So just map. (TODO: maybe collect first if threaded=true so we can thread?)","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"_apply(f, ::Type{Target}, ::Nothing, iterable; kw...) where Target =\n map(x -> _apply(f, Target, x; kw...), iterable)","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Rewrap all FeatureCollectionTrait feature collections as GI.FeatureCollection","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"function _apply(f, ::Type{Target}, ::GI.FeatureCollectionTrait, fc;\n crs=GI.crs(fc), calc_extent=false, threaded=false\n) where Target","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Run _apply on all features in the feature collection, possibly threaded","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":" features = _maptasks(1:GI.nfeature(fc); threaded) do i\n feature = GI.getfeature(fc, i)\n _apply(f, Target, feature; crs, calc_extent, threaded=false)::GI.Feature\n end\n if calc_extent","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Calculate the extent of the features","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":" extent = mapreduce(GI.extent, Extents.union, features)","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Return a FeatureCollection with features, crs and caculated extent","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":" return GI.FeatureCollection(features; crs, extent)\n else","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Return a FeatureCollection with features and crs","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":" return GI.FeatureCollection(features; crs)\n end\nend","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Rewrap all FeatureTrait features as GI.Feature, keeping the properties","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"function _apply(f, ::Type{Target}, ::GI.FeatureTrait, feature;\n crs=GI.crs(feature), calc_extent=false, threaded=false\n) where Target","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Run _apply on the contained geometry","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":" geometry = _apply(f, Target, GI.geometry(feature); crs, calc_extent, threaded)","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Get the feature properties","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":" properties = GI.properties(feature)\n if calc_extent","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Calculate the extent of the geometry","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":" extent = GI.extent(geometry)","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Return a new Feature with the new geometry and calculated extent, but the oroginal properties and crs","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":" return GI.Feature(geometry; properties, crs, extent)\n else","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Return a new Feature with the new geometry, but the oroginal properties and crs","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":" return GI.Feature(geometry; properties, crs)\n end\nend","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Reconstruct nested geometries","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"function _apply(f, ::Type{Target}, trait, geom;\n crs=GI.crs(geom), calc_extent=false, threaded=false\n)::(GI.geointerface_geomtype(trait)) where Target","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Map _apply over all sub geometries of geom to create a new vector of geometries TODO handle zero length","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":" geoms = _maptasks(1:GI.ngeom(geom); threaded) do i\n _apply(f, Target, GI.getgeom(geom, i); crs, calc_extent, threaded=false)\n end\n if calc_extent","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Calculate the extent of the sub geometries","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":" extent = mapreduce(GI.extent, Extents.union, geoms)","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Return a new geometry of the same trait as geom, holding tnew geoms with crs and calcualted extent","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":" return rebuild(geom, geoms; crs, extent)\n else","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Return a new geometryof the same trait as geom, holding the new geoms with crs","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":" return rebuild(geom, geoms; crs)\n end\nend","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Fail loudly if we hit PointTrait without running f (after PointTrait there is no further to dig with _apply)","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"_apply(f, ::Type{Target}, trait::GI.PointTrait, geom; crs=nothing, kw...) where Target =\n throw(ArgumentError(\"target $Target not found, but reached a `PointTrait` leaf\"))","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Finally, these short methods are the main purpose of apply. The Trait is a subtype of the Target (or identical to it) So the Target is found. We apply f to geom and return it to previous _apply calls to be wrapped with the outer geometries/feature/featurecollection/array.","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"_apply(f, ::Type{Target}, ::Trait, geom; crs=GI.crs(geom), kw...) where {Target,Trait<:Target} = f(geom)","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Define some specific cases of this match to avoid method ambiguity","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"_apply(f, ::Type{GI.PointTrait}, trait::GI.PointTrait, geom; kw...) = f(geom)\n_apply(f, ::Type{GI.FeatureTrait}, ::GI.FeatureTrait, feature; kw...) = f(feature)\n_apply(f, ::Type{GI.FeatureCollectionTrait}, ::GI.FeatureCollectionTrait, fc; kw...) = f(fc)\n\n\"\"\"\n unwrap(target::Type{<:AbstractTrait}, obj)\n unwrap(f, target::Type{<:AbstractTrait}, obj)\n\nUnwrap the object newst to vectors, down to the target trait.\n\nIf `f` is passed in it will be applied to the target geometries\nas they are found.\n\"\"\"\nfunction unwrap end\nunwrap(target::Type, geom) = unwrap(identity, target, geom)","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Add dispatch argument for trait","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"unwrap(f, target::Type, geom) = unwrap(f, target, GI.trait(geom), geom)","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Try to unwrap over iterables","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"unwrap(f, target::Type, ::Nothing, iterable) =\n map(x -> unwrap(f, target, x), iterable)","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Rewrap feature collections","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"unwrap(f, target::Type, ::GI.FeatureCollectionTrait, fc) =\n map(x -> unwrap(f, target, x), GI.getfeature(fc))\nunwrap(f, target::Type, ::GI.FeatureTrait, feature) = unwrap(f, target, GI.geometry(feature))\nunwrap(f, target::Type, trait, geom) = map(g -> unwrap(f, target, g), GI.getgeom(geom))","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Apply f to the target geometry","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"unwrap(f, ::Type{Target}, ::Trait, geom) where {Target,Trait<:Target} = f(geom)","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Fail if we hit PointTrait","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"unwrap(f, target::Type, trait::GI.PointTrait, geom) =\n throw(ArgumentError(\"target $target not found, but reached a `PointTrait` leaf\"))","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Specific cases to avoid method ambiguity","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"unwrap(f, target::Type{GI.PointTrait}, trait::GI.PointTrait, geom) = f(geom)\nunwrap(f, target::Type{GI.FeatureTrait}, ::GI.FeatureTrait, feature) = f(feature)\nunwrap(f, target::Type{GI.FeatureCollectionTrait}, ::GI.FeatureCollectionTrait, fc) = f(fc)\n\n\"\"\"\n flatten(target::Type{<:GI.AbstractTrait}, obj)\n flatten(f, target::Type{<:GI.AbstractTrait}, obj)\n\nLazily flatten any `AbstractArray`, iterator, `FeatureCollectionTrait`,\n`FeatureTrait` or `AbstractGeometryTrait` object `obj`, so that objects\nwith the `target` trait are returned by the iterator.\n\nIf `f` is passed in it will be applied to the target geometries.\n\"\"\"\nflatten(::Type{Target}, geom) where {Target<:GI.AbstractTrait} = flatten(identity, Target, geom)\nflatten(f, ::Type{Target}, geom) where {Target<:GI.AbstractTrait} = _flatten(f, Target, geom)\n\n_flatten(f, ::Type{Target}, geom) where Target = _flatten(f, Target, GI.trait(geom), geom)","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Try to flatten over iterables","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"_flatten(f, ::Type{Target}, ::Nothing, iterable) where Target =\n Iterators.flatten(Iterators.map(x -> _flatten(f, Target, x), iterable))","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Flatten feature collections","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"function _flatten(f, ::Type{Target}, ::GI.FeatureCollectionTrait, fc) where Target\n Iterators.map(GI.getfeature(fc)) do feature\n _flatten(f, Target, feature)\n end |> Iterators.flatten\nend\n_flatten(f, ::Type{Target}, ::GI.FeatureTrait, feature) where Target =\n _flatten(f, Target, GI.geometry(feature))","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Apply f to the target geometry","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"_flatten(f, ::Type{Target}, ::Trait, geom) where {Target,Trait<:Target} = (f(geom),)\n_flatten(f, ::Type{Target}, trait, geom) where Target =\n Iterators.flatten(Iterators.map(g -> _flatten(f, Target, g), GI.getgeom(geom)))","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Fail if we hit PointTrait without running f","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"_flatten(f, ::Type{Target}, trait::GI.PointTrait, geom) where Target =\n throw(ArgumentError(\"target $Target not found, but reached a `PointTrait` leaf\"))","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Specific cases to avoid method ambiguity","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"_flatten(f, ::Type{<:GI.PointTrait}, ::GI.PointTrait, geom) = (f(geom),)\n_flatten(f, ::Type{<:GI.FeatureTrait}, ::GI.FeatureTrait, feature) = (f(feature),)\n_flatten(f, ::Type{<:GI.FeatureCollectionTrait}, ::GI.FeatureCollectionTrait, fc) = (f(fc),)\n\n\n\"\"\"\n reconstruct(geom, components)\n\nReconstruct `geom` from an iterable of component objects that match its structure.\n\nAll objects in `components` must have the same `GeoInterface.trait`.\n\nUsusally used in combination with `flatten`.\n\"\"\"\nfunction reconstruct(geom, components)\n obj, iter = _reconstruct(geom, components)\n return obj\nend\n\n_reconstruct(geom, components) =\n _reconstruct(typeof(GI.trait(first(components))), geom, components, 1)\n_reconstruct(::Type{Target}, geom, components, iter) where Target =\n _reconstruct(Target, GI.trait(geom), geom, components, iter)","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Try to reconstruct over iterables","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"function _reconstruct(::Type{Target}, ::Nothing, iterable, components, iter) where Target\n vect = map(iterable) do x","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"iter is updated by _reconstruct here","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":" obj, iter = _reconstruct(Target, x, components, iter)\n obj\n end\n return vect, iter\nend","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Reconstruct feature collections","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"function _reconstruct(::Type{Target}, ::GI.FeatureCollectionTrait, fc, components, iter) where Target\n features = map(GI.getfeature(fc)) do feature","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"iter is updated by _reconstruct here","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":" newfeature, iter = _reconstruct(Target, feature, components, iter)\n newfeature\n end\n return GI.FeatureCollection(features; crs=GI.crs(fc)), iter\nend\nfunction _reconstruct(::Type{Target}, ::GI.FeatureTrait, feature, components, iter) where Target\n geom, iter = _reconstruct(Target, GI.geometry(feature), components, iter)\n return GI.Feature(geom; properties=GI.properties(feature), crs=GI.crs(feature)), iter\nend\nfunction _reconstruct(::Type{Target}, trait, geom, components, iter) where Target\n geoms = map(GI.getgeom(geom)) do subgeom","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"iter is updated by _reconstruct here","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":" subgeom1, iter = _reconstruct(Target, GI.trait(subgeom), subgeom, components, iter)\n subgeom1\n end\n return rebuild(geom, geoms), iter\nend","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Apply f to the target geometry","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"_reconstruct(::Type{Target}, ::Trait, geom, components, iter) where {Target,Trait<:Target} =\n iterate(components, iter)","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Specific cases to avoid method ambiguity","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"_reconstruct(::Type{<:GI.PointTrait}, ::GI.PointTrait, geom, components, iter) = iterate(components, iter)\n_reconstruct(::Type{<:GI.FeatureTrait}, ::GI.FeatureTrait, feature, components, iter) = iterate(feature, iter)\n_reconstruct(::Type{<:GI.FeatureCollectionTrait}, ::GI.FeatureCollectionTrait, fc, components, iter) = iterate(fc, iter)","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Fail if we hit PointTrait without running f","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"_reconstruct(::Type{Target}, trait::GI.PointTrait, geom, components, iter) where Target =\n throw(ArgumentError(\"target $Target not found, but reached a `PointTrait` leaf\"))\n\n\nconst BasicsGeoms = Union{GB.AbstractGeometry,GB.AbstractFace,GB.AbstractPoint,GB.AbstractMesh,\n GB.AbstractPolygon,GB.LineString,GB.MultiPoint,GB.MultiLineString,GB.MultiPolygon,GB.Mesh}\n\n\"\"\"\n rebuild(geom, child_geoms)\n\nRebuild a geometry from child geometries.\n\nBy default geometries will be rebuilt as a `GeoInterface.Wrappers`\ngeometry, but `rebuild` can have methods added to it to dispatch\non geometries from other packages and specify how to rebuild them.\n\n(Maybe it should go into GeoInterface.jl)\n\"\"\"\nrebuild(geom, child_geoms; kw...) = rebuild(GI.trait(geom), geom, child_geoms; kw...)\nfunction rebuild(trait::GI.AbstractTrait, geom, child_geoms; crs=GI.crs(geom), extent=nothing)\n T = GI.geointerface_geomtype(trait)\n if GI.is3d(geom)","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"The Boolean type parameters here indicate 3d-ness and measure coordinate presence respectively.","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":" return T{true,false}(child_geoms; crs, extent)\n else\n return T{false,false}(child_geoms; crs, extent)\n end\nend","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"So that GeometryBasics geoms rebuild as themselves","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"function rebuild(trait::GI.AbstractTrait, geom::BasicsGeoms, child_geoms; crs=nothing)\n GB.geointerface_geomtype(trait)(child_geoms)\nend\nfunction rebuild(trait::GI.AbstractTrait, geom::Union{GB.LineString,GB.MultiPoint}, child_geoms; crs=nothing)\n GB.geointerface_geomtype(trait)(GI.convert.(GB.Point, child_geoms))\nend\nfunction rebuild(trait::GI.PolygonTrait, geom::GB.Polygon, child_geoms; crs=nothing)\n Polygon(child_geoms[1], child_geoms[2:end])\nend\n\nusing Base.Threads: nthreads, @threads, @spawn","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Threading utility, modified Mason Protters threading PSA run f over ntasks, where f recieves an AbstractArray/range of linear indices","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"function _maptasks(f, taskrange; threaded=false)\n if threaded\n ntasks = length(taskrange)","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Customize this as needed. More tasks have more overhead, but better load balancing","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":" tasks_per_thread = 2\n chunk_size = max(1, ntasks ÷ (tasks_per_thread * nthreads()))","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"partition the range into chunks","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":" task_chunks = Iterators.partition(taskrange, chunk_size)","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Map over the chunks","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":" tasks = map(task_chunks) do chunk","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Spawn a task to process this chunk","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":" @spawn begin","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Where we map f over the chunk indices","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":" map(f, chunk)\n end\n end","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Finally we join the results into a new vector","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":" return mapreduce(fetch, vcat, tasks)\n else\n return map(f, taskrange)\n end\nend","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/methods/polygonize/#Polygonizing-raster-data","page":"Polygonizing raster data","title":"Polygonizing raster data","text":"","category":"section"},{"location":"source/methods/polygonize/","page":"Polygonizing raster data","title":"Polygonizing raster data","text":"export polygonize","category":"page"},{"location":"source/methods/polygonize/","page":"Polygonizing raster data","title":"Polygonizing raster data","text":"The methods in this file are able to convert a raster image into a set of polygons, by contour detection using a clockwise Moore neighborhood method.","category":"page"},{"location":"source/methods/polygonize/","page":"Polygonizing raster data","title":"Polygonizing raster data","text":"The main entry point is the polygonize function.","category":"page"},{"location":"source/methods/polygonize/","page":"Polygonizing raster data","title":"Polygonizing raster data","text":"polygonize","category":"page"},{"location":"source/methods/polygonize/#Example","page":"Polygonizing raster data","title":"Example","text":"","category":"section"},{"location":"source/methods/polygonize/","page":"Polygonizing raster data","title":"Polygonizing raster data","text":"Here's a basic implementation, using the Makie.peaks() function. First, let's investigate the nature of the function:","category":"page"},{"location":"source/methods/polygonize/","page":"Polygonizing raster data","title":"Polygonizing raster data","text":"using Makie, GeometryOps\nn = 49\nxs, ys = LinRange(-3, 3, n), LinRange(-3, 3, n)\nzs = Makie.peaks(n)\nz_max_value = maximum(abs.(extrema(zs)))\nf, a, p = heatmap(\n xs, ys, zs;\n axis = (; aspect = DataAspect(), title = \"Exact function\")\n)\ncb = Colorbar(f[1, 2], p; label = \"Z-value\")\nf","category":"page"},{"location":"source/methods/polygonize/","page":"Polygonizing raster data","title":"Polygonizing raster data","text":"Now, we can use the polygonize function to convert the raster data into polygons.","category":"page"},{"location":"source/methods/polygonize/","page":"Polygonizing raster data","title":"Polygonizing raster data","text":"For this particular example, we chose a range of z-values between 0.8 and 3.2, which would provide two distinct polyogns with holes.","category":"page"},{"location":"source/methods/polygonize/","page":"Polygonizing raster data","title":"Polygonizing raster data","text":"polygons = polygonize(xs, ys, 0.8 .< zs .< 3.2)","category":"page"},{"location":"source/methods/polygonize/","page":"Polygonizing raster data","title":"Polygonizing raster data","text":"This returns a list of GeometryBasics.Polygon, which can be plotted immediately, or wrapped directly in a GeometryBasics.MultiPolygon. Let's see how these look:","category":"page"},{"location":"source/methods/polygonize/","page":"Polygonizing raster data","title":"Polygonizing raster data","text":"f, a, p = poly(polygons; label = \"Polygonized polygons\", axis = (; aspect = DataAspect()))","category":"page"},{"location":"source/methods/polygonize/","page":"Polygonizing raster data","title":"Polygonizing raster data","text":"Finally, let's plot the Makie contour lines on top, to see how well the polygonization worked:","category":"page"},{"location":"source/methods/polygonize/","page":"Polygonizing raster data","title":"Polygonizing raster data","text":"contour!(a, zs; labels = true, levels = [0.8, 3.2], label = \"Contour lines\")\nf","category":"page"},{"location":"source/methods/polygonize/#Implementation","page":"Polygonizing raster data","title":"Implementation","text":"","category":"section"},{"location":"source/methods/polygonize/","page":"Polygonizing raster data","title":"Polygonizing raster data","text":"The implementation follows:","category":"page"},{"location":"source/methods/polygonize/","page":"Polygonizing raster data","title":"Polygonizing raster data","text":"\"\"\"\n polygonize(A; minpoints=10)\n polygonize(xs, ys, A; minpoints=10)\n\nConvert matrix `A` to polygons.\n\nIf `xs` and `ys` are passed in they are used as the pixel center points.","category":"page"},{"location":"source/methods/polygonize/","page":"Polygonizing raster data","title":"Polygonizing raster data","text":"Keywords","category":"page"},{"location":"source/methods/polygonize/","page":"Polygonizing raster data","title":"Polygonizing raster data","text":"- `minpoints`: ignore polygons with less than `minpoints` points.\n\"\"\"\npolygonize(A::AbstractMatrix; kw...) = polygonize(axes(A)..., A; kw...)\n\nfunction polygonize(xs, ys, A::AbstractMatrix; minpoints=10)\n # This function uses a lazy map to get contours.\n contours = Iterators.map(get_contours(A)) do contour\n poly = map(contour) do xy\n x, y = Tuple(xy)\n Point2f(x + first(xs) - 1, y + first(ys) - 1)\n end\n end\n # If we filter off the minimum points, then it's a hair more efficient\n # not to convert contours with length < missingpoints to polygons.\n if minpoints > 1\n contours = Iterators.filter(contours) do contour\n length(contour) > minpoints\n end\n return map(Polygon, contours)\n else\n return map(Polygon, contours)\n end\nend\n\n# rotate direction clockwise\nrot_clockwise(dir) = (dir) % 8 + 1\n# rotate direction counterclockwise\nrot_counterclockwise(dir) = (dir + 6) % 8 + 1\n\n# move from current pixel to next in given direction\nfunction move(pixel, image, dir, dir_delta)\n newp = pixel + dir_delta[dir]\n height, width = size(image)\n if (0 < newp[1] <= height) && (0 < newp[2] <= width)\n if image[newp] != 0\n return newp\n end\n end\n return CartesianIndex(0, 0)\nend\n\n# finds direction between two given pixels\nfunction from_to(from, to, dir_delta)\n delta = to - from\n return findall(x -> x == delta, dir_delta)[1]\nend\n\nfunction detect_move(image, p0, p2, nbd, border, done, dir_delta)\n dir = from_to(p0, p2, dir_delta)\n moved = rot_clockwise(dir)\n p1 = CartesianIndex(0, 0)\n while moved != dir ## 3.1\n newp = move(p0, image, moved, dir_delta)\n if newp[1] != 0\n p1 = newp\n break\n end\n moved = rot_clockwise(moved)\n end\n\n if p1 == CartesianIndex(0, 0)\n return\n end\n\n p2 = p1 ## 3.2\n p3 = p0 ## 3.2\n done .= false\n while true\n dir = from_to(p3, p2, dir_delta)\n moved = rot_counterclockwise(dir)\n p4 = CartesianIndex(0, 0)\n done .= false\n while true ## 3.3\n p4 = move(p3, image, moved, dir_delta)\n if p4[1] != 0\n break\n end\n done[moved] = true\n moved = rot_counterclockwise(moved)\n end\n push!(border, p3) ## 3.4\n if p3[1] == size(image, 1) || done[3]\n image[p3] = -nbd\n elseif image[p3] == 1\n image[p3] = nbd\n end\n\n if (p4 == p0 && p3 == p1) ## 3.5\n break\n end\n p2 = p3\n p3 = p4\n end\nend\n\n\"\"\"\n get_contours(A::AbstractMatrix)\n\nReturns contours as vectors of `CartesianIndex`.\n\"\"\"\nfunction get_contours(image::AbstractMatrix)\n nbd = 1\n lnbd = 1\n image = Float64.(image)\n contour_list = Vector{typeof(CartesianIndex[])}()\n done = [false, false, false, false, false, false, false, false]\n\n # Clockwise Moore neighborhood.\n dir_delta = (CartesianIndex(-1, 0), CartesianIndex(-1, 1), CartesianIndex(0, 1), CartesianIndex(1, 1),\n CartesianIndex(1, 0), CartesianIndex(1, -1), CartesianIndex(0, -1), CartesianIndex(-1, -1))\n\n height, width = size(image)\n\n for i = 1:height\n lnbd = 1\n for j = 1:width\n fji = image[i, j]\n is_outer = (image[i, j] == 1 && (j == 1 || image[i, j-1] == 0)) ## 1 (a)\n is_hole = (image[i, j] >= 1 && (j == width || image[i, j+1] == 0))\n\n if is_outer || is_hole\n # 2\n border = CartesianIndex[]\n from = CartesianIndex(i, j)\n\n if is_outer\n nbd += 1\n from -= CartesianIndex(0, 1)\n\n else\n nbd += 1\n if fji > 1\n lnbd = fji\n end\n from += CartesianIndex(0, 1)\n end\n\n p0 = CartesianIndex(i, j)\n detect_move(image, p0, from, nbd, border, done, dir_delta) ## 3\n if isempty(border) ##TODO\n push!(border, p0)\n image[p0] = -nbd\n end\n push!(contour_list, border)\n end\n if fji != 0 && fji != 1\n lnbd = abs(fji)\n end\n\n end\n end\n\n return contour_list\nend","category":"page"},{"location":"source/methods/polygonize/","page":"Polygonizing raster data","title":"Polygonizing raster data","text":"","category":"page"},{"location":"source/methods/polygonize/","page":"Polygonizing raster data","title":"Polygonizing raster data","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/methods/geom_relations/touches/#Touches","page":"Touches","title":"Touches","text":"","category":"section"},{"location":"source/methods/geom_relations/touches/","page":"Touches","title":"Touches","text":"export touches","category":"page"},{"location":"source/methods/geom_relations/touches/#What-is-touches?","page":"Touches","title":"What is touches?","text":"","category":"section"},{"location":"source/methods/geom_relations/touches/","page":"Touches","title":"Touches","text":"The touches function checks if one geometry touches another geometry. In other words, the interiors of the two geometries don't interact, but one of the geometries must have a boundary point that interacts with either the other geometies interior or boundary.","category":"page"},{"location":"source/methods/geom_relations/touches/","page":"Touches","title":"Touches","text":"To provide an example, consider these two lines:","category":"page"},{"location":"source/methods/geom_relations/touches/","page":"Touches","title":"Touches","text":"using GeometryOps\nusing GeometryOps.GeometryBasics\nusing Makie\nusing CairoMakie\n\nl1 = Line([Point(0.0, 0.0), Point(1.0, 0.0)])\nl2 = Line([Point(1.0, 0.0), Point(1.0, -1.0)])\n\nf, a, p = lines(l1)\nlines!(l2)","category":"page"},{"location":"source/methods/geom_relations/touches/","page":"Touches","title":"Touches","text":"We can see that these two lines touch only at their endpoints.","category":"page"},{"location":"source/methods/geom_relations/touches/","page":"Touches","title":"Touches","text":"touches(l1, l2) # true","category":"page"},{"location":"source/methods/geom_relations/touches/#Implementation","page":"Touches","title":"Implementation","text":"","category":"section"},{"location":"source/methods/geom_relations/touches/","page":"Touches","title":"Touches","text":"This is the GeoInterface-compatible implementation.","category":"page"},{"location":"source/methods/geom_relations/touches/","page":"Touches","title":"Touches","text":"First, we implement a wrapper method that dispatches to the correct implementation based on the geometry trait.","category":"page"},{"location":"source/methods/geom_relations/touches/","page":"Touches","title":"Touches","text":"Each of these calls a method in the geomgeomprocessors file. The methods in this file determine if the given geometries meet a set of criteria. For the touches function and arguments g1 and g2, this criteria is as follows: - points of g1 are not allowed to be in the interior of g2 - points of g1 are allowed to be on the boundary of g2 - points of g1 are allowed to be in the exterior of g2 - no points of g1 are required to be in the interior of g2 - at least one point of g1 is required to be on the boundary of g2 - no points of g1 are required to be in the exterior of g2","category":"page"},{"location":"source/methods/geom_relations/touches/","page":"Touches","title":"Touches","text":"The code for the specific implementations is in the geomgeomprocessors file.","category":"page"},{"location":"source/methods/geom_relations/touches/","page":"Touches","title":"Touches","text":"const TOUCHES_POINT_ALLOWED = (in_allow = false, on_allow = true, out_allow = false)\nconst TOUCHES_CURVE_ALLOWED = (over_allow = false, cross_allow = false, on_allow = true, out_allow = true)\nconst TOUCHES_POLYGON_ALLOWS = (in_allow = false, on_allow = true, out_allow = true)\nconst TOUCHES_REQUIRES = (in_require = false, on_require = true, out_require = false)\n\n\"\"\"\n touches(geom1, geom2)::Bool\n\nReturn `true` if the first geometry touches the second geometry. In other words,\nthe two interiors cannot interact, but one of the geometries must have a\nboundary point that interacts with either the other geometies interior or\nboundary.\n\n# Examples\n```jldoctest setup=:(using GeometryOps, GeometryBasics)\nimport GeometryOps as GO, GeoInterface as GI\n\nl1 = GI.Line([(0.0, 0.0), (1.0, 0.0)])\nl2 = GI.Line([(1.0, 1.0), (1.0, -1.0)])\n\nGO.touches(l1, l2)","category":"page"},{"location":"source/methods/geom_relations/touches/","page":"Touches","title":"Touches","text":"output","category":"page"},{"location":"source/methods/geom_relations/touches/","page":"Touches","title":"Touches","text":"true\n```\n\"\"\"\ntouches(g1, g2)::Bool = _touches(trait(g1), g1, trait(g2), g2)","category":"page"},{"location":"source/methods/geom_relations/touches/#Convert-features-to-geometries","page":"Touches","title":"Convert features to geometries","text":"","category":"section"},{"location":"source/methods/geom_relations/touches/","page":"Touches","title":"Touches","text":"_touches(::GI.FeatureTrait, g1, ::Any, g2) = touches(GI.geometry(g1), g2)\n_touches(::Any, g1, t2::GI.FeatureTrait, g2) = touches(g1, GI.geometry(g2))\n_touches(::FeatureTrait, g1, ::FeatureTrait, g2) = touches(GI.geometry(g1), GI.geometry(g2))","category":"page"},{"location":"source/methods/geom_relations/touches/#Point-touches-geometries","page":"Touches","title":"Point touches geometries","text":"","category":"section"},{"location":"source/methods/geom_relations/touches/","page":"Touches","title":"Touches","text":"Point cannot touch another point as if they are equal, interiors interact","category":"page"},{"location":"source/methods/geom_relations/touches/","page":"Touches","title":"Touches","text":"_touches(\n ::GI.PointTrait, g1,\n ::GI.PointTrait, g2,\n) = false","category":"page"},{"location":"source/methods/geom_relations/touches/","page":"Touches","title":"Touches","text":"Point touches a linestring if it equal to the first of last point of the line","category":"page"},{"location":"source/methods/geom_relations/touches/","page":"Touches","title":"Touches","text":"function _touches(\n ::GI.PointTrait, g1,\n ::Union{GI.LineTrait, GI.LineStringTrait}, g2,\n)\n n = GI.npoint(g2)\n p1 = GI.getpoint(g2, 1)\n pn = GI.getpoint(g2, n)\n equals(p1, pn) && return false\n return equals(g1, p1) || equals(g1, pn)\nend","category":"page"},{"location":"source/methods/geom_relations/touches/","page":"Touches","title":"Touches","text":"Point cannot 'touch' a linearring given that the ring has no boundary points","category":"page"},{"location":"source/methods/geom_relations/touches/","page":"Touches","title":"Touches","text":"_touches(\n ::GI.PointTrait, g1,\n ::GI.LinearRingTrait, g2,\n) = false","category":"page"},{"location":"source/methods/geom_relations/touches/","page":"Touches","title":"Touches","text":"Point touches a polygon if it is on the boundary of that polygon","category":"page"},{"location":"source/methods/geom_relations/touches/","page":"Touches","title":"Touches","text":"_touches(\n ::GI.PointTrait, g1,\n ::GI.PolygonTrait, g2,\n) = _point_polygon_process(\n g1, g2;\n TOUCHES_POINT_ALLOWED...,\n)\n\n#= Geometry touches a point if the point is on the geometry boundary. =#\n_touches(\n trait1::Union{GI.AbstractCurveTrait, GI.PolygonTrait}, g1,\n trait2::GI.PointTrait, g2,\n) = _touches(trait2, g2, trait1, g1)","category":"page"},{"location":"source/methods/geom_relations/touches/#Lines-touching-geometries","page":"Touches","title":"Lines touching geometries","text":"","category":"section"},{"location":"source/methods/geom_relations/touches/","page":"Touches","title":"Touches","text":"#= Linestring touches another line if at least one bounday point interacts with\nthe bounday of interior of the other line, but the interiors don't interact. =#\n_touches(\n ::Union{GI.LineTrait, GI.LineStringTrait}, g1,\n ::Union{GI.LineTrait, GI.LineStringTrait}, g2,\n) = _line_curve_process(\n g1, g2;\n TOUCHES_CURVE_ALLOWED...,\n TOUCHES_REQUIRES...,\n closed_line = false,\n closed_curve = false,\n)\n\n\n#= Linestring touches a linearring if at least one of the boundary points of the\nline interacts with the linear ring, but their interiors can't interact. =#\n_touches(\n ::Union{GI.LineTrait, GI.LineStringTrait}, g1,\n ::GI.LinearRingTrait, g2,\n) = _line_curve_process(\n g1, g2;\n TOUCHES_CURVE_ALLOWED...,\n TOUCHES_REQUIRES...,\n closed_line = false,\n closed_curve = true,\n)\n\n#= Linestring touches a polygon if at least one of the boundary points of the\nline interacts with the boundary of the polygon. =#\n_touches(\n ::Union{GI.LineTrait, GI.LineStringTrait}, g1,\n ::GI.PolygonTrait, g2,\n) = _line_polygon_process(\n g1, g2;\n TOUCHES_POLYGON_ALLOWS...,\n TOUCHES_REQUIRES...,\n closed_line = false,\n)","category":"page"},{"location":"source/methods/geom_relations/touches/#Rings-touch-geometries","page":"Touches","title":"Rings touch geometries","text":"","category":"section"},{"location":"source/methods/geom_relations/touches/","page":"Touches","title":"Touches","text":"#= Linearring touches a linestring if at least one of the boundary points of the\nline interacts with the linear ring, but their interiors can't interact. =#\n_touches(\n trait1::GI.LinearRingTrait, g1,\n trait2::Union{GI.LineTrait, GI.LineStringTrait}, g2,\n) = _touches(trait2, g2, trait1, g1)\n\n#= Linearring cannot touch another linear ring since they are both exclusively\nmade up of interior points and no bounday points =#\n_touches(\n ::GI.LinearRingTrait, g1,\n ::GI.LinearRingTrait, g2,\n) = false\n\n#= Linearring touches a polygon if at least one of the points of the ring\ninteract with the polygon bounday and non are in the polygon interior. =#\n_touches(\n ::GI.LinearRingTrait, g1,\n ::GI.PolygonTrait, g2,\n) = _line_polygon_process(\n g1, g2;\n TOUCHES_POLYGON_ALLOWS...,\n TOUCHES_REQUIRES...,\n closed_line = true,\n)","category":"page"},{"location":"source/methods/geom_relations/touches/#Polygons-touch-geometries","page":"Touches","title":"Polygons touch geometries","text":"","category":"section"},{"location":"source/methods/geom_relations/touches/","page":"Touches","title":"Touches","text":"#= Polygon touches a curve if at least one of the curve bounday points interacts\nwith the polygon's bounday and no curve points interact with the interior.=#\n_touches(\n trait1::GI.PolygonTrait, g1,\n trait2::GI.AbstractCurveTrait, g2\n) = _touches(trait2, g2, trait1, g1)\n\n\n#= Polygon touches another polygon if they share at least one boundary point and\nno interior points. =#\n_touches(\n ::GI.PolygonTrait, g1,\n ::GI.PolygonTrait, g2,\n) = _polygon_polygon_process(\n g1, g2;\n TOUCHES_POLYGON_ALLOWS...,\n TOUCHES_REQUIRES...,\n)","category":"page"},{"location":"source/methods/geom_relations/touches/#Geometries-touch-multi-geometry/geometry-collections","page":"Touches","title":"Geometries touch multi-geometry/geometry collections","text":"","category":"section"},{"location":"source/methods/geom_relations/touches/","page":"Touches","title":"Touches","text":"#= Geometry touch a multi-geometry or a collection if the geometry touches at\nleast one of the elements of the collection. =#\nfunction _touches(\n ::Union{GI.PointTrait, GI.AbstractCurveTrait, GI.PolygonTrait}, g1,\n ::Union{\n GI.MultiPointTrait, GI.AbstractMultiCurveTrait,\n GI.MultiPolygonTrait, GI.GeometryCollectionTrait,\n }, g2,\n)\n for sub_g2 in GI.getgeom(g2)\n !touches(g1, sub_g2) && return false\n end\n return true\nend","category":"page"},{"location":"source/methods/geom_relations/touches/#Multi-geometry/geometry-collections-cross-geometries","page":"Touches","title":"Multi-geometry/geometry collections cross geometries","text":"","category":"section"},{"location":"source/methods/geom_relations/touches/","page":"Touches","title":"Touches","text":"#= Multi-geometry or a geometry collection touches a geometry if at least one\nelements of the collection touches the geometry. =#\nfunction _touches(\n ::Union{\n GI.MultiPointTrait, GI.AbstractMultiCurveTrait,\n GI.MultiPolygonTrait, GI.GeometryCollectionTrait,\n }, g1,\n ::GI.AbstractGeometryTrait, g2,\n)\n for sub_g1 in GI.getgeom(g1)\n !touches(sub_g1, g2) && return false\n end\n return true\nend","category":"page"},{"location":"source/methods/geom_relations/touches/","page":"Touches","title":"Touches","text":"","category":"page"},{"location":"source/methods/geom_relations/touches/","page":"Touches","title":"Touches","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/transformations/reproject/#Geometry-reprojection","page":"Geometry reprojection","title":"Geometry reprojection","text":"","category":"section"},{"location":"source/transformations/reproject/","page":"Geometry reprojection","title":"Geometry reprojection","text":"export reproject","category":"page"},{"location":"source/transformations/reproject/","page":"Geometry reprojection","title":"Geometry reprojection","text":"This file is pretty simple - it simply reprojects a geometry pointwise from one CRS to another. It uses the Proj package for the transformation, but this could be moved to an extension if needed.","category":"page"},{"location":"source/transformations/reproject/","page":"Geometry reprojection","title":"Geometry reprojection","text":"This works using the apply functionality.","category":"page"},{"location":"source/transformations/reproject/","page":"Geometry reprojection","title":"Geometry reprojection","text":"\"\"\"\n reproject(geometry; source_crs, target_crs, transform, always_xy, time)\n reproject(geometry, source_crs, target_crs; always_xy, time)\n reproject(geometry, transform; always_xy, time)\n\nReproject any GeoInterface.jl compatible `geometry` from `source_crs` to `target_crs`.\n\nThe returned object will be constructed from `GeoInterface.WrapperGeometry`\ngeometries, wrapping views of a `Vector{Proj.Point{D}}`, where `D` is the dimension.\n\n# Arguments\n\n- `geometry`: Any GeoInterface.jl compatible geometries.\n- `source_crs`: the source coordinate referece system, as a GeoFormatTypes.jl object or a string.\n- `target_crs`: the target coordinate referece system, as a GeoFormatTypes.jl object or a string.\n\nIf these a passed as keywords, `transform` will take priority.\nWithout it `target_crs` is always needed, and `source_crs` is\nneeded if it is not retreivable from the geometry with `GeoInterface.crs(geometry)`.\n\n# Keywords\n\n- `always_xy`: force x, y coordinate order, `true` by default.\n `false` will expect and return points in the crs coordinate order.\n- `time`: the time for the coordinates. `Inf` by default.\n$APPLY_KEYWORDS\n\"\"\"\nfunction reproject(geom;\n source_crs=nothing, target_crs=nothing, transform=nothing, kw...\n)\n if isnothing(transform)\n if isnothing(source_crs)\n source_crs = if GI.trait(geom) isa Nothing && geom isa AbstractArray\n GeoInterface.crs(first(geom))\n else\n GeoInterface.crs(geom)\n end\n end","category":"page"},{"location":"source/transformations/reproject/","page":"Geometry reprojection","title":"Geometry reprojection","text":"If its still nothing, error","category":"page"},{"location":"source/transformations/reproject/","page":"Geometry reprojection","title":"Geometry reprojection","text":" isnothing(source_crs) && throw(ArgumentError(\"geom has no crs attatched. Pass a `source_crs` keyword\"))","category":"page"},{"location":"source/transformations/reproject/","page":"Geometry reprojection","title":"Geometry reprojection","text":"Otherwise reproject","category":"page"},{"location":"source/transformations/reproject/","page":"Geometry reprojection","title":"Geometry reprojection","text":" reproject(geom, source_crs, target_crs; kw...)\n else\n reproject(geom, transform; kw...)\n end\nend\nfunction reproject(geom, source_crs, target_crs;\n time=Inf,\n always_xy=true,\n transform=Proj.Transformation(Proj.CRS(source_crs), Proj.CRS(target_crs); always_xy),\n kw...\n)\n reproject(geom, transform; time, target_crs, kw...)\nend\nfunction reproject(geom, transform::Proj.Transformation; time=Inf, target_crs=nothing, kw...)\n if _is3d(geom)\n return apply(PointTrait, geom; crs=target_crs, kw...) do p\n transform(GI.x(p), GI.y(p), GI.z(p))\n end\n else\n return apply(PointTrait, geom; crs=target_crs, kw...) do p\n transform(GI.x(p), GI.y(p))\n end\n end\nend","category":"page"},{"location":"source/transformations/reproject/","page":"Geometry reprojection","title":"Geometry reprojection","text":"","category":"page"},{"location":"source/transformations/reproject/","page":"Geometry reprojection","title":"Geometry reprojection","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/methods/geom_relations/crosses/#Crossing-checks","page":"Crossing checks","title":"Crossing checks","text":"","category":"section"},{"location":"source/methods/geom_relations/crosses/","page":"Crossing checks","title":"Crossing checks","text":"\"\"\"\n crosses(geom1, geom2)::Bool\n\nReturn `true` if the intersection results in a geometry whose dimension is one less than\nthe maximum dimension of the two source geometries and the intersection set is interior to\nboth source geometries.\n\nTODO: broken\n\n# Examples\n```julia\nimport GeoInterface as GI, GeometryOps as GO\n\nline1 = GI.LineString([(1, 1), (1, 2), (1, 3), (1, 4)])\nline2 = GI.LineString([(-2, 2), (4, 2)])\n\nGO.crosses(line1, line2)","category":"page"},{"location":"source/methods/geom_relations/crosses/","page":"Crossing checks","title":"Crossing checks","text":"output","category":"page"},{"location":"source/methods/geom_relations/crosses/","page":"Crossing checks","title":"Crossing checks","text":"true\n```\n\"\"\"\ncrosses(g1, g2)::Bool = crosses(trait(g1), g1, trait(g2), g2)::Bool\ncrosses(t1::FeatureTrait, g1, t2, g2)::Bool = crosses(GI.geometry(g1), g2)\ncrosses(t1, g1, t2::FeatureTrait, g2)::Bool = crosses(g1, geometry(g2))\ncrosses(::MultiPointTrait, g1, ::LineStringTrait, g2)::Bool = multipoint_crosses_line(g1, g2)\ncrosses(::MultiPointTrait, g1, ::PolygonTrait, g2)::Bool = multipoint_crosses_poly(g1, g2)\ncrosses(::LineStringTrait, g1, ::MultiPointTrait, g2)::Bool = multipoint_crosses_lines(g2, g1)\ncrosses(::LineStringTrait, g1, ::PolygonTrait, g2)::Bool = line_crosses_poly(g1, g2)\ncrosses(::LineStringTrait, g1, ::LineStringTrait, g2)::Bool = line_crosses_line(g1, g2)\ncrosses(::PolygonTrait, g1, ::MultiPointTrait, g2)::Bool = multipoint_crosses_poly(g2, g1)\ncrosses(::PolygonTrait, g1, ::LineStringTrait, g2)::Bool = line_crosses_poly(g2, g1)\n\nfunction multipoint_crosses_line(geom1, geom2)\n int_point = false\n ext_point = false\n i = 1\n np2 = GI.npoint(geom2)\n\n while i < GI.npoint(geom1) && !int_point && !ext_point\n for j in 1:GI.npoint(geom2) - 1\n exclude_boundary = (j === 1 || j === np2 - 2) ? :none : :both\n if _point_on_segment(GI.getpoint(geom1, i), (GI.getpoint(geom2, j), GI.getpoint(geom2, j + 1)); exclude_boundary)\n int_point = true\n else\n ext_point = true\n end\n end\n i += 1\n end\n\n return int_point && ext_point\nend\n\nfunction line_crosses_line(line1, line2)\n np2 = GI.npoint(line2)\n if GeometryOps.intersects(line1, line2)\n for i in 1:GI.npoint(line1) - 1\n for j in 1:GI.npoint(line2) - 1\n exclude_boundary = (j === 1 || j === np2 - 2) ? :none : :both\n pa = GI.getpoint(line1, i)\n pb = GI.getpoint(line1, i + 1)\n p = GI.getpoint(line2, j)\n _point_on_segment(p, (pa, pb); exclude_boundary) && return true\n end\n end\n end\n return false\nend\n\nfunction line_crosses_poly(line, poly)\n for l in flatten(AbstractCurveTrait, poly)\n intersects(line, l) && return true\n end\n return false\nend\n\nfunction multipoint_crosses_poly(mp, poly)\n int_point = false\n ext_point = false\n\n for p in GI.getpoint(mp)\n if _point_polygon_process(\n p, poly;\n in_allow = true, on_allow = true, out_allow = false,\n )\n int_point = true\n else\n ext_point = true\n end\n int_point && ext_point && return true\n end\n return false\nend\n\n#= TODO: Once crosses is swapped over to use the geom relations workflow, can\ndelete these helpers. =#\n\nfunction _point_on_segment(point, (start, stop); exclude_boundary::Symbol=:none)::Bool\n x, y = GI.x(point), GI.y(point)\n x1, y1 = GI.x(start), GI.y(start)\n x2, y2 = GI.x(stop), GI.y(stop)\n\n dxc = x - x1\n dyc = y - y1\n dx1 = x2 - x1\n dy1 = y2 - y1","category":"page"},{"location":"source/methods/geom_relations/crosses/","page":"Crossing checks","title":"Crossing checks","text":"TODO use better predicate for crossing here","category":"page"},{"location":"source/methods/geom_relations/crosses/","page":"Crossing checks","title":"Crossing checks","text":" cross = dxc * dy1 - dyc * dx1\n cross != 0 && return false","category":"page"},{"location":"source/methods/geom_relations/crosses/","page":"Crossing checks","title":"Crossing checks","text":"Will constprop optimise these away?","category":"page"},{"location":"source/methods/geom_relations/crosses/","page":"Crossing checks","title":"Crossing checks","text":" if exclude_boundary === :none\n if abs(dx1) >= abs(dy1)\n return dx1 > 0 ? x1 <= x && x <= x2 : x2 <= x && x <= x1\n end\n return dy1 > 0 ? y1 <= y && y <= y2 : y2 <= y && y <= y1\n elseif exclude_boundary === :start\n if abs(dx1) >= abs(dy1)\n return dx1 > 0 ? x1 < x && x <= x2 : x2 <= x && x < x1\n end\n return dy1 > 0 ? y1 < y && y <= y2 : y2 <= y && y < y1\n elseif exclude_boundary === :end\n if abs(dx1) >= abs(dy1)\n return dx1 > 0 ? x1 <= x && x < x2 : x2 < x && x <= x1\n end\n return dy1 > 0 ? y1 <= y && y < y2 : y2 < y && y <= y1\n elseif exclude_boundary === :both\n if abs(dx1) >= abs(dy1)\n return dx1 > 0 ? x1 < x && x < x2 : x2 < x && x < x1\n end\n return dy1 > 0 ? y1 < y && y < y2 : y2 < y && y < y1\n end\n return false\nend","category":"page"},{"location":"source/methods/geom_relations/crosses/","page":"Crossing checks","title":"Crossing checks","text":"","category":"page"},{"location":"source/methods/geom_relations/crosses/","page":"Crossing checks","title":"Crossing checks","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/methods/barycentric/#Barycentric-coordinates","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"","category":"section"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"export barycentric_coordinates, barycentric_coordinates!, barycentric_interpolate\nexport MeanValue","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"Generalized barycentric coordinates are a generalization of barycentric coordinates, which are typically used in triangles, to arbitrary polygons.","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"They provide a way to express a point within a polygon as a weighted average of the polygon's vertices.","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"In the case of a triangle, barycentric coordinates are a set of three numbers (λ_1 λ_2 λ_3), each associated with a vertex of the triangle. Any point within the triangle can be expressed as a weighted average of the vertices, where the weights are the barycentric coordinates. The weights sum to 1, and each is non-negative.","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"For a polygon with n vertices, generalized barycentric coordinates are a set of n numbers (λ_1 λ_2 λ_n), each associated with a vertex of the polygon. Any point within the polygon can be expressed as a weighted average of the vertices, where the weights are the generalized barycentric coordinates.","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"As with the triangle case, the weights sum to 1, and each is non-negative.","category":"page"},{"location":"source/methods/barycentric/#Example","page":"Barycentric coordinates","title":"Example","text":"","category":"section"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"This example was taken from this page of CGAL's documentation.","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"using GeometryOps, Makie\nusing GeometryOps.GeometryBasics\n# Define a polygon\npolygon_points = Point3f[\n(0.03, 0.05, 0.00), (0.07, 0.04, 0.02), (0.10, 0.04, 0.04),\n(0.14, 0.04, 0.06), (0.17, 0.07, 0.08), (0.20, 0.09, 0.10),\n(0.22, 0.11, 0.12), (0.25, 0.11, 0.14), (0.27, 0.10, 0.16),\n(0.30, 0.07, 0.18), (0.31, 0.04, 0.20), (0.34, 0.03, 0.22),\n(0.37, 0.02, 0.24), (0.40, 0.03, 0.26), (0.42, 0.04, 0.28),\n(0.44, 0.07, 0.30), (0.45, 0.10, 0.32), (0.46, 0.13, 0.34),\n(0.46, 0.19, 0.36), (0.47, 0.26, 0.38), (0.47, 0.31, 0.40),\n(0.47, 0.35, 0.42), (0.45, 0.37, 0.44), (0.41, 0.38, 0.46),\n(0.38, 0.37, 0.48), (0.35, 0.36, 0.50), (0.32, 0.35, 0.52),\n(0.30, 0.37, 0.54), (0.28, 0.39, 0.56), (0.25, 0.40, 0.58),\n(0.23, 0.39, 0.60), (0.21, 0.37, 0.62), (0.21, 0.34, 0.64),\n(0.23, 0.32, 0.66), (0.24, 0.29, 0.68), (0.27, 0.24, 0.70),\n(0.29, 0.21, 0.72), (0.29, 0.18, 0.74), (0.26, 0.16, 0.76),\n(0.24, 0.17, 0.78), (0.23, 0.19, 0.80), (0.24, 0.22, 0.82),\n(0.24, 0.25, 0.84), (0.21, 0.26, 0.86), (0.17, 0.26, 0.88),\n(0.12, 0.24, 0.90), (0.07, 0.20, 0.92), (0.03, 0.15, 0.94),\n(0.01, 0.10, 0.97), (0.02, 0.07, 1.00)]\n# Plot it!\n# First, we'll plot the polygon using Makie's rendering:\nf, a1, p1 = poly(\n polygon_points;\n color = last.(polygon_points), colormap = cgrad(:jet, 18; categorical = true),\n axis = (;\n aspect = DataAspect(), title = \"Makie mesh based polygon rendering\", subtitle = \"CairoMakie\"\n ),\n figure = (; resolution = (800, 400),)\n)\n\nMakie.update_state_before_display!(f) # We have to call this explicitly, to get the axis limits correct\n# Now that we've plotted the first polygon,\n# we can render it using barycentric coordinates.\na1_bbox = a1.finallimits[] # First we get the extent of the axis\next = GeometryOps.GI.Extent(NamedTuple{(:X, :Y)}(zip(minimum(a1_bbox), maximum(a1_bbox))))\n\na2, p2box = poly( # Now, we plot a cropping rectangle around the axis so we only show the polygon\n f[1, 2],\n GeometryOps.GeometryBasics.Polygon( # This is a rectangle with an internal hole shaped like the polygon.\n Point2f[(ext.X[1], ext.Y[1]), (ext.X[2], ext.Y[1]), (ext.X[2], ext.Y[2]), (ext.X[1], ext.Y[2]), (ext.X[1], ext.Y[1])],\n [reverse(Point2f.(polygon_points))]\n );\n color = :white, xautolimits = false, yautolimits = false,\n axis = (;\n aspect = DataAspect(), title = \"Barycentric coordinate based polygon rendering\", subtitle = \"GeometryOps\",\n limits = (ext.X, ext.Y),\n )\n)\nhidedecorations!(a1)\nhidedecorations!(a2)\ncb = Colorbar(f[2, :], p1.plots[1]; vertical = false, flipaxis = true)\n# Finally, we perform barycentric interpolation on a grid,\nxrange = LinRange(ext.X..., widths(a2.scene.px_area[])[1] * 4) # 2 rendered pixels per \"physical\" pixel\nyrange = LinRange(ext.Y..., widths(a2.scene.px_area[])[2] * 4) # 2 rendered pixels per \"physical\" pixel\n@time mean_values = barycentric_interpolate.(\n (MeanValue(),), # The barycentric coordinate algorithm (MeanValue is the only one for now)\n (Point2f.(polygon_points),), # The polygon points as `Point2f`\n (last.(polygon_points,),), # The values per polygon point - can be anything which supports addition and division\n Point2f.(xrange, yrange') # The points at which to interpolate\n)\n# and render!\nhm = heatmap!(\n a2, xrange, yrange, mean_values;\n colormap = p1.colormap, # Use the same colormap as the original polygon plot\n colorrange = p1.plots[1].colorrange[], # Access the rendered mesh plot's colorrange directly\n transformation = (; translation = Vec3f(0,0,-1)), # This gets the heatmap to render \"behind\" the previously plotted polygon\n xautolimits = false, yautolimits = false\n)\nf","category":"page"},{"location":"source/methods/barycentric/#Barycentric-coordinate-API","page":"Barycentric coordinates","title":"Barycentric-coordinate API","text":"","category":"section"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"In some cases, we actually want barycentric interpolation, and have no interest in the coordinates themselves.","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"However, the coordinates can be useful for debugging, and when performing 3D rendering, multiple barycentric values (depth, uv) are needed for depth buffering.","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"const _VecTypes = Union{Tuple{Vararg{T, N}}, GeometryBasics.StaticArraysCore.StaticArray{Tuple{N}, T, 1}} where {N, T}\n\n\"\"\"\n abstract type AbstractBarycentricCoordinateMethod\n\nAbstract supertype for barycentric coordinate methods.\nThe subtypes may serve as dispatch types, or may cache\nsome information about the target polygon.\n\n# API\nThe following methods must be implemented for all subtypes:\n- `barycentric_coordinates!(λs::Vector{<: Real}, method::AbstractBarycentricCoordinateMethod, exterior::Vector{<: Point{2, T1}}, point::Point{2, T2})`\n- `barycentric_interpolate(method::AbstractBarycentricCoordinateMethod, exterior::Vector{<: Point{2, T1}}, values::Vector{V}, point::Point{2, T2})::V`\n- `barycentric_interpolate(method::AbstractBarycentricCoordinateMethod, exterior::Vector{<: Point{2, T1}}, interiors::Vector{<: Vector{<: Point{2, T1}}} values::Vector{V}, point::Point{2, T2})::V`\nThe rest of the methods will be implemented in terms of these, and have efficient dispatches for broadcasting.\n\"\"\"\nabstract type AbstractBarycentricCoordinateMethod end\n\n\nBase.@propagate_inbounds function barycentric_coordinates!(λs::Vector{<: Real}, method::AbstractBarycentricCoordinateMethod, polypoints::AbstractVector{<: Point{N1, T1}}, point::Point{N2, T2}) where {N1, N2, T1 <: Real, T2 <: Real}\n @boundscheck @assert length(λs) == length(polypoints)\n @boundscheck @assert length(polypoints) >= 3\n\n @error(\"Not implemented yet for method $(method).\")\nend\nBase.@propagate_inbounds barycentric_coordinates!(λs::Vector{<: Real}, polypoints::AbstractVector{<: Point{N1, T1}}, point::Point{N2, T2}) where {N1, N2, T1 <: Real, T2 <: Real} = barycentric_coordinates!(λs, MeanValue(), polypoints, point)\n\nBase.@propagate_inbounds function barycentric_coordinates(method::AbstractBarycentricCoordinateMethod, polypoints::AbstractVector{<: Point{N1, T1}}, point::Point{N2, T2}) where {N1, N2, T1 <: Real, T2 <: Real}\n λs = zeros(promote_type(T1, T2), length(polypoints))\n barycentric_coordinates!(λs, method, polypoints, point)\n return λs\nend\nBase.@propagate_inbounds barycentric_coordinates(polypoints::AbstractVector{<: Point{N1, T1}}, point::Point{N2, T2}) where {N1, N2, T1 <: Real, T2 <: Real} = barycentric_coordinates(MeanValue(), polypoints, point)\n\nBase.@propagate_inbounds function barycentric_interpolate(method::AbstractBarycentricCoordinateMethod, polypoints::AbstractVector{<: Point{N, T1}}, values::AbstractVector{V}, point::Point{N, T2}) where {N, T1 <: Real, T2 <: Real, V}\n @boundscheck @assert length(values) == length(polypoints)\n @boundscheck @assert length(polypoints) >= 3\n λs = barycentric_coordinates(method, polypoints, point)\n return sum(λs .* values)\nend\nBase.@propagate_inbounds barycentric_interpolate(polypoints::AbstractVector{<: Point{N, T1}}, values::AbstractVector{V}, point::Point{N, T2}) where {N, T1 <: Real, T2 <: Real, V} = barycentric_interpolate(MeanValue(), polypoints, values, point)\n\nBase.@propagate_inbounds function barycentric_interpolate(method::AbstractBarycentricCoordinateMethod, exterior::AbstractVector{<: Point{N, T1}}, interiors::AbstractVector{<: Point{N, T1}}, values::AbstractVector{V}, point::Point{N, T2}) where {N, T1 <: Real, T2 <: Real, V}\n @boundscheck @assert length(values) == length(exterior) + isempty(interiors) ? 0 : sum(length.(interiors))\n @boundscheck @assert length(exterior) >= 3\n λs = barycentric_coordinates(method, exterior, interiors, point)\n return sum(λs .* values)\nend\nBase.@propagate_inbounds barycentric_interpolate(exterior::AbstractVector{<: Point{N, T1}}, interiors::AbstractVector{<: Point{N, T1}}, values::AbstractVector{V}, point::Point{N, T2}) where {N, T1 <: Real, T2 <: Real, V} = barycentric_interpolate(MeanValue(), exterior, interiors, values, point)\n\nBase.@propagate_inbounds function barycentric_interpolate(method::AbstractBarycentricCoordinateMethod, polygon::Polygon{2, T1}, values::AbstractVector{V}, point::Point{2, T2}) where {T1 <: Real, T2 <: Real, V}\n exterior = decompose(Point{2, promote_type(T1, T2)}, polygon.exterior)\n if isempty(polygon.interiors)\n @boundscheck @assert length(values) == length(exterior)\n return barycentric_interpolate(method, exterior, values, point)\n else # the poly has interiors\n interiors = reverse.(decompose.((Point{2, promote_type(T1, T2)},), polygon.interiors))\n @boundscheck @assert length(values) == length(exterior) + sum(length.(interiors))\n return barycentric_interpolate(method, exterior, interiors, values, point)\n end\nend\nBase.@propagate_inbounds barycentric_interpolate(polygon::Polygon{2, T1}, values::AbstractVector{V}, point::Point{2, T2}) where {T1 <: Real, T2 <: Real, V} = barycentric_interpolate(MeanValue(), polygon, values, point)","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"3D polygons are considered to have their vertices in the XY plane, and the Z coordinate must represent some value. This is to say that the Z coordinate is interpreted as an M coordinate.","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"Base.@propagate_inbounds function barycentric_interpolate(method::AbstractBarycentricCoordinateMethod, polygon::Polygon{3, T1}, point::Point{2, T2}) where {T1 <: Real, T2 <: Real}\n exterior_point3s = decompose(Point{3, promote_type(T1, T2)}, polygon.exterior)\n exterior_values = getindex.(exterior_point3s, 3)\n exterior_points = Point2f.(exterior_point3s)\n if isempty(polygon.interiors)\n return barycentric_interpolate(method, exterior_points, exterior_values, point)\n else # the poly has interiors\n interior_point3s = decompose.((Point{3, promote_type(T1, T2)},), polygon.interiors)\n interior_values = collect(Iterators.flatten((getindex.(point3s, 3) for point3s in interior_point3s)))\n interior_points = map(point3s -> Point2f.(point3s), interior_point3s)\n return barycentric_interpolate(method, exterior_points, interior_points, vcat(exterior_values, interior_values), point)\n end\nend\nBase.@propagate_inbounds barycentric_interpolate(polygon::Polygon{3, T1}, point::Point{2, T2}) where {T1 <: Real, T2 <: Real} = barycentric_interpolate(MeanValue(), polygon, point)","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"This method is the one which supports GeoInterface.","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"Base.@propagate_inbounds function barycentric_interpolate(method::AbstractBarycentricCoordinateMethod, polygon, values::AbstractVector{V}, point) where V\n @assert GeoInterface.trait(polygon) isa GeoInterface.PolygonTrait\n @assert GeoInterface.trait(point) isa GeoInterface.PointTrait\n passable_polygon = GeoInterface.convert(GeometryBasics, polygon)\n @assert passable_polygon isa GeometryBasics.Polygon \"The polygon was converted to a $(typeof(passable_polygon)), which is not a `GeometryBasics.Polygon`.\"\n # first_poly_point = GeoInterface.getpoint(GeoInterface.getexterior(polygon))\n passable_point = GeoInterface.convert(GeometryBasics, point)\n return barycentric_interpolate(method, passable_polygon, Point2(passable_point))\nend\nBase.@propagate_inbounds barycentric_interpolate(polygon, values::AbstractVector{V}, point) where V = barycentric_interpolate(MeanValue(), polygon, values, point)\n\n\"\"\"\n weighted_mean(weight::Real, x1, x2)\n\nReturns the weighted mean of `x1` and `x2`, where `weight` is the weight of `x1`.\n\nSpecifically, calculates `x1 * weight + x2 * (1 - weight)`.\n\n!!! note\n The idea for this method is that you can override this for custom types, like Color types, in extension modules.\n\"\"\"\nfunction weighted_mean(weight::WT, x1, x2) where {WT <: Real}\n return muladd(x1, weight, x2 * (oneunit(WT) - weight))\nend\n\n\n\"\"\"\n MeanValue() <: AbstractBarycentricCoordinateMethod\n\nThis method calculates barycentric coordinates using the mean value method.\n\n# References\n\n\"\"\"\nstruct MeanValue <: AbstractBarycentricCoordinateMethod\nend","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"Before we go to the actual implementation, there are some quick and simple utility functions that we need to implement. These are mainly for convenience and code brevity.","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"\"\"\"\n _det(s1::Point2{T1}, s2::Point2{T2}) where {T1 <: Real, T2 <: Real}\n\nReturns the determinant of the matrix formed by `hcat`'ing two points `s1` and `s2`.\n\nSpecifically, this is:\n```julia\ns1[1] * s2[2] - s1[2] * s2[1]\n```\n\"\"\"\nfunction _det(s1::_VecTypes{2, T1}, s2::_VecTypes{2, T2}) where {T1 <: Real, T2 <: Real}\n return s1[1] * s2[2] - s1[2] * s2[1]\nend\n\n\"\"\"\n t_value(sᵢ, sᵢ₊₁, rᵢ, rᵢ₊₁)\n\nReturns the \"T-value\" as described in Hormann's presentation [^HormannPresentation] on how to calculate\nthe mean-value coordinate.\n\nHere, `sᵢ` is the vector from vertex `vᵢ` to the point, and `rᵢ` is the norm (length) of `sᵢ`.\n`s` must be `Point` and `r` must be real numbers.\n\n```math\ntᵢ = \\\\frac{\\\\mathrm{det}\\\\left(sᵢ, sᵢ₊₁\\\\right)}{rᵢ * rᵢ₊₁ + sᵢ ⋅ sᵢ₊₁}\n```\n\n[^HormannPresentation]: K. Hormann and N. Sukumar. Generalized Barycentric Coordinates in Computer Graphics and Computational Mechanics. Taylor & Fancis, CRC Press, 2017.\n```\n\n\"\"\"\nfunction t_value(sᵢ::_VecTypes{N, T1}, sᵢ₊₁::_VecTypes{N, T1}, rᵢ::T2, rᵢ₊₁::T2) where {N, T1 <: Real, T2 <: Real}\n return _det(sᵢ, sᵢ₊₁) / muladd(rᵢ, rᵢ₊₁, dot(sᵢ, sᵢ₊₁))\nend\n\n\nfunction barycentric_coordinates!(λs::Vector{<: Real}, ::MeanValue, polypoints::AbstractVector{<: Point{2, T1}}, point::Point{2, T2}) where {T1 <: Real, T2 <: Real}\n @boundscheck @assert length(λs) == length(polypoints)\n @boundscheck @assert length(polypoints) >= 3\n n_points = length(polypoints)\n # Initialize counters and register variables\n # Points - these are actually vectors from point to vertices\n # polypoints[i-1], polypoints[i], polypoints[i+1]\n sᵢ₋₁ = polypoints[end] - point\n sᵢ = polypoints[begin] - point\n sᵢ₊₁ = polypoints[begin+1] - point\n # radius / Euclidean distance between points.\n rᵢ₋₁ = norm(sᵢ₋₁)\n rᵢ = norm(sᵢ )\n rᵢ₊₁ = norm(sᵢ₊₁)\n # Perform the first computation explicitly, so we can cut down on\n # a mod in the loop.\n λs[1] = (t_value(sᵢ₋₁, sᵢ, rᵢ₋₁, rᵢ) + t_value(sᵢ, sᵢ₊₁, rᵢ, rᵢ₊₁)) / rᵢ\n # Loop through the rest of the vertices, compute, store in λs\n for i in 2:n_points\n # Increment counters + set variables\n sᵢ₋₁ = sᵢ\n sᵢ = sᵢ₊₁\n sᵢ₊₁ = polypoints[mod1(i+1, n_points)] - point\n rᵢ₋₁ = rᵢ\n rᵢ = rᵢ₊₁\n rᵢ₊₁ = norm(sᵢ₊₁) # radius / Euclidean distance between points.\n λs[i] = (t_value(sᵢ₋₁, sᵢ, rᵢ₋₁, rᵢ) + t_value(sᵢ, sᵢ₊₁, rᵢ, rᵢ₊₁)) / rᵢ\n end\n # Normalize λs to the 1-norm (sum=1)\n λs ./= sum(λs)\n return λs\nend","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"function barycentric_coordinates(::MeanValue, polypoints::NTuple{N, Point{2, T2}}, point::Point{2, T1},) where {N, T1, T2}\n ## Initialize counters and register variables\n ## Points - these are actually vectors from point to vertices\n ## polypoints[i-1], polypoints[i], polypoints[i+1]\n sᵢ₋₁ = polypoints[end] - point\n sᵢ = polypoints[begin] - point\n sᵢ₊₁ = polypoints[begin+1] - point\n ## radius / Euclidean distance between points.\n rᵢ₋₁ = norm(sᵢ₋₁)\n rᵢ = norm(sᵢ )\n rᵢ₊₁ = norm(sᵢ₊₁)\n λ₁ = (t_value(sᵢ₋₁, sᵢ, rᵢ₋₁, rᵢ) + t_value(sᵢ, sᵢ₊₁, rᵢ, rᵢ₊₁)) / rᵢ\n λs = ntuple(N) do i\n if i == 1\n return λ₁\n end\n ## Increment counters + set variables\n sᵢ₋₁ = sᵢ\n sᵢ = sᵢ₊₁\n sᵢ₊₁ = polypoints[mod1(i+1, N)] - point\n rᵢ₋₁ = rᵢ\n rᵢ = rᵢ₊₁\n rᵢ₊₁ = norm(sᵢ₊₁) # radius / Euclidean distance between points.\n return (t_value(sᵢ₋₁, sᵢ, rᵢ₋₁, rᵢ) + t_value(sᵢ, sᵢ₊₁, rᵢ, rᵢ₊₁)) / rᵢ\n end\n\n ∑λ = sum(λs)\n\n return ntuple(N) do i\n λs[i] / ∑λ\n end\nend","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"This performs an inplace accumulation, using less memory and is faster. That's particularly good if you are using a polygon with a large number of points...","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"function barycentric_interpolate(::MeanValue, polypoints::AbstractVector{<: Point{2, T1}}, values::AbstractVector{V}, point::Point{2, T2}) where {T1 <: Real, T2 <: Real, V}\n @boundscheck @assert length(values) == length(polypoints)\n @boundscheck @assert length(polypoints) >= 3\n\n n_points = length(polypoints)\n # Initialize counters and register variables\n # Points - these are actually vectors from point to vertices\n # polypoints[i-1], polypoints[i], polypoints[i+1]\n sᵢ₋₁ = polypoints[end] - point\n sᵢ = polypoints[begin] - point\n sᵢ₊₁ = polypoints[begin+1] - point\n # radius / Euclidean distance between points.\n rᵢ₋₁ = norm(sᵢ₋₁)\n rᵢ = norm(sᵢ )\n rᵢ₊₁ = norm(sᵢ₊₁)\n # Now, we set the interpolated value to the first point's value, multiplied\n # by the weight computed relative to the first point in the polygon.\n wᵢ = (t_value(sᵢ₋₁, sᵢ, rᵢ₋₁, rᵢ) + t_value(sᵢ, sᵢ₊₁, rᵢ, rᵢ₊₁)) / rᵢ\n wₜₒₜ = wᵢ\n interpolated_value = values[begin] * wᵢ\n for i in 2:n_points\n # Increment counters + set variables\n sᵢ₋₁ = sᵢ\n sᵢ = sᵢ₊₁\n sᵢ₊₁ = polypoints[mod1(i+1, n_points)] - point\n rᵢ₋₁ = rᵢ\n rᵢ = rᵢ₊₁\n rᵢ₊₁ = norm(sᵢ₊₁)\n # Now, we calculate the weight:\n wᵢ = (t_value(sᵢ₋₁, sᵢ, rᵢ₋₁, rᵢ) + t_value(sᵢ, sᵢ₊₁, rᵢ, rᵢ₊₁)) / rᵢ\n # perform a weighted sum with the interpolated value:\n interpolated_value += values[i] * wᵢ\n # and add the weight to the total weight accumulator.\n wₜₒₜ += wᵢ\n end\n # Return the normalized interpolated value.\n return interpolated_value / wₜₒₜ\nend","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"When you have holes, then you have to be careful about the order you iterate around points.","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"Specifically, you have to iterate around each linear ring separately and ensure there are no degenerate/repeated points at the start and end!","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"function barycentric_interpolate(::MeanValue, exterior::AbstractVector{<: Point{N, T1}}, interiors::AbstractVector{<: AbstractVector{<: Point{N, T1}}}, values::AbstractVector{V}, point::Point{N, T2}) where {N, T1 <: Real, T2 <: Real, V}\n # @boundscheck @assert length(values) == (length(exterior) + isempty(interiors) ? 0 : sum(length.(interiors)))\n # @boundscheck @assert length(exterior) >= 3\n\n current_index = 1\n l_exterior = length(exterior)\n\n sᵢ₋₁ = exterior[end] - point\n sᵢ = exterior[begin] - point\n sᵢ₊₁ = exterior[begin+1] - point\n rᵢ₋₁ = norm(sᵢ₋₁) # radius / Euclidean distance between points.\n rᵢ = norm(sᵢ ) # radius / Euclidean distance between points.\n rᵢ₊₁ = norm(sᵢ₊₁) # radius / Euclidean distance between points.","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"Now, we set the interpolated value to the first point's value, multiplied by the weight computed relative to the first point in the polygon.","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":" wᵢ = (t_value(sᵢ₋₁, sᵢ, rᵢ₋₁, rᵢ) + t_value(sᵢ, sᵢ₊₁, rᵢ, rᵢ₊₁)) / rᵢ\n wₜₒₜ = wᵢ\n interpolated_value = values[begin] * wᵢ\n\n for i in 2:l_exterior","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"Increment counters + set variables","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":" sᵢ₋₁ = sᵢ\n sᵢ = sᵢ₊₁\n sᵢ₊₁ = exterior[mod1(i+1, l_exterior)] - point\n rᵢ₋₁ = rᵢ\n rᵢ = rᵢ₊₁\n rᵢ₊₁ = norm(sᵢ₊₁) # radius / Euclidean distance between points.\n wᵢ = (t_value(sᵢ₋₁, sᵢ, rᵢ₋₁, rᵢ) + t_value(sᵢ, sᵢ₊₁, rᵢ, rᵢ₊₁)) / rᵢ","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"Updates - first the interpolated value,","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":" interpolated_value += values[current_index] * wᵢ","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"then the accumulators for total weight and current index.","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":" wₜₒₜ += wᵢ\n current_index += 1\n\n end\n for hole in interiors\n l_hole = length(hole)\n sᵢ₋₁ = hole[end] - point\n sᵢ = hole[begin] - point\n sᵢ₊₁ = hole[begin+1] - point\n rᵢ₋₁ = norm(sᵢ₋₁) # radius / Euclidean distance between points.\n rᵢ = norm(sᵢ ) # radius / Euclidean distance between points.\n rᵢ₊₁ = norm(sᵢ₊₁) # radius / Euclidean distance between points.\n # Now, we set the interpolated value to the first point's value, multiplied\n # by the weight computed relative to the first point in the polygon.\n wᵢ = (t_value(sᵢ₋₁, sᵢ, rᵢ₋₁, rᵢ) + t_value(sᵢ, sᵢ₊₁, rᵢ, rᵢ₊₁)) / rᵢ\n\n interpolated_value += values[current_index] * wᵢ\n\n wₜₒₜ += wᵢ\n current_index += 1\n\n for i in 2:l_hole\n # Increment counters + set variables\n sᵢ₋₁ = sᵢ\n sᵢ = sᵢ₊₁\n sᵢ₊₁ = hole[mod1(i+1, l_hole)] - point\n rᵢ₋₁ = rᵢ\n rᵢ = rᵢ₊₁\n rᵢ₊₁ = norm(sᵢ₊₁) ## radius / Euclidean distance between points.\n wᵢ = (t_value(sᵢ₋₁, sᵢ, rᵢ₋₁, rᵢ) + t_value(sᵢ, sᵢ₊₁, rᵢ, rᵢ₊₁)) / rᵢ\n interpolated_value += values[current_index] * wᵢ\n wₜₒₜ += wᵢ\n current_index += 1\n end\n end\n return interpolated_value / wₜₒₜ\n\nend\n\nstruct Wachspress <: AbstractBarycentricCoordinateMethod\nend","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"","category":"page"},{"location":"source/methods/barycentric/","page":"Barycentric coordinates","title":"Barycentric coordinates","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/methods/geom_relations/disjoint/#Disjoint","page":"Disjoint","title":"Disjoint","text":"","category":"section"},{"location":"source/methods/geom_relations/disjoint/","page":"Disjoint","title":"Disjoint","text":"export disjoint","category":"page"},{"location":"source/methods/geom_relations/disjoint/#What-is-disjoint?","page":"Disjoint","title":"What is disjoint?","text":"","category":"section"},{"location":"source/methods/geom_relations/disjoint/","page":"Disjoint","title":"Disjoint","text":"The disjoint function checks if one geometry is outside of another geometry, without sharing any boundaries or interiors.","category":"page"},{"location":"source/methods/geom_relations/disjoint/","page":"Disjoint","title":"Disjoint","text":"To provide an example, consider these two lines:","category":"page"},{"location":"source/methods/geom_relations/disjoint/","page":"Disjoint","title":"Disjoint","text":"using GeometryOps\nusing GeometryOps.GeometryBasics\nusing Makie\nusing CairoMakie\n\nl1 = GI.LineString([(0.0, 0.0), (1.0, 0.0), (0.0, 0.1)])\nl2 = GI.LineString([(2.0, 0.0), (2.75, 0.0)])\nf, a, p = lines(GI.getpoint(l1), color = :blue)\nscatter!(GI.getpoint(l1), color = :blue)\nlines!(GI.getpoint(l2), color = :orange)\nscatter!(GI.getpoint(l2), color = :orange)","category":"page"},{"location":"source/methods/geom_relations/disjoint/","page":"Disjoint","title":"Disjoint","text":"We can see that none of the edges or vertices of l1 interact with l2 so they are disjoint.","category":"page"},{"location":"source/methods/geom_relations/disjoint/","page":"Disjoint","title":"Disjoint","text":"disjoint(l1, l2) # returns true","category":"page"},{"location":"source/methods/geom_relations/disjoint/#Implementation","page":"Disjoint","title":"Implementation","text":"","category":"section"},{"location":"source/methods/geom_relations/disjoint/","page":"Disjoint","title":"Disjoint","text":"This is the GeoInterface-compatible implementation.","category":"page"},{"location":"source/methods/geom_relations/disjoint/","page":"Disjoint","title":"Disjoint","text":"First, we implement a wrapper method that dispatches to the correct implementation based on the geometry trait.","category":"page"},{"location":"source/methods/geom_relations/disjoint/","page":"Disjoint","title":"Disjoint","text":"Each of these calls a method in the geomgeomprocessors file. The methods in this file determine if the given geometries meet a set of criteria. For the disjoint function and arguments g1 and g2, this criteria is as follows: - points of g1 are not allowed to be in the interior of g2 - points of g1 are not allowed to be on the boundary of g2 - points of g1 are allowed to be in the exterior of g2 - no points required to be in the interior of g2 - no points of g1 are required to be on the boundary of g2 - no points of g1 are required to be in the exterior of g2","category":"page"},{"location":"source/methods/geom_relations/disjoint/","page":"Disjoint","title":"Disjoint","text":"The code for the specific implementations is in the geomgeomprocessors file.","category":"page"},{"location":"source/methods/geom_relations/disjoint/","page":"Disjoint","title":"Disjoint","text":"const DISJOINT_ALLOWS = (in_allow = false, on_allow = false, out_allow = true)\nconst DISJOINT_CURVE_ALLOWS = (over_allow = false, cross_allow = false, on_allow = false, out_allow = true)\nconst DISJOINT_REQUIRES = (in_require = false, on_require = false, out_require = false)\n\"\"\"\n disjoint(geom1, geom2)::Bool\n\nReturn `true` if the first geometry is disjoint from the second geometry.\n\nReturn `true` if the first geometry is disjoint from the second geometry. The\ninteriors and boundaries of both geometries must not intersect.\n\n# Examples\n```jldoctest setup=:(using GeometryOps, GeometryBasics)\nimport GeometryOps as GO, GeoInterface as GI\n\nline = GI.LineString([(1, 1), (1, 2), (1, 3), (1, 4)])\npoint = (2, 2)\nGO.disjoint(point, line)","category":"page"},{"location":"source/methods/geom_relations/disjoint/","page":"Disjoint","title":"Disjoint","text":"output","category":"page"},{"location":"source/methods/geom_relations/disjoint/","page":"Disjoint","title":"Disjoint","text":"true\n```\n\"\"\"\ndisjoint(g1, g2) = _disjoint(trait(g1), g1, trait(g2), g2)","category":"page"},{"location":"source/methods/geom_relations/disjoint/#Convert-features-to-geometries","page":"Disjoint","title":"Convert features to geometries","text":"","category":"section"},{"location":"source/methods/geom_relations/disjoint/","page":"Disjoint","title":"Disjoint","text":"_disjoint(::FeatureTrait, g1, ::Any, g2) = disjoint(GI.geometry(g1), g2)\n_disjoint(::Any, g1, ::FeatureTrait, g2) = disjoint(g1, geometry(g2))\n_disjoint(::FeatureTrait, g1, ::FeatureTrait, g2) = disjoint(GI.geometry(g1), GI.geometry(g2))","category":"page"},{"location":"source/methods/geom_relations/disjoint/#Point-disjoint-geometries","page":"Disjoint","title":"Point disjoint geometries","text":"","category":"section"},{"location":"source/methods/geom_relations/disjoint/","page":"Disjoint","title":"Disjoint","text":"Point is disjoint from another point if the points are not equal.","category":"page"},{"location":"source/methods/geom_relations/disjoint/","page":"Disjoint","title":"Disjoint","text":"_disjoint(\n ::GI.PointTrait, g1,\n ::GI.PointTrait, g2,\n) = !equals(g1, g2)","category":"page"},{"location":"source/methods/geom_relations/disjoint/","page":"Disjoint","title":"Disjoint","text":"Point is disjoint from a linestring if it is not on the line's edges/vertices.","category":"page"},{"location":"source/methods/geom_relations/disjoint/","page":"Disjoint","title":"Disjoint","text":"_disjoint(\n ::GI.PointTrait, g1,\n ::Union{GI.LineTrait, GI.LineStringTrait}, g2,\n) = _point_curve_process(\n g1, g2;\n DISJOINT_ALLOWS...,\n closed_curve = false,\n)","category":"page"},{"location":"source/methods/geom_relations/disjoint/","page":"Disjoint","title":"Disjoint","text":"Point is disjoint from a linearring if it is not on the ring's edges/vertices.","category":"page"},{"location":"source/methods/geom_relations/disjoint/","page":"Disjoint","title":"Disjoint","text":"_disjoint(\n ::GI.PointTrait, g1,\n ::GI.LinearRingTrait, g2,\n) = _point_curve_process(\n g1, g2;\n DISJOINT_ALLOWS...,\n closed_curve = true,\n)\n\n#= Point is disjoint from a polygon if it is not on any edges, vertices, or\nwithin the polygon's interior. =#\n_disjoint(\n ::GI.PointTrait, g1,\n ::GI.PolygonTrait, g2,\n) = _point_polygon_process(\n g1, g2;\n DISJOINT_ALLOWS...,\n)\n\n#= Geometry is disjoint from a point if the point is not in the interior or on\nthe boundary of the geometry. =#\n_disjoint(\n trait1::Union{GI.AbstractCurveTrait, GI.PolygonTrait}, g1,\n trait2::GI.PointTrait, g2,\n) = _disjoint(trait2, g2, trait1, g1)","category":"page"},{"location":"source/methods/geom_relations/disjoint/#Lines-disjoint-geometries","page":"Disjoint","title":"Lines disjoint geometries","text":"","category":"section"},{"location":"source/methods/geom_relations/disjoint/","page":"Disjoint","title":"Disjoint","text":"#= Linestring is disjoint from another line if they do not share any interior\nedge/vertex points or boundary points. =#\n_disjoint(\n ::Union{GI.LineTrait, GI.LineStringTrait}, g1,\n ::Union{GI.LineTrait, GI.LineStringTrait}, g2,\n) = _line_curve_process(\n g1, g2;\n DISJOINT_CURVE_ALLOWS...,\n DISJOINT_REQUIRES...,\n closed_line = false,\n closed_curve = false,\n)\n\n#= Linestring is disjoint from a linearring if they do not share any interior\nedge/vertex points or boundary points. =#\n_disjoint(\n ::Union{GI.LineTrait, GI.LineStringTrait}, g1,\n ::GI.LinearRingTrait, g2,\n) = _line_curve_process(\n g1, g2;\n DISJOINT_CURVE_ALLOWS...,\n DISJOINT_REQUIRES...,\n closed_line = false,\n closed_curve = true,\n)\n\n#= Linestring is disjoint from a polygon if the interior and boundary points of\nthe line are not in the polygon's interior or on the polygon's boundary. =#\n_disjoint(\n ::Union{GI.LineTrait, GI.LineStringTrait}, g1,\n ::GI.PolygonTrait, g2,\n) = _line_polygon_process(\n g1, g2;\n DISJOINT_ALLOWS...,\n DISJOINT_REQUIRES...,\n closed_line = false,\n)\n\n#= Geometry is disjoint from a linestring if the line's interior and boundary\npoints don't intersect with the geometrie's interior and boundary points. =#\n_disjoint(\n trait1::Union{GI.LinearRingTrait, GI.PolygonTrait}, g1,\n trait2::Union{GI.LineTrait, GI.LineStringTrait}, g2,\n) = _disjoint(trait2, g2, trait1, g1)","category":"page"},{"location":"source/methods/geom_relations/disjoint/#Rings-disjoint-geometries","page":"Disjoint","title":"Rings disjoint geometries","text":"","category":"section"},{"location":"source/methods/geom_relations/disjoint/","page":"Disjoint","title":"Disjoint","text":"#= Linearrings is disjoint from another linearring if they do not share any\ninterior edge/vertex points or boundary points.=#\n_disjoint(\n ::GI.LinearRingTrait, g1,\n ::GI.LinearRingTrait, g2,\n) = _line_curve_process(\n g1, g2;\n DISJOINT_CURVE_ALLOWS...,\n DISJOINT_REQUIRES...,\n closed_line = true,\n closed_curve = true,\n)\n\n#= Linearring is disjoint from a polygon if the interior and boundary points of\nthe ring are not in the polygon's interior or on the polygon's boundary. =#\n_disjoint(\n ::GI.LinearRingTrait, g1,\n ::GI.PolygonTrait, g2,\n) = _line_polygon_process(\n g1, g2;\n DISJOINT_ALLOWS...,\n DISJOINT_REQUIRES...,\n closed_line = true,\n)","category":"page"},{"location":"source/methods/geom_relations/disjoint/#Polygon-disjoint-geometries","page":"Disjoint","title":"Polygon disjoint geometries","text":"","category":"section"},{"location":"source/methods/geom_relations/disjoint/","page":"Disjoint","title":"Disjoint","text":"#= Polygon is disjoint from another polygon if they do not share any edges or\nvertices and if their interiors do not intersect, excluding any holes. =#\n_disjoint(\n ::GI.PolygonTrait, g1,\n ::GI.PolygonTrait, g2,\n) = _polygon_polygon_process(\n g1, g2;\n DISJOINT_ALLOWS...,\n DISJOINT_REQUIRES...,\n)","category":"page"},{"location":"source/methods/geom_relations/disjoint/#Geometries-disjoint-multi-geometry/geometry-collections","page":"Disjoint","title":"Geometries disjoint multi-geometry/geometry collections","text":"","category":"section"},{"location":"source/methods/geom_relations/disjoint/","page":"Disjoint","title":"Disjoint","text":"#= Geometry is disjoint from a multi-geometry or a collection if all of the\nelements of the collection are disjoint from the geometry. =#\nfunction _disjoint(\n ::Union{GI.PointTrait, GI.AbstractCurveTrait, GI.PolygonTrait}, g1,\n ::Union{\n GI.MultiPointTrait, GI.AbstractMultiCurveTrait,\n GI.MultiPolygonTrait, GI.GeometryCollectionTrait,\n }, g2,\n)\n for sub_g2 in GI.getgeom(g2)\n !disjoint(g1, sub_g2) && return false\n end\n return true\nend","category":"page"},{"location":"source/methods/geom_relations/disjoint/#Multi-geometry/geometry-collections-coveredby-geometries","page":"Disjoint","title":"Multi-geometry/geometry collections coveredby geometries","text":"","category":"section"},{"location":"source/methods/geom_relations/disjoint/","page":"Disjoint","title":"Disjoint","text":"#= Multi-geometry or a geometry collection is covered by a geometry if all\nelements of the collection are covered by the geometry. =#\nfunction _disjoint(\n ::Union{\n GI.MultiPointTrait, GI.AbstractMultiCurveTrait,\n GI.MultiPolygonTrait, GI.GeometryCollectionTrait,\n }, g1,\n ::GI.AbstractGeometryTrait, g2,\n)\n for sub_g1 in GI.getgeom(g1)\n !disjoint(sub_g1, g2) && return false\n end\n return true\nend","category":"page"},{"location":"source/methods/geom_relations/disjoint/","page":"Disjoint","title":"Disjoint","text":"","category":"page"},{"location":"source/methods/geom_relations/disjoint/","page":"Disjoint","title":"Disjoint","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/methods/geom_relations/overlaps/#Overlaps","page":"Overlaps","title":"Overlaps","text":"","category":"section"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":"export overlaps","category":"page"},{"location":"source/methods/geom_relations/overlaps/#What-is-overlaps?","page":"Overlaps","title":"What is overlaps?","text":"","category":"section"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":"The overlaps function checks if two geometries overlap. Two geometries can only overlap if they have the same dimension, and if they overlap, but one is not contained, within, or equal to the other.","category":"page"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":"Note that this means it is impossible for a single point to overlap with a single point and a line only overlaps with another line if only a section of each line is colinear.","category":"page"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":"To provide an example, consider these two lines:","category":"page"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":"using GeometryOps\nusing GeometryOps.GeometryBasics\nusing Makie\nusing CairoMakie\n\nl1 = GI.LineString([(0.0, 0.0), (0.0, 10.0)])\nl2 = GI.LineString([(0.0, -10.0), (0.0, 3.0)])\nf, a, p = lines(GI.getpoint(l1), color = :blue)\nscatter!(GI.getpoint(l1), color = :blue)\nlines!(GI.getpoint(l2), color = :orange)\nscatter!(GI.getpoint(l2), color = :orange)","category":"page"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":"We can see that the two lines overlap in the plot:","category":"page"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":"overlap(l1, l2)","category":"page"},{"location":"source/methods/geom_relations/overlaps/#Implementation","page":"Overlaps","title":"Implementation","text":"","category":"section"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":"This is the GeoInterface-compatible implementation.","category":"page"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":"First, we implement a wrapper method that dispatches to the correct implementation based on the geometry trait. This is also used in the implementation, since it's a lot less work!","category":"page"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":"Note that that since only elements of the same dimension can overlap, any two geometries with traits that are of different dimensions autmoatically can return false.","category":"page"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":"For geometries with the same trait dimension, we must make sure that they share a point, an edge, or area for points, lines, and polygons/multipolygons respectivly, without being contained.","category":"page"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":"\"\"\"\n overlaps(geom1, geom2)::Bool\n\nCompare two Geometries of the same dimension and return true if their\nintersection set results in a geometry different from both but of the same\ndimension. This means one geometry cannot be within or contain the other and\nthey cannot be equal\n\n# Examples\n```jldoctest\nimport GeometryOps as GO, GeoInterface as GI\npoly1 = GI.Polygon([[(0,0), (0,5), (5,5), (5,0), (0,0)]])\npoly2 = GI.Polygon([[(1,1), (1,6), (6,6), (6,1), (1,1)]])\n\nGO.overlaps(poly1, poly2)","category":"page"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":"output","category":"page"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":"true\n```\n\"\"\"\noverlaps(geom1, geom2)::Bool = overlaps(\n GI.trait(geom1),\n geom1,\n GI.trait(geom2),\n geom2,\n)\n\n\"\"\"\n overlaps(::GI.AbstractTrait, geom1, ::GI.AbstractTrait, geom2)::Bool\n\nFor any non-specified pair, all have non-matching dimensions, return false.\n\"\"\"\noverlaps(::GI.AbstractTrait, geom1, ::GI.AbstractTrait, geom2) = false\n\n\"\"\"\n overlaps(\n ::GI.MultiPointTrait, points1,\n ::GI.MultiPointTrait, points2,\n )::Bool\n\nIf the multipoints overlap, meaning some, but not all, of the points within the\nmultipoints are shared, return true.\n\"\"\"\nfunction overlaps(\n ::GI.MultiPointTrait, points1,\n ::GI.MultiPointTrait, points2,\n)\n one_diff = false # assume that all the points are the same\n one_same = false # assume that all points are different\n for p1 in GI.getpoint(points1)\n match_point = false\n for p2 in GI.getpoint(points2)\n if equals(p1, p2) # Point is shared\n one_same = true\n match_point = true\n break\n end\n end\n one_diff |= !match_point # Point isn't shared\n one_same && one_diff && return true\n end\n return false\nend\n\n\"\"\"\n overlaps(::GI.LineTrait, line1, ::GI.LineTrait, line)::Bool\n\nIf the lines overlap, meaning that they are colinear but each have one endpoint\noutside of the other line, return true. Else false.\n\"\"\"\noverlaps(::GI.LineTrait, line1, ::GI.LineTrait, line) =\n _overlaps((a1, a2), (b1, b2))\n\n\"\"\"\n overlaps(\n ::Union{GI.LineStringTrait, GI.LinearRing}, line1,\n ::Union{GI.LineStringTrait, GI.LinearRing}, line2,\n )::Bool\n\nIf the curves overlap, meaning that at least one edge of each curve overlaps,\nreturn true. Else false.\n\"\"\"\nfunction overlaps(\n ::Union{GI.LineStringTrait, GI.LinearRing}, line1,\n ::Union{GI.LineStringTrait, GI.LinearRing}, line2,\n)\n edges_a, edges_b = map(sort! ∘ to_edges, (line1, line2))\n for edge_a in edges_a\n for edge_b in edges_b\n _overlaps(edge_a, edge_b) && return true\n end\n end\n return false\nend\n\n\"\"\"\n overlaps(\n trait_a::GI.PolygonTrait, poly_a,\n trait_b::GI.PolygonTrait, poly_b,\n )::Bool\n\nIf the two polygons intersect with one another, but are not equal, return true.\nElse false.\n\"\"\"\nfunction overlaps(\n trait_a::GI.PolygonTrait, poly_a,\n trait_b::GI.PolygonTrait, poly_b,\n)\n edges_a, edges_b = map(sort! ∘ to_edges, (poly_a, poly_b))\n return _edge_intersects(edges_a, edges_b) &&\n !equals(trait_a, poly_a, trait_b, poly_b)\nend\n\n\"\"\"\n overlaps(\n ::GI.PolygonTrait, poly1,\n ::GI.MultiPolygonTrait, polys2,\n )::Bool\n\nReturn true if polygon overlaps with at least one of the polygons within the\nmultipolygon. Else false.\n\"\"\"\nfunction overlaps(\n ::GI.PolygonTrait, poly1,\n ::GI.MultiPolygonTrait, polys2,\n)\n for poly2 in GI.getgeom(polys2)\n overlaps(poly1, poly2) && return true\n end\n return false\nend\n\n\"\"\"\n overlaps(\n ::GI.MultiPolygonTrait, polys1,\n ::GI.PolygonTrait, poly2,\n )::Bool\n\nReturn true if polygon overlaps with at least one of the polygons within the\nmultipolygon. Else false.\n\"\"\"\noverlaps(trait1::GI.MultiPolygonTrait, polys1, trait2::GI.PolygonTrait, poly2) =\n overlaps(trait2, poly2, trait1, polys1)\n\n\"\"\"\n overlaps(\n ::GI.MultiPolygonTrait, polys1,\n ::GI.MultiPolygonTrait, polys2,\n )::Bool\n\nReturn true if at least one pair of polygons from multipolygons overlap. Else\nfalse.\n\"\"\"\nfunction overlaps(\n ::GI.MultiPolygonTrait, polys1,\n ::GI.MultiPolygonTrait, polys2,\n)\n for poly1 in GI.getgeom(polys1)\n overlaps(poly1, polys2) && return true\n end\n return false\nend\n\n#= If the edges overlap, meaning that they are colinear but each have one endpoint\noutside of the other edge, return true. Else false. =#\nfunction _overlaps(\n (a1, a2)::Edge,\n (b1, b2)::Edge\n)","category":"page"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":"meets in more than one point","category":"page"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":" on_top = ExactPredicates.meet(a1, a2, b1, b2) == 0","category":"page"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":"one end point is outside of other segment","category":"page"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":" a_fully_within = _point_on_seg(a1, b1, b2) && _point_on_seg(a2, b1, b2)\n b_fully_within = _point_on_seg(b1, a1, a2) && _point_on_seg(b2, a1, a2)\n return on_top && (!a_fully_within && !b_fully_within)\nend\n\n#= TODO: Once overlaps is swapped over to use the geom relations workflow, can\ndelete these helpers. =#","category":"page"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":"Checks it vectors of edges intersect","category":"page"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":"function _edge_intersects(\n edges_a::Vector{Edge},\n edges_b::Vector{Edge}\n)","category":"page"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":"Extents.intersects(toextent(edgesa), toextent(edgesb)) || return false","category":"page"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":" for edge_a in edges_a\n for edge_b in edges_b\n _edge_intersects(edge_a, edge_b) && return true\n end\n end\n return false\nend","category":"page"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":"Checks if two edges intersect","category":"page"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":"function _edge_intersects(edge_a::Edge, edge_b::Edge)\n meet_type = ExactPredicates.meet(edge_a..., edge_b...)\n return meet_type == 0 || meet_type == 1\nend","category":"page"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":"Checks if point is on a segment","category":"page"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":"function _point_on_seg(point, start, stop)","category":"page"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":"Parse out points","category":"page"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":" x, y = GI.x(point), GI.y(point)\n x1, y1 = GI.x(start), GI.y(start)\n x2, y2 = GI.x(stop), GI.y(stop)\n Δxl = x2 - x1\n Δyl = y2 - y1","category":"page"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":"Determine if point is on segment","category":"page"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":" cross = (x - x1) * Δyl - (y - y1) * Δxl\n if cross == 0 # point is on line extending to infinity","category":"page"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":"is line between endpoints","category":"page"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":" if abs(Δxl) >= abs(Δyl) # is line between endpoints\n return Δxl > 0 ? x1 <= x <= x2 : x2 <= x <= x1\n else\n return Δyl > 0 ? y1 <= y <= y2 : y2 <= y <= y1\n end\n end\n return false\nend","category":"page"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":"","category":"page"},{"location":"source/methods/geom_relations/overlaps/","page":"Overlaps","title":"Overlaps","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/methods/centroid/#Centroid","page":"Centroid","title":"Centroid","text":"","category":"section"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"export centroid, centroid_and_length, centroid_and_area","category":"page"},{"location":"source/methods/centroid/#What-is-the-centroid?","page":"Centroid","title":"What is the centroid?","text":"","category":"section"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"The centroid is the geometric center of a line string or area(s). Note that the centroid does not need to be inside of a concave area.","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"Further note that by convention a line, or linear ring, is calculated by weighting the line segments by their length, while polygons and multipolygon centroids are calculated by weighting edge's by their 'area components'.","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"To provide an example, consider this concave polygon in the shape of a 'C':","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"using GeometryOps\nusing GeometryOps.GeometryBasics\nusing Makie\nusing CairoMakie\n\ncshape = Polygon([\n Point(0,0), Point(0,3), Point(3,3), Point(3,2), Point(1,2),\n Point(1,1), Point(3,1), Point(3,0), Point(0,0),\n])\nf, a, p = poly(cshape; axis = (; aspect = DataAspect()))","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"Let's see what the centroid looks like (plotted in red):","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"cent = centroid(cshape)\nscatter!(a, GI.x(cent), GI.y(cent), color = :red)\nf","category":"page"},{"location":"source/methods/centroid/#Implementation","page":"Centroid","title":"Implementation","text":"","category":"section"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"This is the GeoInterface-compatible implementation.","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"First, we implement a wrapper method that dispatches to the correct implementation based on the geometry trait. This is also used in the implementation, since it's a lot less work!","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"Note that if you call centroid on a LineString or LinearRing, the centroidandlength function will be called due to the weighting scheme described above, while centroidandarea is called for polygons and multipolygons. However, centroidandarea can still be called on a LineString or LinearRing when they are closed, for example as the interior hole of a polygon.","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"The helper functions centroidandlength and centroidandarea are made availible just in case the user also needs the area or length to decrease repeat computation.","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"\"\"\"\n centroid(geom)::Tuple{T, T}\n\nReturns the centroid of a given line segment, linear ring, polygon, or\nmutlipolygon.\n\"\"\"\ncentroid(geom) = centroid(GI.trait(geom), geom)\n\n\"\"\"\n centroid(\n trait::Union{GI.LineStringTrait, GI.LinearRingTrait},\n geom,\n )::Tuple{T, T}\n\nReturns the centroid of a line string or linear ring, which is calculated by\nweighting line segments by their length by convention.\n\"\"\"\ncentroid(\n trait::Union{GI.LineStringTrait, GI.LinearRingTrait},\n geom,\n) = centroid_and_length(trait, geom)[1]\n\n\"\"\"\n centroid(trait, geom)::Tuple{T, T}\n\nReturns the centroid of a polygon or multipolygon, which is calculated by\nweighting edges by their `area component` by convention.\n\"\"\"\ncentroid(trait, geom) = centroid_and_area(trait, geom)[1]\n\n\"\"\"\n centroid_and_length(geom)::(::Tuple{T, T}, ::Real)\n\nReturns the centroid and length of a given line/ring. Note this is only valid\nfor line strings and linear rings.\n\"\"\"\ncentroid_and_length(geom) = centroid_and_length(GI.trait(geom), geom)\n\n\"\"\"\n centroid_and_area(\n ::Union{GI.LineStringTrait, GI.LinearRingTrait},\n geom,\n )::(::Tuple{T, T}, ::Real)\n\nReturns the centroid and area of a given geom.\n\"\"\"\ncentroid_and_area(geom) = centroid_and_area(GI.trait(geom), geom)\n\n\"\"\"\n centroid_and_length(geom)::(::Tuple{T, T}, ::Real)\n\nReturns the centroid and length of a given line/ring. Note this is only valid\nfor line strings and linear rings.\n\"\"\"\nfunction centroid_and_length(\n ::Union{GI.LineStringTrait, GI.LinearRingTrait},\n geom,\n)\n T = typeof(GI.x(GI.getpoint(geom, 1)))","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"Initialize starting values","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":" xcentroid = T(0)\n ycentroid = T(0)\n length = T(0)\n point₁ = GI.getpoint(geom, 1)","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"Loop over line segments of line string","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":" for point₂ in GI.getpoint(geom)","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"Calculate length of line segment","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":" length_component = sqrt(\n (GI.x(point₂) - GI.x(point₁))^2 +\n (GI.y(point₂) - GI.y(point₁))^2\n )","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"Accumulate the line segment length into length","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":" length += length_component","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"Weighted average of line segment centroids","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":" xcentroid += (GI.x(point₁) + GI.x(point₂)) * (length_component / 2)\n ycentroid += (GI.y(point₁) + GI.y(point₂)) * (length_component / 2)\n #centroid = centroid .+ ((point₁ .+ point₂) .* (length_component / 2))","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"Advance the point buffer by 1 point to move to next line segment","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":" point₁ = point₂\n end\n xcentroid /= length\n ycentroid /= length\n return (xcentroid, ycentroid), length\nend\n\n\"\"\"\n centroid_and_area(\n ::Union{GI.LineStringTrait, GI.LinearRingTrait},\n geom,\n )::(::Tuple{T, T}, ::Real)\n\nReturns the centroid and area of a given a line string or a linear ring.\nNote that this is only valid if the line segment or linear ring is closed.\n\"\"\"\nfunction centroid_and_area(\n ::Union{GI.LineStringTrait, GI.LinearRingTrait},\n geom,\n)\n T = typeof(GI.x(GI.getpoint(geom, 1)))","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"Check that the geometry is closed","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":" @assert(\n GI.getpoint(geom, 1) == GI.getpoint(geom, GI.ngeom(geom)),\n \"centroid_and_area should only be used with closed geometries\"\n )","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"Initialize starting values","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":" xcentroid = T(0)\n ycentroid = T(0)\n area = T(0)\n point₁ = GI.getpoint(geom, 1)","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"Loop over line segments of linear ring","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":" for point₂ in GI.getpoint(geom)\n area_component = GI.x(point₁) * GI.y(point₂) -\n GI.x(point₂) * GI.y(point₁)","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"Accumulate the area component into area","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":" area += area_component","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"Weighted average of centroid components","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":" xcentroid += (GI.x(point₁) + GI.x(point₂)) * area_component\n ycentroid += (GI.y(point₁) + GI.y(point₂)) * area_component","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"Advance the point buffer by 1 point","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":" point₁ = point₂\n end\n area /= 2\n xcentroid /= 6area\n ycentroid /= 6area\n return (xcentroid, ycentroid), abs(area)\nend\n\n\"\"\"\n centroid_and_area(::GI.PolygonTrait, geom)::(::Tuple{T, T}, ::Real)\n\nReturns the centroid and area of a given polygon.\n\"\"\"\nfunction centroid_and_area(::GI.PolygonTrait, geom)","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"Exterior ring's centroid and area","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":" (xcentroid, ycentroid), area = centroid_and_area(GI.getexterior(geom))","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"Weight exterior centroid by area","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":" xcentroid *= area\n ycentroid *= area","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"Loop over any holes within the polygon","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":" for hole in GI.gethole(geom)","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"Hole polygon's centroid and area","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":" (xinterior, yinterior), interior_area = centroid_and_area(hole)","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"Accumulate the area component into area","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":" area -= interior_area","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"Weighted average of centroid components","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":" xcentroid -= xinterior * interior_area\n ycentroid -= yinterior * interior_area\n end\n xcentroid /= area\n ycentroid /= area\n return (xcentroid, ycentroid), area\nend\n\n\"\"\"\n centroid_and_area(::GI.MultiPolygonTrait, geom)::(::Tuple{T, T}, ::Real)\n\nReturns the centroid and area of a given multipolygon.\n\"\"\"\nfunction centroid_and_area(::GI.MultiPolygonTrait, geom)","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"First polygon's centroid and area","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":" (xcentroid, ycentroid), area = centroid_and_area(GI.getpolygon(geom, 1))","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"Weight first polygon's centroid by area","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":" xcentroid *= area\n ycentroid *= area","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"Loop over any polygons within the multipolygon","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":" for i in 2:GI.ngeom(geom)","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"Polygon centroid and area","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":" (xpoly, ypoly), poly_area = centroid_and_area(GI.getpolygon(geom, i))","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"Accumulate the area component into area","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":" area += poly_area","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"Weighted average of centroid components","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":" xcentroid += xpoly * poly_area\n ycentroid += ypoly * poly_area\n end\n xcentroid /= area\n ycentroid /= area\n return (xcentroid, ycentroid), area\nend","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/transformations/extent/","page":"-","title":"-","text":"\"\"\"\n embed_extent(obj)\n\nRecursively wrap the object with a GeoInterface.jl geometry,\ncalculating and adding an `Extents.Extent` to all objects.\n\nThis can improve performance when extents need to be checked multiple times,\nsuch when needing to check if many points are in geometries, and using their extents\nas a quick filter for obviously exterior points.","category":"page"},{"location":"source/transformations/extent/","page":"-","title":"-","text":"Keywords","category":"page"},{"location":"source/transformations/extent/","page":"-","title":"-","text":"$THREADED_KEYWORD\n$CRS_KEYWORD\n\"\"\"\nembed_extent(x; threaded=false, crs=nothing) =\n apply(identity, GI.PointTrait, x; calc_extent=true, threaded, crs)","category":"page"},{"location":"source/transformations/extent/","page":"-","title":"-","text":"","category":"page"},{"location":"source/transformations/extent/","page":"-","title":"-","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/methods/geom_relations/intersects/#Intersection-checks","page":"Intersection checks","title":"Intersection checks","text":"","category":"section"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"export intersects, intersection, intersection_points","category":"page"},{"location":"source/methods/geom_relations/intersects/#What-is-intersects-vs-intersection-vs-intersection_points?","page":"Intersection checks","title":"What is intersects vs intersection vs intersection_points?","text":"","category":"section"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"The intersects function checks if a given geometry intersects with another geometry, or in other words, the either the interiors or boundaries of the two geometries intersect.","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"The intersection methods return the geometry intersection between the two input geometries. The intersection_points method returns a list of intersection points between two geometries.","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"The intersects methods will always return a Boolean. However, note that the intersection methods will not all return the same type. For example, the intersection of two lines will be a point in most cases, unless the lines are parallel. On the other hand, the intersection of two polygons will be another polygon in most cases. Finally, the intersection_points method returns a list of tuple points.","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"To provide an example, consider these two lines:","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"using GeometryOps\nusing GeometryOps.GeometryBasics\nusing Makie\nusing CairoMakie\npoint1, point2 = Point(124.584961,-12.768946), Point(126.738281,-17.224758)\npoint3, point4 = Point(123.354492,-15.961329), Point(127.22168,-14.008696)\nline1 = Line(point1, point2)\nline2 = Line(point3, point4)\nf, a, p = lines([point1, point2])\nlines!([point3, point4])","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"We can see that they intersect, so we expect intersects to return true, and we can visualize the intersection point in red.","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"int_bool = GO.intersects(line1, line2)\nprintln(int_bool)\nint_point = GO.intersection(line1, line2)\nscatter!(int_point, color = :red)\nf","category":"page"},{"location":"source/methods/geom_relations/intersects/#Implementation","page":"Intersection checks","title":"Implementation","text":"","category":"section"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"This is the GeoInterface-compatible implementation.","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"Given that intersects is the exact opposite of disjoint, we simply pass the two inputs variables, swapped in order, to disjoint.","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"\"\"\"\n intersects(geom1, geom2)::Bool\n\nReturn true if the interiors or boundaries of the two geometries interact.\n\n`intersects` returns the exact opposite result of `disjoint`.\n\n# Example\n\n```jldoctest\nimport GeoInterface as GI, GeometryOps as GO\n\nline1 = GI.Line([(124.584961,-12.768946), (126.738281,-17.224758)])\nline2 = GI.Line([(123.354492,-15.961329), (127.22168,-14.008696)])\nGO.intersects(line1, line2)","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"output","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"true\n```\n\"\"\"\nintersects(geom1, geom2) = !disjoint(geom1, geom2)\n\n\n\"\"\"\n intersection(geom_a, geom_b)::Union{Tuple{::Real, ::Real}, ::Nothing}\n\nReturn an intersection point between two geometries. Return nothing if none are\nfound. Else, the return type depends on the input. It will be a union between:\na point, a line, a linear ring, a polygon, or a multipolygon\n\n# Example\n\n```jldoctest\nimport GeoInterface as GI, GeometryOps as GO\n\nline1 = GI.Line([(124.584961,-12.768946), (126.738281,-17.224758)])\nline2 = GI.Line([(123.354492,-15.961329), (127.22168,-14.008696)])\nGO.intersection(line1, line2)","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"output","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"(125.58375366067547, -14.83572303404496)\n```\n\"\"\"\nintersection(geom_a, geom_b) =\n intersection(GI.trait(geom_a), geom_a, GI.trait(geom_b), geom_b)\n\n\"\"\"\n intersection(\n ::GI.LineTrait, line_a,\n ::GI.LineTrait, line_b,\n )::Union{\n ::Tuple{::Real, ::Real},\n ::Nothing\n }\n\nCalculates the intersection between two line segments. Return nothing if\nthere isn't one.\n\"\"\"\nfunction intersection(::GI.LineTrait, line_a, ::GI.LineTrait, line_b)","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"Get start and end points for both lines","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":" a1 = GI.getpoint(line_a, 1)\n a2 = GI.getpoint(line_a, 2)\n b1 = GI.getpoint(line_b, 1)\n b2 = GI.getpoint(line_b, 2)","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"Determine the intersection point","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":" point, fracs = _intersection_point((a1, a2), (b1, b2))","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"Determine if intersection point is on line segments","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":" if !isnothing(point) && 0 <= fracs[1] <= 1 && 0 <= fracs[2] <= 1\n return point\n end\n return nothing\nend\n\nintersection(\n trait_a::Union{GI.LineStringTrait, GI.LinearRingTrait},\n geom_a,\n trait_b::Union{GI.LineStringTrait, GI.LinearRingTrait},\n geom_b,\n) = intersection_points(trait_a, geom_a, trait_b, geom_b)\n\n\"\"\"\n intersection(\n ::GI.PolygonTrait, poly_a,\n ::GI.PolygonTrait, poly_b,\n )::Union{\n ::Vector{Vector{Tuple{::Real, ::Real}}}, # is this a good return type?\n ::Nothing\n }\n\nCalculates the intersection between two line segments. Return nothing if\nthere isn't one.\n\"\"\"\nfunction intersection(::GI.PolygonTrait, poly_a, ::GI.PolygonTrait, poly_b)\n @assert false \"Polygon intersection isn't implemented yet.\"\n return nothing\nend\n\n\"\"\"\n intersection(\n ::GI.AbstractTrait, geom_a,\n ::GI.AbstractTrait, geom_b,\n )::Union{\n ::Vector{Vector{Tuple{::Real, ::Real}}}, # is this a good return type?\n ::Nothing\n }\n\nCalculates the intersection between two line segments. Return nothing if\nthere isn't one.\n\"\"\"\nfunction intersection(\n trait_a::GI.AbstractGeometryTrait, geom_a,\n trait_b::GI.AbstractGeometryTrait, geom_b,\n)\n @assert(\n false,\n \"Intersection between $trait_a and $trait_b isn't implemented yet.\",\n )\n return nothing\nend\n\n\"\"\"\n intersection_points(\n geom_a,\n geom_b,\n )::Union{\n ::Vector{::Tuple{::Real, ::Real}},\n ::Nothing,\n }\n\nReturn a list of intersection points between two geometries. If no intersection\npoint was possible given geometry extents, return nothing. If none are found,\nreturn an empty list.\n\"\"\"\nintersection_points(geom_a, geom_b) =\n intersection_points(GI.trait(geom_a), geom_a, GI.trait(geom_b), geom_b)\n\n\"\"\"\n intersection_points(\n ::GI.AbstractTrait, geom_a,\n ::GI.AbstractTrait, geom_b,\n )::Union{\n ::Vector{::Tuple{::Real, ::Real}},\n ::Nothing,\n }\n\nCalculates the list of intersection points between two geometries, inlcuding\nline segments, line strings, linear rings, polygons, and multipolygons. If no\nintersection points were possible given geometry extents, return nothing. If\nnone are found, return an empty list.\n\"\"\"\nfunction intersection_points(::GI.AbstractTrait, a, ::GI.AbstractTrait, b)","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"Check if the geometries extents even overlap","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":" Extents.intersects(GI.extent(a), GI.extent(b)) || return nothing","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"Create a list of edges from the two input geometries","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":" edges_a, edges_b = map(sort! ∘ to_edges, (a, b))\n npoints_a, npoints_b = length(edges_a), length(edges_b)\n a_closed = npoints_a > 1 && edges_a[1][1] == edges_a[end][1]\n b_closed = npoints_b > 1 && edges_b[1][1] == edges_b[end][1]\n if npoints_a > 0 && npoints_b > 0","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"Initialize an empty list of points","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":" T = typeof(edges_a[1][1][1]) # x-coordinate of first point in first edge\n result = Tuple{T,T}[]","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"Loop over pairs of edges and add any intersection points to results","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":" for i in eachindex(edges_a)\n for j in eachindex(edges_b)\n point, fracs = _intersection_point(edges_a[i], edges_b[j])\n if !isnothing(point)\n #=\n Determine if point is on edge (all edge endpoints excluded\n except for the last edge for an open geometry)\n =#\n α, β = fracs\n on_a_edge = (!a_closed && i == npoints_a && 0 <= α <= 1) ||\n (0 <= α < 1)\n on_b_edge = (!b_closed && j == npoints_b && 0 <= β <= 1) ||\n (0 <= β < 1)\n if on_a_edge && on_b_edge\n push!(result, point)\n end\n end\n end\n end\n return result\n end\n return nothing\nend\n\n\"\"\"\n _intersection_point(\n (a1, a2)::Tuple,\n (b1, b2)::Tuple,\n )\n\nCalculates the intersection point between two lines if it exists, and as if the\nline extended to infinity, and the fractional component of each line from the\ninitial end point to the intersection point.\nInputs:\n (a1, a2)::Tuple{Tuple{::Real, ::Real}, Tuple{::Real, ::Real}} first line\n (b1, b2)::Tuple{Tuple{::Real, ::Real}, Tuple{::Real, ::Real}} second line\nOutputs:\n (x, y)::Tuple{::Real, ::Real} intersection point\n (t, u)::Tuple{::Real, ::Real} fractional length of lines to intersection\n Both are ::Nothing if point doesn't exist!\n\nCalculation derivation can be found here:\n https://stackoverflow.com/questions/563198/\n\"\"\"\nfunction _intersection_point((a1, a2)::Tuple, (b1, b2)::Tuple)","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"First line runs from p to p + r","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":" px, py = GI.x(a1), GI.y(a1)\n rx, ry = GI.x(a2) - px, GI.y(a2) - py","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"Second line runs from q to q + s","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":" qx, qy = GI.x(b1), GI.y(b1)\n sx, sy = GI.x(b2) - qx, GI.y(b2) - qy","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"Intersection will be where p + tr = q + us where 0 < t, u < 1 and","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":" r_cross_s = rx * sy - ry * sx\n if r_cross_s != 0\n Δqp_x = qx - px\n Δqp_y = qy - py\n t = (Δqp_x * sy - Δqp_y * sx) / r_cross_s\n u = (Δqp_x * ry - Δqp_y * rx) / r_cross_s\n x = px + t * rx\n y = py + t * ry\n return (x, y), (t, u)\n end\n return nothing, nothing\nend","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"","category":"page"},{"location":"source/methods/geom_relations/intersects/","page":"Intersection checks","title":"Intersection checks","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/transformations/flip/#Coordinate-flipping","page":"Coordinate flipping","title":"Coordinate flipping","text":"","category":"section"},{"location":"source/transformations/flip/","page":"Coordinate flipping","title":"Coordinate flipping","text":"This is a simple example of how to use the apply functionality in a function, by flipping the x and y coordinates of a geometry.","category":"page"},{"location":"source/transformations/flip/","page":"Coordinate flipping","title":"Coordinate flipping","text":"\"\"\"\n flip(obj)\n\nSwap all of the x and y coordinates in obj, otherwise\nkeeping the original structure (but not necessarily the\noriginal type).\n\n# Keywords\n\n$APPLY_KEYWORDS\n\"\"\"\nfunction flip(geom; kw...)\n if _is3d(geom)\n return apply(PointTrait, geom; kw...) do p\n (GI.y(p), GI.x(p), GI.z(p))\n end\n else\n return apply(PointTrait, geom; kw...) do p\n (GI.y(p), GI.x(p))\n end\n end\nend","category":"page"},{"location":"source/transformations/flip/","page":"Coordinate flipping","title":"Coordinate flipping","text":"","category":"page"},{"location":"source/transformations/flip/","page":"Coordinate flipping","title":"Coordinate flipping","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/transformations/transform/","page":"-","title":"-","text":"\"\"\"\n transform(f, obj)\n\nApply a function `f` to all the points in `obj`.\n\nPoints will be passed to `f` as an `SVector` to allow\nusing CoordinateTransformations.jl and Rotations.jl\nwithout hassle.\n\n`SVector` is also a valid GeoInterface.jl point, so will\nwork in all GeoInterface.jl methods.\n\n# Example\n\n```julia\njulia> import GeoInterface as GI\n\njulia> import GeometryOps as GO\n\njulia> geom = GI.Polygon([GI.LinearRing([(1, 2), (3, 4), (5, 6), (1, 2)]), GI.LinearRing([(3, 4), (5, 6), (6, 7), (3, 4)])]);\n\njulia> f = CoordinateTransformations.Translation(3.5, 1.5)\nTranslation(3.5, 1.5)\n\njulia> GO.transform(f, geom)\nGeoInterface.Wrappers.Polygon{false, false, Vector{GeoInterface.Wrappers.LinearRing{false, false, Vector{StaticArraysCore.SVector{2, Float64}}, Nothing, Nothing}}, Nothing, Nothing}(GeoInterface.Wrappers.Linea\nrRing{false, false, Vector{StaticArraysCore.SVector{2, Float64}}, Nothing, Nothing}[GeoInterface.Wrappers.LinearRing{false, false, Vector{StaticArraysCore.SVector{2, Float64}}, Nothing, Nothing}(StaticArraysCo\nre.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\nrraysCore.SVector{2, Float64}[[6.5, 5.5], [8.5, 7.5], [9.5, 8.5], [6.5, 5.5]], nothing, nothing)], nothing, nothing)\n```\n\nWith Rotations.jl you need to actuall multiply the Rotation\nby the `SVector` point, which is easy using an anonymous function.\n\n```julia\njulia> using Rotations\n\njulia> GO.transform(p -> one(RotMatrix{2}) * p, geom)\nGeoInterface.Wrappers.Polygon{false, false, Vector{GeoInterface.Wrappers.LinearRing{false, false, Vector{StaticArraysCore.SVector{2, Int64}}, Nothing, Nothing}}, Nothing, Nothing}(GeoInterface.Wrappers.LinearR\ning{false, false, Vector{StaticArraysCore.SVector{2, Int64}}, Nothing, Nothing}[GeoInterface.Wrappers.LinearRing{false, false, Vector{StaticArraysCore.SVector{2, Int64}}, Nothing, Nothing}(StaticArraysCore.SVe\nctor{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\n}[[4, 3], [6, 5], [7, 6], [4, 3]], nothing, nothing)], nothing, nothing)\n```\n\"\"\"\nfunction transform(f, geom; kw...)\n if _is3d(geom)\n return apply(PointTrait, geom; kw...) do p\n f(StaticArrays.SVector{3}((GI.x(p), GI.y(p), GI.z(p))))\n end\n else\n return apply(PointTrait, geom; kw...) do p\n f(StaticArrays.SVector{2}((GI.x(p), GI.y(p))))\n end\n end\nend","category":"page"},{"location":"source/transformations/transform/","page":"-","title":"-","text":"","category":"page"},{"location":"source/transformations/transform/","page":"-","title":"-","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/methods/geom_relations/covers/#Covers","page":"Covers","title":"Covers","text":"","category":"section"},{"location":"source/methods/geom_relations/covers/","page":"Covers","title":"Covers","text":"export covers","category":"page"},{"location":"source/methods/geom_relations/covers/#What-is-covers?","page":"Covers","title":"What is covers?","text":"","category":"section"},{"location":"source/methods/geom_relations/covers/","page":"Covers","title":"Covers","text":"The covers function checks if a given geometry completly covers another geometry. For this to be true, the \"contained\" geometry's interior and boundaries must be covered by the \"covering\" geometry's interior and boundaries. The interiors do not need to overlap.","category":"page"},{"location":"source/methods/geom_relations/covers/","page":"Covers","title":"Covers","text":"To provide an example, consider these two lines:","category":"page"},{"location":"source/methods/geom_relations/covers/","page":"Covers","title":"Covers","text":"using GeometryOps\nusing GeometryOps.GeometryBasics\nusing Makie\nusing CairoMakie\n\np1 = Point(0.0, 0.0)\np2 = Point(1.0, 1.0)\nl1 = Line(p1, p2)\n\nf, a, p = lines([p1, p2])\nscatter!(p1, color = :red)","category":"page"},{"location":"source/methods/geom_relations/covers/","page":"Covers","title":"Covers","text":"covers(l1, p1) # returns true\ncovers(p1, l1) # returns false","category":"page"},{"location":"source/methods/geom_relations/covers/#Implementation","page":"Covers","title":"Implementation","text":"","category":"section"},{"location":"source/methods/geom_relations/covers/","page":"Covers","title":"Covers","text":"This is the GeoInterface-compatible implementation.","category":"page"},{"location":"source/methods/geom_relations/covers/","page":"Covers","title":"Covers","text":"Given that covers is the exact opposite of coveredby, we simply pass the two inputs variables, swapped in order, to coveredby.","category":"page"},{"location":"source/methods/geom_relations/covers/","page":"Covers","title":"Covers","text":"\"\"\"\n covers(g1::AbstractGeometry, g2::AbstractGeometry)::Bool\n\nReturn true if the first geometry is completely covers the second geometry,\nThe exterior and boundary of the second geometry must not be outside of the\ninterior and boundary of the first geometry. However, the interiors need not\nintersect.\n\n`covers` returns the exact opposite result of `coveredby`.\n\n# Examples\n\n```jldoctest\nimport GeometryOps as GO, GeoInterface as GI\nl1 = GI.LineString([(1, 1), (1, 2), (1, 3), (1, 4)])\nl2 = GI.LineString([(1, 1), (1, 2)])\n\nGO.covers(l1, l2)","category":"page"},{"location":"source/methods/geom_relations/covers/","page":"Covers","title":"Covers","text":"output","category":"page"},{"location":"source/methods/geom_relations/covers/","page":"Covers","title":"Covers","text":"true\n```\n\"\"\"\ncovers(g1, g2)::Bool = GeometryOps.coveredby(g2, g1)","category":"page"},{"location":"source/methods/geom_relations/covers/","page":"Covers","title":"Covers","text":"","category":"page"},{"location":"source/methods/geom_relations/covers/","page":"Covers","title":"Covers","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/transformations/simplify/#Geometry-simplification","page":"Geometry simplification","title":"Geometry simplification","text":"","category":"section"},{"location":"source/transformations/simplify/","page":"Geometry simplification","title":"Geometry simplification","text":"This file holds implementations for the Douglas-Peucker and Visvalingam-Whyatt algorithms for simplifying geometries (specifically polygons and lines).","category":"page"},{"location":"source/transformations/simplify/","page":"Geometry simplification","title":"Geometry simplification","text":"export simplify, VisvalingamWhyatt, DouglasPeucker, RadialDistance\n\n\n\"\"\"\n abstract type SimplifyAlg\n\nAbstract type for simplification algorithms.\n\n# API\n\nFor now, the algorithm must hold the `number`, `ratio` and `tol` properties.\n\nSimplification algorithm types can hook into the interface by implementing\nthe `_simplify(trait, alg, geom)` methods for whichever traits are necessary.\n\"\"\"\nabstract type SimplifyAlg end\n\nconst SIMPLIFY_ALG_KEYWORDS = \"\"\"\n# Keywords\n\n- `ratio`: the fraction of points that should remain after `simplify`.\n Useful as it will generalise for large collections of objects.\n- `number`: the number of points that should remain after `simplify`.\n Less useful for large collections of mixed size objects.\n\"\"\"\n\nconst MIN_POINTS = 3\n\nfunction checkargs(number, ratio, tol)\n count(isnothing, (number, ratio, tol)) == 2 ||\n error(\"Must provide one of `number`, `ratio` or `tol` keywords\")\n if !isnothing(ratio)\n if ratio <= 0 || ratio > 1\n error(\"`ratio` must be 0 < ratio <= 1. Got $ratio\")\n end\n end\n if !isnothing(number)\n if number < MIN_POINTS\n error(\"`number` must be $MIN_POINTS or larger. Got $number\")\n end\n end\n return nothing\nend\n\n\"\"\"\n simplify(obj; kw...)\n simplify(::SimplifyAlg, obj; kw...)\n\nSimplify a geometry, feature, feature collection,\nor nested vectors or a table of these.\n\n`RadialDistance`, `DouglasPeucker`, or\n`VisvalingamWhyatt` algorithms are available,\nlisted in order of increasing quality but decreaseing performance.\n\n`PoinTrait` and `MultiPointTrait` are returned unchanged.\n\nThe default behaviour is `simplify(DouglasPeucker(; kw...), obj)`.\nPass in other `SimplifyAlg` to use other algorithms.","category":"page"},{"location":"source/transformations/simplify/","page":"Geometry simplification","title":"Geometry simplification","text":"Keywords","category":"page"},{"location":"source/transformations/simplify/","page":"Geometry simplification","title":"Geometry simplification","text":"$APPLY_KEYWORDS\n\nKeywords for DouglasPeucker are allowed when no algorithm is specified:\n\n$SIMPLIFY_ALG_KEYWORDS","category":"page"},{"location":"source/transformations/simplify/","page":"Geometry simplification","title":"Geometry simplification","text":"Example","category":"page"},{"location":"source/transformations/simplify/","page":"Geometry simplification","title":"Geometry simplification","text":"Simplify a polygon to have six points:\n\n```jldoctest\nimport GeoInterface as GI\nimport GeometryOps as GO\n\npoly = GI.Polygon([[\n [-70.603637, -33.399918],\n [-70.614624, -33.395332],\n [-70.639343, -33.392466],\n [-70.659942, -33.394759],\n [-70.683975, -33.404504],\n [-70.697021, -33.419406],\n [-70.701141, -33.434306],\n [-70.700454, -33.446339],\n [-70.694274, -33.458369],\n [-70.682601, -33.465816],\n [-70.668869, -33.472117],\n [-70.646209, -33.473835],\n [-70.624923, -33.472117],\n [-70.609817, -33.468107],\n [-70.595397, -33.458369],\n [-70.587158, -33.442901],\n [-70.587158, -33.426283],\n [-70.590591, -33.414248],\n [-70.594711, -33.406224],\n [-70.603637, -33.399918]]])\n\nsimple = GO.simplify(poly; number=6)\nGI.npoint(simple)","category":"page"},{"location":"source/transformations/simplify/","page":"Geometry simplification","title":"Geometry simplification","text":"output","category":"page"},{"location":"source/transformations/simplify/","page":"Geometry simplification","title":"Geometry simplification","text":"6\n```\n\"\"\"\nsimplify(data; calc_extent=false, threaded=false, crs=nothing, kw...) =\n _simplify(DouglasPeucker(; kw...), data; calc_extent, threaded, crs)\nsimplify(alg::SimplifyAlg, data; kw...) = _simplify(alg, data; kw...)\n\nfunction _simplify(alg::SimplifyAlg, data; kw...)\n # Apply simplication to all curves, multipoints, and points,\n # reconstructing everything else around them.\n simplifier(geom) = _simplify(trait(geom), alg, geom)\n apply(simplifier, Union{PolygonTrait,AbstractCurveTrait,MultiPoint,PointTrait}, data; kw...)\nend\n# For Point and MultiPoint traits we do nothing\n_simplify(::PointTrait, alg, geom) = geom\n_simplify(::MultiPointTrait, alg, geom) = geom\nfunction _simplify(::PolygonTrait, alg, geom)\n # Force treating children as LinearRing\n rebuilder(g) = rebuild(g, _simplify(LinearRingTrait(), alg, g))\n lrs = map(rebuilder, GI.getgeom(geom))\n return rebuild(geom, lrs)\nend\n# For curves and rings we simplify\n_simplify(::AbstractCurveTrait, alg, geom) = rebuild(geom, simplify(alg, tuple_points(geom)))\n\n\"\"\"\n RadialDistance <: SimplifyAlg\n\nSimplifies geometries by removing points less than\n`tol` distance from the line between its neighboring points.\n\n$SIMPLIFY_ALG_KEYWORDS\n- `tol`: the minimum distance between points.\n\"\"\"\nstruct RadialDistance <: SimplifyAlg\n number::Union{Int64,Nothing}\n ratio::Union{Float64,Nothing}\n tol::Union{Float64,Nothing}\nend\nfunction RadialDistance(; number=nothing, ratio=nothing, tol=nothing)\n checkargs(number, ratio, tol)\n return RadialDistance(number, ratio, tol)\nend\n\nsettol(alg::RadialDistance, tol) = RadialDistance(alg.number, alg.ratio, tol)\n\nfunction _simplify(alg::RadialDistance, points::Vector)\n previous = first(points)\n distances = Array{Float64}(undef, length(points))\n for i in eachindex(points)\n point = points[i]\n distances[i] = _squared_dist(point, previous)\n previous = point\n end\n # Never remove the end points\n distances[begin] = distances[end] = Inf\n # This avoids taking the square root of each distance above\n if !isnothing(alg.tol)\n alg = settol(alg, (alg.tol::Float64)^2)\n end\n return _get_points(alg, points, distances)\nend\n\nfunction _squared_dist(p1, p2)\n dx = GI.x(p1) - GI.x(p2)\n dy = GI.y(p1) - GI.y(p2)\n return dx^2 + dy^2\nend\n\n\"\"\"\n DouglasPeucker <: SimplifyAlg\n\n DouglasPeucker(; number, ratio, tol)\n\nSimplifies geometries by removing points below `tol`\ndistance from the line between its neighboring points.\n\n$SIMPLIFY_ALG_KEYWORDS\n- `tol`: the minimum distance a point will be from the line\n joining its neighboring points.\n\"\"\"\nstruct DouglasPeucker <: SimplifyAlg\n number::Union{Int64,Nothing}\n ratio::Union{Float64,Nothing}\n tol::Union{Float64,Nothing}\n prefilter::Bool\nend\nfunction DouglasPeucker(; number=nothing, ratio=nothing, tol=nothing, prefilter=false)\n checkargs(number, ratio, tol)\n return DouglasPeucker(number, ratio, tol, prefilter)\nend\n\nsettol(alg::DouglasPeucker, tol) = DouglasPeucker(alg.number, alg.ratio, tol, alg.prefilter)\n\nfunction _simplify(alg::DouglasPeucker, points::Vector)\n length(points) <= MIN_POINTS && return points\n # TODO do we need this?\n # points = alg.prefilter ? simplify(RadialDistance(alg.tol), points) : points\n\n distances = _build_tolerances(_squared_segdist, points)\n return _get_points(alg, points, distances)\nend\n\nfunction _squared_segdist(l1, p, l2)\n x, y = GI.x(l1), GI.y(l1)\n dx = GI.x(l2) - x\n dy = GI.y(l2) - y\n\n if !iszero(dx) || !iszero(dy)\n t = ((GI.x(p) - x) * dx + (GI.y(p) - y) * dy) / (dx * dx + dy * dy)\n if t > 1\n x = GI.x(l2)\n y = GI.y(l2)\n elseif t > 0\n x += dx * t\n y += dy * t\n end\n end\n\n dx = GI.x(p) - x\n dy = GI.y(p) - y\n\n return dx^2 + dy^2\nend\n\n\n\"\"\"\n VisvalingamWhyatt <: SimplifyAlg\n\n VisvalingamWhyatt(; kw...)\n\nSimplifies geometries by removing points below `tol`\ndistance from the line between its neighboring points.\n\n$SIMPLIFY_ALG_KEYWORDS\n- `tol`: the minimum area of a triangle made with a point and\n its neighboring points.\n\"\"\"\nstruct VisvalingamWhyatt <: SimplifyAlg\n number::Union{Int,Nothing}\n ratio::Union{Float64,Nothing}\n tol::Union{Float64,Nothing}\n prefilter::Bool\nend\nfunction VisvalingamWhyatt(; number=nothing, ratio=nothing, tol=nothing, prefilter=false)\n checkargs(number, ratio, tol)\n return VisvalingamWhyatt(number, ratio, tol, prefilter)\nend\n\nsettol(alg::VisvalingamWhyatt, tol) = VisvalingamWhyatt(alg.number, alg.ratio, tol, alg.prefilter)\n\nfunction _simplify(alg::VisvalingamWhyatt, points::Vector)\n length(points) <= MIN_POINTS && return points\n areas = _build_tolerances(_triangle_double_area, points)\n\n # This avoids diving everything by two\n if !isnothing(alg.tol)\n alg = settol(alg, (alg.tol::Float64)*2)\n end\n return _get_points(alg, points, areas)\nend\n\n# calculates the area of a triangle given its vertices\n_triangle_double_area(p1, p2, p3) =\n abs(p1[1] * (p2[2] - p3[2]) + p2[1] * (p3[2] - p1[2]) + p3[1] * (p1[2] - p2[2]))","category":"page"},{"location":"source/transformations/simplify/#Shared-utils","page":"Geometry simplification","title":"Shared utils","text":"","category":"section"},{"location":"source/transformations/simplify/","page":"Geometry simplification","title":"Geometry simplification","text":"function _build_tolerances(f, points)\n nmax = length(points)\n real_tolerances = _flat_tolerances(f, points)\n\n tolerances = copy(real_tolerances)\n i = collect(1:nmax)\n\n min_vert = argmin(tolerances)\n this_tolerance = tolerances[min_vert]\n _remove!(tolerances, min_vert)\n deleteat!(i, min_vert)\n\n while this_tolerance < Inf\n skip = false\n\n if min_vert < length(i)\n right_tolerance = f(\n points[i[min_vert - 1]],\n points[i[min_vert]],\n points[i[min_vert + 1]],\n )\n if right_tolerance <= this_tolerance\n right_tolerance = this_tolerance\n skip = min_vert == 1\n end\n\n real_tolerances[i[min_vert]] = right_tolerance\n tolerances[min_vert] = right_tolerance\n end\n\n if min_vert > 2\n left_tolerance = f(\n points[i[min_vert - 2]],\n points[i[min_vert - 1]],\n points[i[min_vert]],\n )\n if left_tolerance <= this_tolerance\n left_tolerance = this_tolerance\n skip = min_vert == 2\n end\n real_tolerances[i[min_vert - 1]] = left_tolerance\n tolerances[min_vert - 1] = left_tolerance\n end\n\n if !skip\n min_vert = argmin(tolerances)\n end\n deleteat!(i, min_vert)\n this_tolerance = tolerances[min_vert]\n _remove!(tolerances, min_vert)\n end\n\n return real_tolerances\nend\n\nfunction tuple_points(geom)\n points = Array{Tuple{Float64,Float64}}(undef, GI.ngeom(geom))\n for (i, p) in enumerate(GI.getpoint(geom))\n points[i] = (GI.x(p), GI.y(p))\n end\n return points\nend\n\nfunction _get_points(alg, points, tolerances)\n # This assumes that `alg` has the properties\n # `tol`, `number`, and `ratio` available...\n tol = alg.tol\n number = alg.number\n ratio = alg.ratio\n bit_indices = if !isnothing(tol)\n _tol_indices(alg.tol::Float64, points, tolerances)\n elseif !isnothing(number)\n _number_indices(alg.number::Int64, points, tolerances)\n else\n _ratio_indices(alg.ratio::Float64, points, tolerances)\n end\n return points[bit_indices]\nend\n\nfunction _tol_indices(tol, points, tolerances)\n tolerances .>= tol\nend\n\nfunction _number_indices(n, points, tolerances)\n tol = partialsort(tolerances, length(points) - n + 1)\n bit_indices = _tol_indices(tol, points, tolerances)\n nselected = sum(bit_indices)\n # If there are multiple values exactly at `tol` we will get\n # the wrong output length. So we need to remove some.\n while nselected > n\n min_tol = Inf\n min_i = 0\n for i in eachindex(bit_indices)\n bit_indices[i] || continue\n if tolerances[i] < min_tol\n min_tol = tolerances[i]\n min_i = i\n end\n end\n nselected -= 1\n bit_indices[min_i] = false\n end\n return bit_indices\nend\n\nfunction _ratio_indices(r, points, tolerances)\n n = max(3, round(Int, r * length(points)))\n return _number_indices(n, points, tolerances)\nend\n\nfunction _flat_tolerances(f, points)\n result = Array{Float64}(undef, length(points))\n result[1] = result[end] = Inf\n\n for i in 2:length(result) - 1\n result[i] = f(points[i-1], points[i], points[i+1])\n end\n return result\nend\n\n_remove!(s, i) = s[i:end-1] .= s[i+1:end]","category":"page"},{"location":"source/transformations/simplify/","page":"Geometry simplification","title":"Geometry simplification","text":"","category":"page"},{"location":"source/transformations/simplify/","page":"Geometry simplification","title":"Geometry simplification","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/methods/geom_relations/within/#Within","page":"Within","title":"Within","text":"","category":"section"},{"location":"source/methods/geom_relations/within/","page":"Within","title":"Within","text":"export within","category":"page"},{"location":"source/methods/geom_relations/within/#What-is-within?","page":"Within","title":"What is within?","text":"","category":"section"},{"location":"source/methods/geom_relations/within/","page":"Within","title":"Within","text":"The within function checks if one geometry is inside another geometry. This requires that the two interiors intersect and that the interior and boundary of the first geometry is not in the exterior of the second geometry.","category":"page"},{"location":"source/methods/geom_relations/within/","page":"Within","title":"Within","text":"To provide an example, consider these two lines:","category":"page"},{"location":"source/methods/geom_relations/within/","page":"Within","title":"Within","text":"using GeometryOps\nusing GeometryOps.GeometryBasics\nusing Makie\nusing CairoMakie\n\nl1 = GI.LineString([(0.0, 0.0), (1.0, 0.0), (0.0, 0.1)])\nl2 = GI.LineString([(0.25, 0.0), (0.75, 0.0)])\nf, a, p = lines(GI.getpoint(l1), color = :blue)\nscatter!(GI.getpoint(l1), color = :blue)\nlines!(GI.getpoint(l2), color = :orange)\nscatter!(GI.getpoint(l2), color = :orange)","category":"page"},{"location":"source/methods/geom_relations/within/","page":"Within","title":"Within","text":"We can see that all of the points and edges of l2 are within l1, so l2 is within l1, but l1 is not within l2","category":"page"},{"location":"source/methods/geom_relations/within/","page":"Within","title":"Within","text":"within(l1, l2) # returns false\nwithin(l2, l1) # returns true","category":"page"},{"location":"source/methods/geom_relations/within/#Implementation","page":"Within","title":"Implementation","text":"","category":"section"},{"location":"source/methods/geom_relations/within/","page":"Within","title":"Within","text":"This is the GeoInterface-compatible implementation.","category":"page"},{"location":"source/methods/geom_relations/within/","page":"Within","title":"Within","text":"First, we implement a wrapper method that dispatches to the correct implementation based on the geometry trait.","category":"page"},{"location":"source/methods/geom_relations/within/","page":"Within","title":"Within","text":"Each of these calls a method in the geomgeomprocessors file. The methods in this file determine if the given geometries meet a set of criteria. For the within function and arguments g1 and g2, this criteria is as follows: - points of g1 are allowed to be in the interior of g2 (either through overlap or crossing for lines) - points of g1 are allowed to be on the boundary of g2 - points of g1 are not allowed to be in the exterior of g2 - at least one point of g1 is required to be in the interior of g2 - no points of g1 are required to be on the boundary of g2 - no points of g1 are required to be in the exterior of g2","category":"page"},{"location":"source/methods/geom_relations/within/","page":"Within","title":"Within","text":"The code for the specific implementations is in the geomgeomprocessors file.","category":"page"},{"location":"source/methods/geom_relations/within/","page":"Within","title":"Within","text":"const WITHIN_POINT_ALLOWS = (in_allow = true, on_allow = false, out_allow = false)\nconst WITHIN_CURVE_ALLOWS = (over_allow = true, cross_allow = true, on_allow = true, out_allow = false)\nconst WITHIN_POLYGON_ALLOWS = (in_allow = true, on_allow = true, out_allow = false)\nconst WITHIN_REQUIRES = (in_require = true, on_require = false, out_require = false)\n\n\"\"\"\n within(geom1, geom2)::Bool\n\nReturn `true` if the first geometry is completely within the second geometry.\nThe interiors of both geometries must intersect and the interior and boundary of\nthe primary geometry (geom1) must not intersect the exterior of the secondary\ngeometry (geom2).\n\nFurthermore, `within` returns the exact opposite result of `contains`.\n\n# Examples\n```jldoctest setup=:(using GeometryOps, GeometryBasics)\nimport GeometryOps as GO, GeoInterface as GI\n\nline = GI.LineString([(1, 1), (1, 2), (1, 3), (1, 4)])\npoint = (1, 2)\nGO.within(point, line)","category":"page"},{"location":"source/methods/geom_relations/within/","page":"Within","title":"Within","text":"output","category":"page"},{"location":"source/methods/geom_relations/within/","page":"Within","title":"Within","text":"true\n```\n\"\"\"\nwithin(g1, g2) = _within(trait(g1), g1, trait(g2), g2)","category":"page"},{"location":"source/methods/geom_relations/within/#Convert-features-to-geometries","page":"Within","title":"Convert features to geometries","text":"","category":"section"},{"location":"source/methods/geom_relations/within/","page":"Within","title":"Within","text":"_within(::GI.FeatureTrait, g1, ::Any, g2) = within(GI.geometry(g1), g2)\n_within(::Any, g1, t2::GI.FeatureTrait, g2) = within(g1, GI.geometry(g2))\n_within(::FeatureTrait, g1, ::FeatureTrait, g2) = within(GI.geometry(g1), GI.geometry(g2))","category":"page"},{"location":"source/methods/geom_relations/within/#Points-within-geometries","page":"Within","title":"Points within geometries","text":"","category":"section"},{"location":"source/methods/geom_relations/within/","page":"Within","title":"Within","text":"Point is within another point if those points are equal.","category":"page"},{"location":"source/methods/geom_relations/within/","page":"Within","title":"Within","text":"_within(\n ::GI.PointTrait, g1,\n ::GI.PointTrait, g2,\n) = equals(g1, g2)\n\n#= Point is within a linestring if it is on a vertex or an edge of that line,\nexcluding the start and end vertex if the line is not closed. =#\n_within(\n ::GI.PointTrait, g1,\n ::Union{GI.LineTrait, GI.LineStringTrait}, g2,\n) = _point_curve_process(\n g1, g2;\n WITHIN_POINT_ALLOWS...,\n closed_curve = false,\n)","category":"page"},{"location":"source/methods/geom_relations/within/","page":"Within","title":"Within","text":"Point is within a linearring if it is on a vertex or an edge of that ring.","category":"page"},{"location":"source/methods/geom_relations/within/","page":"Within","title":"Within","text":"_within(\n ::GI.PointTrait, g1,\n ::GI.LinearRingTrait, g2,\n) = _point_curve_process(\n g1, g2;\n WITHIN_POINT_ALLOWS...,\n closed_curve = true,\n)\n\n#= Point is within a polygon if it is inside of that polygon, excluding edges,\nvertices, and holes. =#\n_within(\n ::GI.PointTrait, g1,\n ::GI.PolygonTrait, g2,\n) = _point_polygon_process(\n g1, g2;\n WITHIN_POINT_ALLOWS...,\n)","category":"page"},{"location":"source/methods/geom_relations/within/","page":"Within","title":"Within","text":"No geometries other than points can be within points","category":"page"},{"location":"source/methods/geom_relations/within/","page":"Within","title":"Within","text":"_within(\n ::Union{GI.AbstractCurveTrait, GI.PolygonTrait}, g1,\n ::GI.PointTrait, g2,\n) = false","category":"page"},{"location":"source/methods/geom_relations/within/#Lines-within-geometries","page":"Within","title":"Lines within geometries","text":"","category":"section"},{"location":"source/methods/geom_relations/within/","page":"Within","title":"Within","text":"#= Linestring is within another linestring if their interiors intersect and no\npoints of the first line are in the exterior of the second line. =#\n_within(\n ::Union{GI.LineTrait, GI.LineStringTrait}, g1,\n ::Union{GI.LineTrait, GI.LineStringTrait}, g2,\n) = _line_curve_process(\n g1, g2;\n WITHIN_CURVE_ALLOWS...,\n WITHIN_REQUIRES...,\n closed_line = false,\n closed_curve = false,\n)\n\n#= Linestring is within a linear ring if their interiors intersect and no points\nof the line are in the exterior of the ring. =#\n_within(\n ::Union{GI.LineTrait, GI.LineStringTrait}, g1,\n ::GI.LinearRingTrait, g2,\n) = _line_curve_process(\n g1, g2;\n WITHIN_CURVE_ALLOWS...,\n WITHIN_REQUIRES...,\n closed_line = false,\n closed_curve = true,\n)\n\n#= Linestring is within a polygon if their interiors intersect and no points of\nthe line are in the exterior of the polygon, although they can be on an edge. =#\n_within(\n ::Union{GI.LineTrait, GI.LineStringTrait}, g1,\n ::GI.PolygonTrait, g2,\n) = _line_polygon_process(\n g1, g2;\n WITHIN_POLYGON_ALLOWS...,\n WITHIN_REQUIRES...,\n closed_line = false,\n)","category":"page"},{"location":"source/methods/geom_relations/within/#Rings-covered-by-geometries","page":"Within","title":"Rings covered by geometries","text":"","category":"section"},{"location":"source/methods/geom_relations/within/","page":"Within","title":"Within","text":"#= Linearring is within a linestring if their interiors intersect and no points\nof the ring are in the exterior of the line. =#\n_within(\n ::GI.LinearRingTrait, g1,\n ::Union{GI.LineTrait, GI.LineStringTrait}, g2,\n) = _line_curve_process(\n g1, g2;\n WITHIN_CURVE_ALLOWS...,\n WITHIN_REQUIRES...,\n closed_line = true,\n closed_curve = false,\n)\n\n#= Linearring is within another linearring if their interiors intersect and no\npoints of the first ring are in the exterior of the second ring. =#\n_within(\n ::GI.LinearRingTrait, g1,\n ::GI.LinearRingTrait, g2,\n) = _line_curve_process(\n g1, g2;\n WITHIN_CURVE_ALLOWS...,\n WITHIN_REQUIRES...,\n closed_line = true,\n closed_curve = true,\n)\n\n#= Linearring is within a polygon if their interiors intersect and no points of\nthe ring are in the exterior of the polygon, although they can be on an edge. =#\n_within(\n ::GI.LinearRingTrait, g1,\n ::GI.PolygonTrait, g2,\n) = _line_polygon_process(\n g1, g2;\n WITHIN_POLYGON_ALLOWS...,\n WITHIN_REQUIRES...,\n closed_line = true,\n)","category":"page"},{"location":"source/methods/geom_relations/within/#Polygons-within-geometries","page":"Within","title":"Polygons within geometries","text":"","category":"section"},{"location":"source/methods/geom_relations/within/","page":"Within","title":"Within","text":"#= Polygon is within another polygon if the interior of the first polygon\nintersects with the interior of the second and no points of the first polygon\nare outside of the second polygon. =#\n_within(\n ::GI.PolygonTrait, g1,\n ::GI.PolygonTrait, g2,\n) = _polygon_polygon_process(\n g1, g2;\n WITHIN_POLYGON_ALLOWS...,\n WITHIN_REQUIRES...,\n)","category":"page"},{"location":"source/methods/geom_relations/within/","page":"Within","title":"Within","text":"Polygons cannot be within any curves","category":"page"},{"location":"source/methods/geom_relations/within/","page":"Within","title":"Within","text":"_within(\n ::GI.PolygonTrait, g1,\n ::GI.AbstractCurveTrait, g2,\n) = false","category":"page"},{"location":"source/methods/geom_relations/within/#Geometries-within-multi-geometry/geometry-collections","page":"Within","title":"Geometries within multi-geometry/geometry collections","text":"","category":"section"},{"location":"source/methods/geom_relations/within/","page":"Within","title":"Within","text":"#= Geometry is within a multi-geometry or a collection if the geometry is within\nat least one of the collection elements. =#\nfunction _within(\n ::Union{GI.PointTrait, GI.AbstractCurveTrait, GI.PolygonTrait}, g1,\n ::Union{\n GI.MultiPointTrait, GI.AbstractMultiCurveTrait,\n GI.MultiPolygonTrait, GI.GeometryCollectionTrait,\n }, g2,\n)\n for sub_g2 in GI.getgeom(g2)\n within(g1, sub_g2) && return true\n end\n return false\nend","category":"page"},{"location":"source/methods/geom_relations/within/#Multi-geometry/geometry-collections-within-geometries","page":"Within","title":"Multi-geometry/geometry collections within geometries","text":"","category":"section"},{"location":"source/methods/geom_relations/within/","page":"Within","title":"Within","text":"#= Multi-geometry or a geometry collection is within a geometry if all\nelements of the collection are within the geometry. =#\nfunction _within(\n ::Union{\n GI.MultiPointTrait, GI.AbstractMultiCurveTrait,\n GI.MultiPolygonTrait, GI.GeometryCollectionTrait,\n }, g1,\n ::GI.AbstractGeometryTrait, g2,\n)\n for sub_g1 in GI.getgeom(g1)\n !within(sub_g1, g2) && return false\n end\n return true\nend","category":"page"},{"location":"source/methods/geom_relations/within/","page":"Within","title":"Within","text":"","category":"page"},{"location":"source/methods/geom_relations/within/","page":"Within","title":"Within","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/methods/distance/#Distance-and-signed-distance","page":"Distance and signed distance","title":"Distance and signed distance","text":"","category":"section"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"export distance, signed_distance","category":"page"},{"location":"source/methods/distance/#What-is-distance?-What-is-signed-distance?","page":"Distance and signed distance","title":"What is distance? What is signed distance?","text":"","category":"section"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"Distance is the distance of a point to another geometry. This is always a positive number. If a point is inside of geometry, so on a curve or inside of a polygon, the distance will be zero. Signed distance is mainly used for polygons and multipolygons. If a point is outside of a geometry, signed distance has the same value as distance. However, points within the geometry have a negative distance representing the distance of a point to the closest boundary. Therefore, for all \"non-filled\" geometries, like curves, the distance will either be postitive or 0.","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"To provide an example, consider this rectangle:","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"using GeometryOps\nusing GeometryOps.GeometryBasics\nusing Makie\n\nrect = Polygon([Point(0,0), Point(0,1), Point(1,1), Point(1,0), Point(0, 0)])\npoint_in = Point(0.5, 0.5)\npoint_out = Point(0.5, 1.5)\nf, a, p = poly(rect; axis = (; aspect = DataAspect()))\nscatter!(f, point_in)\nscatter!(f, point_out)\nf","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"This is clearly a rectangle with one point inside and one point outside. The points are both an equal distance to the polygon. The distance to pointin is negative while the distance to pointout is positive.","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"distance(point_in, poly) # == 0\nsigned_distance(point_in, poly) # < 0\nsigned_distance(point_out, poly) # > 0","category":"page"},{"location":"source/methods/distance/#Implementation","page":"Distance and signed distance","title":"Implementation","text":"","category":"section"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"This is the GeoInterface-compatible implementation. First, we implement a wrapper method that dispatches to the correct implementation based on the geometry trait. This is also used in the implementation, since it's a lot less work!","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"Distance and signed distance are only implemented for points to other geometries right now. This could be extended to include distance from other geometries in the future.","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"The distance calculated is the Euclidean distance using the Pythagorean theorem. Also note that singed_distance only makes sense for \"filled-in\" shapes, like polygons, so it isn't implemented for curves.","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"\"\"\"\n distance(point, geom, ::Type{T} = Float64)::T\n\nCalculates the ditance from the geometry `g1` to the `point`. The distance\nwill always be positive or zero.\n\nThe method will differ based on the type of the geometry provided:\n - The distance from a point to a point is just the Euclidean distance\n between the points.\n - The distance from a point to a line is the minimum distance from the point\n to the closest point on the given line.\n - The distance from a point to a linestring is the minimum distance from the\n point to the closest segment of the linestring.\n - The distance from a point to a linear ring is the minimum distance from\n the point to the closest segment of the linear ring.\n - The distance from a point to a polygon is zero if the point is within the\n polygon and otherwise is the minimum distance from the point to an edge of\n the polygon. This includes edges created by holes.\n - The distance from a point to a multigeometry or a geometry collection is\n the minimum distance between the point and any of the sub-geometries.\n\nResult will be of type T, where T is an optional argument with a default value\nof Float64.\n\"\"\"\ndistance(point, geom, ::Type{T} = Float64) where T <: AbstractFloat =\n _distance(T, GI.trait(point), point, GI.trait(geom), geom)\n\n\"\"\"\n signed_distance(point, geom, ::Type{T} = Float64)::T\n\nCalculates the signed distance from the geometry `geom` to the given point.\nPoints within `geom` have a negative signed distance, and points outside of\n`geom` have a positive signed distance.\n - The signed distance from a point to a point, line, linestring, or linear\n ring is equal to the distance between the two.\n - The signed distance from a point to a polygon is negative if the point is\n within the polygon and is positive otherwise. The value of the distance is\n the minimum distance from the point to an edge of the polygon. This includes\n edges created by holes.\n - The signed distance from a point to a multigeometry or a geometry\n collection is the minimum signed distance between the point and any of the\n sub-geometries.\n\nResult will be of type T, where T is an optional argument with a default value\nof Float64.\n\"\"\"\nsigned_distance(point, geom, ::Type{T} = Float64) where T<:AbstractFloat =\n _signed_distance(T, GI.trait(point), point, GI.trait(geom), geom)","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"Swap argument order to point as first argument","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"_distance(\n ::Type{T},\n gtrait::GI.AbstractTrait, geom,\n ptrait::GI.PointTrait, point,\n) where T = _distance(T, ptrait, point, gtrait, geom)\n\n_signed_distance(\n ::Type{T},\n gtrait::GI.AbstractTrait, geom,\n ptrait::GI.PointTrait, point,\n) where T = _signed_distance(T, ptrait, point, gtrait, geom)","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"Point-Point, Point-Line, Point-LineString, Point-LinearRing","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"_distance(::Type{T}, ::GI.PointTrait, point, ::GI.PointTrait, geom) where T =\n _euclid_distance(T, point, geom)\n\n_distance(::Type{T}, ::GI.PointTrait, point, ::GI.LineTrait, geom) where T =\n _distance_line(T, point, GI.getpoint(geom, 1), GI.getpoint(geom, 2))\n\n_distance(::Type{T}, ::GI.PointTrait, point, ::GI.LineStringTrait, geom) where T =\n _distance_curve(T, point, geom, close_curve = false)\n\n_distance(::Type{T}, ::GI.PointTrait, point, ::GI.LinearRingTrait, geom) where T =\n _distance_curve(T, point, geom, close_curve = true)\n\n_signed_distance(::Type{T}, ptrait::GI.PointTrait, point, gtrait::GI.AbstractTrait, geom) where T =\n _distance(T, ptrait, point, gtrait, geom)","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"Point-Polygon","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"function _distance(::Type{T}, ::GI.PointTrait, point, ::GI.PolygonTrait, geom) where T\n GI.within(point, geom) && return zero(T)\n return _distance_polygon(T, point, geom)\nend\n\nfunction _signed_distance(::Type{T}, ::GI.PointTrait, point, ::GI.PolygonTrait, geom) where T\n min_dist = _distance_polygon(T, point, geom)","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"negative if point is inside polygon","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":" return GI.within(point, geom) ? -min_dist : min_dist\nend","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"Point-MultiGeometries / Point-GeometryCollections","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"function _distance(\n ::Type{T},\n ::GI.PointTrait,\n point,\n ::Union{\n GI.MultiPointTrait, GI.MultiCurveTrait,\n GI.MultiPolygonTrait, GI.GeometryCollectionTrait,\n },\n geoms,\n) where T\n min_dist = typemax(T)\n for g in GI.getgeom(geoms)\n dist = distance(point, g, T)\n min_dist = dist < min_dist ? dist : min_dist\n end\n return min_dist\nend\n\nfunction _signed_distance(\n ::Type{T},\n ::GI.PointTrait,\n point,\n ::Union{\n GI.MultiPointTrait, GI.MultiCurveTrait,\n GI.MultiPolygonTrait, GI.GeometryCollectionTrait,\n },\n geoms,\n) where T\n min_dist = typemax(T)\n for g in GI.getgeom(geoms)\n dist = signed_distance(point, g, T)\n min_dist = dist < min_dist ? dist : min_dist\n end\n return min_dist\nend","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"Returns the Euclidean distance between two points.","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"Base.@propagate_inbounds _euclid_distance(::Type{T}, p1, p2) where T =\n _euclid_distance(\n T,\n GeoInterface.x(p1), GeoInterface.y(p1),\n GeoInterface.x(p2), GeoInterface.y(p2),\n )","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"Returns the Euclidean distance between two points given their x and y values.","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"Base.@propagate_inbounds _euclid_distance(::Type{T}, x1, y1, x2, y2) where T =\n T(sqrt((x2 - x1)^2 + (y2 - y1)^2))","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"Returns the minimum distance from point p0 to the line defined by endpoints p1 and p2.","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"function _distance_line(::Type{T}, p0, p1, p2) where T\n x0, y0 = GeoInterface.x(p0), GeoInterface.y(p0)\n x1, y1 = GeoInterface.x(p1), GeoInterface.y(p1)\n x2, y2 = GeoInterface.x(p2), GeoInterface.y(p2)\n\n xfirst, yfirst, xlast, ylast = x1 < x2 ?\n (x1, y1, x2, y2) : (x2, y2, x1, y1)\n\n #=\n Vectors from first point to last point (v) and from first point to point of\n interest (w) to find the projection of w onto v to find closest point\n =#\n v = (xlast - xfirst, ylast - yfirst)\n w = (x0 - xfirst, y0 - yfirst)\n\n c1 = sum(w .* v)\n if c1 <= 0 # p0 is closest to first endpoint\n return _euclid_distance(T, x0, y0, xfirst, yfirst)\n end\n\n c2 = sum(v .* v)\n if c2 <= c1 # p0 is closest to last endpoint\n return _euclid_distance(T, x0, y0, xlast, ylast)\n end\n\n b2 = c1 / c2 # projection fraction\n return _euclid_distance(T, x0, y0, xfirst + (b2 * v[1]), yfirst + (b2 * v[2]))\nend","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"Returns the minimum distance from the given point to the given curve. If close_curve is true, make sure to include the edge from the first to last point of the curve, even if it isn't explicitly repeated.","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"function _distance_curve(::Type{T}, point, curve; close_curve = false) where T","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"see if linear ring has explicitly repeated last point in coordinates","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":" np = GI.npoint(curve)\n first_last_equal = equals(GI.getpoint(curve, 1), GI.getpoint(curve, np))\n close_curve &= first_last_equal\n np -= first_last_equal ? 1 : 0","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"find minimum distance","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":" min_dist = typemax(T)\n p1 = GI.getpoint(curve, close_curve ? np : 1)\n for i in (close_curve ? 1 : 2):np\n p2 = GI.getpoint(curve, i)\n dist = _distance_line(T, point, p1, p2)\n min_dist = dist < min_dist ? dist : min_dist\n p1 = p2\n end\n return min_dist\nend","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"Returns the minimum distance from the given point to an edge of the given polygon, including from edges created by holes. Assumes polygon isn't filled and treats the exterior and each hole as a linear ring.","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"function _distance_polygon(::Type{T}, point, poly) where T\n min_dist = _distance_curve(T, point, GI.getexterior(poly); close_curve = true)\n @inbounds for hole in GI.gethole(poly)\n dist = _distance_curve(T, point, hole; close_curve = true)\n min_dist = dist < min_dist ? dist : min_dist\n end\n return min_dist\nend","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"","category":"page"},{"location":"source/methods/distance/","page":"Distance and signed distance","title":"Distance and signed distance","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/utils/#Utility-functions","page":"Utility functions","title":"Utility functions","text":"","category":"section"},{"location":"source/utils/","page":"Utility functions","title":"Utility functions","text":"_is3d(geom) = _is3d(GI.trait(geom), geom)\n_is3d(::GI.AbstractGeometryTrait, geom) = GI.is3d(geom)\n_is3d(::GI.FeatureTrait, feature) = _is3d(GI.geometry(feature))\n_is3d(::GI.FeatureCollectionTrait, fc) = _is3d(GI.getfeature(fc, 1))\n_is3d(::Nothing, geom) = _is3d(first(geom)) # Otherwise step into an itererable\n\n_npoint(x) = _npoint(trait(x), x)\n_npoint(::Nothing, xs::AbstractArray) = sum(_npoint, xs)\n_npoint(::GI.FeatureCollectionTrait, fc) = sum(_npoint, GI.getfeature(fc))\n_npoint(::GI.FeatureTrait, f) = _npoint(GI.geometry(f))\n_npoint(::GI.AbstractGeometryTrait, x) = GI.npoint(trait(x), x)\n\n_nedge(x) = _nedge(trait(x), x)\n_nedge(::Nothing, xs::AbstractArray) = sum(_nedge, xs)\n_nedge(::GI.FeatureCollectionTrait, fc) = sum(_nedge, GI.getfeature(fc))\n_nedge(::GI.FeatureTrait, f) = _nedge(GI.geometry(f))\nfunction _nedge(::GI.AbstractGeometryTrait, x)\n n = 0\n for g in GI.getgeom(x)\n n += _nedge(g)\n end\n return n\nend\n_nedge(::GI.AbstractCurveTrait, x) = GI.npoint(x) - 1\n_nedge(::GI.PointTrait, x) = error(\"Cant get edges from points\")\n\n\n\"\"\"\n polygon_to_line(poly::Polygon)\n\nConverts a Polygon to LineString or MultiLineString","category":"page"},{"location":"source/utils/","page":"Utility functions","title":"Utility functions","text":"Examples","category":"page"},{"location":"source/utils/","page":"Utility functions","title":"Utility functions","text":"```jldoctest\nimport GeometryOps as GO, GeoInterface as GI\n\npoly = GI.Polygon([[(-2.275543, 53.464547), (-2.275543, 53.489271), (-2.215118, 53.489271), (-2.215118, 53.464547), (-2.275543, 53.464547)]])\nGO.polygon_to_line(poly)","category":"page"},{"location":"source/utils/","page":"Utility functions","title":"Utility functions","text":"output","category":"page"},{"location":"source/utils/","page":"Utility functions","title":"Utility functions","text":"GeoInterface.Wrappers.LineString{false, false, Vector{Tuple{Float64, Float64}}, Nothing, Nothing}([(-2.275543, 53.464547), (-2.275543, 53.489271), (-2.215118, 53.489271), (-2.215118, 53.464547), (-2.275543, 53.464547)], nothing, nothing)\n```\n\"\"\"\nfunction polygon_to_line(poly)\n @assert GI.trait(poly) isa PolygonTrait\n GI.ngeom(poly) > 1 && return GI.MultiLineString(collect(GI.getgeom(poly)))\n return GI.LineString(collect(GI.getgeom(GI.getgeom(poly, 1))))\nend\n\n\n\"\"\"\n to_edges()\n\nConvert any geometry or collection of geometries into a flat\nvector of `Tuple{Tuple{Float64,Float64},Tuple{Float64,Float64}}` edges.\n\"\"\"\nfunction to_edges(x)\n edges = Vector{Edge}(undef, _nedge(x))\n _to_edges!(edges, x, 1)\n return edges\nend\n\n_to_edges!(edges::Vector, x, n) = _to_edges!(edges, trait(x), x, n)\nfunction _to_edges!(edges::Vector, ::GI.FeatureCollectionTrait, fc, n)\n for f in GI.getfeature(fc)\n n = _to_edges!(edges, f, n)\n end\nend\n_to_edges!(edges::Vector, ::GI.FeatureTrait, f, n) = _to_edges!(edges, GI.geometry(f), n)\nfunction _to_edges!(edges::Vector, ::GI.AbstractGeometryTrait, fc, n)\n for f in GI.getgeom(fc)\n n = _to_edges!(edges, f, n)\n end\nend\nfunction _to_edges!(edges::Vector, ::GI.AbstractCurveTrait, geom, n)\n p1 = GI.getpoint(geom, 1)\n p1x, p1y = GI.x(p1), GI.y(p1)\n for i in 2:GI.npoint(geom)\n p2 = GI.getpoint(geom, i)\n p2x, p2y = GI.x(p2), GI.y(p2)\n edges[n] = (p1x, p1y), (p2x, p2y)\n p1x, p1y = p2x, p2y\n n += 1\n end\n return n\nend\n\n_tuple_point(p) = GI.x(p), GI.y(p)\n\nfunction to_extent(edges::Vector{Edge})\n x, y = extrema(first, edges)\n Extents.Extent(X=x, Y=y)\nend\n\nfunction to_points(xs)\n points = Vector{TuplePoint}(undef, _npoint(x))\n _to_points!(points, x, 1)\n return points\nend\n\n_to_points!(points::Vector, x, n) = _to_points!(points, trait(x), x, n)\nfunction _to_points!(points::Vector, ::FeatureCollectionTrait, fc, n)\n for f in GI.getfeature(fc)\n n = _to_points!(points, f, n)\n end\nend\n_to_points!(points::Vector, ::FeatureTrait, f, n) = _to_points!(points, GI.geometry(f), n)\nfunction _to_points!(points::Vector, ::AbstractGeometryTrait, fc, n)\n for f in GI.getgeom(fc)\n n = _to_points!(points, f, n)\n end\nend\nfunction _to_points!(points::Vector, ::Union{AbstractCurveTrait,MultiPointTrait}, geom, n)\n p1 = GI.getpoint(geom, 1)\n p1x, p1y = GI.x(p1), GI.y(p1)\n for i in 2:GI.npoint(geom)\n p2 = GI.getpoint(geom, i)\n p2x, p2y = GI.x(p2), GI.y(p2)\n points[n] = (p1x, p1y), (p2x, p2y)\n p1 = p2\n n += 1\n end\n return n\nend","category":"page"},{"location":"source/utils/","page":"Utility functions","title":"Utility functions","text":"","category":"page"},{"location":"source/utils/","page":"Utility functions","title":"Utility functions","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/methods/geom_relations/coveredby/#CoveredBy","page":"CoveredBy","title":"CoveredBy","text":"","category":"section"},{"location":"source/methods/geom_relations/coveredby/","page":"CoveredBy","title":"CoveredBy","text":"export coveredby","category":"page"},{"location":"source/methods/geom_relations/coveredby/#What-is-coveredby?","page":"CoveredBy","title":"What is coveredby?","text":"","category":"section"},{"location":"source/methods/geom_relations/coveredby/","page":"CoveredBy","title":"CoveredBy","text":"The coveredby function checks if one geometry is covered by another geometry. This is an extension of within that does not require the interiors of the two geometries to intersect, but still does require that the interior and boundary of the first geometry isn't outside of the second geometry.","category":"page"},{"location":"source/methods/geom_relations/coveredby/","page":"CoveredBy","title":"CoveredBy","text":"To provide an example, consider this point and line:","category":"page"},{"location":"source/methods/geom_relations/coveredby/","page":"CoveredBy","title":"CoveredBy","text":"using GeometryOps\nusing GeometryOps.GeometryBasics\nusing Makie\nusing CairoMakie\n\np1 = Point(0.0, 0.0)\np2 = Point(1.0, 1.0)\nl1 = Line(p1, p2)\n\nf, a, p = lines([p1, p2])\nscatter!(p1, color = :red)","category":"page"},{"location":"source/methods/geom_relations/coveredby/","page":"CoveredBy","title":"CoveredBy","text":"As we can see, p1 is on the endpoint of l1. This means it is not within, but it does meet the definition of coveredby.","category":"page"},{"location":"source/methods/geom_relations/coveredby/","page":"CoveredBy","title":"CoveredBy","text":"coveredby(p1, l1) # true","category":"page"},{"location":"source/methods/geom_relations/coveredby/#Implementation","page":"CoveredBy","title":"Implementation","text":"","category":"section"},{"location":"source/methods/geom_relations/coveredby/","page":"CoveredBy","title":"CoveredBy","text":"This is the GeoInterface-compatible implementation.","category":"page"},{"location":"source/methods/geom_relations/coveredby/","page":"CoveredBy","title":"CoveredBy","text":"First, we implement a wrapper method that dispatches to the correct implementation based on the geometry trait.","category":"page"},{"location":"source/methods/geom_relations/coveredby/","page":"CoveredBy","title":"CoveredBy","text":"Each of these calls a method in the geomgeomprocessors file. The methods in this file determine if the given geometries meet a set of criteria. For the coveredby function and arguments g1 and g2, this criteria is as follows: - points of g1 are allowed to be in the interior of g2 (either through overlap or crossing for lines) - points of g1 are allowed to be on the boundary of g2 - points of g1 are not allowed to be in the exterior of g2 - no points of g1 are required to be in the interior of g2 - no points of g1 are required to be on the boundary of g2 - no points of g1 are required to be in the exterior of g2","category":"page"},{"location":"source/methods/geom_relations/coveredby/","page":"CoveredBy","title":"CoveredBy","text":"The code for the specific implementations is in the geomgeomprocessors file.","category":"page"},{"location":"source/methods/geom_relations/coveredby/","page":"CoveredBy","title":"CoveredBy","text":"const COVEREDBY_ALLOWS = (in_allow = true, on_allow = true, out_allow = false)\nconst COVEREDBY_CURVE_ALLOWS = (over_allow = true, cross_allow = true, on_allow = true, out_allow = false)\nconst COVEREDBY_CURVE_REQUIRES = (in_require = false, on_require = false, out_require = false)\nconst COVEREDBY_POLYGON_REQUIRES = (in_require = true, on_require = false, out_require = false,)\n\n\"\"\"\n coveredby(g1, g2)::Bool\n\nReturn `true` if the first geometry is completely covered by the second\ngeometry. The interior and boundary of the primary geometry (g1) must not\nintersect the exterior of the secondary geometry (g2).\n\nFurthermore, `coveredby` returns the exact opposite result of `covers`. They are\nequivalent with the order of the arguments swapped.\n\n# Examples\n```jldoctest setup=:(using GeometryOps, GeometryBasics)\nimport GeometryOps as GO, GeoInterface as GI\np1 = GI.Point(0.0, 0.0)\np2 = GI.Point(1.0, 1.0)\nl1 = GI.Line([p1, p2])\n\nGO.coveredby(p1, l1)","category":"page"},{"location":"source/methods/geom_relations/coveredby/","page":"CoveredBy","title":"CoveredBy","text":"output","category":"page"},{"location":"source/methods/geom_relations/coveredby/","page":"CoveredBy","title":"CoveredBy","text":"true\n```\n\"\"\"\ncoveredby(g1, g2) = _coveredby(trait(g1), g1, trait(g2), g2)","category":"page"},{"location":"source/methods/geom_relations/coveredby/#Convert-features-to-geometries","page":"CoveredBy","title":"Convert features to geometries","text":"","category":"section"},{"location":"source/methods/geom_relations/coveredby/","page":"CoveredBy","title":"CoveredBy","text":"_coveredby(::GI.FeatureTrait, g1, ::Any, g2) = coveredby(GI.geometry(g1), g2)\n_coveredby(::Any, g1, t2::GI.FeatureTrait, g2) = coveredby(g1, GI.geometry(g2))\n_coveredby(::FeatureTrait, g1, ::FeatureTrait, g2) = coveredby(GI.geometry(g1), GI.geometry(g2))","category":"page"},{"location":"source/methods/geom_relations/coveredby/#Points-coveredby-geometries","page":"CoveredBy","title":"Points coveredby geometries","text":"","category":"section"},{"location":"source/methods/geom_relations/coveredby/","page":"CoveredBy","title":"CoveredBy","text":"Point is coveredby another point if those points are equal","category":"page"},{"location":"source/methods/geom_relations/coveredby/","page":"CoveredBy","title":"CoveredBy","text":"_coveredby(\n ::GI.PointTrait, g1,\n ::GI.PointTrait, g2,\n) = equals(g1, g2)","category":"page"},{"location":"source/methods/geom_relations/coveredby/","page":"CoveredBy","title":"CoveredBy","text":"Point is coveredby a line/linestring if it is on a line vertex or an edge","category":"page"},{"location":"source/methods/geom_relations/coveredby/","page":"CoveredBy","title":"CoveredBy","text":"_coveredby(\n ::GI.PointTrait, g1,\n ::Union{GI.LineTrait, GI.LineStringTrait}, g2,\n) = _point_curve_process(\n g1, g2;\n COVEREDBY_ALLOWS...,\n closed_curve = false,\n)","category":"page"},{"location":"source/methods/geom_relations/coveredby/","page":"CoveredBy","title":"CoveredBy","text":"Point is coveredby a linearring if it is on a vertex or an edge of ring","category":"page"},{"location":"source/methods/geom_relations/coveredby/","page":"CoveredBy","title":"CoveredBy","text":"_coveredby(\n ::GI.PointTrait, g1,\n ::GI.LinearRingTrait, g2,\n) = _point_curve_process(\n g1, g2;\n COVEREDBY_ALLOWS...,\n closed_curve = true,\n)","category":"page"},{"location":"source/methods/geom_relations/coveredby/","page":"CoveredBy","title":"CoveredBy","text":"Point is coveredby a polygon if it is inside polygon, including edges/vertices","category":"page"},{"location":"source/methods/geom_relations/coveredby/","page":"CoveredBy","title":"CoveredBy","text":"_coveredby(\n ::GI.PointTrait, g1,\n ::GI.PolygonTrait, g2,\n) = _point_polygon_process(\n g1, g2;\n COVEREDBY_ALLOWS...,\n)","category":"page"},{"location":"source/methods/geom_relations/coveredby/","page":"CoveredBy","title":"CoveredBy","text":"Points cannot cover any geometry other than points","category":"page"},{"location":"source/methods/geom_relations/coveredby/","page":"CoveredBy","title":"CoveredBy","text":"_coveredby(\n ::Union{GI.AbstractCurveTrait, GI.PolygonTrait}, g1,\n ::GI.PointTrait, g2,\n) = false","category":"page"},{"location":"source/methods/geom_relations/coveredby/#Lines-coveredby-geometries","page":"CoveredBy","title":"Lines coveredby geometries","text":"","category":"section"},{"location":"source/methods/geom_relations/coveredby/","page":"CoveredBy","title":"CoveredBy","text":"#= Linestring is coveredby a line if all interior and boundary points of the\nfirst line are on the interior/boundary points of the second line. =#\n_coveredby(\n ::Union{GI.LineTrait, GI.LineStringTrait}, g1,\n ::Union{GI.LineTrait, GI.LineStringTrait}, g2,\n) = _line_curve_process(\n g1, g2;\n COVEREDBY_CURVE_ALLOWS...,\n COVEREDBY_CURVE_REQUIRES...,\n closed_line = false,\n closed_curve = false,\n)\n\n#= Linestring is coveredby a ring if all interior and boundary points of the\nline are on the edges of the ring. =#\n_coveredby(\n ::Union{GI.LineTrait, GI.LineStringTrait}, g1,\n ::GI.LinearRingTrait, g2,\n) = _line_curve_process(\n g1, g2;\n COVEREDBY_CURVE_ALLOWS...,\n COVEREDBY_CURVE_REQUIRES...,\n closed_line = false,\n closed_curve = true,\n)\n\n#= Linestring is coveredby a polygon if all interior and boundary points of the\nline are in the polygon interior or on its edges, inlcuding hole edges. =#\n_coveredby(\n ::Union{GI.LineTrait, GI.LineStringTrait}, g1,\n ::GI.PolygonTrait, g2,\n) = _line_polygon_process(\n g1, g2;\n COVEREDBY_ALLOWS...,\n COVEREDBY_CURVE_REQUIRES...,\n closed_line = false,\n)","category":"page"},{"location":"source/methods/geom_relations/coveredby/#Rings-covered-by-geometries","page":"CoveredBy","title":"Rings covered by geometries","text":"","category":"section"},{"location":"source/methods/geom_relations/coveredby/","page":"CoveredBy","title":"CoveredBy","text":"#= Linearring is covered by a line if all vertices and edges of the ring are on\nthe edges and vertices of the line. =#\n_coveredby(\n ::GI.LinearRingTrait, g1,\n ::Union{GI.LineTrait, GI.LineStringTrait}, g2,\n) = _line_curve_process(\n g1, g2;\n COVEREDBY_CURVE_ALLOWS...,\n COVEREDBY_CURVE_REQUIRES...,\n closed_line = true,\n closed_curve = false,\n)\n\n#= Linearring is covered by another linear ring if all vertices and edges of the\nfirst ring are on the edges/vertices of the second ring. =#\n_coveredby(\n ::GI.LinearRingTrait, g1,\n ::GI.LinearRingTrait, g2,\n) = _line_curve_process(\n g1, g2;\n COVEREDBY_CURVE_ALLOWS...,\n COVEREDBY_CURVE_REQUIRES...,\n closed_line = true,\n closed_curve = true,\n)\n\n#= Linearring is coveredby a polygon if all vertices and edges of the ring are\nin the polygon interior or on the polygon edges, inlcuding hole edges. =#\n_coveredby(\n ::GI.LinearRingTrait, g1,\n ::GI.PolygonTrait, g2,\n) = _line_polygon_process(\n g1, g2;\n COVEREDBY_ALLOWS...,\n COVEREDBY_CURVE_REQUIRES...,\n closed_line = true,\n)","category":"page"},{"location":"source/methods/geom_relations/coveredby/#Polygons-covered-by-geometries","page":"CoveredBy","title":"Polygons covered by geometries","text":"","category":"section"},{"location":"source/methods/geom_relations/coveredby/","page":"CoveredBy","title":"CoveredBy","text":"#= Polygon is covered by another polygon if if the interior and edges of the\nfirst polygon are in the second polygon interior or on polygon edges, including\nhole edges.=#\n_coveredby(\n ::GI.PolygonTrait, g1,\n ::GI.PolygonTrait, g2,\n) = _polygon_polygon_process(\n g1, g2;\n COVEREDBY_ALLOWS...,\n COVEREDBY_POLYGON_REQUIRES...,\n)","category":"page"},{"location":"source/methods/geom_relations/coveredby/","page":"CoveredBy","title":"CoveredBy","text":"Polygons cannot covered by any curves","category":"page"},{"location":"source/methods/geom_relations/coveredby/","page":"CoveredBy","title":"CoveredBy","text":"_coveredby(\n ::GI.PolygonTrait, g1,\n ::GI.AbstractCurveTrait, g2,\n) = false","category":"page"},{"location":"source/methods/geom_relations/coveredby/#Geometries-coveredby-multi-geometry/geometry-collections","page":"CoveredBy","title":"Geometries coveredby multi-geometry/geometry collections","text":"","category":"section"},{"location":"source/methods/geom_relations/coveredby/","page":"CoveredBy","title":"CoveredBy","text":"#= Geometry is covered by a multi-geometry or a collection if one of the elements\nof the collection cover the geometry. =#\nfunction _coveredby(\n ::Union{GI.PointTrait, GI.AbstractCurveTrait, GI.PolygonTrait}, g1,\n ::Union{\n GI.MultiPointTrait, GI.AbstractMultiCurveTrait,\n GI.MultiPolygonTrait, GI.GeometryCollectionTrait,\n }, g2,\n)\n for sub_g2 in GI.getgeom(g2)\n coveredby(g1, sub_g2) && return true\n end\n return false\nend","category":"page"},{"location":"source/methods/geom_relations/coveredby/#Multi-geometry/geometry-collections-coveredby-geometries","page":"CoveredBy","title":"Multi-geometry/geometry collections coveredby geometries","text":"","category":"section"},{"location":"source/methods/geom_relations/coveredby/","page":"CoveredBy","title":"CoveredBy","text":"#= Multi-geometry or a geometry collection is covered by a geometry if all\nelements of the collection are covered by the geometry. =#\nfunction _coveredby(\n ::Union{\n GI.MultiPointTrait, GI.AbstractMultiCurveTrait,\n GI.MultiPolygonTrait, GI.GeometryCollectionTrait,\n }, g1,\n ::GI.AbstractGeometryTrait, g2,\n)\n for sub_g1 in GI.getgeom(g1)\n !coveredby(sub_g1, g2) && return false\n end\n return true\nend","category":"page"},{"location":"source/methods/geom_relations/coveredby/","page":"CoveredBy","title":"CoveredBy","text":"","category":"page"},{"location":"source/methods/geom_relations/coveredby/","page":"CoveredBy","title":"CoveredBy","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/methods/bools/#Boolean-conditions","page":"Boolean conditions","title":"Boolean conditions","text":"","category":"section"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"export isclockwise, isconcave","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"These are all adapted from Turf.jl.","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"The may not necessarily be what want in the end but work for now!","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"\"\"\"\n isclockwise(line::Union{LineString, Vector{Position}})::Bool\n\nTake a ring and return true or false whether or not the ring is clockwise or\ncounter-clockwise.\n\n# Example\n\n```jldoctest\nimport GeoInterface as GI, GeometryOps as GO\n\nring = GI.LinearRing([(0, 0), (1, 1), (1, 0), (0, 0)])\nGO.isclockwise(ring)","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"output","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"true\n```\n\"\"\"\nisclockwise(geom)::Bool = isclockwise(GI.trait(geom), geom)\n\nfunction isclockwise(::AbstractCurveTrait, line)::Bool\n sum = 0.0\n prev = GI.getpoint(line, 1)\n for p in GI.getpoint(line)","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"sum will be zero for the first point as x is subtracted from itself","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":" sum += (GI.x(p) - GI.x(prev)) * (GI.y(p) + GI.y(prev))\n prev = p\n end\n\n return sum > 0.0\nend\n\n\"\"\"\n isconcave(poly::Polygon)::Bool\n\nTake a polygon and return true or false as to whether it is concave or not.\n\n# Examples\n```jldoctest\nimport GeoInterface as GI, GeometryOps as GO\n\npoly = GI.Polygon([[(0, 0), (0, 1), (1, 1), (1, 0), (0, 0)]])\nGO.isconcave(poly)","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"output","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"false\n```\n\"\"\"\nfunction isconcave(poly)::Bool\n sign = false\n\n exterior = GI.getexterior(poly)","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"FIXME handle not closed polygons","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":" GI.npoint(exterior) <= 4 && return false\n n = GI.npoint(exterior) - 1\n\n for i in 1:n\n j = ((i + 1) % n) === 0 ? 1 : (i + 1) % n\n m = ((i + 2) % n) === 0 ? 1 : (i + 2) % n\n\n pti = GI.getpoint(exterior, i)\n ptj = GI.getpoint(exterior, j)\n ptm = GI.getpoint(exterior, m)\n\n dx1 = GI.x(ptm) - GI.x(ptj)\n dy1 = GI.y(ptm) - GI.y(ptj)\n dx2 = GI.x(pti) - GI.x(ptj)\n dy2 = GI.y(pti) - GI.y(ptj)\n\n cross = (dx1 * dy2) - (dy1 * dx2)\n\n if i === 0\n sign = cross > 0\n elseif sign !== (cross > 0)\n return true\n end\n end\n\n return false\nend","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"\"\"\" isparallel(line1::LineString, line2::LineString)::Bool","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"Return true if each segment of line1 is parallel to the correspondent segment of line2","category":"page"},{"location":"source/methods/bools/#Examples","page":"Boolean conditions","title":"Examples","text":"","category":"section"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"import GeoInterface as GI, GeometryOps as GO\njulia> line1 = GI.LineString([(9.170356, 45.477985), (9.164434, 45.482551), (9.166644, 45.484003)])\nGeoInterface.Wrappers.LineString{false, false, Vector{Tuple{Float64, Float64}}, Nothing, Nothing}([(9.170356, 45.477985), (9.164434, 45.482551), (9.166644, 45.484003)], nothing, nothing)\n\njulia> line2 = GI.LineString([(9.169356, 45.477985), (9.163434, 45.482551), (9.165644, 45.484003)])\nGeoInterface.Wrappers.LineString{false, false, Vector{Tuple{Float64, Float64}}, Nothing, Nothing}([(9.169356, 45.477985), (9.163434, 45.482551), (9.165644, 45.484003)], nothing, nothing)\n\njulia>\nGO.isparallel(line1, line2)\ntrue","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"\"\"\" function isparallel(line1, line2)::Bool seg1 = linesegment(line1) seg2 = linesegment(line2)","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"for i in eachindex(seg1)\n coors2 = nothing\n coors1 = seg1[i]\n coors2 = seg2[i]\n _isparallel(coors1, coors2) == false && return false\nend\nreturn true","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"end","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"@inline function isparallel(p1, p2) slope1 = bearingtoazimuth(rhumbbearing(GI.x(p1), GI.x(p2))) slope2 = bearingtoazimuth(rhumb_bearing(GI.y(p1), GI.y(p2)))","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"return slope1 === slope2","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"end","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"_isparallel(((ax, ay), (bx, by)), ((cx, cy), (dx, dy))) =\n _isparallel(bx - ax, by - ay, dx - cx, dy - cy)\n\n_isparallel(Δx1, Δy1, Δx2, Δy2) = (Δx1 * Δy2 == Δy1 * Δx2)\n\n\nfunction point_in_extent(p, extent::Extents.Extent)\n (x1, x2), (y1, y1) = extent.X, extent.Y\n return x1 <= GI.x(p) && y1 <= GI.y(p) && x2 >= GI.x(p) && y2 >= GI.y(p)\nend","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/methods/equals/#Equals","page":"Equals","title":"Equals","text":"","category":"section"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"export equals","category":"page"},{"location":"source/methods/equals/#What-is-equals?","page":"Equals","title":"What is equals?","text":"","category":"section"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"The equals function checks if two geometries are equal. They are equal if they share the same set of points and edges to define the same shape.","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"To provide an example, consider these two lines:","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"using GeometryOps\nusing GeometryOps.GeometryBasics\nusing Makie\nusing CairoMakie\n\nl1 = GI.LineString([(0.0, 0.0), (0.0, 10.0)])\nl2 = GI.LineString([(0.0, -10.0), (0.0, 3.0)])\nf, a, p = lines(GI.getpoint(l1), color = :blue)\nscatter!(GI.getpoint(l1), color = :blue)\nlines!(GI.getpoint(l2), color = :orange)\nscatter!(GI.getpoint(l2), color = :orange)","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"We can see that the two lines do not share a commen set of points and edges in the plot, so they are not equal:","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"equals(l1, l2) # returns false","category":"page"},{"location":"source/methods/equals/#Implementation","page":"Equals","title":"Implementation","text":"","category":"section"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"This is the GeoInterface-compatible implementation.","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"First, we implement a wrapper method that dispatches to the correct implementation based on the geometry trait. This is also used in the implementation, since it's a lot less work!","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"Note that while we need the same set of points and edges, they don't need to be provided in the same order for polygons. For for example, we need the same set points for two multipoints to be equal, but they don't have to be saved in the same order. The winding order also doesn't have to be the same to represent the same geometry. This requires checking every point against every other point in the two geometries we are comparing. Also, some geometries must be \"closed\" like polygons and linear rings. These will be assumed to be closed, even if they don't have a repeated last point explicity written in the coordinates. Additionally, geometries and multi-geometries can be equal if the multi-geometry only includes that single geometry.","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"\"\"\"\n equals(geom1, geom2)::Bool\n\nCompare two Geometries return true if they are the same geometry.\n\n# Examples\n```jldoctest\nimport GeometryOps as GO, GeoInterface as GI\npoly1 = GI.Polygon([[(0,0), (0,5), (5,5), (5,0), (0,0)]])\npoly2 = GI.Polygon([[(0,0), (0,5), (5,5), (5,0), (0,0)]])\n\nGO.equals(poly1, poly2)","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"output","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"true\n```\n\"\"\"\nequals(geom_a, geom_b) = equals(\n GI.trait(geom_a), geom_a,\n GI.trait(geom_b), geom_b,\n)\n\n\"\"\"\n equals(::T, geom_a, ::T, geom_b)::Bool\n\nTwo geometries of the same type, which don't have a equals function to dispatch\noff of should throw an error.\n\"\"\"\nequals(::T, geom_a, ::T, geom_b) where T = error(\"Cant compare $T yet\")\n\n\"\"\"\n equals(trait_a, geom_a, trait_b, geom_b)\n\nTwo geometries which are not of the same type cannot be equal so they always\nreturn false.\n\"\"\"\nequals(trait_a, geom_a, trait_b, geom_b) = false\n\n\"\"\"\n equals(::GI.PointTrait, p1, ::GI.PointTrait, p2)::Bool\n\nTwo points are the same if they have the same x and y (and z if 3D) coordinates.\n\"\"\"\nfunction equals(::GI.PointTrait, p1, ::GI.PointTrait, p2)\n GI.ncoord(p1) == GI.ncoord(p2) || return false\n GI.x(p1) == GI.x(p2) || return false\n GI.y(p1) == GI.y(p2) || return false\n if GI.is3d(p1)\n GI.z(p1) == GI.z(p2) || return false\n end\n return true\nend\n\n\"\"\"\n equals(::GI.PointTrait, p1, ::GI.MultiPointTrait, mp2)::Bool\n\nA point and a multipoint are equal if the multipoint is composed of a single\npoint that is equivalent to the given point.\n\"\"\"\nfunction equals(::GI.PointTrait, p1, ::GI.MultiPointTrait, mp2)\n GI.npoint(mp2) == 1 || return false\n return equals(p1, GI.getpoint(mp2, 1))\nend\n\n\"\"\"\n equals(::GI.MultiPointTrait, mp1, ::GI.PointTrait, p2)::Bool\n\nA point and a multipoint are equal if the multipoint is composed of a single\npoint that is equivalent to the given point.\n\"\"\"\nequals(trait1::GI.MultiPointTrait, mp1, trait2::GI.PointTrait, p2) =\n equals(trait2, p2, trait1, mp1)\n\n\"\"\"\n equals(::GI.MultiPointTrait, mp1, ::GI.MultiPointTrait, mp2)::Bool\n\nTwo multipoints are equal if they share the same set of points.\n\"\"\"\nfunction equals(::GI.MultiPointTrait, mp1, ::GI.MultiPointTrait, mp2)\n GI.npoint(mp1) == GI.npoint(mp2) || return false\n for p1 in GI.getpoint(mp1)\n has_match = false # if point has a matching point in other multipoint\n for p2 in GI.getpoint(mp2)\n if equals(p1, p2)\n has_match = true\n break\n end\n end\n has_match || return false # if no matching point, can't be equal\n end\n return true # all points had a match\nend\n\n\"\"\"\n _equals_curves(c1, c2, closed_type1, closed_type2)::Bool\n\nTwo curves are equal if they share the same set of point, representing the same\ngeometry. Both curves must must be composed of the same set of points, however,\nthey do not have to wind in the same direction, or start on the same point to be\nequivalent.\nInputs:\n c1 first geometry\n c2 second geometry\n closed_type1::Bool true if c1 is closed by definition (polygon, linear ring)\n closed_type2::Bool true if c2 is closed by definition (polygon, linear ring)\n\"\"\"\nfunction _equals_curves(c1, c2, closed_type1, closed_type2)","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"Check if both curves are closed or not","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":" n1 = GI.npoint(c1)\n n2 = GI.npoint(c2)\n c1_repeat_point = GI.getpoint(c1, 1) == GI.getpoint(c1, n1)\n n2 = GI.npoint(c2)\n c2_repeat_point = GI.getpoint(c2, 1) == GI.getpoint(c2, n2)\n closed1 = closed_type1 || c1_repeat_point\n closed2 = closed_type2 || c2_repeat_point\n closed1 == closed2 || return false","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"How many points in each curve","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":" n1 -= c1_repeat_point ? 1 : 0\n n2 -= c2_repeat_point ? 1 : 0\n n1 == n2 || return false\n n1 == 0 && return true","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"Find offset between curves","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":" jstart = nothing\n p1 = GI.getpoint(c1, 1)\n for i in 1:n2\n if equals(p1, GI.getpoint(c2, i))\n jstart = i\n break\n end\n end","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"no point matches the first point","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":" isnothing(jstart) && return false","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"found match for only point","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":" n1 == 1 && return true","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"if isn't closed and first or last point don't match, not same curve","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":" !closed_type1 && (jstart != 1 && jstart != n1) && return false","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"Check if curves are going in same direction","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":" i = 2\n j = jstart + 1\n j -= j > n2 ? n2 : 0\n same_direction = equals(GI.getpoint(c1, i), GI.getpoint(c2, j))","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"if only 2 points, we have already compared both","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":" n1 == 2 && return same_direction","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"Check all remaining points are the same wrapping around line","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":" jstep = same_direction ? 1 : -1\n for i in 2:n1\n ip = GI.getpoint(c1, i)\n j = jstart + (i - 1) * jstep\n j += (0 < j <= n2) ? 0 : (n2 * -jstep)\n jp = GI.getpoint(c2, j)\n equals(ip, jp) || return false\n end\n return true\nend\n\n\"\"\"\n equals(\n ::Union{GI.LineTrait, GI.LineStringTrait}, l1,\n ::Union{GI.LineTrait, GI.LineStringTrait}, l2,\n )::Bool\n\nTwo lines/linestrings are equal if they share the same set of points going\nalong the curve. Note that lines/linestrings aren't closed by defintion.\n\"\"\"\nequals(\n ::Union{GI.LineTrait, GI.LineStringTrait}, l1,\n ::Union{GI.LineTrait, GI.LineStringTrait}, l2,\n) = _equals_curves(l1, l2, false, false)\n\n\"\"\"\n equals(\n ::Union{GI.LineTrait, GI.LineStringTrait}, l1,\n ::GI.LinearRingTrait, l2,\n )::Bool\n\nA line/linestring and a linear ring are equal if they share the same set of\npoints going along the curve. Note that lines aren't closed by defintion, but\nrings are, so the line must have a repeated last point to be equal\n\"\"\"\nequals(\n ::Union{GI.LineTrait, GI.LineStringTrait}, l1,\n ::GI.LinearRingTrait, l2,\n) = _equals_curves(l1, l2, false, true)\n\n\"\"\"\n equals(\n ::GI.LinearRingTrait, l1,\n ::Union{GI.LineTrait, GI.LineStringTrait}, l2,\n )::Bool\n\nA linear ring and a line/linestring are equal if they share the same set of\npoints going along the curve. Note that lines aren't closed by defintion, but\nrings are, so the line must have a repeated last point to be equal\n\"\"\"\nequals(\n ::GI.LinearRingTrait, l1,\n ::Union{GI.LineTrait, GI.LineStringTrait}, l2,\n) = _equals_curves(l1, l2, true, false)\n\n\"\"\"\n equals(\n ::GI.LinearRingTrait, l1,\n ::GI.LinearRingTrait, l2,\n )::Bool\n\nTwo linear rings are equal if they share the same set of points going along the\ncurve. Note that rings are closed by definition, so they can have, but don't\nneed, a repeated last point to be equal.\n\"\"\"\nequals(\n ::GI.LinearRingTrait, l1,\n ::GI.LinearRingTrait, l2,\n) = _equals_curves(l1, l2, true, true)\n\n\"\"\"\n equals(::GI.PolygonTrait, geom_a, ::GI.PolygonTrait, geom_b)::Bool\n\nTwo polygons are equal if they share the same exterior edge and holes.\n\"\"\"\nfunction equals(::GI.PolygonTrait, geom_a, ::GI.PolygonTrait, geom_b)","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"Check if exterior is equal","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":" _equals_curves(\n GI.getexterior(geom_a), GI.getexterior(geom_b),\n true, true, # linear rings are closed by definition\n ) || return false","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"Check if number of holes are equal","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":" GI.nhole(geom_a) == GI.nhole(geom_b) || return false","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"Check if holes are equal","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":" for ihole in GI.gethole(geom_a)\n has_match = false\n for jhole in GI.gethole(geom_b)\n if _equals_curves(\n ihole, jhole,\n true, true, # linear rings are closed by definition\n )\n has_match = true\n break\n end\n end\n has_match || return false\n end\n return true\nend\n\n\"\"\"\n equals(::GI.PolygonTrait, geom_a, ::GI.MultiPolygonTrait, geom_b)::Bool\n\nA polygon and a multipolygon are equal if the multipolygon is composed of a\nsingle polygon that is equivalent to the given polygon.\n\"\"\"\nfunction equals(::GI.PolygonTrait, geom_a, ::MultiPolygonTrait, geom_b)\n GI.npolygon(geom_b) == 1 || return false\n return equals(geom_a, GI.getpolygon(geom_b, 1))\nend\n\n\"\"\"\n equals(::GI.MultiPolygonTrait, geom_a, ::GI.PolygonTrait, geom_b)::Bool\n\nA polygon and a multipolygon are equal if the multipolygon is composed of a\nsingle polygon that is equivalent to the given polygon.\n\"\"\"\nequals(trait_a::GI.MultiPolygonTrait, geom_a, trait_b::PolygonTrait, geom_b) =\n equals(trait_b, geom_b, trait_a, geom_a)\n\n\"\"\"\n equals(::GI.PolygonTrait, geom_a, ::GI.PolygonTrait, geom_b)::Bool\n\nTwo multipolygons are equal if they share the same set of polygons.\n\"\"\"\nfunction equals(::GI.MultiPolygonTrait, geom_a, ::GI.MultiPolygonTrait, geom_b)","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"Check if same number of polygons","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":" GI.npolygon(geom_a) == GI.npolygon(geom_b) || return false","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"Check if each polygon has a matching polygon","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":" for poly_a in GI.getpolygon(geom_a)\n has_match = false\n for poly_b in GI.getpolygon(geom_b)\n if equals(poly_a, poly_b)\n has_match = true\n break\n end\n end\n has_match || return false\n end\n return true\nend","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"","category":"page"},{"location":"source/methods/equals/","page":"Equals","title":"Equals","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/transformations/tuples/#Tuple-conversion","page":"Tuple conversion","title":"Tuple conversion","text":"","category":"section"},{"location":"source/transformations/tuples/","page":"Tuple conversion","title":"Tuple conversion","text":"\"\"\"\n tuples(obj)\n\nConvert all points in `obj` to `Tuple`s, wherever the are nested.\n\nReturns a similar object or collection of objects using GeoInterface.jl\ngeometries wrapping `Tuple` points.","category":"page"},{"location":"source/transformations/tuples/","page":"Tuple conversion","title":"Tuple conversion","text":"Keywords","category":"page"},{"location":"source/transformations/tuples/","page":"Tuple conversion","title":"Tuple conversion","text":"$APPLY_KEYWORDS\n\"\"\"\nfunction tuples(geom; kw...)\n if _is3d(geom)\n return apply(PointTrait, geom; kw...) do p\n (Float64(GI.x(p)), Float64(GI.y(p)), Float64(GI.z(p)))\n end\n else\n return apply(PointTrait, geom; kw...) do p\n (Float64(GI.x(p)), Float64(GI.y(p)))\n end\n end\nend","category":"page"},{"location":"source/transformations/tuples/","page":"Tuple conversion","title":"Tuple conversion","text":"","category":"page"},{"location":"source/transformations/tuples/","page":"Tuple conversion","title":"Tuple conversion","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/methods/geom_relations/contains/#Contains","page":"Contains","title":"Contains","text":"","category":"section"},{"location":"source/methods/geom_relations/contains/","page":"Contains","title":"Contains","text":"export contains","category":"page"},{"location":"source/methods/geom_relations/contains/#What-is-contains?","page":"Contains","title":"What is contains?","text":"","category":"section"},{"location":"source/methods/geom_relations/contains/","page":"Contains","title":"Contains","text":"The contains function checks if a given geometry completly contains another geometry, or in other words, that the second geometry is completly within the first. This requires that the two interiors intersect and that the interior and boundary of the second geometry is not in the exterior of the first geometry.","category":"page"},{"location":"source/methods/geom_relations/contains/","page":"Contains","title":"Contains","text":"To provide an example, consider these two lines:","category":"page"},{"location":"source/methods/geom_relations/contains/","page":"Contains","title":"Contains","text":"using GeometryOps\nusing GeometryOps.GeometryBasics\nusing Makie\nusing CairoMakie\n\nl1 = GI.LineString([(0.0, 0.0), (1.0, 0.0), (0.0, 0.1)])\nl2 = GI.LineString([(0.25, 0.0), (0.75, 0.0)])\nf, a, p = lines(GI.getpoint(l1), color = :blue)\nscatter!(GI.getpoint(l1), color = :blue)\nlines!(GI.getpoint(l2), color = :orange)\nscatter!(GI.getpoint(l2), color = :orange)","category":"page"},{"location":"source/methods/geom_relations/contains/","page":"Contains","title":"Contains","text":"We can see that all of the points and edges of l2 are within l1, so l1 contains l2. However, l2 does not contain l1.","category":"page"},{"location":"source/methods/geom_relations/contains/","page":"Contains","title":"Contains","text":"contains(l1, l2) # returns true\ncontains(l2, l1) # returns false","category":"page"},{"location":"source/methods/geom_relations/contains/#Implementation","page":"Contains","title":"Implementation","text":"","category":"section"},{"location":"source/methods/geom_relations/contains/","page":"Contains","title":"Contains","text":"This is the GeoInterface-compatible implementation.","category":"page"},{"location":"source/methods/geom_relations/contains/","page":"Contains","title":"Contains","text":"Given that contains is the exact opposite of within, we simply pass the two inputs variables, swapped in order, to within.","category":"page"},{"location":"source/methods/geom_relations/contains/","page":"Contains","title":"Contains","text":"\"\"\"\n contains(g1::AbstractGeometry, g2::AbstractGeometry)::Bool\n\nReturn true if the second geometry is completely contained by the first\ngeometry. The interiors of both geometries must intersect and the interior and\nboundary of the secondary (g2) must not intersect the exterior of the first\n(g1).\n\n`contains` returns the exact opposite result of `within`.\n\n# Examples\n\n```jldoctest\nimport GeometryOps as GO, GeoInterface as GI\nline = GI.LineString([(1, 1), (1, 2), (1, 3), (1, 4)])\npoint = GI.Point((1, 2))\n\nGO.contains(line, point)","category":"page"},{"location":"source/methods/geom_relations/contains/","page":"Contains","title":"Contains","text":"output","category":"page"},{"location":"source/methods/geom_relations/contains/","page":"Contains","title":"Contains","text":"true\n```\n\"\"\"\ncontains(g1, g2) = GeometryOps.within(g2, g1)","category":"page"},{"location":"source/methods/geom_relations/contains/","page":"Contains","title":"Contains","text":"","category":"page"},{"location":"source/methods/geom_relations/contains/","page":"Contains","title":"Contains","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/methods/area/#Area-and-signed-area","page":"Area and signed area","title":"Area and signed area","text":"","category":"section"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":"export area, signed_area","category":"page"},{"location":"source/methods/area/#What-is-area?-What-is-signed-area?","page":"Area and signed area","title":"What is area? What is signed area?","text":"","category":"section"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":"Area is the amount of space occupied by a two-dimensional figure. It is always a positive value. Signed area is simply the integral over the exterior path of a polygon, minus the sum of integrals over its interior holes. It is signed such that a clockwise path has a positive area, and a counterclockwise path has a negative area. The area is the absolute value of the signed area.","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":"To provide an example, consider this rectangle:","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":"using GeometryOps\nusing GeometryOps.GeometryBasics\nusing Makie\n\nrect = Polygon([Point(0,0), Point(0,1), Point(1,1), Point(1,0), Point(0, 0)])\nf, a, p = poly(rect; axis = (; aspect = DataAspect()))","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":"This is clearly a rectangle, etc. But now let's look at how the points look:","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":"lines!(a, rect; color = 1:length(coordinates(rect))+1)\nf","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":"The points are ordered in a clockwise fashion, which means that the signed area is negative. If we reverse the order of the points, we get a postive area.","category":"page"},{"location":"source/methods/area/#Implementation","page":"Area and signed area","title":"Implementation","text":"","category":"section"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":"This is the GeoInterface-compatible implementation. First, we implement a wrapper method that dispatches to the correct implementation based on the geometry trait. This is also used in the implementation, since it's a lot less work!","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":"Note that area (and signed area) are zero for all points and curves, even if the curves are closed like with a linear ring. Also note that signed area really only makes sense for polygons, given with a multipolygon can have several polygons each with a different orientation and thus the absolute value of the signed area might not be the area. This is why signed area is only implemented for polygons.","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":"\"\"\"\n area(geom, ::Type{T} = Float64)::T\n\nReturns the area of the geometry. This is computed slighly differently for\ndifferent geometries:\n - The area of a point/multipoint is always zero.\n - The area of a curve/multicurve is always zero.\n - The area of a polygon is the absolute value of the signed area.\n - The area multi-polygon is the sum of the areas of all of the sub-polygons.\n - The area of a geometry collection is the sum of the areas of all of the\n sub-geometries.\n\nResult will be of type T, where T is an optional argument with a default value\nof Float64.\n\"\"\"\narea(geom, ::Type{T} = Float64) where T <: AbstractFloat =\n _area(T, GI.trait(geom), geom)\n\n\"\"\"\n signed_area(geom, ::Type{T} = Float64)::T\n\nReturns the signed area of the geometry, based on winding order. This is\ncomputed slighly differently for different geometries:\n - The signed area of a point is always zero.\n - The signed area of a curve is always zero.\n - The signed area of a polygon is computed with the shoelace formula and is\n positive if the polygon coordinates wind clockwise and negative if\n counterclockwise.\n - You cannot compute the signed area of a multipolygon as it doesn't have a\n meaning as each sub-polygon could have a different winding order.\n\nResult will be of type T, where T is an optional argument with a default value\nof Float64.\n\"\"\"\nsigned_area(geom, ::Type{T} = Float64) where T <: AbstractFloat =\n _signed_area(T, GI.trait(geom), geom)","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":"Points, MultiPoints, Curves, MultiCurves","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":"_area(::Type{T}, ::GI.AbstractGeometryTrait, geom) where T = zero(T)\n\n_signed_area(::Type{T}, ::GI.AbstractGeometryTrait, geom) where T = zero(T)","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":"Polygons","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":"_area(::Type{T}, trait::GI.PolygonTrait, poly) where T =\n abs(_signed_area(T, trait, poly))\n\nfunction _signed_area(::Type{T}, ::GI.PolygonTrait, poly) where T\n GI.isempty(poly) && return zero(T)\n s_area = _signed_area(T, GI.getexterior(poly))\n area = abs(s_area)\n area == 0 && return area","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":"Remove hole areas from total","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":" for hole in GI.gethole(poly)\n area -= abs(_signed_area(T, hole))\n end","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":"Winding of exterior ring determines sign","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":" return area * sign(s_area)\nend","category":"page"},{"location":"source/methods/area/#MultiPolygons-and-GeometryCollections","page":"Area and signed area","title":"MultiPolygons and GeometryCollections","text":"","category":"section"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":"_area(\n ::Type{T},\n ::Union{GI.MultiPolygonTrait, GI.GeometryCollectionTrait},\n geoms,\n) where T =\n sum((area(geom, T) for geom in GI.getgeom(geoms)), init = zero(T))","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":"Helper function:","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":"Calculates the signed area of a given curve. This is equivalent to integrating to find the area under the curve. Even if curve isn't explicitly closed by repeating the first point at the end of the coordinates, curve is still assumed to be closed.","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":"function _signed_area(::Type{T}, geom) where T\n area = zero(T)\n np = GI.npoint(geom)\n np == 0 && return area\n\n first = true\n local pfirst, p1","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":"Integrate the area under the curve","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":" for p2 in GI.getpoint(geom)","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":"Skip the first and do it later This lets us work within one iteration over geom, which means on C call when using points from external libraries.","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":" if first\n p1 = pfirst = p2\n first = false\n continue\n end","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":"Accumulate the area into area","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":" area += GI.x(p1) * GI.y(p2) - GI.y(p1) * GI.x(p2)\n p1 = p2\n end","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":"Complete the last edge. If the first and last where the same this will be zero","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":" p2 = pfirst\n area += GI.x(p1) * GI.y(p2) - GI.y(p1) * GI.x(p2)\n return T(area / 2)\nend","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":"","category":"page"},{"location":"source/methods/area/","page":"Area and signed area","title":"Area and signed area","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"#= Code is based off of DE-9IM Standards (https://en.wikipedia.org/wiki/DE-9IM)\nand attempts a standardized solution for most of the functions.\n=#\n\n@enum PointOrientation point_in=1 point_on=2 point_out=3\n\n@enum LineOrientation line_cross=1 line_hinge=2 line_over=3 line_out=4","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"Determines if a point meets the given checks with respect to a curve.","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"If inallow is true, the point can be on the curve interior. If onallow is true, the point can be on the curve boundary. If out_allow is true, the point can be disjoint from the curve.","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"If the point is in an \"allowed\" location, return true. Else, return false.","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"If closed_curve is true, curve is treated as a closed curve where the first and last point are connected by a segment.","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"function _point_curve_process(\n point, curve;\n in_allow, on_allow, out_allow,\n closed_curve = false,\n)","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"Determine if curve is closed","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":" n = GI.npoint(curve)\n first_last_equal = equals(GI.getpoint(curve, 1), GI.getpoint(curve, n))\n closed_curve |= first_last_equal\n n -= first_last_equal ? 1 : 0","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"Loop through all curve segments","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":" p_start = GI.getpoint(curve, closed_curve ? n : 1)\n @inbounds for i in (closed_curve ? 1 : 2):n\n p_end = GI.getpoint(curve, i)\n seg_val = _point_segment_orientation(point, p_start, p_end)\n seg_val == point_in && return in_allow\n if seg_val == point_on\n if !closed_curve # if point is on curve endpoints, it is \"on\"\n i == 2 && equals(point, p_start) && return on_allow\n i == n && equals(point, p_end) && return on_allow\n end\n return in_allow\n end\n p_start = p_end\n end\n return out_allow\nend","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"Determines if a point meets the given checks with respect to a polygon.","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"If inallow is true, the point can be within the polygon interior If onallow is true, the point can be on the polygon boundary. If out_allow is true, the point can be disjoint from the polygon.","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"If the point is in an \"allowed\" location, return true. Else, return false.","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"function _point_polygon_process(\n point, polygon;\n in_allow, on_allow, out_allow,\n)","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"Check interaction of geom with polygon's exterior boundary","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":" ext_val = _point_filled_curve_orientation(point, GI.getexterior(polygon))","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"If a point is outside, it isn't interacting with any holes","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":" ext_val == point_out && return out_allow","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"if a point is on an external boundary, it isn't interacting with any holes","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":" ext_val == point_on && return on_allow","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"If geom is within the polygon, need to check interactions with holes","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":" for hole in GI.gethole(polygon)\n hole_val = _point_filled_curve_orientation(point, hole)","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"If a point in in a hole, it is outside of the polygon","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":" hole_val == point_in && return out_allow","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"If a point in on a hole edge, it is on the edge of the polygon","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":" hole_val == point_on && return on_allow\n end","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"Point is within external boundary and on in/on any holes","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":" return in_allow\nend","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"Determines if a line meets the given checks with respect to a curve.","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"If overallow is true, segments of the line and curve can be co-linear. If crossallow is true, segments of the line and curve can cross. If onallow is true, endpoints of either the line or curve can intersect a segment of the other geometry. If crossallow is true, segments of the line and curve can be disjoint.","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"If inrequire is true, the interiors of the line and curve must meet in at least one point. If onrequire is true, the bounday of one of the two geometries can meet the interior or boundary of the other geometry in at least one point. If out_require is true, there must be at least one point of the given line that is exterior of the curve.","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"If the point is in an \"allowed\" location and meets all requirments, return true. Else, return false.","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"If closedline is true, line is treated as a closed line where the first and last point are connected by a segment. Same with closedcurve.","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"function _line_curve_process(\n line, curve;\n over_allow, cross_allow, on_allow, out_allow,\n in_require, on_require, out_require,\n closed_line = false,\n closed_curve = false,\n)","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"Set up requirments","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":" in_req_met = !in_require\n on_req_met = !on_require\n out_req_met = !out_require","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"Determine curve endpoints","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":" nl = GI.npoint(line)\n nc = GI.npoint(curve)\n first_last_equal_line = equals(GI.getpoint(line, 1), GI.getpoint(line, nl))\n first_last_equal_curve = equals(GI.getpoint(curve, 1), GI.getpoint(curve, nc))\n nl -= first_last_equal_line ? 1 : 0\n nc -= first_last_equal_curve ? 1 : 0\n closed_line |= first_last_equal_line\n closed_curve |= first_last_equal_curve","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"Loop over each line segment","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":" l_start = GI.getpoint(line, closed_line ? nl : 1)\n i = closed_line ? 1 : 2\n while i ≤ nl\n l_end = GI.getpoint(line, i)\n c_start = GI.getpoint(curve, closed_curve ? nc : 1)","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"Loop over each curve segment","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":" for j in (closed_curve ? 1 : 2):nc\n c_end = GI.getpoint(curve, j)","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"Check if line and curve segments meet","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":" seg_val = _segment_segment_orientation((l_start, l_end), (c_start, c_end))","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"If segments are co-linear","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":" if seg_val == line_over\n !over_allow && return false","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"at least one point in, meets requirments","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":" in_req_met = true\n point_val = _point_segment_orientation(l_start, c_start, c_end)","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"If entire segment isn't covered, consider remaining section","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":" if point_val != point_out\n i, l_start, break_off = _find_new_seg(i, l_start, l_end, c_start, c_end)\n break_off && break\n end\n else\n if seg_val == line_cross\n !cross_allow && return false\n in_req_met = true\n elseif seg_val == line_hinge # could cross or overlap","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"Determine location of intersection point on each segment","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":" (α, β) = _find_intersect_fracs(l_start, l_end, c_start, c_end)\n if ( # Don't consider edges of curves as they can't cross\n (!closed_line && ((α == 0 && i == 2) || (α == 1 && i == nl))) ||\n (!closed_curve && ((β == 0 && j == 2) || (β == 1 && j == nc)))\n )\n !on_allow && return false\n on_req_met = true\n else\n in_req_met = true","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"If needed, determine if hinge actually crosses","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":" if (!cross_allow || !over_allow) && α != 0 && β != 0","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"Find next pieces of hinge to see if line and curve cross","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":" l, c = _find_hinge_next_segments(\n α, β, l_start, l_end, c_start, c_end,\n i, line, j, curve,\n )\n if _segment_segment_orientation(l, c) == line_hinge\n !cross_allow && return false\n else\n !over_allow && return false\n end\n end\n end\n end","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"no overlap for a give segment, some of segment must be out of curve","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":" if j == nc\n !out_allow && return false\n out_req_met = true\n end\n end\n c_start = c_end # consider next segment of curve\n if j == nc # move on to next line segment\n i += 1\n l_start = l_end\n end\n end\n end\n return in_req_met && on_req_met && out_req_met\nend\n\n#= If entire segment (le to ls) isn't covered by segment (cs to ce), find remaining section\npart of section outside of cs to ce. If completly covered, increase segment index i. =#\nfunction _find_new_seg(i, ls, le, cs, ce)\n break_off = true\n if _point_segment_orientation(le, cs, ce) != point_out\n ls = le\n i += 1\n elseif !equals(ls, cs) && _point_segment_orientation(cs, ls, le) != point_out\n ls = cs\n elseif !equals(ls, ce) && _point_segment_orientation(ce, ls, le) != point_out\n ls = ce\n else\n break_off = false\n end\n return i, ls, break_off\nend\n\n#= Find where line and curve segments intersect by fraction of length. α is the fraction of\nthe line (ls to le) and β is the traction of the curve (cs to ce). =#\nfunction _find_intersect_fracs(ls, le, cs, ce)\n _, fracs = _intersection_point(\n (_tuple_point(ls), _tuple_point(le)),\n (_tuple_point(cs), _tuple_point(ce))\n )\n (α, β) = if !isnothing(fracs)\n fracs\n else # line and curve segments are parallel\n if equals(ls, cs)\n (0, 0)\n elseif equals(ls, ce)\n (0, 1)\n elseif equals(le, cs)\n (1, 0)\n else # equals(l_end, c_end)\n (1, 1)\n end\n end\n return α, β\nend\n\n#= Find next set of segments needed to determine if given hinge segments cross or not.=#\n_find_hinge_next_segments(α, β, ls, le, cs, ce, i, line, j, curve) =\n if β == 1\n if α == 1 # hinge at endpoints, so next segment of both is needed\n ((le, GI.getpoint(line, i + 1)), (ce, GI.getpoint(curve, j + 1)))\n else # hinge at curve endpoint and line interior point, curve next segment needed\n ((ls, le), (ce, GI.getpoint(curve, j + 1)))\n end\n else # hinge at curve interior point and line endpoint, line next segment needed\n ((le, GI.getpoint(line, i + 1)), (cs, ce))\n end","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"Determines if a line meets the given checks with respect to a polygon.","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"If inallow is true, segments of the line can be in the polygon interior. If onallow is true, segments of the line can be on the polygon's boundary. If out_allow is true, segments of the line can be outside of the polygon.","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"If inrequire is true, the interiors of the line and polygon must meet in at least one point. If onrequire is true, the line must have at least one point on the polygon'same boundary. If out_require is true, the line must have at least one point outside of the polygon.","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"If the point is in an \"allowed\" location and meets all requirments, return true. Else, return false.","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"If closed_line is true, line is treated as a closed line where the first and last point are connected by a segment.","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"function _line_polygon_process(\n line, polygon;\n in_allow, on_allow, out_allow,\n in_require, on_require, out_require,\n closed_line = false,\n)\n in_req_met = !in_require\n on_req_met = !on_require\n out_req_met = !out_require","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"Check interaction of line with polygon's exterior boundary","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":" in_curve, on_curve, out_curve = _line_filled_curve_interactions(\n line, GI.getexterior(polygon);\n closed_line = closed_line,\n )\n if on_curve\n !on_allow && return false\n on_req_met = true\n end\n if out_curve\n !out_allow && return false\n out_req_met = true\n end","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"If no points within the polygon, the line is disjoint and we are done","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":" !in_curve && return in_req_met && on_req_met && out_req_met","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"Loop over polygon holes","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":" for hole in GI.gethole(polygon)\n in_hole, on_hole, out_hole =_line_filled_curve_interactions(\n line, hole;\n closed_line = closed_line,\n )\n if in_hole # line in hole is equivalent to being out of polygon\n !out_allow && return false\n out_req_met = true\n end\n if on_hole # hole bounday is polygon boundary\n !on_allow && return false\n on_req_met = true\n end\n if !out_hole # entire line is in/on hole, can't be in/on other holes\n in_curve = false\n break\n end\n end\n if in_curve # entirely of curve isn't within a hole\n !in_allow && return false\n in_req_met = true\n end\n return in_req_met && on_req_met && out_req_met\nend","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"Determines if a polygon meets the given checks with respect to a polygon.","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"If inallow is true, the polygon's interiors must intersect. If onallow is true, the one of the polygon's boundaries must either interact with the other polygon's boundary or interior. If out_allow is true, the first polygon must have interior regions outside of the second polygon.","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"If inrequire is true, the polygon interiors must meet in at least one point. If onrequire is true, one of the polygon's must have at least one boundary point in or on the other polygon. If out_require is true, the first polygon must have at least one interior point outside of the second polygon.","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"If the point is in an \"allowed\" location and meets all requirments, return true. Else, return false.","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"function _polygon_polygon_process(\n poly1, poly2;\n in_allow, on_allow, out_allow,\n in_require, on_require, out_require,\n)\n in_req_met = !in_require\n on_req_met = !on_require\n out_req_met = !out_require","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"Check if exterior of poly1 is within poly2","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":" ext1 = GI.getexterior(poly1)\n ext2 = GI.getexterior(poly2)","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"Check if exterior of poly1 is in polygon 2","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":" e1_in_p2, e1_on_p2, e1_out_p2 = _line_polygon_interactions(\n ext1, poly2;\n closed_line = true,\n )\n if e1_on_p2\n !on_allow && return false\n on_req_met = true\n end\n if e1_out_p2\n !out_allow && return false\n out_req_met = true\n end\n\n if !e1_in_p2","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"if exterior ring isn't in poly2, check if it surrounds poly2","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":" _, _, e2_out_e1 = _line_filled_curve_interactions(\n ext2, ext1;\n closed_line = true,\n ) # if they really are disjoint, we are done\n e2_out_e1 && return in_req_met && on_req_met && out_req_met\n end","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"If interiors interact, check if poly2 interacts with any of poly1's holes","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":" for h1 in GI.gethole(poly1)\n h1_in_p2, h1_on_p2, h1_out_p2 = _line_polygon_interactions(\n h1, poly2;\n closed_line = true,\n )\n if h1_on_p2\n !on_allow && return false\n on_req_met = true\n end\n if h1_out_p2\n !out_allow && return false\n out_req_met = true\n end\n if !h1_in_p2","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"If hole isn't in poly2, see if poly2 is in hole","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":" _, _, e2_out_h1 = _line_filled_curve_interactions(\n ext2, h1;\n closed_line = true,\n )","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"hole encompasses all of poly2","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":" !e2_out_h1 && return in_req_met && on_req_met && out_req_met\n break\n end\n end\n #=\n poly2 isn't outside of poly1 and isn't in a hole, poly1 interior must\n interact with poly2 interior\n =#\n !in_allow && return false\n in_req_met = true","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"If any of poly2 holes are within poly1, part of poly1 is exterior to poly2","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":" for h2 in GI.gethole(poly2)\n h2_in_p1, h2_on_p1, _ = _line_polygon_interactions(\n h2, poly1;\n closed_line = true,\n )\n if h2_on_p1\n !on_allow && return false\n on_req_met = true\n end\n if h2_in_p1\n !out_allow && return false\n out_req_met = true\n end\n end\n return in_req_met && on_req_met && out_req_met\nend","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"Determines if a point is in, on, or out of a segment. If the point is on the segment it is on one of the segments endpoints. If it is in, it is on any other point of the segment. If the point is not on any part of the segment, it is out of the segment.","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"Point should be an object of point trait and curve should be an object with a linestring or linearring trait.","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"Can provide values of in, on, and out keywords, which determines return values for each scenario.","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"function _point_segment_orientation(\n point, start, stop;\n in::T = point_in, on::T = point_on, out::T = point_out,\n) where {T}","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"Parse out points","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":" x, y = GI.x(point), GI.y(point)\n x1, y1 = GI.x(start), GI.y(start)\n x2, y2 = GI.x(stop), GI.y(stop)\n Δx_seg = x2 - x1\n Δy_seg = y2 - y1\n Δx_pt = x - x1\n Δy_pt = y - y1\n if (Δx_pt == 0 && Δy_pt == 0) || (Δx_pt == Δx_seg && Δy_pt == Δy_seg)","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"If point is equal to the segment start or end points","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":" return on\n else\n #=\n Determine if the point is on the segment -> see if vector from segment\n start to point is parallel to segment and if point is between the\n segment endpoints\n =#\n on_line = _isparallel(Δx_seg, Δy_seg, Δx_pt, Δy_pt)\n !on_line && return out\n between_endpoints =\n (x2 > x1 ? x1 <= x <= x2 : x2 <= x <= x1) &&\n (y2 > y1 ? y1 <= y <= y2 : y2 <= y <= y1)\n !between_endpoints && return out\n end\n return in\nend","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"Determine if point is in, on, or out of a closed curve, which includes the space enclosed by the closed curve.","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"In means the point is within the closed curve (excluding edges and vertices). On means the point is on an edge or a vertex of the closed curve. Out means the point is outside of the closed curve.","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"Point should be an object of point trait and curve should be an object with a linestring or linearring trait, that is assumed to be closed, regardless of repeated last point.","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"Can provide values of in, on, and out keywords, which determines return values for each scenario.","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"Note that this uses the Algorithm by Hao and Sun (2018): https://doi.org/10.3390/sym10100477 Paper seperates orientation of point and edge into 26 cases. For each case, it is either a case where the point is on the edge (returns on), where a ray from the point (x, y) to infinity along the line y = y cut through the edge (k += 1), or the ray does not pass through the edge (do nothing and continue). If the ray passes through an odd number of edges, it is within the curve, else outside of of the curve if it didn't return 'on'. See paper for more information on cases denoted in comments.","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"function _point_filled_curve_orientation(\n point, curve;\n in::T = point_in, on::T = point_on, out::T = point_out,\n) where {T}\n x, y = GI.x(point), GI.y(point)\n n = GI.npoint(curve)\n n -= equals(GI.getpoint(curve, 1), GI.getpoint(curve, n)) ? 1 : 0\n k = 0 # counter for ray crossings\n p_start = GI.getpoint(curve, n)\n @inbounds for i in 1:n\n p_end = GI.getpoint(curve, i)\n v1 = GI.y(p_start) - y\n v2 = GI.y(p_end) - y\n if !((v1 < 0 && v2 < 0) || (v1 > 0 && v2 > 0)) # if not cases 11 or 26\n u1 = GI.x(p_start) - x\n u2 = GI.x(p_end) - x\n c1 = u1 * v2 # first element of cross product summation\n c2 = u2 * v1 # second element of cross product summation\n f = c1 - c2\n if v2 > 0 && v1 ≤ 0 # Case 3, 9, 16, 21, 13, or 24\n (c1 ≈ c2) && return on # Case 16 or 21\n f > 0 && (k += 1) # Case 3 or 9\n elseif v1 > 0 && v2 ≤ 0 # Case 4, 10, 19, 20, 12, or 25\n (c1 ≈ c2) && return on # Case 19 or 20\n f < 0 && (k += 1) # Case 4 or 10\n elseif v2 == 0 && v1 < 0 # Case 7, 14, or 17\n (c1 ≈ c2) && return on # Case 17\n elseif v1 == 0 && v2 < 0 # Case 8, 15, or 18\n (c1 ≈ c2) && return on # Case 18\n elseif v1 == 0 && v2 == 0 # Case 1, 2, 5, 6, 22, or 23\n u2 ≤ 0 && u1 ≥ 0 && return on # Case 1\n u1 ≤ 0 && u2 ≥ 0 && return on # Case 2\n end\n end\n p_start = p_end\n end\n return iseven(k) ? out : in\nend","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"Determines the type of interaction between two line segments. If the segments cross, this means that they have a single intersection point that isn't on either of their enpoints. If they form a hinge, they meet at one of the segments endpoints. If they are over, then they are co-linear for at least some of the length of the segments. Finally, if they are out, then the segments are disjoint.","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"Point should be an object of point trait and curve should be an object with a linestring or linearring trait.","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"Can provide values of in, on, and out keywords, which determines return values for each scenario.","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"function _segment_segment_orientation(\n (a_point, b_point), (c_point, d_point);\n cross::T = line_cross, hinge::T = line_hinge,\n over::T = line_over, out::T = line_out,\n) where T\n (ax, ay) = Float64.(_tuple_point(a_point))\n (bx, by) = Float64.(_tuple_point(b_point))\n (cx, cy) = Float64.(_tuple_point(c_point))\n (dx, dy) = Float64.(_tuple_point(d_point))\n meet_type = ExactPredicates.meet((ax, ay), (bx, by), (cx, cy), (dx, dy))","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"Lines meet at one point within open segments","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":" meet_type == 1 && return cross","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"Lines don't meet at any points","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":" meet_type == -1 && return out","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"Lines meet at one or more points within closed segments","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":" if _isparallel(((ax, ay), (bx, by)), ((cx, cy), (dx, dy)))\n min_x, max_x = cx < dx ? (cx, dx) : (dx, cx)\n min_y, max_y = cy < dy ? (cy, dy) : (dy, cy)\n if (\n ((ax ≤ min_x && bx ≤ min_x) || (ax ≥ max_x && bx ≥ max_x)) &&\n ((ay ≤ min_y && by ≤ min_y) || (ay ≥ max_y && by ≥ max_y))\n )","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"apoint and bpoint are on the same side of segment, don't overlap","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":" return hinge\n else\n return over\n end\n end","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"if lines aren't parallel then they must hinge","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":" return hinge\nend","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"Determines the types of interactions of a line with a filled-in curve. By filled-in curve, I am referring to the exterior ring of a poylgon, for example.","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"Returns a tuple of booleans: (incurve, oncurve, out_curve).","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"If incurve is true, some of the lines interior points interact with the curve's interior points. If oncurve is true, endpoints of either the line intersect with the curve or the line interacts with the polygon boundary. If out_curve is true, at least one segments of the line is outside the curve.","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"If closed_line is true, line is treated as a closed line where the first and last point are connected by a segment.","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"function _line_filled_curve_interactions(\n line, curve;\n closed_line = false,\n)\n in_curve = false\n on_curve = false\n out_curve = false","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"Determine number of points in curve and line","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":" nl = GI.npoint(line)\n nc = GI.npoint(curve)\n first_last_equal_line = equals(GI.getpoint(line, 1), GI.getpoint(line, nl))\n first_last_equal_curve = equals(GI.getpoint(curve, 1), GI.getpoint(curve, nc))\n nl -= first_last_equal_line ? 1 : 0\n nc -= first_last_equal_curve ? 1 : 0\n closed_line |= first_last_equal_line","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"See if first point is in an acceptable orientation","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":" l_start = GI.getpoint(line, closed_line ? nl : 1)\n point_val = _point_filled_curve_orientation(l_start, curve)\n if point_val == point_in\n in_curve = true\n elseif point_val == point_on\n on_curve = true\n else # point_val == point_out\n out_curve = true\n end","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"Check for any intersections between line and curve","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":" for i in (closed_line ? 1 : 2):nl\n l_end = GI.getpoint(line, i)\n c_start = GI.getpoint(curve, nc)","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"If already interacted with all regions of curve, can stop","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":" in_curve && on_curve && out_curve && break","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"Check next segment of line against curve","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":" for j in 1:nc\n c_end = GI.getpoint(curve, j)","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"Check if two line and curve segments meet","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":" seg_val = _segment_segment_orientation(\n (l_start, l_end),\n (c_start, c_end),\n )\n if seg_val != line_out","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"If line and curve meet, then at least one point is on boundary","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":" on_curve = true\n if seg_val == line_cross","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"When crossing boundary, line is both in and out of curve","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":" in_curve = true\n out_curve = true\n else\n if seg_val == line_over\n sp = _point_segment_orientation(l_start, c_start, c_end)\n lp = _point_segment_orientation(l_end, c_start, c_end)\n if sp != point_in || lp != point_in\n #=\n Line crosses over segment endpoint, creating a hinge\n with another segment.\n =#\n seg_val = line_hinge\n end\n end\n if seg_val == line_hinge\n #=\n Can't determine all types of interactions (in, out) with\n hinge as it could pass through multiple other segments\n so calculate if segment endpoints and intersections are\n in/out of filled curve\n =#\n ipoints = intersection_points(\n GI.Line([l_start, l_end]),\n curve\n )\n npoints = length(ipoints) # since hinge, at least one\n sort!(ipoints, by = p -> _euclid_distance(Float64, p, l_start))\n p_start = _tuple_point(l_start)\n for i in 1:(npoints + 1)\n p_end = i ≤ npoints ?\n ipoints[i] :\n _tuple_point(l_end)\n mid_val = _point_filled_curve_orientation(\n (p_start .+ p_end) ./ 2,\n curve,\n )\n if mid_val == point_in\n in_curve = true\n elseif mid_val == point_out\n out_curve = true\n end\n end","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"already checked segment against whole filled curve","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":" l_start = l_end\n break\n end\n end\n end\n c_start = c_end\n end\n l_start = l_end\n end\n return in_curve, on_curve, out_curve\nend","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"Determines the types of interactions of a line with a polygon.","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"Returns a tuple of booleans: (inpoly, onpoly, out_poly).","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"If inpoly is true, some of the lines interior points interact with the polygon interior points. If inpoly is true, endpoints of either the line intersect with the polygon or the line interacts with the polygon boundary, including hole bounaries. If out_curve is true, at least one segments of the line is outside the polygon, including inside of holes.","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"If closed_line is true, line is treated as a closed line where the first and last point are connected by a segment.","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"function _line_polygon_interactions(\n line, polygon;\n closed_line = false,\n)\n in_poly, on_poly, out_poly = _line_filled_curve_interactions(\n line, GI.getexterior(polygon);\n closed_line = closed_line,\n )\n !in_poly && return (in_poly, on_poly, out_poly)","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"Loop over polygon holes","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":" for hole in GI.gethole(polygon)\n in_hole, on_hole, out_hole =_line_filled_curve_interactions(\n line, hole;\n closed_line = closed_line,\n )\n if in_hole\n out_poly = true\n end\n if on_hole\n on_poly = true\n end\n if !out_hole # entire line is in/on hole, can't be in/on other holes\n in_poly = false\n return (in_poly, on_poly, out_poly)\n end\n end\n return in_poly, on_poly, out_poly\nend\n\nfunction _point_in_extent(p, extent::Extents.Extent)\n (x1, x2), (y1, y2) = extent.X, extent.Y\n return x1 ≤ GI.x(p) ≤ x2 && y1 ≤ GI.y(p) ≤ y2\nend","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"","category":"page"},{"location":"source/methods/geom_relations/geom_geom_processors/","page":"-","title":"-","text":"This page was generated using Literate.jl.","category":"page"},{"location":"","page":"Home","title":"Home","text":"CurrentModule = GeometryOps","category":"page"},{"location":"#GeometryOps","page":"Home","title":"GeometryOps","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Documentation for GeometryOps.","category":"page"},{"location":"","page":"Home","title":"Home","text":"","category":"page"},{"location":"","page":"Home","title":"Home","text":"Modules = [GeometryOps]","category":"page"},{"location":"#GeometryOps.AbstractBarycentricCoordinateMethod","page":"Home","title":"GeometryOps.AbstractBarycentricCoordinateMethod","text":"abstract type AbstractBarycentricCoordinateMethod\n\nAbstract supertype for barycentric coordinate methods. The subtypes may serve as dispatch types, or may cache some information about the target polygon. \n\nAPI\n\nThe following methods must be implemented for all subtypes:\n\nbarycentric_coordinates!(λs::Vector{<: Real}, method::AbstractBarycentricCoordinateMethod, exterior::Vector{<: Point{2, T1}}, point::Point{2, T2})\nbarycentric_interpolate(method::AbstractBarycentricCoordinateMethod, exterior::Vector{<: Point{2, T1}}, values::Vector{V}, point::Point{2, T2})::V\nbarycentric_interpolate(method::AbstractBarycentricCoordinateMethod, exterior::Vector{<: Point{2, T1}}, interiors::Vector{<: Vector{<: Point{2, T1}}} values::Vector{V}, point::Point{2, T2})::V\n\nThe rest of the methods will be implemented in terms of these, and have efficient dispatches for broadcasting.\n\n\n\n\n\n","category":"type"},{"location":"#GeometryOps.DouglasPeucker","page":"Home","title":"GeometryOps.DouglasPeucker","text":"DouglasPeucker <: SimplifyAlg\n\nDouglasPeucker(; number, ratio, tol)\n\nSimplifies geometries by removing points below tol distance from the line between its neighboring points.\n\nKeywords\n\nratio: the fraction of points that should remain after simplify. Useful as it will generalise for large collections of objects.\nnumber: the number of points that should remain after simplify. Less useful for large collections of mixed size objects.\ntol: the minimum distance a point will be from the line joining its neighboring points.\n\n\n\n\n\n","category":"type"},{"location":"#GeometryOps.MeanValue","page":"Home","title":"GeometryOps.MeanValue","text":"MeanValue() <: AbstractBarycentricCoordinateMethod\n\nThis method calculates barycentric coordinates using the mean value method.\n\nReferences\n\n\n\n\n\n","category":"type"},{"location":"#GeometryOps.RadialDistance","page":"Home","title":"GeometryOps.RadialDistance","text":"RadialDistance <: SimplifyAlg\n\nSimplifies geometries by removing points less than tol distance from the line between its neighboring points.\n\nKeywords\n\nratio: the fraction of points that should remain after simplify. Useful as it will generalise for large collections of objects.\nnumber: the number of points that should remain after simplify. Less useful for large collections of mixed size objects.\ntol: the minimum distance between points.\n\n\n\n\n\n","category":"type"},{"location":"#GeometryOps.SimplifyAlg","page":"Home","title":"GeometryOps.SimplifyAlg","text":"abstract type SimplifyAlg\n\nAbstract type for simplification algorithms.\n\nAPI\n\nFor now, the algorithm must hold the number, ratio and tol properties. \n\nSimplification algorithm types can hook into the interface by implementing the _simplify(trait, alg, geom) methods for whichever traits are necessary.\n\n\n\n\n\n","category":"type"},{"location":"#GeometryOps.VisvalingamWhyatt","page":"Home","title":"GeometryOps.VisvalingamWhyatt","text":"VisvalingamWhyatt <: SimplifyAlg\n\nVisvalingamWhyatt(; kw...)\n\nSimplifies geometries by removing points below tol distance from the line between its neighboring points.\n\nKeywords\n\nratio: the fraction of points that should remain after simplify. Useful as it will generalise for large collections of objects.\nnumber: the number of points that should remain after simplify. Less useful for large collections of mixed size objects.\ntol: the minimum area of a triangle made with a point and its neighboring points.\n\n\n\n\n\n","category":"type"},{"location":"#GeometryOps._det-Union{Tuple{T2}, Tuple{T1}, Tuple{Union{Tuple{T1, T1}, StaticArraysCore.StaticArray{Tuple{2}, T1, 1}}, Union{Tuple{T2, T2}, StaticArraysCore.StaticArray{Tuple{2}, T2, 1}}}} where {T1<:Real, T2<:Real}","page":"Home","title":"GeometryOps._det","text":"_det(s1::Point2{T1}, s2::Point2{T2}) where {T1 <: Real, T2 <: Real}\n\nReturns the determinant of the matrix formed by hcat'ing two points s1 and s2.\n\nSpecifically, this is: \n\ns1[1] * s2[2] - s1[2] * s2[1]\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps._equals_curves-NTuple{4, Any}","page":"Home","title":"GeometryOps._equals_curves","text":"_equals_curves(c1, c2, closed_type1, closed_type2)::Bool\n\nTwo 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 closedtype1::Bool true if c1 is closed by definition (polygon, linear ring) closedtype2::Bool true if c2 is closed by definition (polygon, linear ring)\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps._intersection_point-Tuple{Tuple, Tuple}","page":"Home","title":"GeometryOps._intersection_point","text":"_intersection_point(\n (a1, a2)::Tuple,\n (b1, b2)::Tuple,\n)\n\nCalculates the intersection point between two lines if it exists, and as if the line extended to infinity, and the fractional component of each line from the initial end point to the intersection point. Inputs: (a1, a2)::Tuple{Tuple{::Real, ::Real}, Tuple{::Real, ::Real}} first line (b1, b2)::Tuple{Tuple{::Real, ::Real}, Tuple{::Real, ::Real}} second line Outputs: (x, y)::Tuple{::Real, ::Real} intersection point (t, u)::Tuple{::Real, ::Real} fractional length of lines to intersection Both are ::Nothing if point doesn't exist!\n\nCalculation derivation can be found here: https://stackoverflow.com/questions/563198/\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.apply-Union{Tuple{Target}, Tuple{Any, Type{Target}, Any}} where Target","page":"Home","title":"GeometryOps.apply","text":"apply(f, target::Type{<:AbstractTrait}, obj; kw...)\n\nReconstruct a geometry, feature, feature collection, or nested vectors of either using the function f on the target trait.\n\nf(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.\n\nObjects \"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. But \"deeper\" objects may remain unchanged - such as points and linear rings if the target is the same PolygonTrait.\n\nThe result is a functionally similar geometry with values depending on f\n\nthreaded: true or false. Whether to use multithreading. Defaults to false.\ncrs: The CRS to attach to geometries. Defaults to nothing.\ncalc_extent: true or false. Whether to calculate the extent. Defaults to false.\n\nExample\n\nFlipped point the order in any feature or geometry, or iterables of either:\n\n```juia 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)])])\n\nflipped_geom = GO.apply(GI.PointTrait, geom) do p (GI.y(p), GI.x(p)) end\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.area-Union{Tuple{Any}, Tuple{T}, Tuple{Any, Type{T}}} where T<:AbstractFloat","page":"Home","title":"GeometryOps.area","text":"area(geom, ::Type{T} = Float64)::T\n\nReturns the area of the geometry. This is computed slighly 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 is the sum of the areas of all of the sub-geometries. \n\nResult will be of type T, where T is an optional argument with a default value of Float64.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.centroid-Tuple{Any, Any}","page":"Home","title":"GeometryOps.centroid","text":"centroid(trait, geom)::Tuple{T, T}\n\nReturns the centroid of a polygon or multipolygon, which is calculated by weighting edges by their area component by convention.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.centroid-Tuple{Any}","page":"Home","title":"GeometryOps.centroid","text":"centroid(geom)::Tuple{T, T}\n\nReturns the centroid of a given line segment, linear ring, polygon, or mutlipolygon.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.centroid-Tuple{Union{GeoInterface.LineStringTrait, GeoInterface.LinearRingTrait}, Any}","page":"Home","title":"GeometryOps.centroid","text":"centroid(\n trait::Union{GI.LineStringTrait, GI.LinearRingTrait},\n geom,\n)::Tuple{T, T}\n\nReturns the centroid of a line string or linear ring, which is calculated by weighting line segments by their length by convention.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.centroid_and_area-Tuple{Any}","page":"Home","title":"GeometryOps.centroid_and_area","text":"centroid_and_area(\n ::Union{GI.LineStringTrait, GI.LinearRingTrait}, \n geom,\n)::(::Tuple{T, T}, ::Real)\n\nReturns the centroid and area of a given geom.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.centroid_and_area-Tuple{GeoInterface.MultiPolygonTrait, Any}","page":"Home","title":"GeometryOps.centroid_and_area","text":"centroid_and_area(::GI.MultiPolygonTrait, geom)::(::Tuple{T, T}, ::Real)\n\nReturns the centroid and area of a given multipolygon.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.centroid_and_area-Tuple{GeoInterface.PolygonTrait, Any}","page":"Home","title":"GeometryOps.centroid_and_area","text":"centroid_and_area(::GI.PolygonTrait, geom)::(::Tuple{T, T}, ::Real)\n\nReturns the centroid and area of a given polygon.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.centroid_and_area-Tuple{Union{GeoInterface.LineStringTrait, GeoInterface.LinearRingTrait}, Any}","page":"Home","title":"GeometryOps.centroid_and_area","text":"centroid_and_area(\n ::Union{GI.LineStringTrait, GI.LinearRingTrait},\n geom,\n)::(::Tuple{T, T}, ::Real)\n\nReturns the centroid and area of a given a line string or a linear ring. Note that this is only valid if the line segment or linear ring is closed. \n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.centroid_and_length-Tuple{Any}","page":"Home","title":"GeometryOps.centroid_and_length","text":"centroid_and_length(geom)::(::Tuple{T, T}, ::Real)\n\nReturns the centroid and length of a given line/ring. Note this is only valid for line strings and linear rings.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.centroid_and_length-Tuple{Union{GeoInterface.LineStringTrait, GeoInterface.LinearRingTrait}, Any}","page":"Home","title":"GeometryOps.centroid_and_length","text":"centroid_and_length(geom)::(::Tuple{T, T}, ::Real)\n\nReturns the centroid and length of a given line/ring. Note this is only valid for line strings and linear rings.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.contains-Tuple{Any, Any}","page":"Home","title":"GeometryOps.contains","text":"contains(g1::AbstractGeometry, g2::AbstractGeometry)::Bool\n\nReturn 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).\n\ncontains returns the exact opposite result of within.\n\nExamples\n\nimport GeometryOps as GO, GeoInterface as GI\nline = GI.LineString([(1, 1), (1, 2), (1, 3), (1, 4)])\npoint = GI.Point((1, 2))\n\nGO.contains(line, point)\n# output\ntrue\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.coveredby-Tuple{Any, Any}","page":"Home","title":"GeometryOps.coveredby","text":"coveredby(g1, g2)::Bool\n\nReturn 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).\n\nFurthermore, coveredby returns the exact opposite result of covers. They are equivalent with the order of the arguments swapped.\n\nExamples\n\nimport GeometryOps as GO, GeoInterface as GI\np1 = GI.Point(0.0, 0.0)\np2 = GI.Point(1.0, 1.0)\nl1 = GI.Line([p1, p2])\n\nGO.coveredby(p1, l1)\n# output\ntrue\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.covers-Tuple{Any, Any}","page":"Home","title":"GeometryOps.covers","text":"covers(g1::AbstractGeometry, g2::AbstractGeometry)::Bool\n\nReturn 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.\n\ncovers returns the exact opposite result of coveredby.\n\nExamples\n\nimport GeometryOps as GO, GeoInterface as GI\nl1 = GI.LineString([(1, 1), (1, 2), (1, 3), (1, 4)])\nl2 = GI.LineString([(1, 1), (1, 2)])\n\nGO.covers(l1, l2)\n# output\ntrue\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.crosses-Tuple{Any, Any}","page":"Home","title":"GeometryOps.crosses","text":" crosses(geom1, geom2)::Bool\n\nReturn 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.\n\nTODO: broken\n\nExamples\n\nimport GeoInterface as GI, GeometryOps as GO\n\nline1 = GI.LineString([(1, 1), (1, 2), (1, 3), (1, 4)])\nline2 = GI.LineString([(-2, 2), (4, 2)])\n\nGO.crosses(line1, line2)\n# output\ntrue\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.disjoint-Tuple{Any, Any}","page":"Home","title":"GeometryOps.disjoint","text":"disjoint(geom1, geom2)::Bool\n\nReturn true if the first geometry is disjoint from the second geometry.\n\nReturn true if the first geometry is disjoint from the second geometry. The interiors and boundaries of both geometries must not intersect.\n\nExamples\n\nimport GeometryOps as GO, GeoInterface as GI\n\nline = GI.LineString([(1, 1), (1, 2), (1, 3), (1, 4)])\npoint = (2, 2)\nGO.disjoint(point, line)\n\n# output\ntrue\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.distance-Union{Tuple{T}, Tuple{Any, Any}, Tuple{Any, Any, Type{T}}} where T<:AbstractFloat","page":"Home","title":"GeometryOps.distance","text":"distance(point, geom, ::Type{T} = Float64)::T\n\nCalculates the ditance from the geometry g1 to the point. The distance will always be positive or zero.\n\nThe 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.\n\nResult will be of type T, where T is an optional argument with a default value of Float64.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.embed_extent-Tuple{Any}","page":"Home","title":"GeometryOps.embed_extent","text":"embed_extent(obj)\n\nRecursively wrap the object with a GeoInterface.jl geometry, calculating and adding an Extents.Extent to all objects.\n\nThis 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.\n\nKeywords\n\nthreaded: true or false. Whether to use multithreading. Defaults to false.\ncrs: The CRS to attach to geometries. Defaults to nothing.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.equals-NTuple{4, Any}","page":"Home","title":"GeometryOps.equals","text":"equals(trait_a, geom_a, trait_b, geom_b)\n\nTwo geometries which are not of the same type cannot be equal so they always return false.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.equals-Tuple{Any, Any}","page":"Home","title":"GeometryOps.equals","text":"equals(geom1, geom2)::Bool\n\nCompare two Geometries return true if they are the same geometry.\n\nExamples\n\nimport GeometryOps as GO, GeoInterface as GI\npoly1 = GI.Polygon([[(0,0), (0,5), (5,5), (5,0), (0,0)]])\npoly2 = GI.Polygon([[(0,0), (0,5), (5,5), (5,0), (0,0)]])\n\nGO.equals(poly1, poly2)\n# output\ntrue\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.equals-Tuple{GeoInterface.LinearRingTrait, Any, GeoInterface.LinearRingTrait, Any}","page":"Home","title":"GeometryOps.equals","text":"equals(\n ::GI.LinearRingTrait, l1,\n ::GI.LinearRingTrait, l2,\n)::Bool\n\nTwo 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.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.equals-Tuple{GeoInterface.LinearRingTrait, Any, Union{GeoInterface.LineStringTrait, GeoInterface.LineTrait}, Any}","page":"Home","title":"GeometryOps.equals","text":"equals(\n ::GI.LinearRingTrait, l1,\n ::Union{GI.LineTrait, GI.LineStringTrait}, l2,\n)::Bool\n\nA 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 defintion, but rings are, so the line must have a repeated last point to be equal\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.equals-Tuple{GeoInterface.MultiPointTrait, Any, GeoInterface.MultiPointTrait, Any}","page":"Home","title":"GeometryOps.equals","text":"equals(::GI.MultiPointTrait, mp1, ::GI.MultiPointTrait, mp2)::Bool\n\nTwo multipoints are equal if they share the same set of points.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.equals-Tuple{GeoInterface.MultiPointTrait, Any, GeoInterface.PointTrait, Any}","page":"Home","title":"GeometryOps.equals","text":"equals(::GI.MultiPointTrait, mp1, ::GI.PointTrait, p2)::Bool\n\nA point and a multipoint are equal if the multipoint is composed of a single point that is equivalent to the given point.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.equals-Tuple{GeoInterface.MultiPolygonTrait, Any, GeoInterface.MultiPolygonTrait, Any}","page":"Home","title":"GeometryOps.equals","text":"equals(::GI.PolygonTrait, geom_a, ::GI.PolygonTrait, geom_b)::Bool\n\nTwo multipolygons are equal if they share the same set of polygons.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.equals-Tuple{GeoInterface.MultiPolygonTrait, Any, GeoInterface.PolygonTrait, Any}","page":"Home","title":"GeometryOps.equals","text":"equals(::GI.MultiPolygonTrait, geom_a, ::GI.PolygonTrait, geom_b)::Bool\n\nA polygon and a multipolygon are equal if the multipolygon is composed of a single polygon that is equivalent to the given polygon.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.equals-Tuple{GeoInterface.PointTrait, Any, GeoInterface.MultiPointTrait, Any}","page":"Home","title":"GeometryOps.equals","text":"equals(::GI.PointTrait, p1, ::GI.MultiPointTrait, mp2)::Bool\n\nA point and a multipoint are equal if the multipoint is composed of a single point that is equivalent to the given point.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.equals-Tuple{GeoInterface.PointTrait, Any, GeoInterface.PointTrait, Any}","page":"Home","title":"GeometryOps.equals","text":"equals(::GI.PointTrait, p1, ::GI.PointTrait, p2)::Bool\n\nTwo points are the same if they have the same x and y (and z if 3D) coordinates.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.equals-Tuple{GeoInterface.PolygonTrait, Any, GeoInterface.MultiPolygonTrait, Any}","page":"Home","title":"GeometryOps.equals","text":"equals(::GI.PolygonTrait, geom_a, ::GI.MultiPolygonTrait, geom_b)::Bool\n\nA polygon and a multipolygon are equal if the multipolygon is composed of a single polygon that is equivalent to the given polygon.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.equals-Tuple{GeoInterface.PolygonTrait, Any, GeoInterface.PolygonTrait, Any}","page":"Home","title":"GeometryOps.equals","text":"equals(::GI.PolygonTrait, geom_a, ::GI.PolygonTrait, geom_b)::Bool\n\nTwo polygons are equal if they share the same exterior edge and holes.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.equals-Tuple{Union{GeoInterface.LineStringTrait, GeoInterface.LineTrait}, Any, GeoInterface.LinearRingTrait, Any}","page":"Home","title":"GeometryOps.equals","text":"equals(\n ::Union{GI.LineTrait, GI.LineStringTrait}, l1,\n ::GI.LinearRingTrait, l2,\n)::Bool\n\nA 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 defintion, but rings are, so the line must have a repeated last point to be equal\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.equals-Tuple{Union{GeoInterface.LineStringTrait, GeoInterface.LineTrait}, Any, Union{GeoInterface.LineStringTrait, GeoInterface.LineTrait}, Any}","page":"Home","title":"GeometryOps.equals","text":"equals(\n ::Union{GI.LineTrait, GI.LineStringTrait}, l1,\n ::Union{GI.LineTrait, GI.LineStringTrait}, l2,\n)::Bool\n\nTwo lines/linestrings are equal if they share the same set of points going along the curve. Note that lines/linestrings aren't closed by defintion.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.equals-Union{Tuple{T}, Tuple{T, Any, T, Any}} where T","page":"Home","title":"GeometryOps.equals","text":"equals(::T, geom_a, ::T, geom_b)::Bool\n\nTwo geometries of the same type, which don't have a equals function to dispatch off of should throw an error.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.flatten-Union{Tuple{Target}, Tuple{Type{Target}, Any}} where Target<:GeoInterface.AbstractTrait","page":"Home","title":"GeometryOps.flatten","text":"flatten(target::Type{<:GI.AbstractTrait}, obj)\nflatten(f, target::Type{<:GI.AbstractTrait}, obj)\n\nLazily flatten any AbstractArray, iterator, FeatureCollectionTrait, FeatureTrait or AbstractGeometryTrait object obj, so that objects with the target trait are returned by the iterator.\n\nIf f is passed in it will be applied to the target geometries.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.flip-Tuple{Any}","page":"Home","title":"GeometryOps.flip","text":"flip(obj)\n\nSwap all of the x and y coordinates in obj, otherwise keeping the original structure (but not necessarily the original type).\n\nKeywords\n\nthreaded: true or false. Whether to use multithreading. Defaults to false.\ncrs: The CRS to attach to geometries. Defaults to nothing.\ncalc_extent: true or false. Whether to calculate the extent. Defaults to false.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.get_contours-Tuple{AbstractMatrix}","page":"Home","title":"GeometryOps.get_contours","text":"get_contours(A::AbstractMatrix)\n\nReturns contours as vectors of CartesianIndex.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.intersection-Tuple{Any, Any}","page":"Home","title":"GeometryOps.intersection","text":"intersection(geom_a, geom_b)::Union{Tuple{::Real, ::Real}, ::Nothing}\n\nReturn an intersection point between two geometries. Return nothing if none are found. Else, the return type depends on the input. It will be a union between: a point, a line, a linear ring, a polygon, or a multipolygon\n\nExample\n\nimport GeoInterface as GI, GeometryOps as GO\n\nline1 = GI.Line([(124.584961,-12.768946), (126.738281,-17.224758)])\nline2 = GI.Line([(123.354492,-15.961329), (127.22168,-14.008696)])\nGO.intersection(line1, line2)\n\n# output\n(125.58375366067547, -14.83572303404496)\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.intersection-Tuple{GeoInterface.AbstractGeometryTrait, Any, GeoInterface.AbstractGeometryTrait, Any}","page":"Home","title":"GeometryOps.intersection","text":"intersection(\n ::GI.AbstractTrait, geom_a,\n ::GI.AbstractTrait, geom_b,\n)::Union{\n ::Vector{Vector{Tuple{::Real, ::Real}}}, # is this a good return type?\n ::Nothing\n}\n\nCalculates the intersection between two line segments. Return nothing if there isn't one.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.intersection-Tuple{GeoInterface.LineTrait, Any, GeoInterface.LineTrait, Any}","page":"Home","title":"GeometryOps.intersection","text":"intersection(\n ::GI.LineTrait, line_a,\n ::GI.LineTrait, line_b,\n)::Union{\n ::Tuple{::Real, ::Real},\n ::Nothing\n}\n\nCalculates the intersection between two line segments. Return nothing if there isn't one.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.intersection-Tuple{GeoInterface.PolygonTrait, Any, GeoInterface.PolygonTrait, Any}","page":"Home","title":"GeometryOps.intersection","text":"intersection(\n ::GI.PolygonTrait, poly_a,\n ::GI.PolygonTrait, poly_b,\n)::Union{\n ::Vector{Vector{Tuple{::Real, ::Real}}}, # is this a good return type?\n ::Nothing\n}\n\nCalculates the intersection between two line segments. Return nothing if there isn't one.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.intersection_points-Tuple{Any, Any}","page":"Home","title":"GeometryOps.intersection_points","text":"intersection_points(\n geom_a,\n geom_b,\n)::Union{\n ::Vector{::Tuple{::Real, ::Real}},\n ::Nothing,\n}\n\nReturn a list of intersection points between two geometries. If no intersection point was possible given geometry extents, return nothing. If none are found, return an empty list.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.intersection_points-Tuple{GeoInterface.AbstractTrait, Any, GeoInterface.AbstractTrait, Any}","page":"Home","title":"GeometryOps.intersection_points","text":"intersection_points(\n ::GI.AbstractTrait, geom_a,\n ::GI.AbstractTrait, geom_b,\n)::Union{\n ::Vector{::Tuple{::Real, ::Real}},\n ::Nothing,\n}\n\nCalculates the list of intersection points between two geometries, inlcuding line segments, line strings, linear rings, polygons, and multipolygons. If no intersection points were possible given geometry extents, return nothing. If none are found, return an empty list.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.intersects-Tuple{Any, Any}","page":"Home","title":"GeometryOps.intersects","text":"intersects(geom1, geom2)::Bool\n\nReturn true if the interiors or boundaries of the two geometries interact.\n\nintersects returns the exact opposite result of disjoint.\n\nExample\n\nimport GeoInterface as GI, GeometryOps as GO\n\nline1 = GI.Line([(124.584961,-12.768946), (126.738281,-17.224758)])\nline2 = GI.Line([(123.354492,-15.961329), (127.22168,-14.008696)])\nGO.intersects(line1, line2)\n\n# output\ntrue\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.isclockwise-Tuple{Any}","page":"Home","title":"GeometryOps.isclockwise","text":"isclockwise(line::Union{LineString, Vector{Position}})::Bool\n\nTake a ring and return true or false whether or not the ring is clockwise or counter-clockwise.\n\nExample\n\nimport GeoInterface as GI, GeometryOps as GO\n\nring = GI.LinearRing([(0, 0), (1, 1), (1, 0), (0, 0)])\nGO.isclockwise(ring)\n\n# output\ntrue\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.isconcave-Tuple{Any}","page":"Home","title":"GeometryOps.isconcave","text":"isconcave(poly::Polygon)::Bool\n\nTake a polygon and return true or false as to whether it is concave or not.\n\nExamples\n\nimport GeoInterface as GI, GeometryOps as GO\n\npoly = GI.Polygon([[(0, 0), (0, 1), (1, 1), (1, 0), (0, 0)]])\nGO.isconcave(poly)\n\n# output\nfalse\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.overlaps-Tuple{Any, Any}","page":"Home","title":"GeometryOps.overlaps","text":"overlaps(geom1, geom2)::Bool\n\nCompare 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\n\nExamples\n\nimport GeometryOps as GO, GeoInterface as GI\npoly1 = GI.Polygon([[(0,0), (0,5), (5,5), (5,0), (0,0)]])\npoly2 = GI.Polygon([[(1,1), (1,6), (6,6), (6,1), (1,1)]])\n\nGO.overlaps(poly1, poly2)\n# output\ntrue\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.overlaps-Tuple{GeoInterface.AbstractTrait, Any, GeoInterface.AbstractTrait, Any}","page":"Home","title":"GeometryOps.overlaps","text":"overlaps(::GI.AbstractTrait, geom1, ::GI.AbstractTrait, geom2)::Bool\n\nFor any non-specified pair, all have non-matching dimensions, return false.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.overlaps-Tuple{GeoInterface.LineTrait, Any, GeoInterface.LineTrait, Any}","page":"Home","title":"GeometryOps.overlaps","text":"overlaps(::GI.LineTrait, line1, ::GI.LineTrait, line)::Bool\n\nIf the lines overlap, meaning that they are colinear but each have one endpoint outside of the other line, return true. Else false.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.overlaps-Tuple{GeoInterface.MultiPointTrait, Any, GeoInterface.MultiPointTrait, Any}","page":"Home","title":"GeometryOps.overlaps","text":"overlaps(\n ::GI.MultiPointTrait, points1,\n ::GI.MultiPointTrait, points2,\n)::Bool\n\nIf the multipoints overlap, meaning some, but not all, of the points within the multipoints are shared, return true.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.overlaps-Tuple{GeoInterface.MultiPolygonTrait, Any, GeoInterface.MultiPolygonTrait, Any}","page":"Home","title":"GeometryOps.overlaps","text":"overlaps(\n ::GI.MultiPolygonTrait, polys1,\n ::GI.MultiPolygonTrait, polys2,\n)::Bool\n\nReturn true if at least one pair of polygons from multipolygons overlap. Else false.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.overlaps-Tuple{GeoInterface.MultiPolygonTrait, Any, GeoInterface.PolygonTrait, Any}","page":"Home","title":"GeometryOps.overlaps","text":"overlaps(\n ::GI.MultiPolygonTrait, polys1,\n ::GI.PolygonTrait, poly2,\n)::Bool\n\nReturn true if polygon overlaps with at least one of the polygons within the multipolygon. Else false.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.overlaps-Tuple{GeoInterface.PolygonTrait, Any, GeoInterface.MultiPolygonTrait, Any}","page":"Home","title":"GeometryOps.overlaps","text":"overlaps(\n ::GI.PolygonTrait, poly1,\n ::GI.MultiPolygonTrait, polys2,\n)::Bool\n\nReturn true if polygon overlaps with at least one of the polygons within the multipolygon. Else false.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.overlaps-Tuple{GeoInterface.PolygonTrait, Any, GeoInterface.PolygonTrait, Any}","page":"Home","title":"GeometryOps.overlaps","text":"overlaps(\n trait_a::GI.PolygonTrait, poly_a,\n trait_b::GI.PolygonTrait, poly_b,\n)::Bool\n\nIf the two polygons intersect with one another, but are not equal, return true. Else false.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.overlaps-Tuple{Union{GeoInterface.LineStringTrait, GeoInterface.Wrappers.LinearRing}, Any, Union{GeoInterface.LineStringTrait, GeoInterface.Wrappers.LinearRing}, Any}","page":"Home","title":"GeometryOps.overlaps","text":"overlaps(\n ::Union{GI.LineStringTrait, GI.LinearRing}, line1,\n ::Union{GI.LineStringTrait, GI.LinearRing}, line2,\n)::Bool\n\nIf the curves overlap, meaning that at least one edge of each curve overlaps, return true. Else false.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.polygon_to_line-Tuple{Any}","page":"Home","title":"GeometryOps.polygon_to_line","text":"polygon_to_line(poly::Polygon)\n\nConverts a Polygon to LineString or MultiLineString\n\nExamples\n\nimport GeometryOps as GO, GeoInterface as GI\n\npoly = GI.Polygon([[(-2.275543, 53.464547), (-2.275543, 53.489271), (-2.215118, 53.489271), (-2.215118, 53.464547), (-2.275543, 53.464547)]])\nGO.polygon_to_line(poly)\n# output\nGeoInterface.Wrappers.LineString{false, false, Vector{Tuple{Float64, Float64}}, Nothing, Nothing}([(-2.275543, 53.464547), (-2.275543, 53.489271), (-2.215118, 53.489271), (-2.215118, 53.464547), (-2.275543, 53.464547)], nothing, nothing)\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.polygonize-Tuple{AbstractMatrix}","page":"Home","title":"GeometryOps.polygonize","text":"polygonize(A; minpoints=10)\npolygonize(xs, ys, A; minpoints=10)\n\nConvert matrix A to polygons.\n\nIf xs and ys are passed in they are used as the pixel center points.\n\nKeywords\n\nminpoints: ignore polygons with less than minpoints points. \n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.rebuild-Tuple{Any, Any}","page":"Home","title":"GeometryOps.rebuild","text":"rebuild(geom, child_geoms)\n\nRebuild a geometry from child geometries.\n\nBy 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.\n\n(Maybe it should go into GeoInterface.jl)\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.reconstruct-Tuple{Any, Any}","page":"Home","title":"GeometryOps.reconstruct","text":"reconstruct(geom, components)\n\nReconstruct geom from an iterable of component objects that match its structure.\n\nAll objects in components must have the same GeoInterface.trait.\n\nUsusally used in combination with flatten.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.reproject-Tuple{Any}","page":"Home","title":"GeometryOps.reproject","text":"reproject(geometry; source_crs, target_crs, transform, always_xy, time)\nreproject(geometry, source_crs, target_crs; always_xy, time)\nreproject(geometry, transform; always_xy, time)\n\nReproject any GeoInterface.jl compatible geometry from source_crs to target_crs.\n\nThe returned object will be constructed from GeoInterface.WrapperGeometry geometries, wrapping views of a Vector{Proj.Point{D}}, where D is the dimension.\n\nArguments\n\ngeometry: Any GeoInterface.jl compatible geometries.\nsource_crs: the source coordinate referece system, as a GeoFormatTypes.jl object or a string.\ntarget_crs: the target coordinate referece system, as a GeoFormatTypes.jl object or a string.\n\nIf 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 retreivable from the geometry with GeoInterface.crs(geometry).\n\nKeywords\n\nalways_xy: force x, y coordinate order, true by default. false will expect and return points in the crs coordinate order.\ntime: the time for the coordinates. Inf by default.\nthreaded: true or false. Whether to use multithreading. Defaults to false.\ncrs: The CRS to attach to geometries. Defaults to nothing.\ncalc_extent: true or false. Whether to calculate the extent. Defaults to false.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.signed_area-Union{Tuple{Any}, Tuple{T}, Tuple{Any, Type{T}}} where T<:AbstractFloat","page":"Home","title":"GeometryOps.signed_area","text":"signed_area(geom, ::Type{T} = Float64)::T\n\nReturns the signed area of the geometry, based on winding order. This is computed slighly 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.\n\nResult will be of type T, where T is an optional argument with a default value of Float64.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.signed_distance-Union{Tuple{T}, Tuple{Any, Any}, Tuple{Any, Any, Type{T}}} where T<:AbstractFloat","page":"Home","title":"GeometryOps.signed_distance","text":"signed_distance(point, geom, ::Type{T} = Float64)::T\n\nCalculates 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.\n\nResult will be of type T, where T is an optional argument with a default value of Float64.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.simplify-Tuple{Any}","page":"Home","title":"GeometryOps.simplify","text":"simplify(obj; kw...)\nsimplify(::SimplifyAlg, obj; kw...)\n\nSimplify a geometry, feature, feature collection, or nested vectors or a table of these.\n\nRadialDistance, DouglasPeucker, or VisvalingamWhyatt algorithms are available, listed in order of increasing quality but decreaseing performance.\n\nPoinTrait and MultiPointTrait are returned unchanged.\n\nThe default behaviour is simplify(DouglasPeucker(; kw...), obj). Pass in other SimplifyAlg to use other algorithms.\n\nKeywords\n\nthreaded: true or false. Whether to use multithreading. Defaults to false.\ncrs: The CRS to attach to geometries. Defaults to nothing.\ncalc_extent: true or false. Whether to calculate the extent. Defaults to false.\n\nKeywords for DouglasPeucker are allowed when no algorithm is specified:\n\nKeywords\n\nratio: the fraction of points that should remain after simplify. Useful as it will generalise for large collections of objects.\nnumber: the number of points that should remain after simplify. Less useful for large collections of mixed size objects.\n\nExample\n\nSimplify a polygon to have six points:\n\nimport GeoInterface as GI\nimport GeometryOps as GO\n\npoly = GI.Polygon([[\n [-70.603637, -33.399918],\n [-70.614624, -33.395332],\n [-70.639343, -33.392466],\n [-70.659942, -33.394759],\n [-70.683975, -33.404504],\n [-70.697021, -33.419406],\n [-70.701141, -33.434306],\n [-70.700454, -33.446339],\n [-70.694274, -33.458369],\n [-70.682601, -33.465816],\n [-70.668869, -33.472117],\n [-70.646209, -33.473835],\n [-70.624923, -33.472117],\n [-70.609817, -33.468107],\n [-70.595397, -33.458369],\n [-70.587158, -33.442901],\n [-70.587158, -33.426283],\n [-70.590591, -33.414248],\n [-70.594711, -33.406224],\n [-70.603637, -33.399918]]])\n\nsimple = GO.simplify(poly; number=6)\nGI.npoint(simple)\n\n# output\n6\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.t_value-Union{Tuple{T2}, Tuple{T1}, Tuple{N}, Tuple{Union{Tuple{Vararg{T1, N}}, StaticArraysCore.StaticArray{Tuple{N}, T1, 1}}, Union{Tuple{Vararg{T1, N}}, StaticArraysCore.StaticArray{Tuple{N}, T1, 1}}, T2, T2}} where {N, T1<:Real, T2<:Real}","page":"Home","title":"GeometryOps.t_value","text":"t_value(sᵢ, sᵢ₊₁, rᵢ, rᵢ₊₁)\n\nReturns the \"T-value\" as described in Hormann's presentation [HormannPresentation] on how to calculate the mean-value coordinate. \n\nHere, 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.\n\ntᵢ = fracmathrmdetleft(sᵢ sᵢ₁right)rᵢ * rᵢ₁ + sᵢ sᵢ₁\n\n[HormannPresentation]: K. Hormann and N. Sukumar. Generalized Barycentric Coordinates in Computer Graphics and Computational Mechanics. Taylor & Fancis, CRC Press, 2017.\n\n```\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.to_edges-Tuple{Any}","page":"Home","title":"GeometryOps.to_edges","text":"to_edges()\n\nConvert any geometry or collection of geometries into a flat vector of Tuple{Tuple{Float64,Float64},Tuple{Float64,Float64}} edges.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.touches-Tuple{Any, Any}","page":"Home","title":"GeometryOps.touches","text":"touches(geom1, geom2)::Bool\n\nReturn 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 geometies interior or boundary.\n\nExamples\n\nimport GeometryOps as GO, GeoInterface as GI\n\nl1 = GI.Line([(0.0, 0.0), (1.0, 0.0)])\nl2 = GI.Line([(1.0, 1.0), (1.0, -1.0)])\n\nGO.touches(l1, l2)\n# output\ntrue\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.transform-Tuple{Any, Any}","page":"Home","title":"GeometryOps.transform","text":"transform(f, obj)\n\nApply a function f to all the points in obj.\n\nPoints will be passed to f as an SVector to allow using CoordinateTransformations.jl and Rotations.jl without hassle.\n\nSVector is also a valid GeoInterface.jl point, so will work in all GeoInterface.jl methods.\n\nExample\n\njulia> import GeoInterface as GI\n\njulia> import GeometryOps as GO\n\njulia> geom = GI.Polygon([GI.LinearRing([(1, 2), (3, 4), (5, 6), (1, 2)]), GI.LinearRing([(3, 4), (5, 6), (6, 7), (3, 4)])]);\n\njulia> f = CoordinateTransformations.Translation(3.5, 1.5)\nTranslation(3.5, 1.5)\n\njulia> GO.transform(f, geom)\nGeoInterface.Wrappers.Polygon{false, false, Vector{GeoInterface.Wrappers.LinearRing{false, false, Vector{StaticArraysCore.SVector{2, Float64}}, Nothing, Nothing}}, Nothing, Nothing}(GeoInterface.Wrappers.Linea\nrRing{false, false, Vector{StaticArraysCore.SVector{2, Float64}}, Nothing, Nothing}[GeoInterface.Wrappers.LinearRing{false, false, Vector{StaticArraysCore.SVector{2, Float64}}, Nothing, Nothing}(StaticArraysCo\nre.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\nrraysCore.SVector{2, Float64}[[6.5, 5.5], [8.5, 7.5], [9.5, 8.5], [6.5, 5.5]], nothing, nothing)], nothing, nothing)\n\nWith Rotations.jl you need to actuall multiply the Rotation by the SVector point, which is easy using an anonymous function.\n\njulia> using Rotations\n\njulia> GO.transform(p -> one(RotMatrix{2}) * p, geom)\nGeoInterface.Wrappers.Polygon{false, false, Vector{GeoInterface.Wrappers.LinearRing{false, false, Vector{StaticArraysCore.SVector{2, Int64}}, Nothing, Nothing}}, Nothing, Nothing}(GeoInterface.Wrappers.LinearR\ning{false, false, Vector{StaticArraysCore.SVector{2, Int64}}, Nothing, Nothing}[GeoInterface.Wrappers.LinearRing{false, false, Vector{StaticArraysCore.SVector{2, Int64}}, Nothing, Nothing}(StaticArraysCore.SVe\nctor{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\n}[[4, 3], [6, 5], [7, 6], [4, 3]], nothing, nothing)], nothing, nothing)\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.tuples-Tuple{Any}","page":"Home","title":"GeometryOps.tuples","text":"tuples(obj)\n\nConvert all points in obj to Tuples, wherever the are nested.\n\nReturns a similar object or collection of objects using GeoInterface.jl geometries wrapping Tuple points.\n\nKeywords\n\nthreaded: true or false. Whether to use multithreading. Defaults to false.\ncrs: The CRS to attach to geometries. Defaults to nothing.\ncalc_extent: true or false. Whether to calculate the extent. Defaults to false.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.unwrap","page":"Home","title":"GeometryOps.unwrap","text":"unwrap(target::Type{<:AbstractTrait}, obj)\nunwrap(f, target::Type{<:AbstractTrait}, obj)\n\nUnwrap the object newst to vectors, down to the target trait.\n\nIf f is passed in it will be applied to the target geometries as they are found.\n\n\n\n\n\n","category":"function"},{"location":"#GeometryOps.weighted_mean-Union{Tuple{WT}, Tuple{WT, Any, Any}} where WT<:Real","page":"Home","title":"GeometryOps.weighted_mean","text":"weighted_mean(weight::Real, x1, x2)\n\nReturns the weighted mean of x1 and x2, where weight is the weight of x1.\n\nSpecifically, calculates x1 * weight + x2 * (1 - weight).\n\nnote: Note\nThe idea for this method is that you can override this for custom types, like Color types, in extension modules.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.within-Tuple{Any, Any}","page":"Home","title":"GeometryOps.within","text":"within(geom1, geom2)::Bool\n\nReturn true if the first geometry is completely within the second geometry. The interiors of both geometries must intersect and the interior and boundary of the primary geometry (geom1) must not intersect the exterior of the secondary geometry (geom2).\n\nFurthermore, within returns the exact opposite result of contains.\n\nExamples\n\nimport GeometryOps as GO, GeoInterface as GI\n\nline = GI.LineString([(1, 1), (1, 2), (1, 3), (1, 4)])\npoint = (1, 2)\nGO.within(point, line)\n\n# output\ntrue\n\n\n\n\n\n","category":"method"}] } diff --git a/dev/source/GeometryOps/index.html b/dev/source/GeometryOps/index.html index 153a16d29..99eaeb4eb 100644 --- a/dev/source/GeometryOps/index.html +++ b/dev/source/GeometryOps/index.html @@ -1,5 +1,5 @@ -GeometryOps.jl · GeometryOps.jl

      GeometryOps.jl

      module GeometryOps
      +GeometryOps.jl · GeometryOps.jl

      GeometryOps.jl

      module GeometryOps
       
       using GeoInterface
       using GeometryBasics
      @@ -25,11 +25,15 @@
       include("methods/centroid.jl")
       include("methods/distance.jl")
       include("methods/equals.jl")
      -include("methods/geom_relations/intersects.jl")
       include("methods/geom_relations/contains.jl")
      +include("methods/geom_relations/coveredby.jl")
      +include("methods/geom_relations/covers.jl")
       include("methods/geom_relations/crosses.jl")
       include("methods/geom_relations/disjoint.jl")
      +include("methods/geom_relations/geom_geom_processors.jl")
      +include("methods/geom_relations/intersects.jl")
       include("methods/geom_relations/overlaps.jl")
      +include("methods/geom_relations/touches.jl")
       include("methods/geom_relations/within.jl")
       include("methods/polygonize.jl")
       
      @@ -40,4 +44,4 @@
       include("transformations/tuples.jl")
       include("transformations/transform.jl")
       
      -end

      This page was generated using Literate.jl.

      +end

      This page was generated using Literate.jl.

      diff --git a/dev/source/methods/area/index.html b/dev/source/methods/area/index.html index dbd6edd80..1c5e04193 100644 --- a/dev/source/methods/area/index.html +++ b/dev/source/methods/area/index.html @@ -1,5 +1,5 @@ -Area and signed area · GeometryOps.jl

      Area and signed area

      export area, signed_area

      What is area? What is signed area?

      Area is the amount of space occupied by a two-dimensional figure. It is always a positive value. Signed area is simply the integral over the exterior path of a polygon, minus the sum of integrals over its interior holes. It is signed such that a clockwise path has a positive area, and a counterclockwise path has a negative area. The area is the absolute value of the signed area.

      To provide an example, consider this rectangle:

      using GeometryOps
      +Area and signed area · GeometryOps.jl

      Area and signed area

      export area, signed_area

      What is area? What is signed area?

      Area is the amount of space occupied by a two-dimensional figure. It is always a positive value. Signed area is simply the integral over the exterior path of a polygon, minus the sum of integrals over its interior holes. It is signed such that a clockwise path has a positive area, and a counterclockwise path has a negative area. The area is the absolute value of the signed area.

      To provide an example, consider this rectangle:

      using GeometryOps
       using GeometryOps.GeometryBasics
       using Makie
       
      @@ -72,4 +72,4 @@
           end

      Complete the last edge. If the first and last where the same this will be zero

          p2 = pfirst
           area += GI.x(p1) * GI.y(p2) - GI.y(p1) * GI.x(p2)
           return T(area / 2)
      -end

      This page was generated using Literate.jl.

      +end

      This page was generated using Literate.jl.

      diff --git a/dev/source/methods/barycentric/index.html b/dev/source/methods/barycentric/index.html index 2d6f923e3..c571f9d39 100644 --- a/dev/source/methods/barycentric/index.html +++ b/dev/source/methods/barycentric/index.html @@ -1,5 +1,5 @@ -Barycentric coordinates · GeometryOps.jl

      Barycentric coordinates

      export barycentric_coordinates, barycentric_coordinates!, barycentric_interpolate
      +Barycentric coordinates · GeometryOps.jl

      Barycentric coordinates

      export barycentric_coordinates, barycentric_coordinates!, barycentric_interpolate
       export MeanValue

      Generalized barycentric coordinates are a generalization of barycentric coordinates, which are typically used in triangles, to arbitrary polygons.

      They provide a way to express a point within a polygon as a weighted average of the polygon's vertices.

      In the case of a triangle, barycentric coordinates are a set of three numbers $(λ_1, λ_2, λ_3)$, each associated with a vertex of the triangle. Any point within the triangle can be expressed as a weighted average of the vertices, where the weights are the barycentric coordinates. The weights sum to 1, and each is non-negative.

      For a polygon with $n$ vertices, generalized barycentric coordinates are a set of $n$ numbers $(λ_1, λ_2, ..., λ_n)$, each associated with a vertex of the polygon. Any point within the polygon can be expressed as a weighted average of the vertices, where the weights are the generalized barycentric coordinates.

      As with the triangle case, the weights sum to 1, and each is non-negative.

      Example

      This example was taken from this page of CGAL's documentation.

      using GeometryOps, Makie
       using GeometryOps.GeometryBasics
       # Define a polygon
      @@ -376,4 +376,4 @@
       end
       
       struct Wachspress <: AbstractBarycentricCoordinateMethod
      -end

      This page was generated using Literate.jl.

      +end

      This page was generated using Literate.jl.

      diff --git a/dev/source/methods/bools/index.html b/dev/source/methods/bools/index.html index 0f26e0203..56c957e8b 100644 --- a/dev/source/methods/bools/index.html +++ b/dev/source/methods/bools/index.html @@ -1,7 +1,5 @@ -Boolean conditions · GeometryOps.jl

      Boolean conditions

      export isclockwise, isconcave
      -export point_on_line, point_in_polygon, point_in_ring
      -export line_on_line, line_in_polygon, polygon_in_polygon

      These are all adapted from Turf.jl.

      The may not necessarily be what want in the end but work for now!

      """
      +Boolean conditions · GeometryOps.jl

      Boolean conditions

      export isclockwise, isconcave

      These are all adapted from Turf.jl.

      The may not necessarily be what want in the end but work for now!

      """
           isclockwise(line::Union{LineString, Vector{Position}})::Bool
       
       Take a ring and return true or false whether or not the ring is clockwise or
      @@ -85,195 +83,13 @@
           coors2 = seg2[i]
           _isparallel(coors1, coors2) == false && return false
       end
      -return true

      end

      @inline function isparallel(p1, p2) slope1 = bearingtoazimuth(rhumbbearing(GI.x(p1), GI.x(p2))) slope2 = bearingtoazimuth(rhumb_bearing(GI.y(p1), GI.y(p2)))

      return slope1 === slope2

      end

      """
      -    point_on_line(point::Point, line::LineString; ignore_end_vertices::Bool=false)::Bool
      +return true

      end

      @inline function isparallel(p1, p2) slope1 = bearingtoazimuth(rhumbbearing(GI.x(p1), GI.x(p2))) slope2 = bearingtoazimuth(rhumb_bearing(GI.y(p1), GI.y(p2)))

      return slope1 === slope2

      end

      _isparallel(((ax, ay), (bx, by)), ((cx, cy), (dx, dy))) =
      +    _isparallel(bx - ax, by - ay, dx - cx, dy - cy)
       
      -Return true if a point is on a line. Accept a optional parameter to ignore the
      -start and end vertices of the linestring.
      +_isparallel(Δx1, Δy1, Δx2, Δy2) = (Δx1 * Δy2 == Δy1 * Δx2)
       
      -# Examples
      -
      -```jldoctest
      -import GeoInterface as GI, GeometryOps as GO
      -
      -point = (1, 1)
      -line = GI.LineString([(0, 0), (3, 3), (4, 4)])
      -GO.point_on_line(point, line)

      output

      true
      -```
      -"""
      -function point_on_line(point, line; ignore_end_vertices::Bool=false)::Bool
      -    line_points = tuple_points(line)
      -    n = length(line_points)
      -
      -    exclude_boundary = :none
      -    for i in 1:n - 1
      -        if ignore_end_vertices
      -            if i === 1
      -                exclude_boundary = :start
      -            elseif i === n - 2
      -                exclude_boundary = :end
      -            elseif (i === 1 && i + 1 === n - 1)
      -                exclude_boundary = :both
      -            end
      -        end
      -        if point_on_segment(point, (line_points[i], line_points[i + 1]); exclude_boundary)
      -            return true
      -        end
      -    end
      -    return false
      -end
      -
      -function point_on_seg(point, start, stop)

      Parse out points

          x, y = GI.x(point), GI.y(point)
      -    x1, y1 = GI.x(start), GI.y(start)
      -    x2, y2 = GI.x(stop), GI.y(stop)
      -    Δxl = x2 - x1
      -    Δyl = y2 - y1

      Determine if point is on segment

          cross = (x - x1) * Δyl - (y - y1) * Δxl
      -    if cross == 0  # point is on line extending to infinity

      is line between endpoints

              if abs(Δxl) >= abs(Δyl)  # is line between endpoints
      -            return Δxl > 0 ? x1 <= x <= x2 : x2 <= x <= x1
      -        else
      -            return Δyl > 0 ? y1 <= y <= y2 : y2 <= y <= y1
      -        end
      -    end
      -    return false
      -end
      -
      -function point_on_segment(point, (start, stop); exclude_boundary::Symbol=:none)::Bool
      -    x, y = GI.x(point), GI.y(point)
      -    x1, y1 = GI.x(start), GI.y(start)
      -    x2, y2 = GI.x(stop), GI.y(stop)
      -
      -    dxc = x - x1
      -    dyc = y - y1
      -    dx1 = x2 - x1
      -    dy1 = y2 - y1

      TODO use better predicate for crossing here

          cross = dxc * dy1 - dyc * dx1
      -    cross != 0 && return false

      Will constprop optimise these away?

          if exclude_boundary === :none
      -        if abs(dx1) >= abs(dy1)
      -            return dx1 > 0 ? x1 <= x && x <= x2 : x2 <= x && x <= x1
      -        end
      -        return dy1 > 0 ? y1 <= y && y <= y2 : y2 <= y && y <= y1
      -    elseif exclude_boundary === :start
      -        if abs(dx1) >= abs(dy1)
      -             return dx1 > 0 ? x1 < x && x <= x2 : x2 <= x && x < x1
      -        end
      -        return dy1 > 0 ? y1 < y && y <= y2 : y2 <= y && y < y1
      -    elseif exclude_boundary === :end
      -        if abs(dx1) >= abs(dy1)
      -            return dx1 > 0 ? x1 <= x && x < x2 : x2 < x && x <= x1
      -        end
      -        return dy1 > 0 ? y1 <= y && y < y2 : y2 < y && y <= y1
      -    elseif exclude_boundary === :both
      -        if abs(dx1) >= abs(dy1)
      -            return dx1 > 0 ? x1 < x && x < x2 : x2 < x && x < x1
      -        end
      -        return dy1 > 0 ? y1 < y && y < y2 : y2 < y && y < y1
      -    end
      -    return false
      -end
      -
      -"""
      -    point_in_polygon(point::Point, polygon::Union{Polygon, MultiPolygon}, ignore_boundary::Bool=false)::Bool
      -
      -Take a Point and a Polygon and determine if the point
      -resides inside the polygon. The polygon can be convex or concave. The function accounts for holes.
      -
      -# Examples
      -
      -```jldoctest
      -import GeoInterface as GI, GeometryOps as GO
      -
      -point = (-77.0, 44.0)
      -poly = GI.Polygon([[(-81, 41), (-81, 47), (-72, 47), (-72, 41), (-81, 41)]])
      -GO.point_in_polygon(point, poly)

      output

      true
      -```
      -"""
      -point_in_polygon(point, polygon; kw...)::Bool =
      -    point_in_polygon(GI.trait(point), point, GI.trait(polygon), polygon; kw...)
      -function point_in_polygon(
      -    ::PointTrait, point,
      -    ::PolygonTrait, poly;
      -    ignore_boundary::Bool=false,
      -    check_extent::Bool=false,
      -)::Bool

      Cheaply check that the point is inside the polygon extent

          if check_extent
      -        point_in_extent(point, GI.extent(poly)) || return false
      -    end

      Then check the point is inside the exterior ring

          point_in_polygon(
      -        point,GI.getexterior(poly);
      -        ignore_boundary, check_extent=false,
      -    ) || return false

      Finally make sure the point is not in any of the holes, flipping the boundary condition

          for ring in GI.gethole(poly)
      -        point_in_polygon(
      -            point, ring;
      -            ignore_boundary=!ignore_boundary,
      -        ) && return false
      -    end
      -    return true
      -end
      -
      -function point_in_polygon(
      -    ::PointTrait, pt,
      -    ::Union{LineStringTrait,LinearRingTrait}, ring;
      -    ignore_boundary::Bool=false,
      -    check_extent::Bool=false,
      -)::Bool
      -    x, y = GI.x(pt), GI.y(pt)

      Cheaply check that the point is inside the ring extent

          if check_extent
      -        point_in_extent(point, GI.extent(ring)) || return false
      -    end

      Then check the point is inside the ring

          inside = false
      -    n = GI.npoint(ring)
      -    p_start = GI.getpoint(ring, 1)
      -    p_end = GI.getpoint(ring, n)

      Handle closed vs opne rings

          if GI.x(p_start) == GI.x(p_end) && GI.y(p_start) == GI.y(p_end)
      -        n -= 1
      -    end

      Loop over all points in the ring

          for i in 1:(n - 1)

      First point on edge

              p_i = GI.getpoint(ring, i)
      -        xi, yi = GI.x(p_i), GI.y(p_i)

      Second point on edge (j = i + 1)

              p_j = GI.getpoint(ring, i + 1)
      -        xj, yj = GI.x(p_j), GI.y(p_j)

      Check if point is on the ring boundary

              on_boundary = (  # vertex to point has same slope as edge
      -            yi * (xj - x) + yj * (x - xi) == y * (xj - xi) &&
      -            (xi - x) * (xj - x) <= 0 &&  # x is between xi and xj
      -            (yi - y) * (yj - y) <= 0     # y is between yi and yj
      -        )
      -        on_boundary && return !ignore_boundary

      Check if ray from point passes through edge

              intersects = (
      -            (yi > y) !== (yj > y) &&
      -            (x < (xj - xi) * (y - yi) / (yj - yi) + xi)
      -        )
      -        if intersects
      -            inside = !inside
      -        end
      -    end
      -    return inside
      -end
       
       function point_in_extent(p, extent::Extents.Extent)
           (x1, x2), (y1, y1) = extent.X, extent.Y
           return x1 <= GI.x(p) && y1 <= GI.y(p) && x2 >= GI.x(p) && y2 >= GI.y(p)
      -end
      -
      -line_on_line(line1, line2) = line_on_line(trait(line1), line1, trait(line2), line2)
      -function line_on_line(t1::GI.AbstractCurveTrait, line1, t2::AbstractCurveTrait, line2)
      -    for p in GI.getpoint(line1)

      FIXME: all points being on the line doesn't actually mean the whole line is on the line...

              point_on_line(p, line2) || return false
      -    end
      -    return true
      -end
      -
      -line_in_polygon(line, poly) = line_in_polygon(trait(line), line, trait(poly), poly)
      -
      -function line_in_polygon(
      -    ::AbstractCurveTrait, line,
      -    ::Union{AbstractPolygonTrait,LinearRingTrait}, poly
      -)
      -    Extents.intersects(GI.extent(poly), GI.extent(line)) || return false
      -
      -    inside = false
      -    for i in 1:GI.npoint(line) - 1
      -        p = GI.getpoint(line, i)
      -        p2 = GI.getpoint(line, i + 1)
      -        point_in_polygon(p, poly) || return false
      -        if !inside
      -            inside = point_in_polygon(p, poly; ignore_boundary=true)
      -        end

      FIXME This seems like a hack, we should check for intersections rather than midpoint??

              if !inside
      -            mid = ((GI.x(p) + GI.x(p2)) / 2, (GI.y(p) + GI.y(p2)) / 2)
      -            inside = point_in_polygon(mid, poly; ignore_boundary=true)
      -        end
      -    end
      -    return inside
      -end
      -
      -function polygon_in_polygon(poly1, poly2)

      edges1, edges2 = toedges(poly1), toedges(poly2) extent1, extent2 = toextent(edges1), toextent(edges2) Check the extents intersect

          Extents.intersects(GI.extent(poly1), GI.extent(poly2)) || return false

      Check all points in poly1 are in poly2

          for point in GI.getpoint(poly1)
      -        point_in_polygon(point, poly2) || return false
      -    end

      Check the line of poly1 does not intersect the line of poly2

          #intersects(poly1, poly2) && return false

      poly1 must be in poly2

          return true
      - end

      This page was generated using Literate.jl.

      +end

      This page was generated using Literate.jl.

      diff --git a/dev/source/methods/centroid/index.html b/dev/source/methods/centroid/index.html index 9f493b3c4..8ae8fb4f7 100644 --- a/dev/source/methods/centroid/index.html +++ b/dev/source/methods/centroid/index.html @@ -1,5 +1,5 @@ -Centroid · GeometryOps.jl

      Centroid

      export centroid, centroid_and_length, centroid_and_area

      What is the centroid?

      The centroid is the geometric center of a line string or area(s). Note that the centroid does not need to be inside of a concave area.

      Further note that by convention a line, or linear ring, is calculated by weighting the line segments by their length, while polygons and multipolygon centroids are calculated by weighting edge's by their 'area components'.

      To provide an example, consider this concave polygon in the shape of a 'C':

      using GeometryOps
      +Centroid · GeometryOps.jl

      Centroid

      export centroid, centroid_and_length, centroid_and_area

      What is the centroid?

      The centroid is the geometric center of a line string or area(s). Note that the centroid does not need to be inside of a concave area.

      Further note that by convention a line, or linear ring, is calculated by weighting the line segments by their length, while polygons and multipolygon centroids are calculated by weighting edge's by their 'area components'.

      To provide an example, consider this concave polygon in the shape of a 'C':

      using GeometryOps
       using GeometryOps.GeometryBasics
       using Makie
       using CairoMakie
      @@ -139,4 +139,4 @@
           xcentroid /= area
           ycentroid /= area
           return (xcentroid, ycentroid), area
      -end

      This page was generated using Literate.jl.

      +end

      This page was generated using Literate.jl.

      diff --git a/dev/source/methods/distance/index.html b/dev/source/methods/distance/index.html index fb6f5589d..42006ba2b 100644 --- a/dev/source/methods/distance/index.html +++ b/dev/source/methods/distance/index.html @@ -1,5 +1,5 @@ -Distance and signed distance · GeometryOps.jl

      Distance and signed distance

      export distance, signed_distance

      What is distance? What is signed distance?

      Distance is the distance of a point to another geometry. This is always a positive number. If a point is inside of geometry, so on a curve or inside of a polygon, the distance will be zero. Signed distance is mainly used for polygons and multipolygons. If a point is outside of a geometry, signed distance has the same value as distance. However, points within the geometry have a negative distance representing the distance of a point to the closest boundary. Therefore, for all "non-filled" geometries, like curves, the distance will either be postitive or 0.

      To provide an example, consider this rectangle:

      using GeometryOps
      +Distance and signed distance · GeometryOps.jl

      Distance and signed distance

      export distance, signed_distance

      What is distance? What is signed distance?

      Distance is the distance of a point to another geometry. This is always a positive number. If a point is inside of geometry, so on a curve or inside of a polygon, the distance will be zero. Signed distance is mainly used for polygons and multipolygons. If a point is outside of a geometry, signed distance has the same value as distance. However, points within the geometry have a negative distance representing the distance of a point to the closest boundary. Therefore, for all "non-filled" geometries, like curves, the distance will either be postitive or 0.

      To provide an example, consider this rectangle:

      using GeometryOps
       using GeometryOps.GeometryBasics
       using Makie
       
      @@ -174,4 +174,4 @@
               min_dist = dist < min_dist ? dist : min_dist
           end
           return min_dist
      -end

      This page was generated using Literate.jl.

      +end

      This page was generated using Literate.jl.

      diff --git a/dev/source/methods/equals/index.html b/dev/source/methods/equals/index.html index 3984a3a8d..a2de6f0c9 100644 --- a/dev/source/methods/equals/index.html +++ b/dev/source/methods/equals/index.html @@ -1,5 +1,5 @@ -Equals · GeometryOps.jl

      Equals

      export equals

      What is equals?

      The equals function checks if two geometries are equal. They are equal if they share the same set of points and edges to define the same shape.

      To provide an example, consider these two lines:

      using GeometryOps
      +Equals · GeometryOps.jl

      Equals

      export equals

      What is equals?

      The equals function checks if two geometries are equal. They are equal if they share the same set of points and edges to define the same shape.

      To provide an example, consider these two lines:

      using GeometryOps
       using GeometryOps.GeometryBasics
       using Makie
       using CairoMakie
      @@ -262,4 +262,4 @@
               has_match || return false
           end
           return true
      -end

      This page was generated using Literate.jl.

      +end

      This page was generated using Literate.jl.

      diff --git a/dev/source/methods/geom_relations/contains/index.html b/dev/source/methods/geom_relations/contains/index.html index c1091d8c2..34fdfa5f6 100644 --- a/dev/source/methods/geom_relations/contains/index.html +++ b/dev/source/methods/geom_relations/contains/index.html @@ -1,12 +1,23 @@ -Containment · GeometryOps.jl

      Containment

      export contains
      +Contains · GeometryOps.jl

      Contains

      export contains

      What is contains?

      The contains function checks if a given geometry completly contains another geometry, or in other words, that the second geometry is completly within the first. This requires that the two interiors intersect and that the interior and boundary of the second geometry is not in the exterior of the first geometry.

      To provide an example, consider these two lines:

      using GeometryOps
      +using GeometryOps.GeometryBasics
      +using Makie
      +using CairoMakie
       
      -"""
      -    contains(ft1::AbstractGeometry, ft2::AbstractGeometry)::Bool
      +l1 = GI.LineString([(0.0, 0.0), (1.0, 0.0), (0.0, 0.1)])
      +l2 = GI.LineString([(0.25, 0.0), (0.75, 0.0)])
      +f, a, p = lines(GI.getpoint(l1), color = :blue)
      +scatter!(GI.getpoint(l1), color = :blue)
      +lines!(GI.getpoint(l2), color = :orange)
      +scatter!(GI.getpoint(l2), color = :orange)

      We can see that all of the points and edges of l2 are within l1, so l1 contains l2. However, l2 does not contain l1.

      contains(l1, l2)  # returns true
      +contains(l2, l1)  # returns false

      Implementation

      This is the GeoInterface-compatible implementation.

      Given that contains is the exact opposite of within, we simply pass the two inputs variables, swapped in order, to within.

      """
      +    contains(g1::AbstractGeometry, g2::AbstractGeometry)::Bool
      +
      +Return 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).
       
      -Return 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 (geometry b)
      -must not intersect the exterior of the primary (geometry a).
       `contains` returns the exact opposite result of `within`.
       
       # Examples
      @@ -14,9 +25,9 @@
       ```jldoctest
       import GeometryOps as GO, GeoInterface as GI
       line = GI.LineString([(1, 1), (1, 2), (1, 3), (1, 4)])
      -point = (1, 2)
      +point = GI.Point((1, 2))
       
       GO.contains(line, point)

      output

      true
       ```
       """
      -contains(g1, g2)::Bool = within(g2, g1)

      This page was generated using Literate.jl.

      +contains(g1, g2) = GeometryOps.within(g2, g1)

      This page was generated using Literate.jl.

      diff --git a/dev/source/methods/geom_relations/coveredby/index.html b/dev/source/methods/geom_relations/coveredby/index.html new file mode 100644 index 000000000..df7251c82 --- /dev/null +++ b/dev/source/methods/geom_relations/coveredby/index.html @@ -0,0 +1,176 @@ + +CoveredBy · GeometryOps.jl

      CoveredBy

      export coveredby

      What is coveredby?

      The coveredby function checks if one geometry is covered by another geometry. This is an extension of within that does not require the interiors of the two geometries to intersect, but still does require that the interior and boundary of the first geometry isn't outside of the second geometry.

      To provide an example, consider this point and line:

      using GeometryOps
      +using GeometryOps.GeometryBasics
      +using Makie
      +using CairoMakie
      +
      +p1 = Point(0.0, 0.0)
      +p2 = Point(1.0, 1.0)
      +l1 = Line(p1, p2)
      +
      +f, a, p = lines([p1, p2])
      +scatter!(p1, color = :red)
      Scatter{Tuple{Vector{Point{2, Float32}}}}

      As we can see, p1 is on the endpoint of l1. This means it is not within, but it does meet the definition of coveredby.

      coveredby(p1, l1)  # true
      true

      Implementation

      This is the GeoInterface-compatible implementation.

      First, we implement a wrapper method that dispatches to the correct implementation based on the geometry trait.

      Each of these calls a method in the geomgeomprocessors file. The methods in this file determine if the given geometries meet a set of criteria. For the coveredby function and arguments g1 and g2, this criteria is as follows: - points of g1 are allowed to be in the interior of g2 (either through overlap or crossing for lines) - points of g1 are allowed to be on the boundary of g2 - points of g1 are not allowed to be in the exterior of g2 - no points of g1 are required to be in the interior of g2 - no points of g1 are required to be on the boundary of g2 - no points of g1 are required to be in the exterior of g2

      The code for the specific implementations is in the geomgeomprocessors file.

      const COVEREDBY_ALLOWS = (in_allow = true, on_allow = true, out_allow = false)
      +const COVEREDBY_CURVE_ALLOWS = (over_allow = true, cross_allow = true, on_allow = true, out_allow = false)
      +const COVEREDBY_CURVE_REQUIRES = (in_require = false, on_require = false, out_require = false)
      +const COVEREDBY_POLYGON_REQUIRES = (in_require = true, on_require = false, out_require = false,)
      +
      +"""
      +    coveredby(g1, g2)::Bool
      +
      +Return `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
      +```jldoctest setup=:(using GeometryOps, GeometryBasics)
      +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

      true
      +```
      +"""
      +coveredby(g1, g2) = _coveredby(trait(g1), g1, trait(g2), g2)

      Convert features to geometries

      _coveredby(::GI.FeatureTrait, g1, ::Any, g2) = coveredby(GI.geometry(g1), g2)
      +_coveredby(::Any, g1, t2::GI.FeatureTrait, g2) = coveredby(g1, GI.geometry(g2))
      +_coveredby(::FeatureTrait, g1, ::FeatureTrait, g2) = coveredby(GI.geometry(g1), GI.geometry(g2))

      Points coveredby geometries

      Point is coveredby another point if those points are equal

      _coveredby(
      +    ::GI.PointTrait, g1,
      +    ::GI.PointTrait, g2,
      +) = equals(g1, g2)

      Point is coveredby a line/linestring if it is on a line vertex or an edge

      _coveredby(
      +    ::GI.PointTrait, g1,
      +    ::Union{GI.LineTrait, GI.LineStringTrait}, g2,
      +) = _point_curve_process(
      +    g1, g2;
      +    COVEREDBY_ALLOWS...,
      +    closed_curve = false,
      +)

      Point is coveredby a linearring if it is on a vertex or an edge of ring

      _coveredby(
      +    ::GI.PointTrait, g1,
      +    ::GI.LinearRingTrait, g2,
      +) = _point_curve_process(
      +    g1, g2;
      +    COVEREDBY_ALLOWS...,
      +    closed_curve = true,
      +)

      Point is coveredby a polygon if it is inside polygon, including edges/vertices

      _coveredby(
      +    ::GI.PointTrait, g1,
      +    ::GI.PolygonTrait, g2,
      +) = _point_polygon_process(
      +    g1, g2;
      +    COVEREDBY_ALLOWS...,
      +)

      Points cannot cover any geometry other than points

      _coveredby(
      +    ::Union{GI.AbstractCurveTrait, GI.PolygonTrait}, g1,
      +    ::GI.PointTrait, g2,
      +) = false

      Lines coveredby geometries

      #= Linestring is coveredby a line if all interior and boundary points of the
      +first line are on the interior/boundary points of the second line. =#
      +_coveredby(
      +    ::Union{GI.LineTrait, GI.LineStringTrait}, g1,
      +    ::Union{GI.LineTrait, GI.LineStringTrait}, g2,
      +) = _line_curve_process(
      +    g1, g2;
      +    COVEREDBY_CURVE_ALLOWS...,
      +    COVEREDBY_CURVE_REQUIRES...,
      +    closed_line = false,
      +    closed_curve = false,
      +)
      +
      +#= Linestring is coveredby a ring if all interior and boundary points of the
      +line are on the edges of the ring. =#
      +_coveredby(
      +    ::Union{GI.LineTrait, GI.LineStringTrait}, g1,
      +    ::GI.LinearRingTrait, g2,
      +) = _line_curve_process(
      +    g1, g2;
      +    COVEREDBY_CURVE_ALLOWS...,
      +    COVEREDBY_CURVE_REQUIRES...,
      +    closed_line = false,
      +    closed_curve = true,
      +)
      +
      +#= Linestring is coveredby a polygon if all interior and boundary points of the
      +line are in the polygon interior or on its edges, inlcuding hole edges. =#
      +_coveredby(
      +    ::Union{GI.LineTrait, GI.LineStringTrait}, g1,
      +    ::GI.PolygonTrait, g2,
      +) = _line_polygon_process(
      +    g1, g2;
      +    COVEREDBY_ALLOWS...,
      +    COVEREDBY_CURVE_REQUIRES...,
      +    closed_line = false,
      +)

      Rings covered by geometries

      #= Linearring is covered by a line if all vertices and edges of the ring are on
      +the edges and vertices of the line. =#
      +_coveredby(
      +    ::GI.LinearRingTrait, g1,
      +    ::Union{GI.LineTrait, GI.LineStringTrait}, g2,
      +) = _line_curve_process(
      +    g1, g2;
      +    COVEREDBY_CURVE_ALLOWS...,
      +    COVEREDBY_CURVE_REQUIRES...,
      +    closed_line = true,
      +    closed_curve = false,
      +)
      +
      +#= Linearring is covered by another linear ring if all vertices and edges of the
      +first ring are on the edges/vertices of the second ring. =#
      +_coveredby(
      +    ::GI.LinearRingTrait, g1,
      +    ::GI.LinearRingTrait, g2,
      +) = _line_curve_process(
      +    g1, g2;
      +    COVEREDBY_CURVE_ALLOWS...,
      +    COVEREDBY_CURVE_REQUIRES...,
      +    closed_line = true,
      +    closed_curve = true,
      +)
      +
      +#= Linearring is coveredby a polygon if all vertices and edges of the ring are
      +in the polygon interior or on the polygon edges, inlcuding hole edges. =#
      +_coveredby(
      +    ::GI.LinearRingTrait, g1,
      +    ::GI.PolygonTrait, g2,
      +) = _line_polygon_process(
      +    g1, g2;
      +    COVEREDBY_ALLOWS...,
      +    COVEREDBY_CURVE_REQUIRES...,
      +    closed_line = true,
      +)

      Polygons covered by geometries

      #= Polygon is covered by another polygon if if the interior and edges of the
      +first polygon are in the second polygon interior or on polygon edges, including
      +hole edges.=#
      +_coveredby(
      +    ::GI.PolygonTrait, g1,
      +    ::GI.PolygonTrait, g2,
      +) = _polygon_polygon_process(
      +    g1, g2;
      +    COVEREDBY_ALLOWS...,
      +    COVEREDBY_POLYGON_REQUIRES...,
      +)

      Polygons cannot covered by any curves

      _coveredby(
      +    ::GI.PolygonTrait, g1,
      +    ::GI.AbstractCurveTrait, g2,
      +) = false

      Geometries coveredby multi-geometry/geometry collections

      #= Geometry is covered by a multi-geometry or a collection if one of the elements
      +of the collection cover the geometry. =#
      +function _coveredby(
      +    ::Union{GI.PointTrait, GI.AbstractCurveTrait, GI.PolygonTrait}, g1,
      +    ::Union{
      +        GI.MultiPointTrait, GI.AbstractMultiCurveTrait,
      +        GI.MultiPolygonTrait, GI.GeometryCollectionTrait,
      +    }, g2,
      +)
      +    for sub_g2 in GI.getgeom(g2)
      +        coveredby(g1, sub_g2) && return true
      +    end
      +    return false
      +end

      Multi-geometry/geometry collections coveredby geometries

      #= Multi-geometry or a geometry collection is covered by a geometry if all
      +elements of the collection are covered by the geometry. =#
      +function _coveredby(
      +    ::Union{
      +        GI.MultiPointTrait, GI.AbstractMultiCurveTrait,
      +        GI.MultiPolygonTrait, GI.GeometryCollectionTrait,
      +    }, g1,
      +    ::GI.AbstractGeometryTrait, g2,
      +)
      +    for sub_g1 in GI.getgeom(g1)
      +        !coveredby(sub_g1, g2) && return false
      +    end
      +    return true
      +end

      This page was generated using Literate.jl.

      diff --git a/dev/source/methods/geom_relations/covers/index.html b/dev/source/methods/geom_relations/covers/index.html new file mode 100644 index 000000000..8befa9e64 --- /dev/null +++ b/dev/source/methods/geom_relations/covers/index.html @@ -0,0 +1,33 @@ + +Covers · GeometryOps.jl

      Covers

      export covers

      What is covers?

      The covers function checks if a given geometry completly covers another geometry. For this to be true, the "contained" geometry's interior and boundaries must be covered by the "covering" geometry's interior and boundaries. The interiors do not need to overlap.

      To provide an example, consider these two lines:

      using GeometryOps
      +using GeometryOps.GeometryBasics
      +using Makie
      +using CairoMakie
      +
      +p1 = Point(0.0, 0.0)
      +p2 = Point(1.0, 1.0)
      +l1 = Line(p1, p2)
      +
      +f, a, p = lines([p1, p2])
      +scatter!(p1, color = :red)
      Scatter{Tuple{Vector{Point{2, Float32}}}}
      covers(l1, p1)  # returns true
      +covers(p1, l1)  # returns false
      false

      Implementation

      This is the GeoInterface-compatible implementation.

      Given that covers is the exact opposite of coveredby, we simply pass the two inputs variables, swapped in order, to coveredby.

      """
      +    covers(g1::AbstractGeometry, g2::AbstractGeometry)::Bool
      +
      +Return 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
      +
      +```jldoctest
      +import GeometryOps as GO, GeoInterface as GI
      +l1 = GI.LineString([(1, 1), (1, 2), (1, 3), (1, 4)])
      +l2 = GI.LineString([(1, 1), (1, 2)])
      +
      +GO.covers(l1, l2)

      output

      true
      +```
      +"""
      +covers(g1, g2)::Bool = GeometryOps.coveredby(g2, g1)

      This page was generated using Literate.jl.

      diff --git a/dev/source/methods/geom_relations/crosses/index.html b/dev/source/methods/geom_relations/crosses/index.html index 0ace25270..771db0c23 100644 --- a/dev/source/methods/geom_relations/crosses/index.html +++ b/dev/source/methods/geom_relations/crosses/index.html @@ -1,5 +1,5 @@ -Crossing checks · GeometryOps.jl

      Crossing checks

      """
      +Crossing checks · GeometryOps.jl

      Crossing checks

      """
            crosses(geom1, geom2)::Bool
       
       Return `true` if the intersection results in a geometry whose dimension is one less than
      @@ -38,7 +38,7 @@
           while i < GI.npoint(geom1) && !int_point && !ext_point
               for j in 1:GI.npoint(geom2) - 1
                   exclude_boundary = (j === 1 || j === np2 - 2) ? :none : :both
      -            if point_on_segment(GI.getpoint(geom1, i), (GI.getpoint(geom2, j), GI.getpoint(geom2, j + 1)); exclude_boundary)
      +            if _point_on_segment(GI.getpoint(geom1, i), (GI.getpoint(geom2, j), GI.getpoint(geom2, j + 1)); exclude_boundary)
                       int_point = true
                   else
                       ext_point = true
      @@ -52,14 +52,14 @@
       
       function line_crosses_line(line1, line2)
           np2 = GI.npoint(line2)
      -    if intersects(line1, line2)
      +    if GeometryOps.intersects(line1, line2)
               for i in 1:GI.npoint(line1) - 1
                   for j in 1:GI.npoint(line2) - 1
                       exclude_boundary = (j === 1 || j === np2 - 2) ? :none : :both
                       pa = GI.getpoint(line1, i)
                       pb = GI.getpoint(line1, i + 1)
                       p = GI.getpoint(line2, j)
      -                point_on_segment(p, (pa, pb); exclude_boundary) && return true
      +                _point_on_segment(p, (pa, pb); exclude_boundary) && return true
                   end
               end
           end
      @@ -78,7 +78,10 @@
           ext_point = false
       
           for p in GI.getpoint(mp)
      -        if point_in_polygon(p, poly)
      +        if _point_polygon_process(
      +            p, poly;
      +            in_allow = true, on_allow = true, out_allow = false,
      +        )
                   int_point = true
               else
                   ext_point = true
      @@ -86,4 +89,40 @@
               int_point && ext_point && return true
           end
           return false
      -end

      This page was generated using Literate.jl.

      +end + +#= TODO: Once crosses is swapped over to use the geom relations workflow, can +delete these helpers. =# + +function _point_on_segment(point, (start, stop); exclude_boundary::Symbol=:none)::Bool + x, y = GI.x(point), GI.y(point) + x1, y1 = GI.x(start), GI.y(start) + x2, y2 = GI.x(stop), GI.y(stop) + + dxc = x - x1 + dyc = y - y1 + dx1 = x2 - x1 + dy1 = y2 - y1

      TODO use better predicate for crossing here

          cross = dxc * dy1 - dyc * dx1
      +    cross != 0 && return false

      Will constprop optimise these away?

          if exclude_boundary === :none
      +        if abs(dx1) >= abs(dy1)
      +            return dx1 > 0 ? x1 <= x && x <= x2 : x2 <= x && x <= x1
      +        end
      +        return dy1 > 0 ? y1 <= y && y <= y2 : y2 <= y && y <= y1
      +    elseif exclude_boundary === :start
      +        if abs(dx1) >= abs(dy1)
      +             return dx1 > 0 ? x1 < x && x <= x2 : x2 <= x && x < x1
      +        end
      +        return dy1 > 0 ? y1 < y && y <= y2 : y2 <= y && y < y1
      +    elseif exclude_boundary === :end
      +        if abs(dx1) >= abs(dy1)
      +            return dx1 > 0 ? x1 <= x && x < x2 : x2 < x && x <= x1
      +        end
      +        return dy1 > 0 ? y1 <= y && y < y2 : y2 < y && y <= y1
      +    elseif exclude_boundary === :both
      +        if abs(dx1) >= abs(dy1)
      +            return dx1 > 0 ? x1 < x && x < x2 : x2 < x && x < x1
      +        end
      +        return dy1 > 0 ? y1 < y && y < y2 : y2 < y && y < y1
      +    end
      +    return false
      +end

      This page was generated using Literate.jl.

      diff --git a/dev/source/methods/geom_relations/disjoint/index.html b/dev/source/methods/geom_relations/disjoint/index.html index 39084e9e1..6dc134b3b 100644 --- a/dev/source/methods/geom_relations/disjoint/index.html +++ b/dev/source/methods/geom_relations/disjoint/index.html @@ -1,34 +1,169 @@ -Disjointness checks · GeometryOps.jl

      Disjointness checks

      """
      +Disjoint · GeometryOps.jl

      Disjoint

      export disjoint

      What is disjoint?

      The disjoint function checks if one geometry is outside of another geometry, without sharing any boundaries or interiors.

      To provide an example, consider these two lines:

      using GeometryOps
      +using GeometryOps.GeometryBasics
      +using Makie
      +using CairoMakie
      +
      +l1 = GI.LineString([(0.0, 0.0), (1.0, 0.0), (0.0, 0.1)])
      +l2 = GI.LineString([(2.0, 0.0), (2.75, 0.0)])
      +f, a, p = lines(GI.getpoint(l1), color = :blue)
      +scatter!(GI.getpoint(l1), color = :blue)
      +lines!(GI.getpoint(l2), color = :orange)
      +scatter!(GI.getpoint(l2), color = :orange)

      We can see that none of the edges or vertices of l1 interact with l2 so they are disjoint.

      disjoint(l1, l2)  # returns true

      Implementation

      This is the GeoInterface-compatible implementation.

      First, we implement a wrapper method that dispatches to the correct implementation based on the geometry trait.

      Each of these calls a method in the geomgeomprocessors file. The methods in this file determine if the given geometries meet a set of criteria. For the disjoint function and arguments g1 and g2, this criteria is as follows: - points of g1 are not allowed to be in the interior of g2 - points of g1 are not allowed to be on the boundary of g2 - points of g1 are allowed to be in the exterior of g2 - no points required to be in the interior of g2 - no points of g1 are required to be on the boundary of g2 - no points of g1 are required to be in the exterior of g2

      The code for the specific implementations is in the geomgeomprocessors file.

      const DISJOINT_ALLOWS = (in_allow = false, on_allow = false, out_allow = true)
      +const DISJOINT_CURVE_ALLOWS = (over_allow = false, cross_allow = false, on_allow = false, out_allow = true)
      +const DISJOINT_REQUIRES = (in_require = false, on_require = false, out_require = false)
      +"""
           disjoint(geom1, geom2)::Bool
       
      -Return `true` if the intersection of the two geometries is an empty set.

      Examples

      ```jldoctest
      +Return `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
      +```jldoctest setup=:(using GeometryOps, GeometryBasics)
       import GeometryOps as GO, GeoInterface as GI
       
      -poly = GI.Polygon([[(-1, 2), (3, 2), (3, 3), (-1, 3), (-1, 2)]])
      -point = (1, 1)
      -GO.disjoint(poly, point)

      output

      true
      +line = GI.LineString([(1, 1), (1, 2), (1, 3), (1, 4)])
      +point = (2, 2)
      +GO.disjoint(point, line)

      output

      true
       ```
       """
      -disjoint(g1, g2)::Bool = disjoint(trait(g1), g1, trait(g2), g2)
      -disjoint(::FeatureTrait, g1, ::Any, g2)::Bool = disjoint(GI.geometry(g1), g2)
      -disjoint(::Any, g1, t2::FeatureTrait, g2)::Bool = disjoint(g1, geometry(g2))
      -disjoint(::PointTrait, g1, ::PointTrait, g2)::Bool = !point_equals_point(g1, g2)
      -disjoint(::PointTrait, g1, ::LineStringTrait, g2)::Bool = !point_on_line(g1, g2)
      -disjoint(::PointTrait, g1, ::PolygonTrait, g2)::Bool = !point_in_polygon(g1, g2)
      -disjoint(::LineStringTrait, g1, ::PointTrait, g2)::Bool = !point_on_line(g2, g1)
      -disjoint(::LineStringTrait, g1, ::LineStringTrait, g2)::Bool = !line_on_line(g1, g2)
      -disjoint(::LineStringTrait, g1, ::PolygonTrait, g2)::Bool = !line_in_polygon(g2, g1)
      -disjoint(::PolygonTrait, g1, ::PointTrait, g2)::Bool = !point_in_polygon(g2, g1)
      -disjoint(::PolygonTrait, g1, ::LineStringTrait, g2)::Bool = !line_in_polygon(g2, g1)
      -disjoint(::PolygonTrait, g1, ::PolygonTrait, g2)::Bool = polygon_disjoint(g2, g1)
      -
      -function polygon_disjoint(poly1, poly2)
      -    for point in GI.getpoint(poly1)
      -        point_in_polygon(point, poly2) && return false
      +disjoint(g1, g2) = _disjoint(trait(g1), g1, trait(g2), g2)

      Convert features to geometries

      _disjoint(::FeatureTrait, g1, ::Any, g2) = disjoint(GI.geometry(g1), g2)
      +_disjoint(::Any, g1, ::FeatureTrait, g2) = disjoint(g1, geometry(g2))
      +_disjoint(::FeatureTrait, g1, ::FeatureTrait, g2) = disjoint(GI.geometry(g1), GI.geometry(g2))

      Point disjoint geometries

      Point is disjoint from another point if the points are not equal.

      _disjoint(
      +    ::GI.PointTrait, g1,
      +    ::GI.PointTrait, g2,
      +) = !equals(g1, g2)

      Point is disjoint from a linestring if it is not on the line's edges/vertices.

      _disjoint(
      +    ::GI.PointTrait, g1,
      +    ::Union{GI.LineTrait, GI.LineStringTrait}, g2,
      +) = _point_curve_process(
      +    g1, g2;
      +    DISJOINT_ALLOWS...,
      +    closed_curve = false,
      +)

      Point is disjoint from a linearring if it is not on the ring's edges/vertices.

      _disjoint(
      +    ::GI.PointTrait, g1,
      +    ::GI.LinearRingTrait, g2,
      +) = _point_curve_process(
      +    g1, g2;
      +    DISJOINT_ALLOWS...,
      +    closed_curve = true,
      +)
      +
      +#= Point is disjoint from a polygon if it is not on any edges, vertices, or
      +within the polygon's interior. =#
      +_disjoint(
      +    ::GI.PointTrait, g1,
      +    ::GI.PolygonTrait, g2,
      +) = _point_polygon_process(
      +    g1, g2;
      +    DISJOINT_ALLOWS...,
      +)
      +
      +#= Geometry is disjoint from a point if the point is not in the interior or on
      +the boundary of the geometry. =#
      +_disjoint(
      +    trait1::Union{GI.AbstractCurveTrait, GI.PolygonTrait}, g1,
      +    trait2::GI.PointTrait, g2,
      +) = _disjoint(trait2, g2, trait1, g1)

      Lines disjoint geometries

      #= Linestring is disjoint from another line if they do not share any interior
      +edge/vertex points or boundary points. =#
      +_disjoint(
      +    ::Union{GI.LineTrait, GI.LineStringTrait}, g1,
      +    ::Union{GI.LineTrait, GI.LineStringTrait}, g2,
      +) = _line_curve_process(
      +    g1, g2;
      +    DISJOINT_CURVE_ALLOWS...,
      +    DISJOINT_REQUIRES...,
      +    closed_line = false,
      +    closed_curve = false,
      +)
      +
      +#= Linestring is disjoint from a linearring if they do not share any interior
      +edge/vertex points or boundary points. =#
      +_disjoint(
      +    ::Union{GI.LineTrait, GI.LineStringTrait}, g1,
      +    ::GI.LinearRingTrait, g2,
      +) = _line_curve_process(
      +    g1, g2;
      +    DISJOINT_CURVE_ALLOWS...,
      +    DISJOINT_REQUIRES...,
      +    closed_line = false,
      +    closed_curve = true,
      +)
      +
      +#= Linestring is disjoint from a polygon if the interior and boundary points of
      +the line are not in the polygon's interior or on the polygon's boundary. =#
      +_disjoint(
      +    ::Union{GI.LineTrait, GI.LineStringTrait}, g1,
      +    ::GI.PolygonTrait, g2,
      +) = _line_polygon_process(
      +    g1, g2;
      +    DISJOINT_ALLOWS...,
      +    DISJOINT_REQUIRES...,
      +    closed_line = false,
      +)
      +
      +#= Geometry is disjoint from a linestring if the line's interior and boundary
      +points don't intersect with the geometrie's interior and boundary points. =#
      +_disjoint(
      +    trait1::Union{GI.LinearRingTrait, GI.PolygonTrait}, g1,
      +    trait2::Union{GI.LineTrait, GI.LineStringTrait}, g2,
      +) = _disjoint(trait2, g2, trait1, g1)

      Rings disjoint geometries

      #= Linearrings is disjoint from another linearring if they do not share any
      +interior edge/vertex points or boundary points.=#
      +_disjoint(
      +    ::GI.LinearRingTrait, g1,
      +    ::GI.LinearRingTrait, g2,
      +) = _line_curve_process(
      +    g1, g2;
      +    DISJOINT_CURVE_ALLOWS...,
      +    DISJOINT_REQUIRES...,
      +    closed_line = true,
      +    closed_curve = true,
      +)
      +
      +#= Linearring is disjoint from a polygon if the interior and boundary points of
      +the ring are not in the polygon's interior or on the polygon's boundary. =#
      +_disjoint(
      +    ::GI.LinearRingTrait, g1,
      +    ::GI.PolygonTrait, g2,
      +) = _line_polygon_process(
      +    g1, g2;
      +    DISJOINT_ALLOWS...,
      +    DISJOINT_REQUIRES...,
      +    closed_line = true,
      +)

      Polygon disjoint geometries

      #= Polygon is disjoint from another polygon if they do not share any edges or
      +vertices and if their interiors do not intersect, excluding any holes. =#
      +_disjoint(
      +    ::GI.PolygonTrait, g1,
      +    ::GI.PolygonTrait, g2,
      +) = _polygon_polygon_process(
      +    g1, g2;
      +    DISJOINT_ALLOWS...,
      +    DISJOINT_REQUIRES...,
      +)

      Geometries disjoint multi-geometry/geometry collections

      #= Geometry is disjoint from a multi-geometry or a collection if all of the
      +elements of the collection are disjoint from the geometry. =#
      +function _disjoint(
      +    ::Union{GI.PointTrait, GI.AbstractCurveTrait, GI.PolygonTrait}, g1,
      +    ::Union{
      +        GI.MultiPointTrait, GI.AbstractMultiCurveTrait,
      +        GI.MultiPolygonTrait, GI.GeometryCollectionTrait,
      +    }, g2,
      +)
      +    for sub_g2 in GI.getgeom(g2)
      +        !disjoint(g1, sub_g2) && return false
           end
      -    for point in GI.getpoint(poly2)
      -        point_in_polygon(point, poly1) && return false
      +    return true
      +end

      Multi-geometry/geometry collections coveredby geometries

      #= Multi-geometry or a geometry collection is covered by a geometry if all
      +elements of the collection are covered by the geometry. =#
      +function _disjoint(
      +    ::Union{
      +        GI.MultiPointTrait, GI.AbstractMultiCurveTrait,
      +        GI.MultiPolygonTrait, GI.GeometryCollectionTrait,
      +    }, g1,
      +    ::GI.AbstractGeometryTrait, g2,
      +)
      +    for sub_g1 in GI.getgeom(g1)
      +        !disjoint(sub_g1, g2) && return false
           end
      -    return !intersects(poly1, poly2)
      -end

      This page was generated using Literate.jl.

      + return true +end

      This page was generated using Literate.jl.

      diff --git a/dev/source/methods/geom_relations/geom_geom_processors/index.html b/dev/source/methods/geom_relations/geom_geom_processors/index.html new file mode 100644 index 000000000..8abd197d1 --- /dev/null +++ b/dev/source/methods/geom_relations/geom_geom_processors/index.html @@ -0,0 +1,449 @@ + +- · GeometryOps.jl
      #= Code is based off of DE-9IM Standards (https://en.wikipedia.org/wiki/DE-9IM)
      +and attempts a standardized solution for most of the functions.
      +=#
      +
      +@enum PointOrientation point_in=1 point_on=2 point_out=3
      +
      +@enum LineOrientation line_cross=1 line_hinge=2 line_over=3 line_out=4

      Determines if a point meets the given checks with respect to a curve.

      If inallow is true, the point can be on the curve interior. If onallow is true, the point can be on the curve boundary. If out_allow is true, the point can be disjoint from the curve.

      If the point is in an "allowed" location, return true. Else, return false.

      If closed_curve is true, curve is treated as a closed curve where the first and last point are connected by a segment.

      function _point_curve_process(
      +    point, curve;
      +    in_allow, on_allow, out_allow,
      +    closed_curve = false,
      +)

      Determine if curve is closed

          n = GI.npoint(curve)
      +    first_last_equal = equals(GI.getpoint(curve, 1), GI.getpoint(curve, n))
      +    closed_curve |= first_last_equal
      +    n -= first_last_equal ? 1 : 0

      Loop through all curve segments

          p_start = GI.getpoint(curve, closed_curve ? n : 1)
      +    @inbounds for i in (closed_curve ? 1 : 2):n
      +        p_end = GI.getpoint(curve, i)
      +        seg_val = _point_segment_orientation(point, p_start, p_end)
      +        seg_val == point_in && return in_allow
      +        if seg_val == point_on
      +            if !closed_curve  # if point is on curve endpoints, it is "on"
      +                i == 2 && equals(point, p_start) && return on_allow
      +                i == n && equals(point, p_end) && return on_allow
      +            end
      +            return in_allow
      +        end
      +        p_start = p_end
      +    end
      +    return out_allow
      +end

      Determines if a point meets the given checks with respect to a polygon.

      If inallow is true, the point can be within the polygon interior If onallow is true, the point can be on the polygon boundary. If out_allow is true, the point can be disjoint from the polygon.

      If the point is in an "allowed" location, return true. Else, return false.

      function _point_polygon_process(
      +    point, polygon;
      +    in_allow, on_allow, out_allow,
      +)

      Check interaction of geom with polygon's exterior boundary

          ext_val = _point_filled_curve_orientation(point, GI.getexterior(polygon))

      If a point is outside, it isn't interacting with any holes

          ext_val == point_out && return out_allow

      if a point is on an external boundary, it isn't interacting with any holes

          ext_val == point_on && return on_allow

      If geom is within the polygon, need to check interactions with holes

          for hole in GI.gethole(polygon)
      +        hole_val = _point_filled_curve_orientation(point, hole)

      If a point in in a hole, it is outside of the polygon

              hole_val == point_in && return out_allow

      If a point in on a hole edge, it is on the edge of the polygon

              hole_val == point_on && return on_allow
      +    end

      Point is within external boundary and on in/on any holes

          return in_allow
      +end

      Determines if a line meets the given checks with respect to a curve.

      If overallow is true, segments of the line and curve can be co-linear. If crossallow is true, segments of the line and curve can cross. If onallow is true, endpoints of either the line or curve can intersect a segment of the other geometry. If crossallow is true, segments of the line and curve can be disjoint.

      If inrequire is true, the interiors of the line and curve must meet in at least one point. If onrequire is true, the bounday of one of the two geometries can meet the interior or boundary of the other geometry in at least one point. If out_require is true, there must be at least one point of the given line that is exterior of the curve.

      If the point is in an "allowed" location and meets all requirments, return true. Else, return false.

      If closedline is true, line is treated as a closed line where the first and last point are connected by a segment. Same with closedcurve.

      function _line_curve_process(
      +    line, curve;
      +    over_allow, cross_allow, on_allow, out_allow,
      +    in_require, on_require, out_require,
      +    closed_line = false,
      +    closed_curve = false,
      +)

      Set up requirments

          in_req_met = !in_require
      +    on_req_met = !on_require
      +    out_req_met = !out_require

      Determine curve endpoints

          nl = GI.npoint(line)
      +    nc = GI.npoint(curve)
      +    first_last_equal_line = equals(GI.getpoint(line, 1), GI.getpoint(line, nl))
      +    first_last_equal_curve = equals(GI.getpoint(curve, 1), GI.getpoint(curve, nc))
      +    nl -= first_last_equal_line ? 1 : 0
      +    nc -= first_last_equal_curve ? 1 : 0
      +    closed_line |= first_last_equal_line
      +    closed_curve |= first_last_equal_curve

      Loop over each line segment

          l_start = GI.getpoint(line, closed_line ? nl : 1)
      +    i = closed_line ? 1 : 2
      +    while i ≤ nl
      +        l_end = GI.getpoint(line, i)
      +        c_start = GI.getpoint(curve, closed_curve ? nc : 1)

      Loop over each curve segment

              for j in (closed_curve ? 1 : 2):nc
      +            c_end = GI.getpoint(curve, j)

      Check if line and curve segments meet

                  seg_val = _segment_segment_orientation((l_start, l_end), (c_start, c_end))

      If segments are co-linear

                  if seg_val == line_over
      +                !over_allow && return false

      at least one point in, meets requirments

                      in_req_met = true
      +                point_val = _point_segment_orientation(l_start, c_start, c_end)

      If entire segment isn't covered, consider remaining section

                      if point_val != point_out
      +                    i, l_start, break_off = _find_new_seg(i, l_start, l_end, c_start, c_end)
      +                    break_off && break
      +                end
      +            else
      +                if seg_val == line_cross
      +                    !cross_allow && return false
      +                    in_req_met = true
      +                elseif seg_val == line_hinge  # could cross or overlap

      Determine location of intersection point on each segment

                          (α, β) = _find_intersect_fracs(l_start, l_end, c_start, c_end)
      +                    if ( # Don't consider edges of curves as they can't cross
      +                        (!closed_line && ((α == 0 && i == 2) || (α == 1 && i == nl))) ||
      +                        (!closed_curve && ((β == 0 && j == 2) || (β == 1 && j == nc)))
      +                    )
      +                        !on_allow && return false
      +                        on_req_met = true
      +                    else
      +                        in_req_met = true

      If needed, determine if hinge actually crosses

                              if (!cross_allow || !over_allow) && α != 0 && β != 0

      Find next pieces of hinge to see if line and curve cross

                                  l, c = _find_hinge_next_segments(
      +                                α, β, l_start, l_end, c_start, c_end,
      +                                i, line, j, curve,
      +                            )
      +                            if _segment_segment_orientation(l, c) == line_hinge
      +                                !cross_allow && return false
      +                            else
      +                                !over_allow && return false
      +                            end
      +                        end
      +                    end
      +                end

      no overlap for a give segment, some of segment must be out of curve

                      if j == nc
      +                    !out_allow && return false
      +                    out_req_met = true
      +                end
      +            end
      +            c_start = c_end  # consider next segment of curve
      +            if j == nc  # move on to next line segment
      +                i += 1
      +                l_start = l_end
      +            end
      +        end
      +    end
      +    return in_req_met && on_req_met && out_req_met
      +end
      +
      +#= If entire segment (le to ls) isn't covered by segment (cs to ce), find remaining section
      +part of section outside of cs to ce. If completly covered, increase segment index i. =#
      +function _find_new_seg(i, ls, le, cs, ce)
      +    break_off = true
      +    if _point_segment_orientation(le, cs, ce) != point_out
      +        ls = le
      +        i += 1
      +    elseif !equals(ls, cs) && _point_segment_orientation(cs, ls, le) != point_out
      +        ls = cs
      +    elseif !equals(ls, ce) && _point_segment_orientation(ce, ls, le) != point_out
      +        ls = ce
      +    else
      +        break_off = false
      +    end
      +    return i, ls, break_off
      +end
      +
      +#= Find where line and curve segments intersect by fraction of length. α is the fraction of
      +the line (ls to le) and β is the traction of the curve (cs to ce). =#
      +function _find_intersect_fracs(ls, le, cs, ce)
      +    _, fracs = _intersection_point(
      +        (_tuple_point(ls), _tuple_point(le)),
      +        (_tuple_point(cs), _tuple_point(ce))
      +    )
      +    (α, β) = if !isnothing(fracs)
      +        fracs
      +    else  # line and curve segments are parallel
      +        if equals(ls, cs)
      +            (0, 0)
      +        elseif equals(ls, ce)
      +            (0, 1)
      +        elseif equals(le, cs)
      +            (1, 0)
      +        else  # equals(l_end, c_end)
      +            (1, 1)
      +        end
      +    end
      +    return α, β
      +end
      +
      +#= Find next set of segments needed to determine if given hinge segments cross or not.=#
      +_find_hinge_next_segments(α, β, ls, le, cs, ce, i, line, j, curve) =
      +    if β == 1
      +        if α == 1  # hinge at endpoints, so next segment of both is needed
      +            ((le, GI.getpoint(line, i + 1)), (ce, GI.getpoint(curve, j + 1)))
      +        else  # hinge at curve endpoint and line interior point, curve next segment needed
      +            ((ls, le), (ce, GI.getpoint(curve, j + 1)))
      +        end
      +    else  # hinge at curve interior point and line endpoint, line next segment needed
      +        ((le, GI.getpoint(line, i + 1)), (cs, ce))
      +    end

      Determines if a line meets the given checks with respect to a polygon.

      If inallow is true, segments of the line can be in the polygon interior. If onallow is true, segments of the line can be on the polygon's boundary. If out_allow is true, segments of the line can be outside of the polygon.

      If inrequire is true, the interiors of the line and polygon must meet in at least one point. If onrequire is true, the line must have at least one point on the polygon'same boundary. If out_require is true, the line must have at least one point outside of the polygon.

      If the point is in an "allowed" location and meets all requirments, return true. Else, return false.

      If closed_line is true, line is treated as a closed line where the first and last point are connected by a segment.

      function _line_polygon_process(
      +    line, polygon;
      +    in_allow, on_allow, out_allow,
      +    in_require, on_require, out_require,
      +    closed_line = false,
      +)
      +    in_req_met = !in_require
      +    on_req_met = !on_require
      +    out_req_met = !out_require

      Check interaction of line with polygon's exterior boundary

          in_curve, on_curve, out_curve = _line_filled_curve_interactions(
      +        line, GI.getexterior(polygon);
      +        closed_line = closed_line,
      +    )
      +    if on_curve
      +        !on_allow && return false
      +        on_req_met = true
      +    end
      +    if out_curve
      +        !out_allow && return false
      +        out_req_met = true
      +    end

      If no points within the polygon, the line is disjoint and we are done

          !in_curve && return in_req_met && on_req_met && out_req_met

      Loop over polygon holes

          for hole in GI.gethole(polygon)
      +        in_hole, on_hole, out_hole =_line_filled_curve_interactions(
      +            line, hole;
      +            closed_line = closed_line,
      +        )
      +        if in_hole  # line in hole is equivalent to being out of polygon
      +            !out_allow && return false
      +            out_req_met = true
      +        end
      +        if on_hole  # hole bounday is polygon boundary
      +            !on_allow && return false
      +            on_req_met = true
      +        end
      +        if !out_hole  # entire line is in/on hole, can't be in/on other holes
      +            in_curve = false
      +            break
      +        end
      +    end
      +    if in_curve  # entirely of curve isn't within a hole
      +        !in_allow && return false
      +        in_req_met = true
      +    end
      +    return in_req_met && on_req_met && out_req_met
      +end

      Determines if a polygon meets the given checks with respect to a polygon.

      If inallow is true, the polygon's interiors must intersect. If onallow is true, the one of the polygon's boundaries must either interact with the other polygon's boundary or interior. If out_allow is true, the first polygon must have interior regions outside of the second polygon.

      If inrequire is true, the polygon interiors must meet in at least one point. If onrequire is true, one of the polygon's must have at least one boundary point in or on the other polygon. If out_require is true, the first polygon must have at least one interior point outside of the second polygon.

      If the point is in an "allowed" location and meets all requirments, return true. Else, return false.

      function _polygon_polygon_process(
      +    poly1, poly2;
      +    in_allow, on_allow, out_allow,
      +    in_require, on_require, out_require,
      +)
      +    in_req_met = !in_require
      +    on_req_met = !on_require
      +    out_req_met = !out_require

      Check if exterior of poly1 is within poly2

          ext1 = GI.getexterior(poly1)
      +    ext2 = GI.getexterior(poly2)

      Check if exterior of poly1 is in polygon 2

          e1_in_p2, e1_on_p2, e1_out_p2 = _line_polygon_interactions(
      +        ext1, poly2;
      +        closed_line = true,
      +    )
      +    if e1_on_p2
      +        !on_allow && return false
      +        on_req_met = true
      +    end
      +    if e1_out_p2
      +        !out_allow && return false
      +        out_req_met = true
      +    end
      +
      +    if !e1_in_p2

      if exterior ring isn't in poly2, check if it surrounds poly2

              _, _, e2_out_e1 = _line_filled_curve_interactions(
      +            ext2, ext1;
      +            closed_line = true,
      +        )  # if they really are disjoint, we are done
      +        e2_out_e1 && return in_req_met && on_req_met && out_req_met
      +    end

      If interiors interact, check if poly2 interacts with any of poly1's holes

          for h1 in GI.gethole(poly1)
      +        h1_in_p2, h1_on_p2, h1_out_p2 = _line_polygon_interactions(
      +            h1, poly2;
      +            closed_line = true,
      +        )
      +        if h1_on_p2
      +            !on_allow && return false
      +            on_req_met = true
      +        end
      +        if h1_out_p2
      +            !out_allow && return false
      +            out_req_met = true
      +        end
      +        if !h1_in_p2

      If hole isn't in poly2, see if poly2 is in hole

                  _, _, e2_out_h1 = _line_filled_curve_interactions(
      +                ext2, h1;
      +                closed_line = true,
      +            )

      hole encompasses all of poly2

                  !e2_out_h1 && return in_req_met && on_req_met && out_req_met
      +            break
      +        end
      +    end
      +    #=
      +    poly2 isn't outside of poly1 and isn't in a hole, poly1 interior must
      +    interact with poly2 interior
      +    =#
      +    !in_allow && return false
      +    in_req_met = true

      If any of poly2 holes are within poly1, part of poly1 is exterior to poly2

          for h2 in GI.gethole(poly2)
      +        h2_in_p1, h2_on_p1, _ = _line_polygon_interactions(
      +            h2, poly1;
      +            closed_line = true,
      +        )
      +        if h2_on_p1
      +            !on_allow && return false
      +            on_req_met = true
      +        end
      +        if h2_in_p1
      +            !out_allow && return false
      +            out_req_met = true
      +        end
      +    end
      +    return in_req_met && on_req_met && out_req_met
      +end

      Determines if a point is in, on, or out of a segment. If the point is on the segment it is on one of the segments endpoints. If it is in, it is on any other point of the segment. If the point is not on any part of the segment, it is out of the segment.

      Point should be an object of point trait and curve should be an object with a linestring or linearring trait.

      Can provide values of in, on, and out keywords, which determines return values for each scenario.

      function _point_segment_orientation(
      +    point, start, stop;
      +    in::T = point_in, on::T = point_on, out::T = point_out,
      +) where {T}

      Parse out points

          x, y = GI.x(point), GI.y(point)
      +    x1, y1 = GI.x(start), GI.y(start)
      +    x2, y2 = GI.x(stop), GI.y(stop)
      +    Δx_seg = x2 - x1
      +    Δy_seg = y2 - y1
      +    Δx_pt = x - x1
      +    Δy_pt = y - y1
      +    if (Δx_pt == 0 && Δy_pt == 0) || (Δx_pt == Δx_seg && Δy_pt == Δy_seg)

      If point is equal to the segment start or end points

              return on
      +    else
      +        #=
      +        Determine if the point is on the segment -> see if vector from segment
      +        start to point is parallel to segment and if point is between the
      +        segment endpoints
      +        =#
      +        on_line = _isparallel(Δx_seg, Δy_seg, Δx_pt, Δy_pt)
      +        !on_line && return out
      +        between_endpoints =
      +            (x2 > x1 ? x1 <= x <= x2 : x2 <= x <= x1) &&
      +            (y2 > y1 ? y1 <= y <= y2 : y2 <= y <= y1)
      +        !between_endpoints && return out
      +    end
      +    return in
      +end

      Determine if point is in, on, or out of a closed curve, which includes the space enclosed by the closed curve.

      In means the point is within the closed curve (excluding edges and vertices). On means the point is on an edge or a vertex of the closed curve. Out means the point is outside of the closed curve.

      Point should be an object of point trait and curve should be an object with a linestring or linearring trait, that is assumed to be closed, regardless of repeated last point.

      Can provide values of in, on, and out keywords, which determines return values for each scenario.

      Note that this uses the Algorithm by Hao and Sun (2018): https://doi.org/10.3390/sym10100477 Paper seperates orientation of point and edge into 26 cases. For each case, it is either a case where the point is on the edge (returns on), where a ray from the point (x, y) to infinity along the line y = y cut through the edge (k += 1), or the ray does not pass through the edge (do nothing and continue). If the ray passes through an odd number of edges, it is within the curve, else outside of of the curve if it didn't return 'on'. See paper for more information on cases denoted in comments.

      function _point_filled_curve_orientation(
      +    point, curve;
      +    in::T = point_in, on::T = point_on, out::T = point_out,
      +) where {T}
      +    x, y = GI.x(point), GI.y(point)
      +    n = GI.npoint(curve)
      +    n -= equals(GI.getpoint(curve, 1), GI.getpoint(curve, n)) ? 1 : 0
      +    k = 0  # counter for ray crossings
      +    p_start = GI.getpoint(curve, n)
      +    @inbounds for i in 1:n
      +        p_end = GI.getpoint(curve, i)
      +        v1 = GI.y(p_start) - y
      +        v2 = GI.y(p_end) - y
      +        if !((v1 < 0 && v2 < 0) || (v1 > 0 && v2 > 0)) # if not cases 11 or 26
      +            u1 = GI.x(p_start) - x
      +            u2 = GI.x(p_end) - x
      +            c1 = u1 * v2  # first element of cross product summation
      +            c2 = u2 * v1  # second element of cross product summation
      +            f = c1 - c2
      +            if v2 > 0 && v1 ≤ 0                # Case 3, 9, 16, 21, 13, or 24
      +                (c1 ≈ c2) && return on         # Case 16 or 21
      +                f > 0 && (k += 1)              # Case 3 or 9
      +            elseif v1 > 0 && v2 ≤ 0            # Case 4, 10, 19, 20, 12, or 25
      +                (c1 ≈ c2) && return on         # Case 19 or 20
      +                f < 0 && (k += 1)              # Case 4 or 10
      +            elseif v2 == 0 && v1 < 0           # Case 7, 14, or 17
      +                (c1 ≈ c2) && return on         # Case 17
      +            elseif v1 == 0 && v2 < 0           # Case 8, 15, or 18
      +                (c1 ≈ c2) && return on         # Case 18
      +            elseif v1 == 0 && v2 == 0          # Case 1, 2, 5, 6, 22, or 23
      +                u2 ≤ 0 && u1 ≥ 0 && return on  # Case 1
      +                u1 ≤ 0 && u2 ≥ 0 && return on  # Case 2
      +            end
      +        end
      +        p_start = p_end
      +    end
      +    return iseven(k) ? out : in
      +end

      Determines the type of interaction between two line segments. If the segments cross, this means that they have a single intersection point that isn't on either of their enpoints. If they form a hinge, they meet at one of the segments endpoints. If they are over, then they are co-linear for at least some of the length of the segments. Finally, if they are out, then the segments are disjoint.

      Point should be an object of point trait and curve should be an object with a linestring or linearring trait.

      Can provide values of in, on, and out keywords, which determines return values for each scenario.

      function _segment_segment_orientation(
      +    (a_point, b_point), (c_point, d_point);
      +    cross::T = line_cross, hinge::T = line_hinge,
      +    over::T = line_over, out::T = line_out,
      +) where T
      +    (ax, ay) = Float64.(_tuple_point(a_point))
      +    (bx, by) = Float64.(_tuple_point(b_point))
      +    (cx, cy) = Float64.(_tuple_point(c_point))
      +    (dx, dy) = Float64.(_tuple_point(d_point))
      +    meet_type = ExactPredicates.meet((ax, ay), (bx, by), (cx, cy), (dx, dy))

      Lines meet at one point within open segments

          meet_type == 1 && return cross

      Lines don't meet at any points

          meet_type == -1 && return out

      Lines meet at one or more points within closed segments

          if _isparallel(((ax, ay), (bx, by)), ((cx, cy), (dx, dy)))
      +        min_x, max_x = cx < dx ? (cx, dx) : (dx, cx)
      +        min_y, max_y = cy < dy ? (cy, dy) : (dy, cy)
      +        if (
      +            ((ax ≤ min_x && bx ≤ min_x) || (ax ≥ max_x && bx ≥ max_x)) &&
      +            ((ay ≤ min_y && by ≤ min_y) || (ay ≥ max_y && by ≥ max_y))
      +        )

      apoint and bpoint are on the same side of segment, don't overlap

                  return hinge
      +        else
      +            return over
      +        end
      +    end

      if lines aren't parallel then they must hinge

          return hinge
      +end

      Determines the types of interactions of a line with a filled-in curve. By filled-in curve, I am referring to the exterior ring of a poylgon, for example.

      Returns a tuple of booleans: (incurve, oncurve, out_curve).

      If incurve is true, some of the lines interior points interact with the curve's interior points. If oncurve is true, endpoints of either the line intersect with the curve or the line interacts with the polygon boundary. If out_curve is true, at least one segments of the line is outside the curve.

      If closed_line is true, line is treated as a closed line where the first and last point are connected by a segment.

      function _line_filled_curve_interactions(
      +    line, curve;
      +    closed_line = false,
      +)
      +    in_curve = false
      +    on_curve = false
      +    out_curve = false

      Determine number of points in curve and line

          nl = GI.npoint(line)
      +    nc = GI.npoint(curve)
      +    first_last_equal_line = equals(GI.getpoint(line, 1), GI.getpoint(line, nl))
      +    first_last_equal_curve = equals(GI.getpoint(curve, 1), GI.getpoint(curve, nc))
      +    nl -= first_last_equal_line ? 1 : 0
      +    nc -= first_last_equal_curve ? 1 : 0
      +    closed_line |= first_last_equal_line

      See if first point is in an acceptable orientation

          l_start = GI.getpoint(line, closed_line ? nl : 1)
      +    point_val = _point_filled_curve_orientation(l_start, curve)
      +    if point_val == point_in
      +        in_curve = true
      +    elseif point_val == point_on
      +        on_curve = true
      +    else  # point_val == point_out
      +        out_curve = true
      +    end

      Check for any intersections between line and curve

          for i in (closed_line ? 1 : 2):nl
      +        l_end = GI.getpoint(line, i)
      +        c_start = GI.getpoint(curve, nc)

      If already interacted with all regions of curve, can stop

              in_curve && on_curve && out_curve && break

      Check next segment of line against curve

              for j in 1:nc
      +            c_end = GI.getpoint(curve, j)

      Check if two line and curve segments meet

                  seg_val = _segment_segment_orientation(
      +                (l_start, l_end),
      +                (c_start, c_end),
      +            )
      +            if seg_val != line_out

      If line and curve meet, then at least one point is on boundary

                      on_curve = true
      +                if seg_val == line_cross

      When crossing boundary, line is both in and out of curve

                          in_curve = true
      +                    out_curve = true
      +                else
      +                    if seg_val == line_over
      +                        sp = _point_segment_orientation(l_start, c_start, c_end)
      +                        lp = _point_segment_orientation(l_end, c_start, c_end)
      +                        if sp != point_in || lp != point_in
      +                            #=
      +                            Line crosses over segment endpoint, creating a hinge
      +                            with another segment.
      +                            =#
      +                            seg_val = line_hinge
      +                        end
      +                    end
      +                    if seg_val == line_hinge
      +                        #=
      +                        Can't determine all types of interactions (in, out) with
      +                        hinge as it could pass through multiple other segments
      +                        so calculate if segment endpoints and intersections are
      +                        in/out of filled curve
      +                        =#
      +                        ipoints = intersection_points(
      +                            GI.Line([l_start, l_end]),
      +                            curve
      +                        )
      +                        npoints = length(ipoints)  # since hinge, at least one
      +                        sort!(ipoints, by = p -> _euclid_distance(Float64, p, l_start))
      +                        p_start = _tuple_point(l_start)
      +                        for i in 1:(npoints + 1)
      +                            p_end = i ≤ npoints ?
      +                                ipoints[i] :
      +                                _tuple_point(l_end)
      +                            mid_val = _point_filled_curve_orientation(
      +                                (p_start .+ p_end) ./ 2,
      +                                curve,
      +                            )
      +                            if mid_val == point_in
      +                                in_curve = true
      +                            elseif mid_val == point_out
      +                                out_curve = true
      +                            end
      +                        end

      already checked segment against whole filled curve

                              l_start = l_end
      +                        break
      +                    end
      +                end
      +            end
      +            c_start = c_end
      +        end
      +        l_start = l_end
      +    end
      +    return in_curve, on_curve, out_curve
      +end

      Determines the types of interactions of a line with a polygon.

      Returns a tuple of booleans: (inpoly, onpoly, out_poly).

      If inpoly is true, some of the lines interior points interact with the polygon interior points. If inpoly is true, endpoints of either the line intersect with the polygon or the line interacts with the polygon boundary, including hole bounaries. If out_curve is true, at least one segments of the line is outside the polygon, including inside of holes.

      If closed_line is true, line is treated as a closed line where the first and last point are connected by a segment.

      function _line_polygon_interactions(
      +    line, polygon;
      +    closed_line = false,
      +)
      +    in_poly, on_poly, out_poly = _line_filled_curve_interactions(
      +        line, GI.getexterior(polygon);
      +        closed_line = closed_line,
      +    )
      +    !in_poly && return (in_poly, on_poly, out_poly)

      Loop over polygon holes

          for hole in GI.gethole(polygon)
      +        in_hole, on_hole, out_hole =_line_filled_curve_interactions(
      +            line, hole;
      +            closed_line = closed_line,
      +        )
      +        if in_hole
      +            out_poly = true
      +        end
      +        if on_hole
      +            on_poly = true
      +        end
      +        if !out_hole  # entire line is in/on hole, can't be in/on other holes
      +            in_poly = false
      +            return (in_poly, on_poly, out_poly)
      +        end
      +    end
      +    return in_poly, on_poly, out_poly
      +end
      +
      +function _point_in_extent(p, extent::Extents.Extent)
      +    (x1, x2), (y1, y2) = extent.X, extent.Y
      +    return x1 ≤ GI.x(p) ≤ x2 && y1 ≤ GI.y(p) ≤ y2
      +end

      This page was generated using Literate.jl.

      diff --git a/dev/source/methods/geom_relations/intersects/index.html b/dev/source/methods/geom_relations/intersects/index.html index c7b494c20..b59c9cffe 100644 --- a/dev/source/methods/geom_relations/intersects/index.html +++ b/dev/source/methods/geom_relations/intersects/index.html @@ -1,5 +1,5 @@ -Intersection checks · GeometryOps.jl

      Intersection checks

      export intersects, intersection, intersection_points

      What is intersects vs intersection vs intersection_points?

      The intersects methods check whether two geometries intersect with each other. The intersection methods return the geometry intersection between the two input geometries. The intersection_points method returns a list of intersection points between two geometries.

      The intersects methods will always return a Boolean. However, note that the intersection methods will not all return the same type. For example, the intersection of two lines will be a point in most cases, unless the lines are parallel. On the other hand, the intersection of two polygons will be another polygon in most cases. Finally, the intersection_points method returns a list of tuple points.

      To provide an example, consider these two lines:

      using GeometryOps
      +Intersection checks · GeometryOps.jl

      Intersection checks

      export intersects, intersection, intersection_points

      What is intersects vs intersection vs intersection_points?

      The intersects function checks if a given geometry intersects with another geometry, or in other words, the either the interiors or boundaries of the two geometries intersect.

      The intersection methods return the geometry intersection between the two input geometries. The intersection_points method returns a list of intersection points between two geometries.

      The intersects methods will always return a Boolean. However, note that the intersection methods will not all return the same type. For example, the intersection of two lines will be a point in most cases, unless the lines are parallel. On the other hand, the intersection of two polygons will be another polygon in most cases. Finally, the intersection_points method returns a list of tuple points.

      To provide an example, consider these two lines:

      using GeometryOps
       using GeometryOps.GeometryBasics
       using Makie
       using CairoMakie
      @@ -12,10 +12,12 @@
       println(int_bool)
       int_point = GO.intersection(line1, line2)
       scatter!(int_point, color = :red)
      -f

      Implementation

      This is the GeoInterface-compatible implementation.

      First, we implement a wrapper method for intersects, intersection, and intersectionpoints that dispatches to the correct implementation based on the geometry trait. The two underlying helper functions that are widely used in all geometry dispatches are _lineintersects, which determines if two line segments intersect and intersectionpoint which determines the intersection point between two line segments.

      """
      +f

      Implementation

      This is the GeoInterface-compatible implementation.

      Given that intersects is the exact opposite of disjoint, we simply pass the two inputs variables, swapped in order, to disjoint.

      """
           intersects(geom1, geom2)::Bool
       
      -Check if two geometries intersect, returning true if so and false otherwise.
      +Return true if the interiors or boundaries of the two geometries interact.
      +
      +`intersects` returns the exact opposite result of `disjoint`.
       
       # Example
       
      @@ -27,75 +29,8 @@
       GO.intersects(line1, line2)

      output

      true
       ```
       """
      -intersects(geom1, geom2) = intersects(
      -    GI.trait(geom1),
      -    geom1,
      -    GI.trait(geom2),
      -    geom2
      -)
      -
      -"""
      -    intersects(::GI.LineTrait, a, ::GI.LineTrait, b)::Bool
      -
      -Returns true if two line segments intersect and false otherwise.
      -"""
      -function intersects(::GI.LineTrait, a, ::GI.LineTrait, b)
      -    a1 = _tuple_point(GI.getpoint(a, 1))
      -    a2 = _tuple_point(GI.getpoint(a, 2))
      -    b1 = _tuple_point(GI.getpoint(b, 1))
      -    b2 = _tuple_point(GI.getpoint(b, 2))
      -    meet_type = ExactPredicates.meet(a1, a2, b1, b2)
      -    return meet_type == 0 || meet_type == 1
      -end
      -
      -"""
      -    intersects(::GI.AbstractTrait, a, ::GI.AbstractTrait, b)::Bool
      -
      -Returns true if two geometries intersect with one another and false
      -otherwise. For all geometries but lines, convert the geometry to a list of edges
      -and cross compare the edges for intersections.
      -"""
      -function intersects(
      -    trait_a::GI.AbstractTrait, a_geom,
      -    trait_b::GI.AbstractTrait, b_geom,
      -)   edges_a, edges_b = map(sort! ∘ to_edges, (a_geom, b_geom))
      -    return _line_intersects(edges_a, edges_b) ||
      -        within(trait_a, a_geom, trait_b, b_geom) ||
      -        within(trait_b, b_geom, trait_a, a_geom)
      -end
      +intersects(geom1, geom2) = !disjoint(geom1, geom2)
       
      -"""
      -    _line_intersects(
      -        edges_a::Vector{Edge},
      -        edges_b::Vector{Edge}
      -    )::Bool
      -
      -Returns true if there is at least one intersection between edges within the
      -two lists of edges.
      -"""
      -function _line_intersects(
      -    edges_a::Vector{Edge},
      -    edges_b::Vector{Edge}
      -)

      Extents.intersects(toextent(edgesa), toextent(edgesb)) || return false

          for edge_a in edges_a
      -        for edge_b in edges_b
      -            _line_intersects(edge_a, edge_b) && return true
      -        end
      -    end
      -    return false
      -end
      -
      -"""
      -    _line_intersects(
      -        edge_a::Edge,
      -        edge_b::Edge,
      -    )::Bool
      -
      -Returns true if there is at least one intersection between two edges.
      -"""
      -function _line_intersects(edge_a::Edge, edge_b::Edge)
      -    meet_type = ExactPredicates.meet(edge_a..., edge_b...)
      -    return meet_type == 0 || meet_type == 1
      -end
       
       """
           intersection(geom_a, geom_b)::Union{Tuple{::Real, ::Real}, ::Nothing}
      @@ -175,8 +110,8 @@
       there isn't one.
       """
       function intersection(
      -    trait_a::GI.AbstractTrait, geom_a,
      -    trait_b::GI.AbstractTrait, geom_b,
      +    trait_a::GI.AbstractGeometryTrait, geom_a,
      +    trait_b::GI.AbstractGeometryTrait, geom_b,
       )
           @assert(
               false,
      @@ -277,4 +212,4 @@
               return (x, y), (t, u)
           end
           return nothing, nothing
      -end

      This page was generated using Literate.jl.

      +end

      This page was generated using Literate.jl.

      diff --git a/dev/source/methods/geom_relations/overlaps/index.html b/dev/source/methods/geom_relations/overlaps/index.html index 598522bae..4cd076eda 100644 --- a/dev/source/methods/geom_relations/overlaps/index.html +++ b/dev/source/methods/geom_relations/overlaps/index.html @@ -1,5 +1,5 @@ -Overlaps · GeometryOps.jl

      Overlaps

      export overlaps

      What is overlaps?

      The overlaps function checks if two geometries overlap. Two geometries can only overlap if they have the same dimension, and if they overlap, but one is not contained, within, or equal to the other.

      Note that this means it is impossible for a single point to overlap with a single point and a line only overlaps with another line if only a section of each line is colinear.

      To provide an example, consider these two lines:

      using GeometryOps
      +Overlaps · GeometryOps.jl

      Overlaps

      export overlaps

      What is overlaps?

      The overlaps function checks if two geometries overlap. Two geometries can only overlap if they have the same dimension, and if they overlap, but one is not contained, within, or equal to the other.

      Note that this means it is impossible for a single point to overlap with a single point and a line only overlaps with another line if only a section of each line is colinear.

      To provide an example, consider these two lines:

      using GeometryOps
       using GeometryOps.GeometryBasics
       using Makie
       using CairoMakie
      @@ -115,7 +115,7 @@
           trait_b::GI.PolygonTrait, poly_b,
       )
           edges_a, edges_b = map(sort! ∘ to_edges, (poly_a, poly_b))
      -    return _line_intersects(edges_a, edges_b) &&
      +    return _edge_intersects(edges_a, edges_b) &&
               !equals(trait_a, poly_a, trait_b, poly_b)
       end
       
      @@ -169,19 +169,39 @@
           return false
       end
       
      -"""
      -    _overlaps(
      -        (a1, a2)::Edge,
      -        (b1, b2)::Edge
      -    )::Bool
      -
      -If the edges overlap, meaning that they are colinear but each have one endpoint
      -outside of the other edge, return true. Else false.
      -"""
      +#= If the edges overlap, meaning that they are colinear but each have one endpoint
      +outside of the other edge, return true. Else false. =#
       function _overlaps(
           (a1, a2)::Edge,
           (b1, b2)::Edge
      -)

      meets in more than one point

          on_top = ExactPredicates.meet(a1, a2, b1, b2) == 0

      one end point is outside of other segment

          a_fully_within = point_on_seg(a1, b1, b2) && point_on_seg(a2, b1, b2)
      -    b_fully_within = point_on_seg(b1, a1, a2) && point_on_seg(b2, a1, a2)
      +)

      meets in more than one point

          on_top = ExactPredicates.meet(a1, a2, b1, b2) == 0

      one end point is outside of other segment

          a_fully_within = _point_on_seg(a1, b1, b2) && _point_on_seg(a2, b1, b2)
      +    b_fully_within = _point_on_seg(b1, a1, a2) && _point_on_seg(b2, a1, a2)
           return on_top && (!a_fully_within && !b_fully_within)
      -end

      This page was generated using Literate.jl.

      +end + +#= TODO: Once overlaps is swapped over to use the geom relations workflow, can +delete these helpers. =#

      Checks it vectors of edges intersect

      function _edge_intersects(
      +    edges_a::Vector{Edge},
      +    edges_b::Vector{Edge}
      +)

      Extents.intersects(toextent(edgesa), toextent(edgesb)) || return false

          for edge_a in edges_a
      +        for edge_b in edges_b
      +            _edge_intersects(edge_a, edge_b) && return true
      +        end
      +    end
      +    return false
      +end

      Checks if two edges intersect

      function _edge_intersects(edge_a::Edge, edge_b::Edge)
      +    meet_type = ExactPredicates.meet(edge_a..., edge_b...)
      +    return meet_type == 0 || meet_type == 1
      +end

      Checks if point is on a segment

      function _point_on_seg(point, start, stop)

      Parse out points

          x, y = GI.x(point), GI.y(point)
      +    x1, y1 = GI.x(start), GI.y(start)
      +    x2, y2 = GI.x(stop), GI.y(stop)
      +    Δxl = x2 - x1
      +    Δyl = y2 - y1

      Determine if point is on segment

          cross = (x - x1) * Δyl - (y - y1) * Δxl
      +    if cross == 0  # point is on line extending to infinity

      is line between endpoints

              if abs(Δxl) >= abs(Δyl)  # is line between endpoints
      +            return Δxl > 0 ? x1 <= x <= x2 : x2 <= x <= x1
      +        else
      +            return Δyl > 0 ? y1 <= y <= y2 : y2 <= y <= y1
      +        end
      +    end
      +    return false
      +end

      This page was generated using Literate.jl.

      diff --git a/dev/source/methods/geom_relations/touches/index.html b/dev/source/methods/geom_relations/touches/index.html new file mode 100644 index 000000000..ed04c7775 --- /dev/null +++ b/dev/source/methods/geom_relations/touches/index.html @@ -0,0 +1,167 @@ + +Touches · GeometryOps.jl

      Touches

      export touches

      What is touches?

      The touches function checks if one geometry touches another geometry. In other words, the interiors of the two geometries don't interact, but one of the geometries must have a boundary point that interacts with either the other geometies interior or boundary.

      To provide an example, consider these two lines:

      using GeometryOps
      +using GeometryOps.GeometryBasics
      +using Makie
      +using CairoMakie
      +
      +l1 = Line([Point(0.0, 0.0), Point(1.0, 0.0)])
      +l2 = Line([Point(1.0, 0.0), Point(1.0, -1.0)])
      +
      +f, a, p = lines(l1)
      +lines!(l2)

      We can see that these two lines touch only at their endpoints.

      touches(l1, l2)  # true

      Implementation

      This is the GeoInterface-compatible implementation.

      First, we implement a wrapper method that dispatches to the correct implementation based on the geometry trait.

      Each of these calls a method in the geomgeomprocessors file. The methods in this file determine if the given geometries meet a set of criteria. For the touches function and arguments g1 and g2, this criteria is as follows: - points of g1 are not allowed to be in the interior of g2 - points of g1 are allowed to be on the boundary of g2 - points of g1 are allowed to be in the exterior of g2 - no points of g1 are required to be in the interior of g2 - at least one point of g1 is required to be on the boundary of g2 - no points of g1 are required to be in the exterior of g2

      The code for the specific implementations is in the geomgeomprocessors file.

      const TOUCHES_POINT_ALLOWED = (in_allow = false, on_allow = true, out_allow = false)
      +const TOUCHES_CURVE_ALLOWED = (over_allow = false, cross_allow = false, on_allow = true, out_allow = true)
      +const TOUCHES_POLYGON_ALLOWS = (in_allow = false, on_allow = true, out_allow = true)
      +const TOUCHES_REQUIRES = (in_require = false, on_require = true, out_require = false)
      +
      +"""
      +    touches(geom1, geom2)::Bool
      +
      +Return `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 geometies interior or
      +boundary.
      +
      +# Examples
      +```jldoctest setup=:(using GeometryOps, GeometryBasics)
      +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

      true
      +```
      +"""
      +touches(g1, g2)::Bool = _touches(trait(g1), g1, trait(g2), g2)

      Convert features to geometries

      _touches(::GI.FeatureTrait, g1, ::Any, g2) = touches(GI.geometry(g1), g2)
      +_touches(::Any, g1, t2::GI.FeatureTrait, g2) = touches(g1, GI.geometry(g2))
      +_touches(::FeatureTrait, g1, ::FeatureTrait, g2) = touches(GI.geometry(g1), GI.geometry(g2))

      Point touches geometries

      Point cannot touch another point as if they are equal, interiors interact

      _touches(
      +    ::GI.PointTrait, g1,
      +    ::GI.PointTrait, g2,
      +) = false

      Point touches a linestring if it equal to the first of last point of the line

      function _touches(
      +    ::GI.PointTrait, g1,
      +    ::Union{GI.LineTrait, GI.LineStringTrait}, g2,
      +)
      +    n = GI.npoint(g2)
      +    p1 = GI.getpoint(g2, 1)
      +    pn = GI.getpoint(g2, n)
      +    equals(p1, pn) && return false
      +    return equals(g1, p1) || equals(g1, pn)
      +end

      Point cannot 'touch' a linearring given that the ring has no boundary points

      _touches(
      +    ::GI.PointTrait, g1,
      +    ::GI.LinearRingTrait, g2,
      +) = false

      Point touches a polygon if it is on the boundary of that polygon

      _touches(
      +    ::GI.PointTrait, g1,
      +    ::GI.PolygonTrait, g2,
      +) = _point_polygon_process(
      +    g1, g2;
      +    TOUCHES_POINT_ALLOWED...,
      +)
      +
      +#= Geometry touches a point if the point is on the geometry boundary. =#
      +_touches(
      +    trait1::Union{GI.AbstractCurveTrait, GI.PolygonTrait}, g1,
      +    trait2::GI.PointTrait, g2,
      +) = _touches(trait2, g2, trait1, g1)

      Lines touching geometries

      #= Linestring touches another line if at least one bounday point interacts with
      +the bounday of interior of the other line, but the interiors don't interact. =#
      +_touches(
      +    ::Union{GI.LineTrait, GI.LineStringTrait}, g1,
      +    ::Union{GI.LineTrait, GI.LineStringTrait}, g2,
      +) = _line_curve_process(
      +    g1, g2;
      +    TOUCHES_CURVE_ALLOWED...,
      +    TOUCHES_REQUIRES...,
      +    closed_line = false,
      +    closed_curve = false,
      +)
      +
      +
      +#= Linestring touches a linearring if at least one of the boundary points of the
      +line interacts with the linear ring, but their interiors can't interact. =#
      +_touches(
      +    ::Union{GI.LineTrait, GI.LineStringTrait}, g1,
      +    ::GI.LinearRingTrait, g2,
      +) = _line_curve_process(
      +    g1, g2;
      +    TOUCHES_CURVE_ALLOWED...,
      +    TOUCHES_REQUIRES...,
      +    closed_line = false,
      +    closed_curve = true,
      +)
      +
      +#= Linestring touches a polygon if at least one of the boundary points of the
      +line interacts with the boundary of the polygon. =#
      +_touches(
      +    ::Union{GI.LineTrait, GI.LineStringTrait}, g1,
      +    ::GI.PolygonTrait, g2,
      +) = _line_polygon_process(
      +    g1, g2;
      +    TOUCHES_POLYGON_ALLOWS...,
      +    TOUCHES_REQUIRES...,
      +    closed_line = false,
      +)

      Rings touch geometries

      #= Linearring touches a linestring if at least one of the boundary points of the
      +line interacts with the linear ring, but their interiors can't interact. =#
      +_touches(
      +    trait1::GI.LinearRingTrait, g1,
      +    trait2::Union{GI.LineTrait, GI.LineStringTrait}, g2,
      +) = _touches(trait2, g2, trait1, g1)
      +
      +#= Linearring cannot touch another linear ring since they are both exclusively
      +made up of interior points and no bounday points =#
      +_touches(
      +    ::GI.LinearRingTrait, g1,
      +    ::GI.LinearRingTrait, g2,
      +) = false
      +
      +#= Linearring touches a polygon if at least one of the points of the ring
      +interact with the polygon bounday and non are in the polygon interior. =#
      +_touches(
      +    ::GI.LinearRingTrait, g1,
      +    ::GI.PolygonTrait, g2,
      +) = _line_polygon_process(
      +    g1, g2;
      +    TOUCHES_POLYGON_ALLOWS...,
      +    TOUCHES_REQUIRES...,
      +    closed_line = true,
      +)

      Polygons touch geometries

      #= Polygon touches a curve if at least one of the curve bounday points interacts
      +with the polygon's bounday and no curve points interact with the interior.=#
      +_touches(
      +    trait1::GI.PolygonTrait, g1,
      +    trait2::GI.AbstractCurveTrait, g2
      +) = _touches(trait2, g2, trait1, g1)
      +
      +
      +#= Polygon touches another polygon if they share at least one boundary point and
      +no interior points. =#
      +_touches(
      +    ::GI.PolygonTrait, g1,
      +    ::GI.PolygonTrait, g2,
      +) = _polygon_polygon_process(
      +    g1, g2;
      +    TOUCHES_POLYGON_ALLOWS...,
      +    TOUCHES_REQUIRES...,
      +)

      Geometries touch multi-geometry/geometry collections

      #= Geometry touch a multi-geometry or a collection if the geometry touches at
      +least one of the elements of the collection. =#
      +function _touches(
      +    ::Union{GI.PointTrait, GI.AbstractCurveTrait, GI.PolygonTrait}, g1,
      +    ::Union{
      +        GI.MultiPointTrait, GI.AbstractMultiCurveTrait,
      +        GI.MultiPolygonTrait, GI.GeometryCollectionTrait,
      +    }, g2,
      +)
      +    for sub_g2 in GI.getgeom(g2)
      +        !touches(g1, sub_g2) && return false
      +    end
      +    return true
      +end

      Multi-geometry/geometry collections cross geometries

      #= Multi-geometry or a geometry collection touches a geometry if at least one
      +elements of the collection touches the geometry. =#
      +function _touches(
      +    ::Union{
      +        GI.MultiPointTrait, GI.AbstractMultiCurveTrait,
      +        GI.MultiPolygonTrait, GI.GeometryCollectionTrait,
      +    }, g1,
      +    ::GI.AbstractGeometryTrait, g2,
      +)
      +    for sub_g1 in GI.getgeom(g1)
      +        !touches(sub_g1, g2) && return false
      +    end
      +    return true
      +end

      This page was generated using Literate.jl.

      diff --git a/dev/source/methods/geom_relations/within/index.html b/dev/source/methods/geom_relations/within/index.html index f6cef8ed6..f90e63cc5 100644 --- a/dev/source/methods/geom_relations/within/index.html +++ b/dev/source/methods/geom_relations/within/index.html @@ -1,14 +1,29 @@ -Containment/withinness · GeometryOps.jl

      Containment/withinness

      export within
      +Within · GeometryOps.jl

      Within

      export within

      What is within?

      The within function checks if one geometry is inside another geometry. This requires that the two interiors intersect and that the interior and boundary of the first geometry is not in the exterior of the second geometry.

      To provide an example, consider these two lines:

      using GeometryOps
      +using GeometryOps.GeometryBasics
      +using Makie
      +using CairoMakie
       
      +l1 = GI.LineString([(0.0, 0.0), (1.0, 0.0), (0.0, 0.1)])
      +l2 = GI.LineString([(0.25, 0.0), (0.75, 0.0)])
      +f, a, p = lines(GI.getpoint(l1), color = :blue)
      +scatter!(GI.getpoint(l1), color = :blue)
      +lines!(GI.getpoint(l2), color = :orange)
      +scatter!(GI.getpoint(l2), color = :orange)

      We can see that all of the points and edges of l2 are within l1, so l2 is within l1, but l1 is not within l2

      within(l1, l2)  # returns false
      +within(l2, l1)  # returns true

      Implementation

      This is the GeoInterface-compatible implementation.

      First, we implement a wrapper method that dispatches to the correct implementation based on the geometry trait.

      Each of these calls a method in the geomgeomprocessors file. The methods in this file determine if the given geometries meet a set of criteria. For the within function and arguments g1 and g2, this criteria is as follows: - points of g1 are allowed to be in the interior of g2 (either through overlap or crossing for lines) - points of g1 are allowed to be on the boundary of g2 - points of g1 are not allowed to be in the exterior of g2 - at least one point of g1 is required to be in the interior of g2 - no points of g1 are required to be on the boundary of g2 - no points of g1 are required to be in the exterior of g2

      The code for the specific implementations is in the geomgeomprocessors file.

      const WITHIN_POINT_ALLOWS = (in_allow = true, on_allow = false, out_allow = false)
      +const WITHIN_CURVE_ALLOWS = (over_allow = true, cross_allow = true, on_allow = true, out_allow = false)
      +const WITHIN_POLYGON_ALLOWS = (in_allow = true, on_allow = true, out_allow = false)
      +const WITHIN_REQUIRES = (in_require = true, on_require = false, out_require = false)
       
       """
      -    within(geom1, geom)::Bool
      +    within(geom1, geom2)::Bool
       
       Return `true` if the first geometry is completely within the second geometry.
      -The interiors of both geometries must intersect and, the interior and boundary of the primary (geometry a)
      -must not intersect the exterior of the secondary (geometry b).
      -`within` returns the exact opposite result of `contains`.
      +The interiors of both geometries must intersect and the interior and boundary of
      +the primary geometry (geom1) must not intersect the exterior of the secondary
      +geometry (geom2).
      +
      +Furthermore, `within` returns the exact opposite result of `contains`.
       
       # Examples
       ```jldoctest setup=:(using GeometryOps, GeometryBasics)
      @@ -18,10 +33,152 @@
       point = (1, 2)
       GO.within(point, line)

      output

      true
       ```
      -"""

      Syntactic sugar

      within(g1, g2)::Bool = within(trait(g1), g1, trait(g2), g2)::Bool
      -within(::GI.FeatureTrait, g1, ::Any, g2)::Bool = within(GI.geometry(g1), g2)
      -within(::Any, g1, t2::GI.FeatureTrait, g2)::Bool = within(g1, GI.geometry(g2))

      Points in geometries

      within(::GI.PointTrait, g1, ::GI.LineStringTrait, g2)::Bool = point_on_line(g1, g2; ignore_end_vertices=true)
      -within(::GI.PointTrait, g1, ::GI.LinearRingTrait, g2)::Bool = point_on_line(g1, g2; ignore_end_vertices=true)
      -within(::GI.PointTrait, g1, ::GI.PolygonTrait, g2)::Bool = point_in_polygon(g1, g2; ignore_boundary=true)

      Lines in geometries

      within(::GI.LineStringTrait, g1, ::GI.LineStringTrait, g2)::Bool = line_on_line(g1, g2)
      -within(::GI.LineStringTrait, g1, ::GI.LinearRingTrait, g2)::Bool = line_on_line(g1, g2)
      -within(::GI.LineStringTrait, g1, ::GI.PolygonTrait, g2)::Bool = line_in_polygon(g1, g2)

      Polygons within geometries

      within(::GI.PolygonTrait, g1, ::GI.PolygonTrait, g2)::Bool = polygon_in_polygon(g1, g2)

      Everything not specified TODO: Add multipolygons

      within(::GI.AbstractTrait, g1, ::GI.AbstractCurveTrait, g2)::Bool = false

      This page was generated using Literate.jl.

      +""" +within(g1, g2) = _within(trait(g1), g1, trait(g2), g2)

      Convert features to geometries

      _within(::GI.FeatureTrait, g1, ::Any, g2) = within(GI.geometry(g1), g2)
      +_within(::Any, g1, t2::GI.FeatureTrait, g2) = within(g1, GI.geometry(g2))
      +_within(::FeatureTrait, g1, ::FeatureTrait, g2) = within(GI.geometry(g1), GI.geometry(g2))

      Points within geometries

      Point is within another point if those points are equal.

      _within(
      +    ::GI.PointTrait, g1,
      +    ::GI.PointTrait, g2,
      +) = equals(g1, g2)
      +
      +#= Point is within a linestring if it is on a vertex or an edge of that line,
      +excluding the start and end vertex if the line is not closed. =#
      +_within(
      +    ::GI.PointTrait, g1,
      +    ::Union{GI.LineTrait, GI.LineStringTrait}, g2,
      +) = _point_curve_process(
      +    g1, g2;
      +    WITHIN_POINT_ALLOWS...,
      +    closed_curve = false,
      +)

      Point is within a linearring if it is on a vertex or an edge of that ring.

      _within(
      +    ::GI.PointTrait, g1,
      +    ::GI.LinearRingTrait, g2,
      +) = _point_curve_process(
      +    g1, g2;
      +    WITHIN_POINT_ALLOWS...,
      +    closed_curve = true,
      +)
      +
      +#= Point is within a polygon if it is inside of that polygon, excluding edges,
      +vertices, and holes. =#
      +_within(
      +    ::GI.PointTrait, g1,
      +    ::GI.PolygonTrait, g2,
      +) = _point_polygon_process(
      +    g1, g2;
      +    WITHIN_POINT_ALLOWS...,
      +)

      No geometries other than points can be within points

      _within(
      +    ::Union{GI.AbstractCurveTrait, GI.PolygonTrait}, g1,
      +    ::GI.PointTrait, g2,
      +) = false

      Lines within geometries

      #= Linestring is within another linestring if their interiors intersect and no
      +points of the first line are in the exterior of the second line. =#
      +_within(
      +    ::Union{GI.LineTrait, GI.LineStringTrait}, g1,
      +    ::Union{GI.LineTrait, GI.LineStringTrait}, g2,
      +) = _line_curve_process(
      +    g1, g2;
      +    WITHIN_CURVE_ALLOWS...,
      +    WITHIN_REQUIRES...,
      +    closed_line = false,
      +    closed_curve = false,
      +)
      +
      +#= Linestring is within a linear ring if their interiors intersect and no points
      +of the line are in the exterior of the ring. =#
      +_within(
      +    ::Union{GI.LineTrait, GI.LineStringTrait}, g1,
      +    ::GI.LinearRingTrait, g2,
      +) = _line_curve_process(
      +    g1, g2;
      +    WITHIN_CURVE_ALLOWS...,
      +    WITHIN_REQUIRES...,
      +    closed_line = false,
      +    closed_curve = true,
      +)
      +
      +#= Linestring is within a polygon if their interiors intersect and no points of
      +the line are in the exterior of the polygon, although they can be on an edge. =#
      +_within(
      +    ::Union{GI.LineTrait, GI.LineStringTrait}, g1,
      +    ::GI.PolygonTrait, g2,
      +) = _line_polygon_process(
      +    g1, g2;
      +    WITHIN_POLYGON_ALLOWS...,
      +    WITHIN_REQUIRES...,
      +    closed_line = false,
      +)

      Rings covered by geometries

      #= Linearring is within a linestring if their interiors intersect and no points
      +of the ring are in the exterior of the line. =#
      +_within(
      +    ::GI.LinearRingTrait, g1,
      +    ::Union{GI.LineTrait, GI.LineStringTrait}, g2,
      +) = _line_curve_process(
      +    g1, g2;
      +    WITHIN_CURVE_ALLOWS...,
      +    WITHIN_REQUIRES...,
      +    closed_line = true,
      +    closed_curve = false,
      +)
      +
      +#= Linearring is within another linearring if their interiors intersect and no
      +points of the first ring are in the exterior of the second ring. =#
      +_within(
      +    ::GI.LinearRingTrait, g1,
      +    ::GI.LinearRingTrait, g2,
      +) = _line_curve_process(
      +    g1, g2;
      +    WITHIN_CURVE_ALLOWS...,
      +    WITHIN_REQUIRES...,
      +    closed_line = true,
      +    closed_curve = true,
      +)
      +
      +#= Linearring is within a polygon if their interiors intersect and no points of
      +the ring are in the exterior of the polygon, although they can be on an edge. =#
      +_within(
      +    ::GI.LinearRingTrait, g1,
      +    ::GI.PolygonTrait, g2,
      +) = _line_polygon_process(
      +    g1, g2;
      +    WITHIN_POLYGON_ALLOWS...,
      +    WITHIN_REQUIRES...,
      +    closed_line = true,
      +)

      Polygons within geometries

      #= Polygon is within another polygon if the interior of the first polygon
      +intersects with the interior of the second and no points of the first polygon
      +are outside of the second polygon. =#
      +_within(
      +    ::GI.PolygonTrait, g1,
      +    ::GI.PolygonTrait, g2,
      +) = _polygon_polygon_process(
      +    g1, g2;
      +    WITHIN_POLYGON_ALLOWS...,
      +    WITHIN_REQUIRES...,
      +)

      Polygons cannot be within any curves

      _within(
      +    ::GI.PolygonTrait, g1,
      +    ::GI.AbstractCurveTrait, g2,
      +) = false

      Geometries within multi-geometry/geometry collections

      #= Geometry is within a multi-geometry or a collection if the geometry is within
      +at least one of the collection elements. =#
      +function _within(
      +    ::Union{GI.PointTrait, GI.AbstractCurveTrait, GI.PolygonTrait}, g1,
      +    ::Union{
      +        GI.MultiPointTrait, GI.AbstractMultiCurveTrait,
      +        GI.MultiPolygonTrait, GI.GeometryCollectionTrait,
      +    }, g2,
      +)
      +    for sub_g2 in GI.getgeom(g2)
      +        within(g1, sub_g2) && return true
      +    end
      +    return false
      +end

      Multi-geometry/geometry collections within geometries

      #= Multi-geometry or a geometry collection is within a geometry if all
      +elements of the collection are within the geometry. =#
      +function _within(
      +    ::Union{
      +        GI.MultiPointTrait, GI.AbstractMultiCurveTrait,
      +        GI.MultiPolygonTrait, GI.GeometryCollectionTrait,
      +    }, g1,
      +    ::GI.AbstractGeometryTrait, g2,
      +)
      +    for sub_g1 in GI.getgeom(g1)
      +        !within(sub_g1, g2) && return false
      +    end
      +    return true
      +end

      This page was generated using Literate.jl.

      diff --git a/dev/source/methods/polygonize/index.html b/dev/source/methods/polygonize/index.html index 0489589bd..74e77745d 100644 --- a/dev/source/methods/polygonize/index.html +++ b/dev/source/methods/polygonize/index.html @@ -1,5 +1,5 @@ -Polygonizing raster data · GeometryOps.jl

      Polygonizing raster data

      export polygonize

      The methods in this file are able to convert a raster image into a set of polygons, by contour detection using a clockwise Moore neighborhood method.

      The main entry point is the polygonize function.

      polygonize

      Example

      Here's a basic implementation, using the Makie.peaks() function. First, let's investigate the nature of the function:

      using Makie, GeometryOps
      +Polygonizing raster data · GeometryOps.jl

      Polygonizing raster data

      export polygonize

      The methods in this file are able to convert a raster image into a set of polygons, by contour detection using a clockwise Moore neighborhood method.

      The main entry point is the polygonize function.

      polygonize

      Example

      Here's a basic implementation, using the Makie.peaks() function. First, let's investigate the nature of the function:

      using Makie, GeometryOps
       n = 49
       xs, ys = LinRange(-3, 3, n), LinRange(-3, 3, n)
       zs = Makie.peaks(n)
      @@ -174,4 +174,4 @@
           end
       
           return contour_list
      -end

      This page was generated using Literate.jl.

      +end

      This page was generated using Literate.jl.

      diff --git a/dev/source/primitives/index.html b/dev/source/primitives/index.html index d696c75c8..4dae8d99d 100644 --- a/dev/source/primitives/index.html +++ b/dev/source/primitives/index.html @@ -1,5 +1,5 @@ -Primitive functions · GeometryOps.jl
      const THREADED_KEYWORD = "- `threaded`: `true` or `false`. Whether to use multithreading. Defaults to `false`."
      +Primitive functions · GeometryOps.jl
      const THREADED_KEYWORD = "- `threaded`: `true` or `false`. Whether to use multithreading. Defaults to `false`."
       const CRS_KEYWORD = "- `crs`: The CRS to attach to geometries. Defaults to `nothing`."
       const CALC_EXTENT_KEYWORD = "- `calc_extent`: `true` or `false`. Whether to calculate the extent. Defaults to `false`."
       
      @@ -200,4 +200,4 @@
           else
               return map(f, taskrange)
           end
      -end

      This page was generated using Literate.jl.

      +end

      This page was generated using Literate.jl.

      diff --git a/dev/source/transformations/extent/index.html b/dev/source/transformations/extent/index.html index fe6af5ca3..e2e9412fb 100644 --- a/dev/source/transformations/extent/index.html +++ b/dev/source/transformations/extent/index.html @@ -1,5 +1,5 @@ -- · GeometryOps.jl
      """
      +- · GeometryOps.jl
      """
           embed_extent(obj)
       
       Recursively wrap the object with a GeoInterface.jl geometry,
      @@ -11,4 +11,4 @@
       $CRS_KEYWORD
       """
       embed_extent(x; threaded=false, crs=nothing) =
      -    apply(identity, GI.PointTrait, x; calc_extent=true, threaded, crs)

      This page was generated using Literate.jl.

      + apply(identity, GI.PointTrait, x; calc_extent=true, threaded, crs)

      This page was generated using Literate.jl.

      diff --git a/dev/source/transformations/flip/index.html b/dev/source/transformations/flip/index.html index ead396e8d..c09e42a55 100644 --- a/dev/source/transformations/flip/index.html +++ b/dev/source/transformations/flip/index.html @@ -1,5 +1,5 @@ -Coordinate flipping · GeometryOps.jl

      Coordinate flipping

      This is a simple example of how to use the apply functionality in a function, by flipping the x and y coordinates of a geometry.

      """
      +Coordinate flipping · GeometryOps.jl

      Coordinate flipping

      This is a simple example of how to use the apply functionality in a function, by flipping the x and y coordinates of a geometry.

      """
           flip(obj)
       
       Swap all of the x and y coordinates in obj, otherwise
      @@ -20,4 +20,4 @@
                   (GI.y(p), GI.x(p))
               end
           end
      -end

      This page was generated using Literate.jl.

      +end

      This page was generated using Literate.jl.

      diff --git a/dev/source/transformations/reproject/index.html b/dev/source/transformations/reproject/index.html index 7b38679f6..be9dc6a13 100644 --- a/dev/source/transformations/reproject/index.html +++ b/dev/source/transformations/reproject/index.html @@ -1,5 +1,5 @@ -Geometry reprojection · GeometryOps.jl

      Geometry reprojection

      export reproject

      This file is pretty simple - it simply reprojects a geometry pointwise from one CRS to another. It uses the Proj package for the transformation, but this could be moved to an extension if needed.

      This works using the apply functionality.

      """
      +Geometry reprojection · GeometryOps.jl

      Geometry reprojection

      export reproject

      This file is pretty simple - it simply reprojects a geometry pointwise from one CRS to another. It uses the Proj package for the transformation, but this could be moved to an extension if needed.

      This works using the apply functionality.

      """
           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)
      @@ -59,4 +59,4 @@
                   transform(GI.x(p), GI.y(p))
               end
           end
      -end

      This page was generated using Literate.jl.

      +end

      This page was generated using Literate.jl.

      diff --git a/dev/source/transformations/simplify/index.html b/dev/source/transformations/simplify/index.html index b18e6a11b..acecad816 100644 --- a/dev/source/transformations/simplify/index.html +++ b/dev/source/transformations/simplify/index.html @@ -1,5 +1,5 @@ -Geometry simplification · GeometryOps.jl

      Geometry simplification

      This file holds implementations for the Douglas-Peucker and Visvalingam-Whyatt algorithms for simplifying geometries (specifically polygons and lines).

      export simplify, VisvalingamWhyatt, DouglasPeucker, RadialDistance
      +Geometry simplification · GeometryOps.jl

      Geometry simplification

      This file holds implementations for the Douglas-Peucker and Visvalingam-Whyatt algorithms for simplifying geometries (specifically polygons and lines).

      export simplify, VisvalingamWhyatt, DouglasPeucker, RadialDistance
       
       
       """
      @@ -374,4 +374,4 @@
           return result
       end
       
      -_remove!(s, i) = s[i:end-1] .= s[i+1:end]

      This page was generated using Literate.jl.

      +_remove!(s, i) = s[i:end-1] .= s[i+1:end]

      This page was generated using Literate.jl.

      diff --git a/dev/source/transformations/transform/index.html b/dev/source/transformations/transform/index.html index c036fa30f..0f227a5c0 100644 --- a/dev/source/transformations/transform/index.html +++ b/dev/source/transformations/transform/index.html @@ -1,5 +1,5 @@ -- · GeometryOps.jl
      """
      +- · GeometryOps.jl
      """
           transform(f, obj)
       
       Apply a function `f` to all the points in `obj`.
      @@ -53,4 +53,4 @@
                   f(StaticArrays.SVector{2}((GI.x(p), GI.y(p))))
               end
           end
      -end

      This page was generated using Literate.jl.

      +end

      This page was generated using Literate.jl.

      diff --git a/dev/source/transformations/tuples/index.html b/dev/source/transformations/tuples/index.html index 4bca56754..5e641cedb 100644 --- a/dev/source/transformations/tuples/index.html +++ b/dev/source/transformations/tuples/index.html @@ -1,5 +1,5 @@ -Tuple conversion · GeometryOps.jl

      Tuple conversion

      """
      +Tuple conversion · GeometryOps.jl

      Tuple conversion

      """
           tuples(obj)
       
       Convert all points in `obj` to `Tuple`s, wherever the are nested.
      @@ -17,4 +17,4 @@
                   (Float64(GI.x(p)), Float64(GI.y(p)))
               end
           end
      -end

      This page was generated using Literate.jl.

      +end

      This page was generated using Literate.jl.

      diff --git a/dev/source/utils/index.html b/dev/source/utils/index.html index 50e55f54e..3dc30d942 100644 --- a/dev/source/utils/index.html +++ b/dev/source/utils/index.html @@ -1,5 +1,5 @@ -Utility functions · GeometryOps.jl

      Utility functions

      _is3d(geom) = _is3d(GI.trait(geom), geom)
      +Utility functions · GeometryOps.jl

      Utility functions

      _is3d(geom) = _is3d(GI.trait(geom), geom)
       _is3d(::GI.AbstractGeometryTrait, geom) = GI.is3d(geom)
       _is3d(::GI.FeatureTrait, feature) = _is3d(GI.geometry(feature))
       _is3d(::GI.FeatureCollectionTrait, fc) = _is3d(GI.getfeature(fc, 1))
      @@ -116,4 +116,4 @@
               n += 1
           end
           return n
      -end

      This page was generated using Literate.jl.

      +end

      This page was generated using Literate.jl.