diff --git a/dev/index.html b/dev/index.html index 7d1f11c22..1a4ccf87f 100644 --- a/dev/index.html +++ b/dev/index.html @@ -1,36 +1,83 @@ -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.applyMethod
apply(f, target::Type{<:AbstractTrait}, obj; crs)

Reconstruct a geometry or feature using the function f on the target trait.

f(target_geom) => x where x also has the target trait, or an equivalent.

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

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.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
+line = GI.LineString([(1, 1), (1, 2), (1, 3), (1, 4)])
+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
+
+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
+
+poly = GI.Polygon([[(-1, 2), (3, 2), (3, 3), (-1, 3), (-1, 2)]])
+point = (1, 1)
+GO.disjoint(poly, 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.applyMethod
apply(f, target::Type{<:AbstractTrait}, obj; crs)

Reconstruct a geometry or feature using the function f on the target trait.

f(target_geom) => x where x also has the target trait, or an equivalent.

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

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.containsMethod
contains(pointlist, point)::Bool

Returns true if point is contained in pointlist (geometrically, not as a set) ,and false otherwise.

source
GeometryOps.flattenMethod
flatten(target::Type{<:GI.AbstractTrait}, geom)

Lazily flatten any geometry, feature or iterator of geometries or features so that objects with the specified trait are returned by the iterator.

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

source
GeometryOps.intersectionMethod
intersection(line_a, line_b)

Find a point that intersects LineStrings with two coordinates each.

Returns nothing if no point is found.

Examples

import GeoInterface as GI
-import 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.intersectsMethod
intersects(line_a, line_b)

Check if line_a intersects with line_b.

These can be LineTrait, LineStringTrait or LinearRingTrait

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.

Examples

import GeoInterface as GI, GeometryOps as GO
-line = GI.LineString([(0, 0), (1, 1), (1, 0), (0, 0)])
-GO.isclockwise(line)
+true
source
GeometryOps.flattenMethod
flatten(target::Type{<:GI.AbstractTrait}, geom)

Lazily flatten any geometry, feature or iterator of geometries or features so that objects with the specified trait are returned by the iterator.

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

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.point_in_polygonFunction
point_in_polygon(point::Point, polygon::Union{Polygon, MultiPolygon}, ignoreBoundary::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
+false
source
GeometryOps.line_intersectionMethod
line_intersection(line_a, line_b)

Find a point that intersects LineStrings with two coordinates each.

Returns nothing if no point is found.

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.line_intersection(line1, line2)
+
+# output
+(125.58375366067547, -14.83572303404496)
source
GeometryOps.line_intersectsMethod
line_intersects(line_a, line_b)

Check if line_a intersects with line_b.

These can be LineTrait, LineStringTrait or LinearRingTrait

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.line_intersects(line1, line2)
+
+# output
+true
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. It applies to Polygon/Polygon, LineString/LineString, Multipoint/Multipoint, MultiLineString/MultiLineString and MultiPolygon/MultiPolygon.

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.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]]])
+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, ignoreEndVertices::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 = GI.Point(1, 1)
+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
+
+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
-true
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.

source
GeometryOps.signed_distanceMethod
signed_distance(geom, x::Real, y::Real)::Float64

Calculates the signed distance from the geometry geom to the point defined by (x, y). Points within geom have a negative distance, and points outside of geom have a positive distance.

If geom is a MultiPolygon, then this function returns the maximum distance to any of the polygons in geom.

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.

source
GeometryOps.signed_distanceMethod
signed_distance(geom, x::Real, y::Real)::Float64

Calculates the signed distance from the geometry geom to the point defined by (x, y). Points within geom have a negative distance, and points outside of geom have a positive distance.

If geom is a MultiPolygon, then this function returns the maximum distance to any of the polygons in geom.

source
GeometryOps.simplifyMethod
simplify(obj; kw...)
 simplify(::SimplifyAlg, obj)

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.

Example

Simplify a polygon to have six points:

import GeoInterface as GI
 import GeometryOps as GO
 
@@ -60,5 +107,12 @@
 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.unwrapFunction
unwrap(target::Type{<:AbstractTrait}, obj)
-unwrap(f, target::Type{<:AbstractTrait}, obj)

Unwrap the geometry 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.
+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},{Float64,Float64}} edges.

source
GeometryOps.unwrapFunction
unwrap(target::Type{<:AbstractTrait}, obj)
+unwrap(f, target::Type{<:AbstractTrait}, obj)

Unwrap the geometry 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, geom)::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.

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 79015dee1..ad641e8ea 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 16fd92084..a0c2c0855 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\n\nusing GeoInterface.Extents: Extents\n\nconst GI = GeoInterface\nconst GB = GeometryBasics\n\ninclude(\"primitives.jl\")\ninclude(\"utils.jl\")\n\ninclude(\"methods/bools.jl\")\ninclude(\"methods/signed_distance.jl\")\ninclude(\"methods/signed_area.jl\")\ninclude(\"methods/centroid.jl\")\ninclude(\"methods/intersects.jl\")\ninclude(\"methods/contains.jl\")\ninclude(\"methods/polygonize.jl\")\ninclude(\"methods/barycentric.jl\")\n\ninclude(\"transformations/flip.jl\")\ninclude(\"transformations/simplify.jl\")\ninclude(\"transformations/reproject.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/#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/","page":"Primitive functions","title":"Primitive functions","text":"\"\"\"\n apply(f, target::Type{<:AbstractTrait}, obj; crs)\n\nReconstruct a geometry or feature using the function `f` on the `target` trait.\n\n`f(target_geom) => x` where `x` also has the `target` trait, or an equivalent.\n\nThe result is an functionally similar geometry with values depending on `f`","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:","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"```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...)\n\n_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":"Try to _apply over iterables","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 feature collections","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"function _apply(f, ::Type{Target}, ::GI.FeatureCollectionTrait, fc; crs=GI.crs(fc)) where Target\n applicator(feature) = _apply(f, Target, feature; crs)::GI.Feature\n features = map(applicator, GI.getfeature(fc))\n return GI.FeatureCollection(features; crs)\nend","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Rewrap features","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"function _apply(f, ::Type{Target}, ::GI.FeatureTrait, feature; crs=GI.crs(feature)) where Target\n properties = GI.properties(feature)\n geometry = _apply(f, Target, GI.geometry(feature); crs)\n return GI.Feature(geometry; properties, crs)\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; crs=GI.crs(geom))::(GI.geointerface_geomtype(trait)) where Target","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"TODO handle zero length...","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":" applicator(g) = _apply(f, Target, g; crs)\n geoms = map(applicator, GI.getgeom(geom))\n return rebuild(geom, geoms; crs)\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":"_apply(f, ::Type{Target}, ::Trait, geom; crs=GI.crs(geom)) where {Target,Trait<:Target} = f(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":"_apply(f, ::Type{Target}, trait::GI.PointTrait, geom; crs=nothing) 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":"_apply(f, ::Type{GI.PointTrait}, trait::GI.PointTrait, geom; crs=nothing) = f(geom)\n_apply(f, ::Type{GI.FeatureTrait}, ::GI.FeatureTrait, feature; crs=GI.crs(feature)) = f(feature)\n_apply(f, ::Type{GI.FeatureCollectionTrait}, ::GI.FeatureCollectionTrait, fc; crs=GI.crs(fc)) = f(fc)\n\n\"\"\"\n unwrap(target::Type{<:AbstractTrait}, obj)\n unwrap(f, target::Type{<:AbstractTrait}, obj)\n\nUnwrap the geometry 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}, geom)\n\nLazily flatten any geometry, feature or iterator of geometries or features\nso that objects with the specified trait are returned by the iterator.\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\"\"\"\nreconstruct(geom, components) = first(_reconstruct(geom, components))\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\n 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\n 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\n 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))\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)\n else\n return T{false,false}(child_geoms; crs)\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","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\"\"\"\nfunction reproject(geom;\n source_crs=nothing, target_crs=nothing, transform=nothing, kw...\n)\n if isnothing(transform)\n source_crs = isnothing(source_crs) ? GeoInterface.crs(geom) : source_crs\n isnothing(source_crs) && throw(ArgumentError(\"geom has no crs attatched. Pass a `source_crs` keyword\"))\n 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)\n reproject(geom, transform; time, target_crs)\nend\nfunction reproject(geom, transform::Proj.Transformation; time=Inf, target_crs=nothing)\n if _is3d(geom)\n return apply(PointTrait, geom; crs=target_crs) 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) 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/overlaps/#Overlap-checks","page":"Overlap checks","title":"Overlap checks","text":"","category":"section"},{"location":"source/methods/overlaps/","page":"Overlap checks","title":"Overlap checks","text":"export overlaps","category":"page"},{"location":"source/methods/overlaps/","page":"Overlap checks","title":"Overlap checks","text":"This code checks whether geometries overlap with each other.","category":"page"},{"location":"source/methods/overlaps/","page":"Overlap checks","title":"Overlap checks","text":"It does not compute the overlap or intersection geometry.","category":"page"},{"location":"source/methods/overlaps/","page":"Overlap checks","title":"Overlap checks","text":"\"\"\"\n overlaps(geom1, geom2)::Bool\n\nCompare two Geometries of the same dimension and return true if their intersection set results in a geometry\ndifferent from both but of the same dimension. It applies to Polygon/Polygon, LineString/LineString,\nMultipoint/Multipoint, MultiLineString/MultiLineString and MultiPolygon/MultiPolygon.\n\n# Examples\n```jldoctest\njulia> poly1 = Polygon([[[0,0],[0,5],[5,5],[5,0],[0,0]]])\nPolygon(Array{Array{Float64,1},1}[[[0.0, 0.0], [0.0, 5.0], [5.0, 5.0], [5.0, 0.0], [0.0, 0.0]]])\n\njulia> poly2 = Polygon([[[1,1],[1,6],[6,6],[6,1],[1,1]]])\nPolygon(Array{Array{Float64,1},1}[[[1.0, 1.0], [1.0, 6.0], [6.0, 6.0], [6.0, 1.0], [1.0, 1.0]]])\n\njulia> overlap(poly1, poly2)\ntrue\n```\n\"\"\"\noverlaps(g1, g2)::Bool = overlaps(trait(g1), g1, trait(g2), g2)::Bool\noverlaps(t1::FeatureTrait, g1, t2, g2)::Bool = overlaps(GI.geometry(g1), g2)\noverlaps(t1, g1, t2::FeatureTrait, g2)::Bool = overlaps(g1, geometry(g2))\nfunction overlaps(::MultiPointTrait, g1, ::MultiPointTrait, g2)::Bool\n for p1 in GI.getpoint(g1)\n for p2 in GI.getpoint(g2)\n equals(p1, p2) && return true\n end\n end\nend\nfunction overlaps(::PolygonTrait, g1, ::PolygonTrait, g2)::Bool\n line1 = polygon_to_line(geom1)\n line2 = polygon_to_line(geom2)\n\n intersection(line1, line2)\nend\noverlaps(::PolygonTrait, mp, ::MultiPointTrait, p)::Bool = overlaps(p, mp)\nfunction overlaps(t1::MultiPolygonTrait, mp, t2::Polygon, p1)::Bool\n for p2 in GI.getgeom(mp)\n overlaps(p1, p2)\n end\nend\nfunction overlaps(::MultiPolygonTrait, g1, ::MultiPolygonTrait, g2)::Bool\n for p1 in GI.getgeom(g1)\n overlaps(PolygonTrait(), mp, PolygonTrait(), p1)\n end\nend","category":"page"},{"location":"source/methods/overlaps/","page":"Overlap checks","title":"Overlap checks","text":"","category":"page"},{"location":"source/methods/overlaps/","page":"Overlap checks","title":"Overlap 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/disjoint/#Disjointness-checks","page":"Disjointness checks","title":"Disjointness checks","text":"","category":"section"},{"location":"source/methods/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/disjoint/","page":"Disjointness checks","title":"Disjointness checks","text":"Examples","category":"page"},{"location":"source/methods/disjoint/","page":"Disjointness checks","title":"Disjointness checks","text":"```jldoctest\njulia> poly = Polygon([[[-1, 2], [3, 2], [3, 3], [-1, 3], [-1, 2]]])\nPolygon(Array{Array{Float64,1},1}[[[-1.0, 2.0], [3.0, 2.0], [3.0, 3.0], [-1.0, 3.0], [-1.0, 2.0]]])\n\njulia> point = Point([1, 1])\nPoint([1.0, 1.0])\n\njulia> disjoint(poly, point)\ntrue\n```\n\"\"\"\ndisjoint(t1::FeatureTrait, g1, t2, g2)::Bool = disjoint(GI.geometry(g1), g2)\ndisjoint(t1, g1, t2::FeatureTrait, g2)::Bool = disjoint(g1, geometry(g2))\ndisjoint(t1::PointTrait, g1, t2::PointTrait, g2)::Bool = !point_equals_point(g1, g2)\ndisjoint(t1::PointTrait, g1, t2::LineStringTrait, g2)::Bool = !point_on_line(g1, g2)\ndisjoint(t1::PointTrait, g1, t2::PolygonTrait, g2)::Bool = !point_in_polygon(g1, g2)\ndisjoint(t1::LineStringTrait, g1, t2::PointTrait, g2)::Bool = !point_on_line(g2, g1)\ndisjoint(t1::LineStringTrait, g1, t2::LineStringTrait, g2)::Bool = !line_on_line(g1, g2)\ndisjoint(t1::LineStringTrait, g1, t2::PolygonTrait, g2)::Bool = !line_in_polygon(g2, g1)\ndisjoint(t1::PolygonTrait, g1, t2::PointTrait, g2)::Bool = !point_in_polygon(g2, g1)\ndisjoint(t1::PolygonTrait, g1, t2::LineStringTrait, g2)::Bool = !line_in_polygon(g2, g1)\ndisjoint(t1::PolygonTrait, g1, t2::PolygonTrait, g2)::Bool = !poly_in_poly(g2, g1)","category":"page"},{"location":"source/methods/disjoint/","page":"Disjointness checks","title":"Disjointness checks","text":"","category":"page"},{"location":"source/methods/disjoint/","page":"Disjointness checks","title":"Disjointness checks","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/methods/crosses/#Crossing-checks","page":"Crossing checks","title":"Crossing checks","text":"","category":"section"},{"location":"source/methods/crosses/","page":"Crossing checks","title":"Crossing checks","text":"\"\"\"\n crosses(ft1::AbstractGeometry, ft2::AbstractGeometry)::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\n# Examples\n```jldoctest\njulia> line = LineString([[1, 1], [1, 2], [1, 3], [1, 4]])\nLineString(Array{Float64,1}[[1.0, 1.0], [1.0, 2.0], [1.0, 3.0], [1.0, 4.0]])\n\njulia> line2 = LineString([[-2, 2], [4, 2]])\nLineString(Array{Float64,1}[[-2.0, 2.0], [4.0, 2.0]])\n\njulia> crosses(line2, line)\ntrue\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_cross_line(g1, g2)\ncrosses(::MultiPointTrait, g1::PolygonTrait, , g2)::Bool = multipoint_cross_poly(g1, g2)\ncrosses(::LineStringTrait, g1, ::MultiPointTrait, g2)::Bool = multipoint_cross_lines(g2, g1)\ncrosses(::LineStringTrait, g1, ::PolygonTrait, g2)::Bool = line_cross_poly(g1, g2)\ncrosses(::LineStringTrait, g1, ::LineStringTrait, g2)::Bool = line_cross_line(g1, g2)\ncrosses(::PolygonTrait, g1, ::MultiPointTrait, g2)::Bool = multipoint_cross_poly(g2, g1)\ncrosses(::PolygonTrait, g1, ::LineStringTrait, g2)::Bool = line_cross_poly(g2, g1)\n\nfunction multipoint_cross_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) && !intPoint && !extPoint\n for j in 1:GI.npoint(geom2) - 1\n inc_vertices = (j === 1 || j === np2 - 2) ? :none : :both\n\n if is_point_on_segment(GI.getpoint(geom2, j), GI.getpoint(geom2.coordinates, j + 1), GI.getpoint(geom1, i), inc_vertices)\n int_point = true\n else\n ext_point = true\n end\n\n end\n i += 1\n end\n\n return int_point && ext_point\nend\n\nfunction line_cross_line(line1, line2)\n inter = intersection(line1, line2)\n\n np2 = GI.npoint(line2)\n if !isnothing(inter)\n for i in 1:GI.npoint(line1) - 1\n for j in 1:GI.npoint(line2) - 1\n inc_vertices = (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 is_point_on_segment(pa, pb, p, inc_vertices) && return true\n end\n end\n end\n return false\nend\n\nfunction line_cross_poly(line, poly) =\n\n for line in flatten(AbstractCurveTrait, poly)\n intersects(line)\n end\nend\n\nfunction multipoint_cross_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 in_point && ext_point && return true\n end\n return false\nend","category":"page"},{"location":"source/methods/crosses/","page":"Crossing checks","title":"Crossing checks","text":"","category":"page"},{"location":"source/methods/crosses/","page":"Crossing checks","title":"Crossing checks","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/methods/signed_distance/#Signed-distance","page":"Signed distance","title":"Signed distance","text":"","category":"section"},{"location":"source/methods/signed_distance/","page":"Signed distance","title":"Signed distance","text":"export signed_distance","category":"page"},{"location":"source/methods/signed_distance/","page":"Signed distance","title":"Signed distance","text":"TODO: clean this up. It already supports GeoInterface.","category":"page"},{"location":"source/methods/signed_distance/","page":"Signed distance","title":"Signed distance","text":"Base.@propagate_inbounds euclid_distance(p1, p2) = sqrt((GeoInterface.x(p2)-GeoInterface.x(p1))^2 + (GeoInterface.y(p2)-GeoInterface.y(p1))^2)\neuclid_distance(x1, y1, x2, y2) = sqrt((x2-x1)^2 + (y2-y1)^2)\n\n\n\n\" Distance from p0 to the line segment formed by p1 and p2. Implementation from Turf.jl.\"\nfunction _distance(p0, p1, p2)\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 if x1 < x2\n xfirst, yfirst = x1, y1\n xlast, ylast = x2, y2\n else\n xfirst, yfirst = x2, y2\n xlast, ylast = x1, y1\n end\n\n v = (xlast - xfirst, ylast - yfirst)\n w = (x0 - xfirst, y0 - yfirst)\n\n c1 = sum(w .* v)\n if c1 <= 0\n return euclid_distance(x0, y0, xfirst, yfirst)\n end\n\n c2 = sum(v .* v)\n\n if c2 <= c1\n return euclid_distance(x0, y0, xlast, ylast)\n end\n\n b2 = c1 / c2\n\n return euclid_distance(x0, y0, xfirst + (b2 * v[1]), yfirst + (b2 * v[2]))\nend\n\n\nfunction _distance(linestring, xy)\n mindist = typemax(Float64)\n N = GeoInterface.npoint(linestring)\n @assert N ≥ 3\n p1 = GeoInterface.getpoint(linestring, 1)\n p2 = p1\n\n for point_ind in 2:N\n p2 = GeoInterface.getpoint(linestring, point_ind)\n newdist = _distance(xy, p1, p2)\n if newdist < mindist\n mindist = newdist\n end\n p1 = p2\n end\n\n return mindist\nend\n\nfunction signed_distance(::GeoInterface.PolygonTrait, poly, x, y)\n\n xy = (x, y)\n mindist = _distance(GeoInterface.getexterior(poly), xy)\n\n @inbounds for hole in GeoInterface.gethole(poly)\n newdist = _distance(hole, xy)\n if newdist < mindist\n mindist = newdist\n end\n end\n\n if GeoInterface.contains(poly, GeoInterface.convert(Base.parentmodule(typeof(poly)), (x, y)))\n return mindist\n else\n return -mindist\n end\nend\n\nfunction signed_distance(::GeoInterface.MultiPolygonTrait, multipoly, x, y)\n distances = signed_distance.(GeoInterface.getpolygon(multipoly), x, y)\n max_val, max_ind = findmax(distances)\n return max_val\nend\n\n\n\"\"\"\n signed_distance(geom, x::Real, y::Real)::Float64\n\nCalculates the signed distance from the geometry `geom` to the point\ndefined by `(x, y)`. Points within `geom` have a negative distance,\nand points outside of `geom` have a positive distance.\n\nIf `geom` is a MultiPolygon, then this function returns the maximum distance\nto any of the polygons in `geom`.\n\"\"\"\nsigned_distance(geom, x, y) = signed_distance(GeoInterface.geomtrait(geom), geom, x, y)","category":"page"},{"location":"source/methods/signed_distance/","page":"Signed distance","title":"Signed distance","text":"","category":"page"},{"location":"source/methods/signed_distance/","page":"Signed distance","title":"Signed distance","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","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"These are all GeometryBasics.jl methods so far. They need to be converted to GeoInterface.","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"The reason that there is a centroid_and_signed_area function, is because in conputing the centroid, you end up computing the signed area.","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"In some computational geometry applications this may be a useful source of efficiency, so I added it here.","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"However, it's totally fine to ignore this and not have this code path. We simply need to decide on this.","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"function centroid(ls::LineString{2, T}) where T\n centroid = Point{2, T}(0)\n total_area = T(0)\n if length(ls) == 1\n return sum(ls[1])/2\n end\n\n p0 = ls[1][1]\n\n for i in 1:(length(ls)-1)\n p1 = ls[i][2]\n p2 = ls[i+1][2]\n area = signed_area(p0, p1, p2)\n centroid = centroid .+ Point{2, T}((p0[1] + p1[1] + p2[1])/3, (p0[2] + p1[2] + p2[2])/3) * area\n total_area += area\n end\n return centroid ./ total_area\nend","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"a more optimized function, so we only calculate signed area once!","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"function centroid_and_signed_area(ls::LineString{2, T}) where T\n centroid = Point{2, T}(0)\n total_area = T(0)\n if length(ls) == 1\n return sum(ls[1])/2\n end\n\n p0 = ls[1][1]\n\n for i in 1:(length(ls)-1)\n p1 = ls[i][2]\n p2 = ls[i+1][2]\n area = signed_area(p0, p1, p2)\n centroid = centroid .+ Point{2, T}((p0[1] + p1[1] + p2[1])/3, (p0[2] + p1[2] + p2[2])/3) * area\n total_area += area\n end\n return (centroid ./ total_area, total_area)\nend\n\nfunction centroid(poly::GeometryBasics.Polygon{2, T}) where T\n exterior_centroid, exterior_area = centroid_and_signed_area(poly.exterior)\n\n total_area = exterior_area\n interior_numerator = Point{2, T}(0)\n for interior in poly.interiors\n interior_centroid, interior_area = centroid_and_signed_area(interior)\n total_area += interior_area\n interior_numerator += interior_centroid * interior_area\n end\n\n return (exterior_centroid * exterior_area - interior_numerator) / total_area\n\nend\n\nfunction centroid(multipoly::MultiPolygon)\n centroids = centroid.(multipoly.polygons)\n areas = signed_area.(multipoly.polygons)\n areas ./= sum(areas)\n\n return sum(centroids .* areas) / sum(areas)\nend\n\n\nfunction centroid(rect::Rect{N, T}) where {N, T}\n return Point{N, T}(rect.origin .- rect.widths ./ 2)\nend\n\nfunction centroid(sphere::HyperSphere{N, T}) where {N, T}\n return sphere.center\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/methods/contains/#Containment","page":"Containment","title":"Containment","text":"","category":"section"},{"location":"source/methods/contains/","page":"Containment","title":"Containment","text":"export contains","category":"page"},{"location":"source/methods/contains/","page":"Containment","title":"Containment","text":"This currently works for point-in-linestring or point-in-polygon.","category":"page"},{"location":"source/methods/contains/","page":"Containment","title":"Containment","text":"More GeometryBasics code","category":"page"},{"location":"source/methods/contains/","page":"Containment","title":"Containment","text":"_cross(p1, p2, p3) = (GI.x(p1) - GI.x(p3)) * (GI.y(p2) - GI.y(p3)) - (GI.x(p2) - GI.x(p3)) * (GI.y(p1) - GI.y(p3))\n\n\"\"\"\n contains(pointlist, point)::Bool\n\nReturns `true` if `point` is contained in `pointlist` (geometrically, not as a set)\n,and `false` otherwise.\n\"\"\"\ncontains(pointlist, point) = contains(GI.trait(pointlist), GI.trait(point), pointlist, point)","category":"page"},{"location":"source/methods/contains/","page":"Containment","title":"Containment","text":"Implementation of a point-in-polygon algorithm from Luxor.jl. This is the Hormann-Agathos (2001) algorithm.","category":"page"},{"location":"source/methods/contains/","page":"Containment","title":"Containment","text":"For the source, see the code from Luxor.jl.","category":"page"},{"location":"source/methods/contains/","page":"Containment","title":"Containment","text":"function contains(::Union{GI.LineStringTrait, GI.LinearRingTrait}, ::GI.PointTrait, pointlist, point)\n n = GI.npoint(pointlist)\n c = false\n q1 = GI.getpoint(pointlist, 1)\n q2 = GI.getpoint(pointlist, 1)\n @inbounds for (counter, current_point) in enumerate(Iterators.drop(GI.getpoint(pointlist), 1))\n q1 = q2\n # if reached last point, set \"next point\" to first point.\n #\n if counter == (n-1)\n q2 = GI.getpoint(pointlist, 1)\n else\n q2 = current_point\n end\n if GI.x(q1) == GI.x(point) && GI.x(q1) == GI.y(point)\n # allowonedge || error(\"isinside(): VertexException a\")\n continue\n end\n if GI.y(q2) == GI.y(point)\n if GI.x(q2) == GI.x(point)\n # allowonedge || error(\"isinside(): VertexException b\")\n continue\n elseif (GI.y(q1) == GI.y(point)) && ((GI.x(q2) > GI.x(point)) == (GI.x(q1) < GI.x(point)))\n # allowonedge || error(\"isinside(): EdgeException\")\n continue\n end\n end\n if (GI.y(q1) < GI.y(point)) != (GI.y(q2) < GI.y(point)) # crossing\n if GI.x(q1) >= GI.x(point)\n if GI.x(q2) > GI.x(point)\n c = !c\n elseif ((_cross(q1, q2, point) > 0) == (GI.y(q2) > GI.y(q1)))\n c = !c\n end\n elseif GI.x(q2) > GI.x(point)\n if ((_cross(q1, q2, point) > 0) == (GI.y(q2) > GI.y(q1)))\n c = !c\n end\n end\n end\n end\n return c\n\nend\n\nfunction contains(poly::Polygon{2, T1}, point::Point{2, T2}) where {T1, T2}\n c = contains(poly.exterior, point)\n for interior in poly.interiors\n if contains(interior, point)\n return false\n end\n end\n return c\nend","category":"page"},{"location":"source/methods/contains/","page":"Containment","title":"Containment","text":"TODOs: implement contains for mesh, simplex, and 3d objects (eg rect, triangle, etc.)","category":"page"},{"location":"source/methods/contains/","page":"Containment","title":"Containment","text":"contains(mp::MultiPolygon{2, T1}, point::Point{2, T2}) where {T1, T2} = any((contains(poly, point) for poly in mp.polygons))","category":"page"},{"location":"source/methods/contains/","page":"Containment","title":"Containment","text":"","category":"page"},{"location":"source/methods/contains/","page":"Containment","title":"Containment","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\"\"\"\nfunction flip(geom)\n if _is3d(geom)\n return apply(PointTrait, geom) do p\n (GI.y(p), GI.x(p), GI.z(p))\n end\n else\n return apply(PointTrait, geom) 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/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\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- `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)\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":"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; kw...) = _simplify(DouglasPeucker(; kw...), data)\nsimplify(alg::SimplifyAlg, data) = _simplify(alg, data)\n\nfunction _simplify(alg::SimplifyAlg, data)\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)\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)))\nfunction _simplify(::LinearRingTrait, alg, geom)\n # Make a vector of points\n points = tuple_points(geom)\n\n # Simplify it once\n simple = _simplify(alg, points)\n\n return rebuild(geom, simple)\nend\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/within/#Containment/withinness","page":"Containment/withinness","title":"Containment/withinness","text":"","category":"section"},{"location":"source/methods/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)\njulia> line = LineString([[1, 1], [1, 2], [1, 3], [1, 4]])\nLineString(Array{Float64,1}[[1.0, 1.0], [1.0, 2.0], [1.0, 3.0], [1.0, 4.0]])\n\njulia> point = Point([1, 2])\nPoint([1.0, 2.0])\n\njulia> within(point, line)\ntrue\n```\n\"\"\"\nwithin(g1, g2)::Bool = within(trait(g1), g1, trait(g2), g2)::Bool\nwithin(t1::FeatureTrait, g1, t2, g2)::Bool = within(GI.geometry(g1), g2)\nwithin(t1, g1, t2::FeatureTrait, g2)::Bool = within(g1, geometry(g2))\nwithin(t1::PointTrait, g1::LineStringTrait, t2, g2)::Bool = point_on_line(ft1, ft2, true)\nwithin(t1::PointTrait, g1, t2::PolygonTrait, g2)::Bool = point_in_polygon(ft1, ft2, true)\nwithin(t1::LineStringTrait, g1, t2::PolygonTrait, g2)::Bool = line_in_polygon(ft1, ft2)\nwithin(t1::LineStringTrait, g1, t2::LineStringTrait, g2)::Bool = line_on_line(ft1, ft2)\nwithin(t1::PolygonTrait, g1, t2::PolygonTrait, g2)::Bool = polygon_in_polygon(ft1, ft2, true)","category":"page"},{"location":"source/methods/within/","page":"Containment/withinness","title":"Containment/withinness","text":"","category":"page"},{"location":"source/methods/within/","page":"Containment/withinness","title":"Containment/withinness","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/methods/signed_area/#Signed-area","page":"Signed area","title":"Signed area","text":"","category":"section"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"export signed_area","category":"page"},{"location":"source/methods/signed_area/#What-is-signed-area?","page":"Signed area","title":"What is signed area?","text":"","category":"section"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"Signed area is simply the integral over the exterior path of a polygon, minus the sum of integrals over its interior holes.","category":"page"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"It is signed such that a clockwise path has a positive area, and a counterclockwise path has a negative area.","category":"page"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"To provide an example, consider this rectangle:","category":"page"},{"location":"source/methods/signed_area/","page":"Signed area","title":"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/signed_area/","page":"Signed area","title":"Signed area","text":"This is clearly a rectangle, etc. But now let's look at how the points look:","category":"page"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"lines!(a, rect; color = 1:length(coordinates(rect))+1)\nf","category":"page"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"The points are ordered in a clockwise fashion, which means that the signed area is positive. If we reverse the order of the points, we get a negative area.","category":"page"},{"location":"source/methods/signed_area/#Implementation","page":"Signed area","title":"Implementation","text":"","category":"section"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"This is the GeoInterface-compatible implementation.","category":"page"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"First, we implement a wrapper method that dispatches to the correct implementation based on the geometry trait.","category":"page"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"This is also used in the implementation, since it's a lot less work!","category":"page"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"\"\"\"\n signed_area(geom)::Real\n\nReturns the signed area of the geometry, based on winding order.\n\"\"\"\nsigned_area(x) = signed_area(GI.trait(x), x)","category":"page"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"TODOS here:","category":"page"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"This could conceivably be multithreaded. How to indicate that it should be so?\nWhat to do for corner cases (nan point, etc)?","category":"page"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"function signed_area(::Union{LineStringTrait, LinearRingTrait}, geom)","category":"page"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"Basically, we integrate the area under the line string, which gives us the signed area.","category":"page"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":" point₁ = GI.getpoint(geom, 1)\n point₂ = GI.getpoint(geom, 2)\n area = GI.x(point₁) * GI.y(point₂) - GI.y(point₁) * GI.x(point₂)\n for point in GI.getpoint(geom)","category":"page"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"Advance the point buffers by 1 point","category":"page"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":" point₁ = point₂\n point₂ = point","category":"page"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"Accumulate the area into area","category":"page"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":" area += GI.x(point₁) * GI.y(point₂) - GI.y(point₁) * GI.x(point₂)\n end\n area /= 2\n return area\nend","category":"page"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"This subtracts the","category":"page"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"function signed_area(::PolygonTrait, geom)\n s_area = signed_area(GI.getexterior(geom))\n area = abs(s_area)\n for hole in GI.gethole(geom)\n area -= abs(signed_area(hole))\n end\n return area * sign(s_area)\nend\n\nsigned_area(::MultiPolygonTrait, geom) = sum((signed_area(poly) for poly in GI.getpolygon(geom)))","category":"page"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"This should theoretically work for anything, but I haven't actually tested yet!","category":"page"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"Below is the original GeometryBasics implementation:","category":"page"},{"location":"source/methods/signed_area/#julia","page":"Signed area","title":"```julia","text":"","category":"section"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"function signed_area(a::Point{2, T}, b::Point{2, T}, c::Point{2, T}) where T return ((b[1] - a[1]) * (c[2] - a[2]) - (c[1] - a[1]) * (b[2] - a[2])) / 2 end","category":"page"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"function signed_area(points::AbstractVector{<: Point{2, T}}) where {T} area = sum((points[i][1] * points[i+1][2] - points[i][2] * points[i+1][1] for i in 1:(length(points)-1))) / 2.0 end","category":"page"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"function signedarea(ls::GeometryBasics.LineString) # coords = GeometryBasics.decompose(Point2f, ls) return sum((p1[1] * p2[2] - p1[2] * p2[1] for (p1, p2) in ls)) / 2.0#signedarea(coords) end","category":"page"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"function signedarea(poly::GeometryBasics.Polygon{2}) sarea = signedarea(poly.exterior) area = abs(sarea) for hole in poly.interiors area -= abs(signedarea(hole)) end return area * sign(sarea) end","category":"page"},{"location":"source/methods/signed_area/#WARNING:-this-may-not-do-what-you-expect,-since-it's","page":"Signed area","title":"WARNING: this may not do what you expect, since it's","text":"","category":"section"},{"location":"source/methods/signed_area/#sensitive-to-winding-order.-Use-GeoInterface.area-instead.","page":"Signed area","title":"sensitive to winding order. Use GeoInterface.area instead.","text":"","category":"section"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"signedarea(mp::MultiPolygon) = sum(signedarea.(mp.polygons)) ```","category":"page"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"","category":"page"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/methods/intersects/#Intersection-checks","page":"Intersection checks","title":"Intersection checks","text":"","category":"section"},{"location":"source/methods/intersects/","page":"Intersection checks","title":"Intersection checks","text":"export intersects, intersection","category":"page"},{"location":"source/methods/intersects/","page":"Intersection checks","title":"Intersection checks","text":"This code checks whether geometries intersect with each other.","category":"page"},{"location":"source/methods/intersects/","page":"Intersection checks","title":"Intersection checks","text":"note: Note\nThis does not compute intersections, only checks if they exist.","category":"page"},{"location":"source/methods/intersects/","page":"Intersection checks","title":"Intersection checks","text":"\"\"\"\n intersects(line_a, line_b)\n\nCheck if `line_a` intersects with `line_b`.\n\nThese can be `LineTrait`, `LineStringTrait` or `LinearRingTrait`\n\"\"\"\nintersects(a, b) = isnothing(intersection) # Probably faster ways to do this\n\n\"\"\"\n intersection(line_a, line_b)\n\nFind a point that intersects LineStrings with two coordinates each.\n\nReturns `nothing` if no point is found.","category":"page"},{"location":"source/methods/intersects/","page":"Intersection checks","title":"Intersection checks","text":"Examples","category":"page"},{"location":"source/methods/intersects/","page":"Intersection checks","title":"Intersection checks","text":"```jldoctest\nimport GeoInterface as GI\nimport GeometryOps as GO\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/intersects/","page":"Intersection checks","title":"Intersection checks","text":"output","category":"page"},{"location":"source/methods/intersects/","page":"Intersection checks","title":"Intersection checks","text":"(125.58375366067547, -14.83572303404496)\n```\n\"\"\"\nintersection(line_a, line_b) = intersection(trait(line_a), line_a, trait(line_b), line_b)\nfunction intersection(\n ::Union{LineStringTrait,LinearRingTrait}, line_a,\n ::Union{LineStringTrait,LinearRingTrait}, line_b,\n)\n result = Tuple{Float64,Float64}[] # TODO handle 3d, and other Real ?\n a1 = GI.getpoint(line_a, 1)\n b1 = GI.getpoint(line_b, 1)","category":"page"},{"location":"source/methods/intersects/","page":"Intersection checks","title":"Intersection checks","text":"TODO we can check all of these against the extent of line_b and continue the loop if theyre outside","category":"page"},{"location":"source/methods/intersects/","page":"Intersection checks","title":"Intersection checks","text":" for i in 1:GI.npoint(line_a) - 1\n for j in 1:GI.npoint(line_b) - 1\n a2 = GI.getpoint(line_a, i + 1)\n b2 = GI.getpoint(line_b, j + 1)\n inter = _intersection((a1, a2), (b1, b2))\n isnothing(inter) || push!(result, inter)\n a1 = a2\n b1 = b2\n end\n end\n return unique!(result)\nend\n\nfunction intersection(::LineTrait, line_a, ::LineTrait, line_b)\n a1 = GI.getpoint(line_a, 1)\n b1 = GI.getpoint(line_b, 1)\n a2 = GI.getpoint(line_a, 2)\n b2 = GI.getpoint(line_b, 2)\n\n return _intersection((a1, a2), (b1, b2))\nend\n\nfunction _intersection((p11, p12)::Tuple, (p21, p22)::Tuple)","category":"page"},{"location":"source/methods/intersects/","page":"Intersection checks","title":"Intersection checks","text":"Get points from lines","category":"page"},{"location":"source/methods/intersects/","page":"Intersection checks","title":"Intersection checks","text":" x1, y1 = GI.x(p11), GI.y(p11)\n x2, y2 = GI.x(p12), GI.y(p12)\n x3, y3 = GI.x(p21), GI.y(p21)\n x4, y4 = GI.x(p22), GI.y(p22)\n\n d = ((y4 - y3) * (x2 - x1)) - ((x4 - x3) * (y2 - y1))\n a = ((x4 - x3) * (y1 - y3)) - ((y4 - y3) * (x1 - x3))\n b = ((x2 - x1) * (y1 - y3)) - ((y2 - y1) * (x1 - x3))\n\n if d == 0\n if a == 0 && b == 0\n return nothing\n end\n return nothing\n end\n\n ã = a / d\n b̃ = b / d\n\n if ã >= 0 && ã <= 1 && b̃ >= 0 && b̃ <= 1\n x = x1 + (ã * (x2 - x1))\n y = y1 + (ã * (y2 - y1))\n return (x, y)\n end\n\n return nothing\nend","category":"page"},{"location":"source/methods/intersects/","page":"Intersection checks","title":"Intersection checks","text":"","category":"page"},{"location":"source/methods/intersects/","page":"Intersection checks","title":"Intersection checks","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","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 counter-clockwise.\n\n# Examples\n```jldoctest\nimport GeoInterface as GI, GeometryOps as GO\nline = GI.LineString([(0, 0), (1, 1), (1, 0), (0, 0)])\nGO.isclockwise(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\"\"\"\nisclockwise(geom)::Bool = isclockwise(GI.trait(geom), geom)\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\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)\n GI.npoint(exterior) <= 4 && return false\n\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\n\n\nfunction equals(geo1, geo2)\n GI.geomtrait(geo1) !== GI.geomtrait(geo2) && return false\n\n GI.geomtrait(geo1) isa PointTrait && return compare_points(geo1, geo2)\n GI.geomtrait(geo1) isa LineStringTrait && return compare_lines(geo1, geo2)\n\n error(\"Cant compare $(GI.trait(geo1)) and $(GI.trait(geo2)) yet\")\nend\n\nfunction compare_points(p1, p2)\n length(p1) !== length(p2) && return false\n\n for i in eachindex(p1)\n round(p1[i]; digits=10) !== round(p2[i]; digits=10) && return false\n end\n\n return true\nend\n\nfunction compare_lines(p1::Vector, p2::Vector)","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"TODO: complete this","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":" length(p1[1]) !== length(p2[1]) && return false\nend","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"\"\"\" parallel(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, ignoreEndVertices::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```jldoctest\nimport GeoInterface as GI, GeometryOps as GO\npoint = GI.Point(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 ignore = :none\n for i in 1:n - 1\n if ignore_end_vertices == true\n if i === 1\n ignore = :start\n elseif i === n - 2\n ignore = :end\n elseif (i === 1 && i + 1 === n - 1)\n ignore = :both\n end\n end\n if point_on_segment(line_points[i], line_points[i + 1], point, ignore)\n return true\n end\n end\n return false\nend\n\nfunction point_on_segment(start, stop, point, 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}, ignoreBoundary::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```jldoctest\nimport GeoInterface as GI, GeometryOps as GO\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\"\"\"\nfunction point_in_polygon(p, polygon, ignore_boundary::Bool=false)::Bool\n GI.trait(polygon) isa PolygonTrait || throw(ArgumentError(\"Not a polygon\"))\n\n point_in_extent(p, GI.extent(polygon)) || return false\n point_in_ring(p, GI.getexterior(polygon), ignore_boundary) || return false\n\n for ring in GI.gethole(polygon)\n point_in_ring(pt, ring, !ignore_boundary) && return false\n end\n return true\nend\n\nfunction point_in_ring(pt, ring, ignore_boundary::Bool=false)\n GI.trait(ring) isa Union{LineStringTrait,LinearRingTrait} || throw(ArgumentError(\"Not a ring\"))\n inside = false\n n = GI.npoint(ring)\n p1 = first(GI.getpoint(ring))\n p_end = GI.getpoint(ring, n)\n\n l = if GI.x(p1) == GI.x(p_end) && GI.y(p1) == GI.y(p_end)\n l = n -1\n else\n n\n end\n\n for i in 1:l - 1\n j = i + 1\n\n p_i = GI.getpoint(ring, i)\n p_j = GI.getpoint(ring, j)\n xi = GI.x(p_i)\n yi = GI.y(p_i)\n xj = GI.x(p_j)\n yj = GI.y(p_j)\n\n on_boundary = (GI.y(pt) * (xi - xj) + yi * (xj - GI.x(pt)) + yj * (GI.x(pt) - xi) == 0) &&\n ((xi - GI.x(pt)) * (xj - GI.x(pt)) <= 0) && ((yi - GI.y(pt)) * (yj - GI.y(pt)) <= 0)\n\n on_boundary && return !ignore_boundary\n\n intersects = ((yi > GI.y(pt)) !== (yj > GI.y(pt))) &&\n (GI.x(pt) < (xj - xi) * (GI.y(pt) - yi) / (yj - yi) + xi)\n\n if intersects\n inside = !inside\n end\n end\n\n return inside\nend\n\nfunction point_in_extent(p, extent::Extents.Extent)\n extent.X[1] <= GI.x(p) && extent.Y[1] <= GI.y(p) &&\n extent.X[2] >= GI.x(p) && extent.Y[2] >= GI.y(p)\nend\n\nfunction line_in_polygon(poly, line)\n out = false\n\n polybox = bbox(poly)\n linebox = bbox(line)\n\n !(bboxOverlap(polybox, linebox)) && return false\n\n coords = line.coordinates\n\n for i in 1:length(coords) - 1\n mid = [(coords[i][1] + coords[i + 1][1]) / 2, (coords[i][2] + coords[i + 1][2]) / 2]\n if point_in_polygon(Point(mid), poly, true)\n out = true\n break\n end\n end\n return out\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)\n 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)\nfunction line_in_polygon(::LineStringTrait, line, ::PolygonTrait, poly)\n polybox = bbox(poly)\n linebox = bbox(line)\n\n !(bboxOverlap(polybox, linebox)) && return false\n\n coords = line.coordinates\n inside = false\n\n for i in 1:length(coords) - 1\n !(point_in_polygon(Point(coords[i]), poly)) && return false\n !inside && (inside = point_in_polygon(Point(coords[i]), poly, true))\n if !inside\n mid = [(coords[i][1] + coords[i + 1][1]) / 2, (coords[i][2] + coords[i + 1][2]) / 2]\n inside = point_in_polygon(Point(mid), poly, true)\n end\n end\n return inside\nend","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"TODO - why were there two methods for this in Turf.jl?","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"function polygon_in_polygon(ft1, ft2, reverse::Bool=false)\n polybox1 = bbox(ft1)\n polybox2 = bbox(ft2)\n coords = []\n\n if reverse\n !(bbox_overlap(polybox2, polybox1)) && return false\n\n for point in GI.getpoint(ft1)\n !(point_in_polygon(point, ft2)) && return false\n end\n else\n !(bbox_overlap(polybox1, polybox2)) && return false\n\n for point in GI.getpoint(ft2)\n !(point_in_polygon(point, ft1)) && return false\n end\n end\n\n return true\nend\nfunction poly_in_poly(poly1, poly2)\n\n for point in GI.getpoint(poly1)\n (point_in_polygon(point, poly2)) && return true\n end\n\n for point in GI.getpoint(poly2)\n (point_in_polygon(point, poly1)) && return true\n end\n\n inter = line_intersects(polygon_to_line(poly1), polygon_to_line(poly2))\n inter != nothing && return true\n\n return false\n\nend\n\nfunction bbox_overlap(box1::Vector{T}, box2::Vector{T}) where {T <: Real}\n box1[1] > box2[1] && return false\n box1[3] < box2[3] && return false\n box1[2] > box2[2] && return false\n box1[4] < box2[4] && return false\n return true\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/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 on obj to `Tuple`s.\n\"\"\"\nfunction tuples(geom)\n if _is3d(geom)\n return apply(PointTrait, geom) do p\n (GI.x(p), GI.y(p), GI.z(p))\n end\n else\n return apply(PointTrait, geom) do p\n (GI.x(p), 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":"","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._distance-Tuple{Any, Any, Any}","page":"Home","title":"GeometryOps._distance","text":"Distance from p0 to the line segment formed by p1 and p2. Implementation from Turf.jl.\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; crs)\n\nReconstruct a geometry or feature using the function f on the target trait.\n\nf(target_geom) => x where x also has the target trait, or an equivalent.\n\nThe result is an functionally similar geometry with values depending on f\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.contains-Tuple{Any, Any}","page":"Home","title":"GeometryOps.contains","text":"contains(pointlist, point)::Bool\n\nReturns true if point is contained in pointlist (geometrically, not as a set) ,and false otherwise.\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}, geom)\n\nLazily flatten any geometry, feature or iterator of geometries or features so that objects with the specified trait are returned by the iterator.\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\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(line_a, line_b)\n\nFind a point that intersects LineStrings with two coordinates each.\n\nReturns nothing if no point is found.\n\nExamples\n\nimport GeoInterface as GI\nimport GeometryOps as GO\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# output\n(125.58375366067547, -14.83572303404496)\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.intersects-Tuple{Any, Any}","page":"Home","title":"GeometryOps.intersects","text":"intersects(line_a, line_b)\n\nCheck if line_a intersects with line_b.\n\nThese can be LineTrait, LineStringTrait or LinearRingTrait\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\nExamples\n\nimport GeoInterface as GI, GeometryOps as GO\nline = GI.LineString([(0, 0), (1, 1), (1, 0), (0, 0)])\nGO.isclockwise(line)\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\npoly = GI.Polygon([[(0, 0), (0, 1), (1, 1), (1, 0), (0, 0)]])\nGO.isconcave(poly)\n# output\nfalse\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.point_in_polygon","page":"Home","title":"GeometryOps.point_in_polygon","text":"point_in_polygon(point::Point, polygon::Union{Polygon, MultiPolygon}, ignoreBoundary::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\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# output\ntrue\n\n\n\n\n\n","category":"function"},{"location":"#GeometryOps.point_on_line-Tuple{Any, Any}","page":"Home","title":"GeometryOps.point_on_line","text":"point_on_line(point::Point, line::LineString, ignoreEndVertices::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\npoint = GI.Point(1, 1)\nline = GI.LineString([(0, 0), (3, 3), (4, 4)])\nGO.point_on_line(point, line)\n# output\ntrue\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\n-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.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.signed_area-Tuple{Any}","page":"Home","title":"GeometryOps.signed_area","text":"signed_area(geom)::Real\n\nReturns the signed area of the geometry, based on winding order.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.signed_distance-Tuple{Any, Any, Any}","page":"Home","title":"GeometryOps.signed_distance","text":"signed_distance(geom, x::Real, y::Real)::Float64\n\nCalculates the signed distance from the geometry geom to the point defined by (x, y). Points within geom have a negative distance, and points outside of geom have a positive distance.\n\nIf geom is a MultiPolygon, then this function returns the maximum distance to any of the polygons in geom.\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)\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\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.unwrap","page":"Home","title":"GeometryOps.unwrap","text":"unwrap(target::Type{<:AbstractTrait}, obj)\nunwrap(f, target::Type{<:AbstractTrait}, obj)\n\nUnwrap the geometry 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\n\nusing GeoInterface.Extents: Extents\n\nconst GI = GeoInterface\nconst GB = GeometryBasics\n\ninclude(\"primitives.jl\")\ninclude(\"utils.jl\")\n\ninclude(\"methods/bools.jl\")\ninclude(\"methods/signed_distance.jl\")\ninclude(\"methods/signed_area.jl\")\ninclude(\"methods/centroid.jl\")\ninclude(\"methods/intersects.jl\")\ninclude(\"methods/contains.jl\")\ninclude(\"methods/crosses.jl\")\ninclude(\"methods/disjoint.jl\")\ninclude(\"methods/overlaps.jl\")\ninclude(\"methods/within.jl\")\ninclude(\"methods/polygonize.jl\")\ninclude(\"methods/barycentric.jl\")\n\ninclude(\"transformations/flip.jl\")\ninclude(\"transformations/simplify.jl\")\ninclude(\"transformations/reproject.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/#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/","page":"Primitive functions","title":"Primitive functions","text":"\"\"\"\n apply(f, target::Type{<:AbstractTrait}, obj; crs)\n\nReconstruct a geometry or feature using the function `f` on the `target` trait.\n\n`f(target_geom) => x` where `x` also has the `target` trait, or an equivalent.\n\nThe result is an functionally similar geometry with values depending on `f`","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:","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"```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...)\n\n_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":"Try to _apply over iterables","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 feature collections","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"function _apply(f, ::Type{Target}, ::GI.FeatureCollectionTrait, fc; crs=GI.crs(fc)) where Target\n applicator(feature) = _apply(f, Target, feature; crs)::GI.Feature\n features = map(applicator, GI.getfeature(fc))\n return GI.FeatureCollection(features; crs)\nend","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"Rewrap features","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"function _apply(f, ::Type{Target}, ::GI.FeatureTrait, feature; crs=GI.crs(feature)) where Target\n properties = GI.properties(feature)\n geometry = _apply(f, Target, GI.geometry(feature); crs)\n return GI.Feature(geometry; properties, crs)\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; crs=GI.crs(geom))::(GI.geointerface_geomtype(trait)) where Target","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":"TODO handle zero length...","category":"page"},{"location":"source/primitives/","page":"Primitive functions","title":"Primitive functions","text":" applicator(g) = _apply(f, Target, g; crs)\n geoms = map(applicator, GI.getgeom(geom))\n return rebuild(geom, geoms; crs)\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":"_apply(f, ::Type{Target}, ::Trait, geom; crs=GI.crs(geom)) where {Target,Trait<:Target} = f(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":"_apply(f, ::Type{Target}, trait::GI.PointTrait, geom; crs=nothing) 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":"_apply(f, ::Type{GI.PointTrait}, trait::GI.PointTrait, geom; crs=nothing) = f(geom)\n_apply(f, ::Type{GI.FeatureTrait}, ::GI.FeatureTrait, feature; crs=GI.crs(feature)) = f(feature)\n_apply(f, ::Type{GI.FeatureCollectionTrait}, ::GI.FeatureCollectionTrait, fc; crs=GI.crs(fc)) = f(fc)\n\n\"\"\"\n unwrap(target::Type{<:AbstractTrait}, obj)\n unwrap(f, target::Type{<:AbstractTrait}, obj)\n\nUnwrap the geometry 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}, geom)\n\nLazily flatten any geometry, feature or iterator of geometries or features\nso that objects with the specified trait are returned by the iterator.\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\"\"\"\nreconstruct(geom, components) = first(_reconstruct(geom, components))\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\n 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\n 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\n 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))\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)\n else\n return T{false,false}(child_geoms; crs)\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","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\"\"\"\nfunction reproject(geom;\n source_crs=nothing, target_crs=nothing, transform=nothing, kw...\n)\n if isnothing(transform)\n source_crs = isnothing(source_crs) ? GeoInterface.crs(geom) : source_crs\n isnothing(source_crs) && throw(ArgumentError(\"geom has no crs attatched. Pass a `source_crs` keyword\"))\n 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)\n reproject(geom, transform; time, target_crs)\nend\nfunction reproject(geom, transform::Proj.Transformation; time=Inf, target_crs=nothing)\n if _is3d(geom)\n return apply(PointTrait, geom; crs=target_crs) 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) 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/overlaps/#Overlap-checks","page":"Overlap checks","title":"Overlap checks","text":"","category":"section"},{"location":"source/methods/overlaps/","page":"Overlap checks","title":"Overlap checks","text":"export overlaps","category":"page"},{"location":"source/methods/overlaps/","page":"Overlap checks","title":"Overlap checks","text":"This code checks whether geometries overlap with each other.","category":"page"},{"location":"source/methods/overlaps/","page":"Overlap checks","title":"Overlap checks","text":"It does not compute the overlap or intersection geometry.","category":"page"},{"location":"source/methods/overlaps/","page":"Overlap checks","title":"Overlap checks","text":"\"\"\"\n overlaps(geom1, geom2)::Bool\n\nCompare two Geometries of the same dimension and return true if their intersection set results in a geometry\ndifferent from both but of the same dimension. It applies to Polygon/Polygon, LineString/LineString,\nMultipoint/Multipoint, MultiLineString/MultiLineString and MultiPolygon/MultiPolygon.\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/overlaps/","page":"Overlap checks","title":"Overlap checks","text":"output","category":"page"},{"location":"source/methods/overlaps/","page":"Overlap checks","title":"Overlap checks","text":"true\n```\n\"\"\"\noverlaps(g1, g2)::Bool = overlaps(trait(g1), g1, trait(g2), g2)::Bool\noverlaps(t1::FeatureTrait, g1, t2, g2)::Bool = overlaps(GI.geometry(g1), g2)\noverlaps(t1, g1, t2::FeatureTrait, g2)::Bool = overlaps(g1, geometry(g2))\noverlaps(t1::FeatureTrait, g1, t2::FeatureTrait, g2)::Bool = overlaps(geometry(g1), geometry(g2))\noverlaps(::PolygonTrait, mp, ::MultiPolygonTrait, p)::Bool = overlaps(p, mp)\nfunction overlaps(::MultiPointTrait, g1, ::MultiPointTrait, g2)::Bool\n for p1 in GI.getpoint(g1)\n for p2 in GI.getpoint(g2)\n equals(p1, p2) && return true\n end\n end\nend\nfunction overlaps(::PolygonTrait, g1, ::PolygonTrait, g2)::Bool\n return line_intersects(g1, g2)\nend\nfunction overlaps(t1::MultiPolygonTrait, mp, t2::PolygonTrait, p1)::Bool\n for p2 in GI.getgeom(mp)\n overlaps(p1, thp2) && return true\n end\nend\nfunction overlaps(::MultiPolygonTrait, g1, ::MultiPolygonTrait, g2)::Bool\n for p1 in GI.getgeom(g1)\n overlaps(PolygonTrait(), mp, PolygonTrait(), p1) && return true\n end\nend","category":"page"},{"location":"source/methods/overlaps/","page":"Overlap checks","title":"Overlap checks","text":"","category":"page"},{"location":"source/methods/overlaps/","page":"Overlap checks","title":"Overlap 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/disjoint/#Disjointness-checks","page":"Disjointness checks","title":"Disjointness checks","text":"","category":"section"},{"location":"source/methods/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/disjoint/","page":"Disjointness checks","title":"Disjointness checks","text":"Examples","category":"page"},{"location":"source/methods/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/disjoint/","page":"Disjointness checks","title":"Disjointness checks","text":"output","category":"page"},{"location":"source/methods/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 !line_intersects(poly1, poly2)\nend","category":"page"},{"location":"source/methods/disjoint/","page":"Disjointness checks","title":"Disjointness checks","text":"","category":"page"},{"location":"source/methods/disjoint/","page":"Disjointness checks","title":"Disjointness checks","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/methods/crosses/#Crossing-checks","page":"Crossing checks","title":"Crossing checks","text":"","category":"section"},{"location":"source/methods/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/crosses/","page":"Crossing checks","title":"Crossing checks","text":"output","category":"page"},{"location":"source/methods/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 line_intersects(line1, line2; meets=MEETS_CLOSED)\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 line_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/crosses/","page":"Crossing checks","title":"Crossing checks","text":"","category":"page"},{"location":"source/methods/crosses/","page":"Crossing checks","title":"Crossing checks","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/methods/signed_distance/#Signed-distance","page":"Signed distance","title":"Signed distance","text":"","category":"section"},{"location":"source/methods/signed_distance/","page":"Signed distance","title":"Signed distance","text":"export signed_distance","category":"page"},{"location":"source/methods/signed_distance/","page":"Signed distance","title":"Signed distance","text":"TODO: clean this up. It already supports GeoInterface.","category":"page"},{"location":"source/methods/signed_distance/","page":"Signed distance","title":"Signed distance","text":"Base.@propagate_inbounds euclid_distance(p1, p2) = sqrt((GeoInterface.x(p2)-GeoInterface.x(p1))^2 + (GeoInterface.y(p2)-GeoInterface.y(p1))^2)\neuclid_distance(x1, y1, x2, y2) = sqrt((x2-x1)^2 + (y2-y1)^2)\n\n\n\n\" Distance from p0 to the line segment formed by p1 and p2. Implementation from Turf.jl.\"\nfunction _distance(p0, p1, p2)\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 if x1 < x2\n xfirst, yfirst = x1, y1\n xlast, ylast = x2, y2\n else\n xfirst, yfirst = x2, y2\n xlast, ylast = x1, y1\n end\n\n v = (xlast - xfirst, ylast - yfirst)\n w = (x0 - xfirst, y0 - yfirst)\n\n c1 = sum(w .* v)\n if c1 <= 0\n return euclid_distance(x0, y0, xfirst, yfirst)\n end\n\n c2 = sum(v .* v)\n\n if c2 <= c1\n return euclid_distance(x0, y0, xlast, ylast)\n end\n\n b2 = c1 / c2\n\n return euclid_distance(x0, y0, xfirst + (b2 * v[1]), yfirst + (b2 * v[2]))\nend\n\n\nfunction _distance(linestring, xy)\n mindist = typemax(Float64)\n N = GeoInterface.npoint(linestring)\n @assert N ≥ 3\n p1 = GeoInterface.getpoint(linestring, 1)\n p2 = p1\n\n for point_ind in 2:N\n p2 = GeoInterface.getpoint(linestring, point_ind)\n newdist = _distance(xy, p1, p2)\n if newdist < mindist\n mindist = newdist\n end\n p1 = p2\n end\n\n return mindist\nend\n\nfunction signed_distance(::GeoInterface.PolygonTrait, poly, x, y)\n\n xy = (x, y)\n mindist = _distance(GeoInterface.getexterior(poly), xy)\n\n @inbounds for hole in GeoInterface.gethole(poly)\n newdist = _distance(hole, xy)\n if newdist < mindist\n mindist = newdist\n end\n end\n\n if GeoInterface.contains(poly, GeoInterface.convert(Base.parentmodule(typeof(poly)), (x, y)))\n return mindist\n else\n return -mindist\n end\nend\n\nfunction signed_distance(::GeoInterface.MultiPolygonTrait, multipoly, x, y)\n distances = signed_distance.(GeoInterface.getpolygon(multipoly), x, y)\n max_val, max_ind = findmax(distances)\n return max_val\nend\n\n\n\"\"\"\n signed_distance(geom, x::Real, y::Real)::Float64\n\nCalculates the signed distance from the geometry `geom` to the point\ndefined by `(x, y)`. Points within `geom` have a negative distance,\nand points outside of `geom` have a positive distance.\n\nIf `geom` is a MultiPolygon, then this function returns the maximum distance\nto any of the polygons in `geom`.\n\"\"\"\nsigned_distance(geom, x, y) = signed_distance(GeoInterface.geomtrait(geom), geom, x, y)","category":"page"},{"location":"source/methods/signed_distance/","page":"Signed distance","title":"Signed distance","text":"","category":"page"},{"location":"source/methods/signed_distance/","page":"Signed distance","title":"Signed distance","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","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"These are all GeometryBasics.jl methods so far. They need to be converted to GeoInterface.","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"The reason that there is a centroid_and_signed_area function, is because in conputing the centroid, you end up computing the signed area.","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"In some computational geometry applications this may be a useful source of efficiency, so I added it here.","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"However, it's totally fine to ignore this and not have this code path. We simply need to decide on this.","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"function centroid(ls::GB.LineString{2, T}) where T\n centroid = Point{2, T}(0)\n total_area = T(0)\n if length(ls) == 1\n return sum(ls[1])/2\n end\n\n p0 = ls[1][1]\n\n for i in 1:(length(ls)-1)\n p1 = ls[i][2]\n p2 = ls[i+1][2]\n area = signed_area(p0, p1, p2)\n centroid = centroid .+ Point{2, T}((p0[1] + p1[1] + p2[1])/3, (p0[2] + p1[2] + p2[2])/3) * area\n total_area += area\n end\n return centroid ./ total_area\nend","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"a more optimized function, so we only calculate signed area once!","category":"page"},{"location":"source/methods/centroid/","page":"Centroid","title":"Centroid","text":"function centroid_and_signed_area(ls::GB.LineString{2, T}) where T\n centroid = Point{2, T}(0)\n total_area = T(0)\n if length(ls) == 1\n return sum(ls[1])/2\n end\n\n p0 = ls[1][1]\n\n for i in 1:(length(ls)-1)\n p1 = ls[i][2]\n p2 = ls[i+1][2]\n area = signed_area(p0, p1, p2)\n centroid = centroid .+ Point{2, T}((p0[1] + p1[1] + p2[1])/3, (p0[2] + p1[2] + p2[2])/3) * area\n total_area += area\n end\n return (centroid ./ total_area, total_area)\nend\n\nfunction centroid(poly::GB.Polygon{2, T}) where T\n exterior_centroid, exterior_area = centroid_and_signed_area(poly.exterior)\n\n total_area = exterior_area\n interior_numerator = Point{2, T}(0)\n for interior in poly.interiors\n interior_centroid, interior_area = centroid_and_signed_area(interior)\n total_area += interior_area\n interior_numerator += interior_centroid * interior_area\n end\n\n return (exterior_centroid * exterior_area - interior_numerator) / total_area\n\nend\n\nfunction centroid(multipoly::GB.MultiPolygon)\n centroids = centroid.(multipoly.polygons)\n areas = signed_area.(multipoly.polygons)\n areas ./= sum(areas)\n\n return sum(centroids .* areas) / sum(areas)\nend\n\n\nfunction centroid(rect::GB.Rect{N, T}) where {N, T}\n return Point{N, T}(rect.origin .- rect.widths ./ 2)\nend\n\nfunction centroid(sphere::GB.HyperSphere{N, T}) where {N, T}\n return sphere.center\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/methods/contains/#Containment","page":"Containment","title":"Containment","text":"","category":"section"},{"location":"source/methods/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/contains/","page":"Containment","title":"Containment","text":"output","category":"page"},{"location":"source/methods/contains/","page":"Containment","title":"Containment","text":"true\n```\n\"\"\"\ncontains(g1, g2)::Bool = within(g2, g1)","category":"page"},{"location":"source/methods/contains/","page":"Containment","title":"Containment","text":"","category":"page"},{"location":"source/methods/contains/","page":"Containment","title":"Containment","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\"\"\"\nfunction flip(geom)\n if _is3d(geom)\n return apply(PointTrait, geom) do p\n (GI.y(p), GI.x(p), GI.z(p))\n end\n else\n return apply(PointTrait, geom) 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/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\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- `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)\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":"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; kw...) = _simplify(DouglasPeucker(; kw...), data)\nsimplify(alg::SimplifyAlg, data) = _simplify(alg, data)\n\nfunction _simplify(alg::SimplifyAlg, data)\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)\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)))\nfunction _simplify(::LinearRingTrait, alg, geom)\n # Make a vector of points\n points = tuple_points(geom)\n\n # Simplify it once\n simple = _simplify(alg, points)\n\n return rebuild(geom, simple)\nend\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/within/#Containment/withinness","page":"Containment/withinness","title":"Containment/withinness","text":"","category":"section"},{"location":"source/methods/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/within/","page":"Containment/withinness","title":"Containment/withinness","text":"output","category":"page"},{"location":"source/methods/within/","page":"Containment/withinness","title":"Containment/withinness","text":"true\n```\n\"\"\"\nwithin(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, geometry(g2))\nwithin(::GI.PointTrait, g1, ::GI.LineStringTrait, 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)\nwithin(::GI.LineStringTrait, g1, ::GI.PolygonTrait, g2)::Bool = line_in_polygon(g1, g2)\nwithin(::GI.LineStringTrait, g1, ::GI.LineStringTrait, g2)::Bool = line_on_line(g1, g2)\nwithin(::GI.PolygonTrait, g1, ::GI.PolygonTrait, g2)::Bool = polygon_in_polygon(g1, g2)","category":"page"},{"location":"source/methods/within/","page":"Containment/withinness","title":"Containment/withinness","text":"","category":"page"},{"location":"source/methods/within/","page":"Containment/withinness","title":"Containment/withinness","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/methods/signed_area/#Signed-area","page":"Signed area","title":"Signed area","text":"","category":"section"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"export signed_area","category":"page"},{"location":"source/methods/signed_area/#What-is-signed-area?","page":"Signed area","title":"What is signed area?","text":"","category":"section"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"Signed area is simply the integral over the exterior path of a polygon, minus the sum of integrals over its interior holes.","category":"page"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"It is signed such that a clockwise path has a positive area, and a counterclockwise path has a negative area.","category":"page"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"To provide an example, consider this rectangle:","category":"page"},{"location":"source/methods/signed_area/","page":"Signed area","title":"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/signed_area/","page":"Signed area","title":"Signed area","text":"This is clearly a rectangle, etc. But now let's look at how the points look:","category":"page"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"lines!(a, rect; color = 1:length(coordinates(rect))+1)\nf","category":"page"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"The points are ordered in a clockwise fashion, which means that the signed area is positive. If we reverse the order of the points, we get a negative area.","category":"page"},{"location":"source/methods/signed_area/#Implementation","page":"Signed area","title":"Implementation","text":"","category":"section"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"This is the GeoInterface-compatible implementation.","category":"page"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"First, we implement a wrapper method that dispatches to the correct implementation based on the geometry trait.","category":"page"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"This is also used in the implementation, since it's a lot less work!","category":"page"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"\"\"\"\n signed_area(geom)::Real\n\nReturns the signed area of the geometry, based on winding order.\n\"\"\"\nsigned_area(x) = signed_area(GI.trait(x), x)","category":"page"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"TODOS here:","category":"page"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"This could conceivably be multithreaded. How to indicate that it should be so?\nWhat to do for corner cases (nan point, etc)?","category":"page"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"function signed_area(::Union{GI.LineStringTrait,GI.LinearRingTrait}, geom)","category":"page"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"Basically, we integrate the area under the line string, which gives us the signed area.","category":"page"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":" point₁ = GI.getpoint(geom, 1)\n point₂ = GI.getpoint(geom, 2)\n area = GI.x(point₁) * GI.y(point₂) - GI.y(point₁) * GI.x(point₂)\n for point in GI.getpoint(geom)","category":"page"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"Advance the point buffers by 1 point","category":"page"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":" point₁ = point₂\n point₂ = point","category":"page"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"Accumulate the area into area","category":"page"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":" area += GI.x(point₁) * GI.y(point₂) - GI.y(point₁) * GI.x(point₂)\n end\n area /= 2\n return area\nend","category":"page"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"This subtracts the","category":"page"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"function signed_area(::GI.PolygonTrait, geom)\n s_area = signed_area(GI.getexterior(geom))\n area = abs(s_area)\n for hole in GI.gethole(geom)\n area -= abs(signed_area(hole))\n end\n return area * sign(s_area)\nend\n\nsigned_area(::GI.MultiPolygonTrait, geom) = sum((signed_area(poly) for poly in GI.getpolygon(geom)))","category":"page"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"This should theoretically work for anything, but I haven't actually tested yet!","category":"page"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"Below is the original GeometryBasics implementation:","category":"page"},{"location":"source/methods/signed_area/#julia","page":"Signed area","title":"```julia","text":"","category":"section"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"function signed_area(a::Point{2, T}, b::Point{2, T}, c::Point{2, T}) where T return ((b[1] - a[1]) * (c[2] - a[2]) - (c[1] - a[1]) * (b[2] - a[2])) / 2 end","category":"page"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"function signed_area(points::AbstractVector{<: Point{2, T}}) where {T} area = sum((points[i][1] * points[i+1][2] - points[i][2] * points[i+1][1] for i in 1:(length(points)-1))) / 2.0 end","category":"page"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"function signedarea(ls::GeometryBasics.LineString) # coords = GeometryBasics.decompose(Point2f, ls) return sum((p1[1] * p2[2] - p1[2] * p2[1] for (p1, p2) in ls)) / 2.0#signedarea(coords) end","category":"page"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"function signedarea(poly::GeometryBasics.Polygon{2}) sarea = signedarea(poly.exterior) area = abs(sarea) for hole in poly.interiors area -= abs(signedarea(hole)) end return area * sign(sarea) end","category":"page"},{"location":"source/methods/signed_area/#WARNING:-this-may-not-do-what-you-expect,-since-it's","page":"Signed area","title":"WARNING: this may not do what you expect, since it's","text":"","category":"section"},{"location":"source/methods/signed_area/#sensitive-to-winding-order.-Use-GeoInterface.area-instead.","page":"Signed area","title":"sensitive to winding order. Use GeoInterface.area instead.","text":"","category":"section"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"signedarea(mp::MultiPolygon) = sum(signedarea.(mp.polygons)) ```","category":"page"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"","category":"page"},{"location":"source/methods/signed_area/","page":"Signed area","title":"Signed area","text":"This page was generated using Literate.jl.","category":"page"},{"location":"source/methods/intersects/#Intersection-checks","page":"Intersection checks","title":"Intersection checks","text":"","category":"section"},{"location":"source/methods/intersects/","page":"Intersection checks","title":"Intersection checks","text":"export intersects, intersection","category":"page"},{"location":"source/methods/intersects/","page":"Intersection checks","title":"Intersection checks","text":"This code checks whether geometries intersect with each other.","category":"page"},{"location":"source/methods/intersects/","page":"Intersection checks","title":"Intersection checks","text":"note: Note\nThis does not compute intersections, only checks if they exist.","category":"page"},{"location":"source/methods/intersects/","page":"Intersection checks","title":"Intersection checks","text":"const MEETS_OPEN = 1\nconst MEETS_CLOSED = 0\n\n\"\"\"\n line_intersects(line_a, line_b)\n\nCheck if `line_a` intersects with `line_b`.\n\nThese can be `LineTrait`, `LineStringTrait` or `LinearRingTrait`\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.line_intersects(line1, line2)","category":"page"},{"location":"source/methods/intersects/","page":"Intersection checks","title":"Intersection checks","text":"output","category":"page"},{"location":"source/methods/intersects/","page":"Intersection checks","title":"Intersection checks","text":"true\n```\n\"\"\"\nline_intersects(a, b; kw...) = line_intersects(trait(a), a, trait(b), b; kw...)","category":"page"},{"location":"source/methods/intersects/","page":"Intersection checks","title":"Intersection checks","text":"Skip to_edges for LineTrait","category":"page"},{"location":"source/methods/intersects/","page":"Intersection checks","title":"Intersection checks","text":"function line_intersects(::GI.LineTrait, a, ::GI.LineTrait, b; meets=MEETS_OPEN)\n a1 = _tuple_point(GI.getpoint(a, 1))\n b1 = _tuple_point(GI.getpoint(b, 1))\n a2 = _tuple_point(GI.getpoint(a, 2))\n b2 = _tuple_point(GI.getpoint(b, 2))\n return ExactPredicates.meet(a1, a2, b1, b2) == meets\nend\nfunction line_intersects(::GI.AbstractTrait, a, ::GI.AbstractTrait, b; kw...)\n edges_a, edges_b = map(sort! ∘ to_edges, (a, b))\n return line_intersects(edges_a, edges_b; kw...)\nend\nfunction line_intersects(edges_a::Vector{Edge}, edges_b::Vector{Edge}; meets=MEETS_OPEN)","category":"page"},{"location":"source/methods/intersects/","page":"Intersection checks","title":"Intersection checks","text":"Extents.intersects(toextent(edgesa), toextent(edgesb)) || return false","category":"page"},{"location":"source/methods/intersects/","page":"Intersection checks","title":"Intersection checks","text":" for edge_a in edges_a\n for edge_b in edges_b\n ExactPredicates.meet(edge_a..., edge_b...) == meets && return true\n end\n end\n return false\nend\n\n\"\"\"\n line_intersection(line_a, line_b)\n\nFind a point that intersects LineStrings with two coordinates each.\n\nReturns `nothing` if no point is found.\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.line_intersection(line1, line2)","category":"page"},{"location":"source/methods/intersects/","page":"Intersection checks","title":"Intersection checks","text":"output","category":"page"},{"location":"source/methods/intersects/","page":"Intersection checks","title":"Intersection checks","text":"(125.58375366067547, -14.83572303404496)\n```\n\"\"\"\nline_intersection(line_a, line_b) = line_intersection(trait(line_a), line_a, trait(line_b), line_b)\nfunction line_intersection(::GI.AbstractTrait, a, ::GI.AbstractTrait, b)\n Extents.intersects(GI.extent(a), GI.extent(b)) || return nothing\n result = Tuple{Float64,Float64}[]\n edges_a, edges_b = map(sort! ∘ to_edges, (a, b))\n for edge_a in edges_a\n for edge_b in edges_b\n x = _line_intersection(edge_a, edge_b)\n isnothing(x) || push!(result, x)\n end\n end\n return result\nend\nfunction line_intersection(::GI.LineTrait, line_a, ::GI.LineTrait, line_b)\n a1 = GI.getpoint(line_a, 1)\n b1 = GI.getpoint(line_b, 1)\n a2 = GI.getpoint(line_a, 2)\n b2 = GI.getpoint(line_b, 2)\n\n return _line_intersection((a1, a2), (b1, b2))\nend\nfunction _line_intersection((p11, p12)::Tuple, (p21, p22)::Tuple)","category":"page"},{"location":"source/methods/intersects/","page":"Intersection checks","title":"Intersection checks","text":"Get points from lines","category":"page"},{"location":"source/methods/intersects/","page":"Intersection checks","title":"Intersection checks","text":" x1, y1 = GI.x(p11), GI.y(p11)\n x2, y2 = GI.x(p12), GI.y(p12)\n x3, y3 = GI.x(p21), GI.y(p21)\n x4, y4 = GI.x(p22), GI.y(p22)\n\n d = ((y4 - y3) * (x2 - x1)) - ((x4 - x3) * (y2 - y1))\n a = ((x4 - x3) * (y1 - y3)) - ((y4 - y3) * (x1 - x3))\n b = ((x2 - x1) * (y1 - y3)) - ((y2 - y1) * (x1 - x3))\n\n if d == 0\n if a == 0 && b == 0\n return nothing\n end\n return nothing\n end\n\n ã = a / d\n b̃ = b / d\n\n if ã >= 0 && ã <= 1 && b̃ >= 0 && b̃ <= 1\n x = x1 + (ã * (x2 - x1))\n y = y1 + (ã * (y2 - y1))\n return (x, y)\n end\n\n return nothing\nend","category":"page"},{"location":"source/methods/intersects/","page":"Intersection checks","title":"Intersection checks","text":"","category":"page"},{"location":"source/methods/intersects/","page":"Intersection checks","title":"Intersection checks","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\nconst TuplePoint = Tuple{Float64,Float64}\nconst Edge = Tuple{TuplePoint,TuplePoint}\n\n\"\"\"\n to_edges()\n\nConvert any geometry or collection of geometries into a flat\nvector of `Tuple{Tuple{Float64,Float64},{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_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 counter-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)\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\n\nequals(geo1, geo2) = _equals(trait(geo1), geo1, trait(geo2), geo2)\n\n_equals(::T, geo1, ::T, geo2) where T = error(\"Cant compare $T yet\")\nfunction _equals(::T, p1, ::T, p2) where {T<:PointTrait}\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\nfunction _equals(::T, l1, ::T, l2) where {T<:AbstractCurveTrait}","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"Check line lengths match","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":" GI.npoint(l1) == GI.npoint(l2) || return false","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":"Then check all points are the same","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":" for (p1, p2) in zip(GI.getpoint(l1), GI.getpoint(l2))\n equals(p1, p2) || return false\n end\n return true\nend\n_equals(t1, geo1, t2, geo2) = false","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_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(point, GI.getexterior(poly); ignore_boundary, check_extent=false) || 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(point, ring; ignore_boundary=!ignore_boundary) && return false\n end\n return true\nend\nfunction point_in_polygon(\n ::PointTrait, pt,\n ::Union{LineStringTrait,LinearRingTrait}, ring;\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 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 on non-closed rings","category":"page"},{"location":"source/methods/bools/","page":"Boolean conditions","title":"Boolean conditions","text":" l = if GI.x(p_start) == GI.x(p_end) && GI.y(p_start) == GI.y(p_end)\n l = n - 1\n else\n n\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:l - 1\n j = i + 1\n\n p_i = GI.getpoint(ring, i)\n p_j = GI.getpoint(ring, j)\n xi = GI.x(p_i)\n yi = GI.y(p_i)\n xj = GI.x(p_j)\n yj = GI.y(p_j)\n\n on_boundary = (GI.y(pt) * (xi - xj) + yi * (xj - GI.x(pt)) + yj * (GI.x(pt) - xi) == 0) &&\n ((xi - GI.x(pt)) * (xj - GI.x(pt)) <= 0) && ((yi - GI.y(pt)) * (yj - GI.y(pt)) <= 0)\n\n on_boundary && return !ignore_boundary\n\n intersects = ((yi > GI.y(pt)) !== (yj > GI.y(pt))) &&\n (GI.x(pt) < (xj - xi) * (GI.y(pt) - yi) / (yj - yi) + xi)\n\n if intersects\n inside = !inside\n end\n end\n\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)\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":" line_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/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 on obj to `Tuple`s.\n\"\"\"\nfunction tuples(geom)\n if _is3d(geom)\n return apply(PointTrait, geom) do p\n (GI.x(p), GI.y(p), GI.z(p))\n end\n else\n return apply(PointTrait, geom) do p\n (GI.x(p), 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":"","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._distance-Tuple{Any, Any, Any}","page":"Home","title":"GeometryOps._distance","text":"Distance from p0 to the line segment formed by p1 and p2. Implementation from Turf.jl.\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; crs)\n\nReconstruct a geometry or feature using the function f on the target trait.\n\nf(target_geom) => x where x also has the target trait, or an equivalent.\n\nThe result is an functionally similar geometry with values depending on f\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.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.flatten-Union{Tuple{Target}, Tuple{Type{Target}, Any}} where Target<:GeoInterface.AbstractTrait","page":"Home","title":"GeometryOps.flatten","text":"flatten(target::Type{<:GI.AbstractTrait}, geom)\n\nLazily flatten any geometry, feature or iterator of geometries or features so that objects with the specified trait are returned by the iterator.\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\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.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.line_intersection-Tuple{Any, Any}","page":"Home","title":"GeometryOps.line_intersection","text":"line_intersection(line_a, line_b)\n\nFind a point that intersects LineStrings with two coordinates each.\n\nReturns nothing if no point is found.\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.line_intersection(line1, line2)\n\n# output\n(125.58375366067547, -14.83572303404496)\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.line_intersects-Tuple{Any, Any}","page":"Home","title":"GeometryOps.line_intersects","text":"line_intersects(line_a, line_b)\n\nCheck if line_a intersects with line_b.\n\nThese can be LineTrait, LineStringTrait or LinearRingTrait\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.line_intersects(line1, line2)\n\n# output\ntrue\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. It applies to Polygon/Polygon, LineString/LineString, Multipoint/Multipoint, MultiLineString/MultiLineString and MultiPolygon/MultiPolygon.\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.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\n-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.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.signed_area-Tuple{Any}","page":"Home","title":"GeometryOps.signed_area","text":"signed_area(geom)::Real\n\nReturns the signed area of the geometry, based on winding order.\n\n\n\n\n\n","category":"method"},{"location":"#GeometryOps.signed_distance-Tuple{Any, Any, Any}","page":"Home","title":"GeometryOps.signed_distance","text":"signed_distance(geom, x::Real, y::Real)::Float64\n\nCalculates the signed distance from the geometry geom to the point defined by (x, y). Points within geom have a negative distance, and points outside of geom have a positive distance.\n\nIf geom is a MultiPolygon, then this function returns the maximum distance to any of the polygons in geom.\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)\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\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},{Float64,Float64}} edges.\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 geometry 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, geom)::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 a) must not intersect the exterior of the secondary (geometry b). 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 e796b572c..8a9293fb2 100644 --- a/dev/source/GeometryOps/index.html +++ b/dev/source/GeometryOps/index.html @@ -5,6 +5,7 @@ using GeometryBasics import Proj using LinearAlgebra +import ExactPredicates using GeoInterface.Extents: Extents @@ -20,6 +21,10 @@ include("methods/centroid.jl") include("methods/intersects.jl") include("methods/contains.jl") +include("methods/crosses.jl") +include("methods/disjoint.jl") +include("methods/overlaps.jl") +include("methods/within.jl") include("methods/polygonize.jl") include("methods/barycentric.jl") @@ -27,4 +32,4 @@ include("transformations/simplify.jl") include("transformations/reproject.jl") -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 cd4a60903..d644a6778 100644 --- a/dev/source/methods/barycentric/index.html +++ b/dev/source/methods/barycentric/index.html @@ -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 64056b463..c46209662 100644 --- a/dev/source/methods/bools/index.html +++ b/dev/source/methods/bools/index.html @@ -6,11 +6,13 @@ Take a ring and return true or false whether or not the ring is clockwise or counter-clockwise. -# Examples +# Example + ```jldoctest import GeoInterface as GI, GeometryOps as GO -line = GI.LineString([(0, 0), (1, 1), (1, 0), (0, 0)]) -GO.isclockwise(line)

      output

      true
      +
      +ring = GI.LinearRing([(0, 0), (1, 1), (1, 0), (0, 0)])
      +GO.isclockwise(ring)

      output

      true
       ```
       """
       isclockwise(geom)::Bool = isclockwise(GI.trait(geom), geom)
      @@ -32,6 +34,7 @@
       # Examples
       ```jldoctest
       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
       ```
      @@ -39,9 +42,7 @@
       function isconcave(poly)::Bool
           sign = false
       
      -    exterior = GI.getexterior(poly)
      -    GI.npoint(exterior) <= 4 && return false
      -
      +    exterior = GI.getexterior(poly)

      FIXME handle not closed polygons

          GI.npoint(exterior) <= 4 && return false
           n = GI.npoint(exterior) - 1
       
           for i in 1:n
      @@ -69,28 +70,24 @@
           return false
       end
       
      +equals(geo1, geo2) = _equals(trait(geo1), geo1, trait(geo2), geo2)
       
      -function equals(geo1, geo2)
      -    GI.geomtrait(geo1) !== GI.geomtrait(geo2) && return false
      -
      -    GI.geomtrait(geo1) isa PointTrait && return compare_points(geo1, geo2)
      -    GI.geomtrait(geo1) isa LineStringTrait && return compare_lines(geo1, geo2)
      -
      -    error("Cant compare $(GI.trait(geo1)) and $(GI.trait(geo2)) yet")
      +_equals(::T, geo1, ::T, geo2) where T = error("Cant compare $T yet")
      +function _equals(::T, p1, ::T, p2) where {T<:PointTrait}
      +    GI.ncoord(p1) == GI.ncoord(p2) || return false
      +    GI.x(p1) == GI.x(p2) || return false
      +    GI.y(p1) == GI.y(p2) || return false
      +    if GI.is3d(p1)
      +        GI.z(p1) == GI.z(p2) || return false
      +    end
      +    return true
       end
      -
      -function compare_points(p1, p2)
      -    length(p1) !== length(p2) && return false
      -
      -    for i in eachindex(p1)
      -        round(p1[i]; digits=10) !== round(p2[i]; digits=10) && return false
      +function _equals(::T, l1, ::T, l2) where {T<:AbstractCurveTrait}

      Check line lengths match

          GI.npoint(l1) == GI.npoint(l2) || return false

      Then check all points are the same

          for (p1, p2) in zip(GI.getpoint(l1), GI.getpoint(l2))
      +        equals(p1, p2) || return false
           end
      -
           return true
       end
      -
      -function compare_lines(p1::Vector, p2::Vector)

      TODO: complete this

          length(p1[1]) !== length(p2[1]) && return false
      -end

      """ parallel(line1::LineString, line2::LineString)::Bool

      Return true if each segment of line1 is parallel to the correspondent segment of line2

      Examples

      import GeoInterface as GI, GeometryOps as GO
      +_equals(t1, geo1, t2, geo2) = false

      """ isparallel(line1::LineString, line2::LineString)::Bool

      Return true if each segment of line1 is parallel to the correspondent segment of line2

      Examples

      import GeoInterface as GI, GeometryOps as GO
       julia> line1 = GI.LineString([(9.170356, 45.477985), (9.164434, 45.482551), (9.166644, 45.484003)])
       GeoInterface.Wrappers.LineString{false, false, Vector{Tuple{Float64, Float64}}, Nothing, Nothing}([(9.170356, 45.477985), (9.164434, 45.482551), (9.166644, 45.484003)], nothing, nothing)
       
      @@ -106,15 +103,17 @@
           _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, ignoreEndVertices::Bool=false)::Bool
      +    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
      +
       ```jldoctest
       import GeoInterface as GI, GeometryOps as GO
      -point = GI.Point(1, 1)
      +
      +point = (1, 1)
       line = GI.LineString([(0, 0), (3, 3), (4, 4)])
       GO.point_on_line(point, line)

      output

      true
       ```
      @@ -123,25 +122,25 @@
           line_points = tuple_points(line)
           n = length(line_points)
       
      -    ignore = :none
      +    exclude_boundary = :none
           for i in 1:n - 1
      -        if ignore_end_vertices == true
      +        if ignore_end_vertices
                   if i === 1
      -                ignore = :start
      +                exclude_boundary = :start
                   elseif i === n - 2
      -                ignore = :end
      +                exclude_boundary = :end
                   elseif (i === 1 && i + 1 === n - 1)
      -                ignore = :both
      +                exclude_boundary = :both
                   end
               end
      -        if point_on_segment(line_points[i], line_points[i + 1], point, ignore)
      +        if point_on_segment(point, (line_points[i], line_points[i + 1]); exclude_boundary)
                   return true
               end
           end
           return false
       end
       
      -function point_on_segment(start, stop, point, exclude_boundary::Symbol=:none)::Bool
      +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)
      @@ -175,45 +174,50 @@
       end
       
       """
      -    point_in_polygon(point::Point, polygon::Union{Polygon, MultiPolygon}, ignoreBoundary::Bool=false)::Bool
      +    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]]])
      +poly = GI.Polygon([[(-81, 41), (-81, 47), (-72, 47), (-72, 41), (-81, 41)]])
       GO.point_in_polygon(point, poly)

      output

      true
       ```
       """
      -function point_in_polygon(p, polygon, ignore_boundary::Bool=false)::Bool
      -    GI.trait(polygon) isa PolygonTrait || throw(ArgumentError("Not a polygon"))
      -
      -    point_in_extent(p, GI.extent(polygon)) || return false
      -    point_in_ring(p, GI.getexterior(polygon), ignore_boundary) || return false
      -
      -    for ring in GI.gethole(polygon)
      -        point_in_ring(pt, ring, !ignore_boundary) && return false
      +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_ring(pt, ring, ignore_boundary::Bool=false)
      -    GI.trait(ring) isa Union{LineStringTrait,LinearRingTrait} || throw(ArgumentError("Not a ring"))
      -    inside = false
      +function point_in_polygon(
      +    ::PointTrait, pt,
      +    ::Union{LineStringTrait,LinearRingTrait}, ring;
      +    ignore_boundary::Bool=false,
      +    check_extent::Bool=false,
      +)::Bool

      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)
      -    p1 = first(GI.getpoint(ring))
      -    p_end = GI.getpoint(ring, n)
      -
      -    l = if GI.x(p1) == GI.x(p_end) && GI.y(p1) == GI.y(p_end)
      -        l = n -1
      +    p_start = GI.getpoint(ring, 1)
      +    p_end = GI.getpoint(ring, n)

      Handle closed on non-closed rings

          l = if GI.x(p_start) == GI.x(p_end) && GI.y(p_start) == GI.y(p_end)
      +        l = n - 1
           else
               n
      -    end
      -
      -    for i in 1:l - 1
      +    end

      Loop over all points in the ring

          for i in 1:l - 1
               j = i + 1
       
               p_i = GI.getpoint(ring, i)
      @@ -240,99 +244,40 @@
       end
       
       function point_in_extent(p, extent::Extents.Extent)
      -    extent.X[1] <= GI.x(p) && extent.Y[1] <= GI.y(p) &&
      -        extent.X[2] >= GI.x(p) && extent.Y[2] >= GI.y(p)
      -end
      -
      -function line_in_polygon(poly, line)
      -    out = false
      -
      -    polybox = bbox(poly)
      -    linebox = bbox(line)
      -
      -    !(bboxOverlap(polybox, linebox)) && return false
      -
      -    coords = line.coordinates
      -
      -    for i in 1:length(coords) - 1
      -        mid = [(coords[i][1] + coords[i + 1][1]) / 2, (coords[i][2] + coords[i + 1][2]) / 2]
      -        if point_in_polygon(Point(mid), poly, true)
      -            out = true
      -            break
      -        end
      -    end
      -    return out
      +    (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)
      -        point_on_line(p, line2) || return false
      +    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(::LineStringTrait, line, ::PolygonTrait, poly)
      -    polybox = bbox(poly)
      -    linebox = bbox(line)
      -
      -    !(bboxOverlap(polybox, linebox)) && return false
      +function line_in_polygon(
      +    ::AbstractCurveTrait, line,
      +    ::Union{AbstractPolygonTrait,LinearRingTrait}, poly
      +)
      +    Extents.intersects(GI.extent(poly), GI.extent(line)) || return false
       
      -    coords = line.coordinates
           inside = false
      -
      -    for i in 1:length(coords) - 1
      -        !(point_in_polygon(Point(coords[i]), poly)) && return false
      -        !inside && (inside = point_in_polygon(Point(coords[i]), poly, true))
      +    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
      -            mid = [(coords[i][1] + coords[i + 1][1]) / 2, (coords[i][2] + coords[i + 1][2]) / 2]
      -            inside = point_in_polygon(Point(mid), poly, true)
      +            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

      TODO - why were there two methods for this in Turf.jl?

      function polygon_in_polygon(ft1, ft2, reverse::Bool=false)
      -    polybox1 = bbox(ft1)
      -    polybox2 = bbox(ft2)
      -    coords = []
      -
      -    if reverse
      -        !(bbox_overlap(polybox2, polybox1)) && return false
      -
      -        for point in GI.getpoint(ft1)
      -            !(point_in_polygon(point, ft2)) && return false
      -        end
      -    else
      -        !(bbox_overlap(polybox1, polybox2)) && return false
      -
      -        for point in GI.getpoint(ft2)
      -            !(point_in_polygon(point, ft1)) && return false
      -        end
      -    end
      -
      -    return true
       end
      -function poly_in_poly(poly1, poly2)
      -
      -    for point in GI.getpoint(poly1)
      -        (point_in_polygon(point, poly2)) && return true
      -    end
      -
      -    for point in GI.getpoint(poly2)
      -        (point_in_polygon(point, poly1)) && return true
      -    end
       
      -    inter = line_intersects(polygon_to_line(poly1), polygon_to_line(poly2))
      -    inter != nothing && return true
      -
      -    return false
      -
      -end
      -
      -function bbox_overlap(box1::Vector{T}, box2::Vector{T}) where {T <: Real}
      -    box1[1] > box2[1] && return false
      -    box1[3] < box2[3] && return false
      -    box1[2] > box2[2] && return false
      -    box1[4] < box2[4] && return false
      -    return true
      -end

      This page was generated using Literate.jl.

      +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

           line_intersects(poly1, poly2) && return false

      poly1 must be in poly2

           return true
      + 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 5c16e6380..714ce14b6 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

      These are all GeometryBasics.jl methods so far. They need to be converted to GeoInterface.

      The reason that there is a centroid_and_signed_area function, is because in conputing the centroid, you end up computing the signed area.

      In some computational geometry applications this may be a useful source of efficiency, so I added it here.

      However, it's totally fine to ignore this and not have this code path. We simply need to decide on this.

      function centroid(ls::LineString{2, T}) where T
      +Centroid · GeometryOps.jl

      Centroid

      export centroid

      These are all GeometryBasics.jl methods so far. They need to be converted to GeoInterface.

      The reason that there is a centroid_and_signed_area function, is because in conputing the centroid, you end up computing the signed area.

      In some computational geometry applications this may be a useful source of efficiency, so I added it here.

      However, it's totally fine to ignore this and not have this code path. We simply need to decide on this.

      function centroid(ls::GB.LineString{2, T}) where T
           centroid = Point{2, T}(0)
           total_area = T(0)
           if length(ls) == 1
      @@ -16,7 +16,7 @@
               total_area += area
           end
           return centroid ./ total_area
      -end

      a more optimized function, so we only calculate signed area once!

      function centroid_and_signed_area(ls::LineString{2, T}) where T
      +end

      a more optimized function, so we only calculate signed area once!

      function centroid_and_signed_area(ls::GB.LineString{2, T}) where T
           centroid = Point{2, T}(0)
           total_area = T(0)
           if length(ls) == 1
      @@ -35,7 +35,7 @@
           return (centroid ./ total_area, total_area)
       end
       
      -function centroid(poly::GeometryBasics.Polygon{2, T}) where T
      +function centroid(poly::GB.Polygon{2, T}) where T
           exterior_centroid, exterior_area = centroid_and_signed_area(poly.exterior)
       
           total_area = exterior_area
      @@ -50,7 +50,7 @@
       
       end
       
      -function centroid(multipoly::MultiPolygon)
      +function centroid(multipoly::GB.MultiPolygon)
           centroids = centroid.(multipoly.polygons)
           areas = signed_area.(multipoly.polygons)
           areas ./= sum(areas)
      @@ -59,10 +59,10 @@
       end
       
       
      -function centroid(rect::Rect{N, T}) where {N, T}
      +function centroid(rect::GB.Rect{N, T}) where {N, T}
           return Point{N, T}(rect.origin .- rect.widths ./ 2)
       end
       
      -function centroid(sphere::HyperSphere{N, T}) where {N, T}
      +function centroid(sphere::GB.HyperSphere{N, T}) where {N, T}
           return sphere.center
      -end

      This page was generated using Literate.jl.

      +end

      This page was generated using Literate.jl.

      diff --git a/dev/source/methods/contains/index.html b/dev/source/methods/contains/index.html index 0f0184801..1cdced22d 100644 --- a/dev/source/methods/contains/index.html +++ b/dev/source/methods/contains/index.html @@ -1,63 +1,22 @@ -Containment · GeometryOps.jl

      Containment

      export contains

      This currently works for point-in-linestring or point-in-polygon.

      More GeometryBasics code

      _cross(p1, p2, p3) = (GI.x(p1) - GI.x(p3)) * (GI.y(p2) - GI.y(p3)) - (GI.x(p2) - GI.x(p3)) * (GI.y(p1) - GI.y(p3))
      +Containment · GeometryOps.jl

      Containment

      export contains
       
       """
      -    contains(pointlist, point)::Bool
      +    contains(ft1::AbstractGeometry, ft2::AbstractGeometry)::Bool
       
      -Returns `true` if `point` is contained in `pointlist` (geometrically, not as a set)
      -,and  `false` otherwise.
      -"""
      -contains(pointlist, point) = contains(GI.trait(pointlist), GI.trait(point), pointlist, point)

      Implementation of a point-in-polygon algorithm from Luxor.jl. This is the Hormann-Agathos (2001) algorithm.

      For the source, see the code from Luxor.jl.

      function contains(::Union{GI.LineStringTrait, GI.LinearRingTrait}, ::GI.PointTrait, pointlist, point)
      -    n = GI.npoint(pointlist)
      -    c = false
      -    q1 = GI.getpoint(pointlist, 1)
      -    q2 = GI.getpoint(pointlist, 1)
      -    @inbounds for (counter, current_point) in enumerate(Iterators.drop(GI.getpoint(pointlist), 1))
      -        q1 = q2
      -        # if reached last point, set "next point" to first point.
      -        #
      -        if counter == (n-1)
      -            q2 = GI.getpoint(pointlist, 1)
      -        else
      -            q2 = current_point
      -        end
      -        if GI.x(q1) == GI.x(point) && GI.x(q1) == GI.y(point)
      -            # allowonedge || error("isinside(): VertexException a")
      -            continue
      -        end
      -        if GI.y(q2) == GI.y(point)
      -            if GI.x(q2) == GI.x(point)
      -                # allowonedge || error("isinside(): VertexException b")
      -                continue
      -            elseif (GI.y(q1) == GI.y(point)) && ((GI.x(q2) > GI.x(point)) == (GI.x(q1) < GI.x(point)))
      -                # allowonedge || error("isinside(): EdgeException")
      -                continue
      -            end
      -        end
      -        if (GI.y(q1) < GI.y(point)) != (GI.y(q2) < GI.y(point)) # crossing
      -            if GI.x(q1) >= GI.x(point)
      -                if GI.x(q2) > GI.x(point)
      -                    c = !c
      -                elseif ((_cross(q1, q2, point) > 0) == (GI.y(q2) > GI.y(q1)))
      -                    c = !c
      -                end
      -            elseif GI.x(q2) > GI.x(point)
      -                if ((_cross(q1, q2, point) > 0) == (GI.y(q2) > GI.y(q1)))
      -                    c = !c
      -                end
      -            end
      -        end
      -    end
      -    return c
      +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
       
      -end
      +```jldoctest
      +import GeometryOps as GO, GeoInterface as GI
      +line = GI.LineString([(1, 1), (1, 2), (1, 3), (1, 4)])
      +point = (1, 2)
       
      -function contains(poly::Polygon{2, T1}, point::Point{2, T2}) where {T1, T2}
      -    c = contains(poly.exterior, point)
      -    for interior in poly.interiors
      -        if contains(interior, point)
      -            return false
      -        end
      -    end
      -    return c
      -end

      TODOs: implement contains for mesh, simplex, and 3d objects (eg rect, triangle, etc.)

      contains(mp::MultiPolygon{2, T1}, point::Point{2, T2}) where {T1, T2} = any((contains(poly, point) for poly in mp.polygons))

      This page was generated using Literate.jl.

      +GO.contains(line, point)

      output

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

      This page was generated using Literate.jl.

      diff --git a/dev/source/methods/crosses/index.html b/dev/source/methods/crosses/index.html index 87dec0168..b588b59f4 100644 --- a/dev/source/methods/crosses/index.html +++ b/dev/source/methods/crosses/index.html @@ -1,50 +1,48 @@ Crossing checks · GeometryOps.jl

      Crossing checks

      """
      -     crosses(ft1::AbstractGeometry, ft2::AbstractGeometry)::Bool
      +     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
      -```jldoctest
      -julia> line = LineString([[1, 1], [1, 2], [1, 3], [1, 4]])
      -LineString(Array{Float64,1}[[1.0, 1.0], [1.0, 2.0], [1.0, 3.0], [1.0, 4.0]])
      +```julia
      +import GeoInterface as GI, GeometryOps as GO
       
      -julia> line2 = LineString([[-2, 2], [4, 2]])
      -LineString(Array{Float64,1}[[-2.0, 2.0], [4.0, 2.0]])
      +line1 = GI.LineString([(1, 1), (1, 2), (1, 3), (1, 4)])
      +line2 = GI.LineString([(-2, 2), (4, 2)])
       
      -julia> crosses(line2, line)
      -true
      +GO.crosses(line1, line2)

      output

      true
       ```
       """
       crosses(g1, g2)::Bool = crosses(trait(g1), g1, trait(g2), g2)::Bool
       crosses(t1::FeatureTrait, g1, t2, g2)::Bool = crosses(GI.geometry(g1), g2)
       crosses(t1, g1, t2::FeatureTrait, g2)::Bool = crosses(g1, geometry(g2))
      -crosses(::MultiPointTrait, g1::LineStringTrait, , g2)::Bool = multipoint_cross_line(g1, g2)
      -crosses(::MultiPointTrait, g1::PolygonTrait, , g2)::Bool = multipoint_cross_poly(g1, g2)
      -crosses(::LineStringTrait, g1, ::MultiPointTrait, g2)::Bool = multipoint_cross_lines(g2, g1)
      -crosses(::LineStringTrait, g1, ::PolygonTrait, g2)::Bool = line_cross_poly(g1, g2)
      -crosses(::LineStringTrait, g1, ::LineStringTrait, g2)::Bool = line_cross_line(g1, g2)
      -crosses(::PolygonTrait, g1, ::MultiPointTrait, g2)::Bool = multipoint_cross_poly(g2, g1)
      -crosses(::PolygonTrait, g1, ::LineStringTrait, g2)::Bool = line_cross_poly(g2, g1)
      +crosses(::MultiPointTrait, g1, ::LineStringTrait, g2)::Bool = multipoint_crosses_line(g1, g2)
      +crosses(::MultiPointTrait, g1, ::PolygonTrait, g2)::Bool = multipoint_crosses_poly(g1, g2)
      +crosses(::LineStringTrait, g1, ::MultiPointTrait, g2)::Bool = multipoint_crosses_lines(g2, g1)
      +crosses(::LineStringTrait, g1, ::PolygonTrait, g2)::Bool = line_crosses_poly(g1, g2)
      +crosses(::LineStringTrait, g1, ::LineStringTrait, g2)::Bool = line_crosses_line(g1, g2)
      +crosses(::PolygonTrait, g1, ::MultiPointTrait, g2)::Bool = multipoint_crosses_poly(g2, g1)
      +crosses(::PolygonTrait, g1, ::LineStringTrait, g2)::Bool = line_crosses_poly(g2, g1)
       
      -function multipoint_cross_line(geom1, geom2)
      +function multipoint_crosses_line(geom1, geom2)
           int_point = false
           ext_point = false
           i = 1
           np2 = GI.npoint(geom2)
       
      -    while i < GI.npoint(geom1) && !intPoint && !extPoint
      +    while i < GI.npoint(geom1) && !int_point && !ext_point
               for j in 1:GI.npoint(geom2) - 1
      -            inc_vertices = (j === 1 || j === np2 - 2) ? :none : :both
      -
      -            if is_point_on_segment(GI.getpoint(geom2, j), GI.getpoint(geom2.coordinates, j + 1), GI.getpoint(geom1, i), inc_vertices)
      +            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)
                       int_point = true
                   else
                       ext_point = true
                   end
      -
               end
               i += 1
           end
      @@ -52,32 +50,30 @@
           return int_point && ext_point
       end
       
      -function line_cross_line(line1, line2)
      -    inter = intersection(line1, line2)
      -
      +function line_crosses_line(line1, line2)
           np2 = GI.npoint(line2)
      -    if !isnothing(inter)
      +    if line_intersects(line1, line2; meets=MEETS_CLOSED)
               for i in 1:GI.npoint(line1) - 1
                   for j in 1:GI.npoint(line2) - 1
      -                inc_vertices = (j === 1 || j === np2 - 2) ? :none : :both
      +                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)
      -                is_point_on_segment(pa, pb, p, inc_vertices) && return true
      +                point_on_segment(p, (pa, pb); exclude_boundary) && return true
                   end
               end
           end
           return false
       end
       
      -function line_cross_poly(line, poly) =
      -
      -    for line in flatten(AbstractCurveTrait, poly)
      -        intersects(line)
      +function line_crosses_poly(line, poly)
      +    for l in flatten(AbstractCurveTrait, poly)
      +        line_intersects(line, l) && return true
           end
      +    return false
       end
       
      -function multipoint_cross_poly(mp, poly)
      +function multipoint_crosses_poly(mp, poly)
           int_point = false
           ext_point = false
       
      @@ -87,7 +83,7 @@
               else
                   ext_point = true
               end
      -        in_point && ext_point && return true
      +        int_point && ext_point && return true
           end
           return false
      -end

      This page was generated using Literate.jl.

      +end

      This page was generated using Literate.jl.

      diff --git a/dev/source/methods/disjoint/index.html b/dev/source/methods/disjoint/index.html index 23f5a98c5..5b0a4db80 100644 --- a/dev/source/methods/disjoint/index.html +++ b/dev/source/methods/disjoint/index.html @@ -3,24 +3,32 @@ disjoint(geom1, geom2)::Bool Return `true` if the intersection of the two geometries is an empty set.

      Examples

      ```jldoctest
      -julia> poly = Polygon([[[-1, 2], [3, 2], [3, 3], [-1, 3], [-1, 2]]])
      -Polygon(Array{Array{Float64,1},1}[[[-1.0, 2.0], [3.0, 2.0], [3.0, 3.0], [-1.0, 3.0], [-1.0, 2.0]]])
      +import GeometryOps as GO, GeoInterface as GI
       
      -julia> point = Point([1, 1])
      -Point([1.0, 1.0])
      -
      -julia> disjoint(poly, point)
      -true
      +poly = GI.Polygon([[(-1, 2), (3, 2), (3, 3), (-1, 3), (-1, 2)]])
      +point = (1, 1)
      +GO.disjoint(poly, point)

      output

      true
       ```
       """
      -disjoint(t1::FeatureTrait, g1, t2, g2)::Bool = disjoint(GI.geometry(g1), g2)
      -disjoint(t1, g1, t2::FeatureTrait, g2)::Bool = disjoint(g1, geometry(g2))
      -disjoint(t1::PointTrait, g1, t2::PointTrait, g2)::Bool = !point_equals_point(g1, g2)
      -disjoint(t1::PointTrait, g1, t2::LineStringTrait, g2)::Bool = !point_on_line(g1, g2)
      -disjoint(t1::PointTrait, g1, t2::PolygonTrait, g2)::Bool = !point_in_polygon(g1, g2)
      -disjoint(t1::LineStringTrait, g1, t2::PointTrait, g2)::Bool = !point_on_line(g2, g1)
      -disjoint(t1::LineStringTrait, g1, t2::LineStringTrait, g2)::Bool = !line_on_line(g1, g2)
      -disjoint(t1::LineStringTrait, g1, t2::PolygonTrait, g2)::Bool = !line_in_polygon(g2, g1)
      -disjoint(t1::PolygonTrait, g1, t2::PointTrait, g2)::Bool = !point_in_polygon(g2, g1)
      -disjoint(t1::PolygonTrait, g1, t2::LineStringTrait, g2)::Bool = !line_in_polygon(g2, g1)
      -disjoint(t1::PolygonTrait, g1, t2::PolygonTrait, g2)::Bool = !poly_in_poly(g2, g1)

      This page was generated using Literate.jl.

      +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 + end + for point in GI.getpoint(poly2) + point_in_polygon(point, poly1) && return false + end + return !line_intersects(poly1, poly2) +end

      This page was generated using Literate.jl.

      diff --git a/dev/source/methods/intersects/index.html b/dev/source/methods/intersects/index.html index 3a8592b5f..c35479697 100644 --- a/dev/source/methods/intersects/index.html +++ b/dev/source/methods/intersects/index.html @@ -1,56 +1,82 @@ -Intersection checks · GeometryOps.jl

      Intersection checks

      export intersects, intersection

      This code checks whether geometries intersect with each other.

      Note

      This does not compute intersections, only checks if they exist.

      """
      -    intersects(line_a, line_b)
      +Intersection checks · GeometryOps.jl

      Intersection checks

      export intersects, intersection

      This code checks whether geometries intersect with each other.

      Note

      This does not compute intersections, only checks if they exist.

      const MEETS_OPEN = 1
      +const MEETS_CLOSED = 0
      +
      +"""
      +    line_intersects(line_a, line_b)
       
       Check if `line_a` intersects with `line_b`.
       
       These can be `LineTrait`, `LineStringTrait` or `LinearRingTrait`
      +
      +# Example
      +
      +```jldoctest
      +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.line_intersects(line1, line2)

      output

      true
      +```
       """
      -intersects(a, b) = isnothing(intersection) # Probably faster ways to do this
      +line_intersects(a, b; kw...) = line_intersects(trait(a), a, trait(b), b; kw...)

      Skip to_edges for LineTrait

      function line_intersects(::GI.LineTrait, a, ::GI.LineTrait, b; meets=MEETS_OPEN)
      +    a1 = _tuple_point(GI.getpoint(a, 1))
      +    b1 = _tuple_point(GI.getpoint(b, 1))
      +    a2 = _tuple_point(GI.getpoint(a, 2))
      +    b2 = _tuple_point(GI.getpoint(b, 2))
      +    return ExactPredicates.meet(a1, a2, b1, b2) == meets
      +end
      +function line_intersects(::GI.AbstractTrait, a, ::GI.AbstractTrait, b; kw...)
      +    edges_a, edges_b = map(sort! ∘ to_edges, (a, b))
      +    return line_intersects(edges_a, edges_b; kw...)
      +end
      +function line_intersects(edges_a::Vector{Edge}, edges_b::Vector{Edge}; meets=MEETS_OPEN)

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

          for edge_a in edges_a
      +        for edge_b in edges_b
      +            ExactPredicates.meet(edge_a..., edge_b...) == meets && return true
      +        end
      +    end
      +    return false
      +end
       
       """
      -    intersection(line_a, line_b)
      +    line_intersection(line_a, line_b)
       
       Find a point that intersects LineStrings with two coordinates each.
       
      -Returns `nothing` if no point is found.

      Examples

      ```jldoctest
      -import GeoInterface as GI
      -import GeometryOps as GO
      +Returns `nothing` if no point is found.
      +
      +# Example
      +
      +```jldoctest
      +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)
      +GO.line_intersection(line1, line2)

      output

      (125.58375366067547, -14.83572303404496)
       ```
       """
      -intersection(line_a, line_b) = intersection(trait(line_a), line_a, trait(line_b), line_b)
      -function intersection(
      -    ::Union{LineStringTrait,LinearRingTrait}, line_a,
      -    ::Union{LineStringTrait,LinearRingTrait}, line_b,
      -)
      -    result = Tuple{Float64,Float64}[] # TODO handle 3d, and other Real ?
      -    a1 = GI.getpoint(line_a, 1)
      -    b1 = GI.getpoint(line_b, 1)

      TODO we can check all of these against the extent of line_b and continue the loop if theyre outside

          for i in 1:GI.npoint(line_a) - 1
      -        for j in 1:GI.npoint(line_b) - 1
      -            a2 = GI.getpoint(line_a, i + 1)
      -            b2 = GI.getpoint(line_b, j + 1)
      -            inter = _intersection((a1, a2), (b1, b2))
      -            isnothing(inter) || push!(result, inter)
      -            a1 = a2
      -            b1 = b2
      +line_intersection(line_a, line_b) = line_intersection(trait(line_a), line_a, trait(line_b), line_b)
      +function line_intersection(::GI.AbstractTrait, a, ::GI.AbstractTrait, b)
      +    Extents.intersects(GI.extent(a), GI.extent(b)) || return nothing
      +    result = Tuple{Float64,Float64}[]
      +    edges_a, edges_b = map(sort! ∘ to_edges, (a, b))
      +    for edge_a in edges_a
      +        for edge_b in edges_b
      +            x = _line_intersection(edge_a, edge_b)
      +            isnothing(x) || push!(result, x)
               end
           end
      -    return unique!(result)
      +    return result
       end
      -
      -function intersection(::LineTrait, line_a, ::LineTrait, line_b)
      +function line_intersection(::GI.LineTrait, line_a, ::GI.LineTrait, line_b)
           a1 = GI.getpoint(line_a, 1)
           b1 = GI.getpoint(line_b, 1)
           a2 = GI.getpoint(line_a, 2)
           b2 = GI.getpoint(line_b, 2)
       
      -    return _intersection((a1, a2), (b1, b2))
      +    return _line_intersection((a1, a2), (b1, b2))
       end
      -
      -function _intersection((p11, p12)::Tuple, (p21, p22)::Tuple)

      Get points from lines

          x1, y1 = GI.x(p11), GI.y(p11)
      +function _line_intersection((p11, p12)::Tuple, (p21, p22)::Tuple)

      Get points from lines

          x1, y1 = GI.x(p11), GI.y(p11)
           x2, y2 = GI.x(p12), GI.y(p12)
           x3, y3 = GI.x(p21), GI.y(p21)
           x4, y4 = GI.x(p22), GI.y(p22)
      @@ -76,4 +102,4 @@
           end
       
           return nothing
      -end

      This page was generated using Literate.jl.

      +end

      This page was generated using Literate.jl.

      diff --git a/dev/source/methods/overlaps/index.html b/dev/source/methods/overlaps/index.html index 1e299fdc2..7b12a6374 100644 --- a/dev/source/methods/overlaps/index.html +++ b/dev/source/methods/overlaps/index.html @@ -8,19 +8,18 @@ # Examples ```jldoctest -julia> poly1 = Polygon([[[0,0],[0,5],[5,5],[5,0],[0,0]]]) -Polygon(Array{Array{Float64,1},1}[[[0.0, 0.0], [0.0, 5.0], [5.0, 5.0], [5.0, 0.0], [0.0, 0.0]]]) +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)]]) -julia> poly2 = Polygon([[[1,1],[1,6],[6,6],[6,1],[1,1]]]) -Polygon(Array{Array{Float64,1},1}[[[1.0, 1.0], [1.0, 6.0], [6.0, 6.0], [6.0, 1.0], [1.0, 1.0]]]) - -julia> overlap(poly1, poly2) -true +GO.overlaps(poly1, poly2)

      output

      true
       ```
       """
       overlaps(g1, g2)::Bool = overlaps(trait(g1), g1, trait(g2), g2)::Bool
       overlaps(t1::FeatureTrait, g1, t2, g2)::Bool = overlaps(GI.geometry(g1), g2)
       overlaps(t1, g1, t2::FeatureTrait, g2)::Bool = overlaps(g1, geometry(g2))
      +overlaps(t1::FeatureTrait, g1, t2::FeatureTrait, g2)::Bool = overlaps(geometry(g1), geometry(g2))
      +overlaps(::PolygonTrait, mp, ::MultiPolygonTrait, p)::Bool = overlaps(p, mp)
       function overlaps(::MultiPointTrait, g1, ::MultiPointTrait, g2)::Bool
           for p1 in GI.getpoint(g1)
               for p2 in GI.getpoint(g2)
      @@ -29,19 +28,15 @@
           end
       end
       function overlaps(::PolygonTrait, g1, ::PolygonTrait, g2)::Bool
      -    line1 = polygon_to_line(geom1)
      -    line2 = polygon_to_line(geom2)
      -
      -    intersection(line1, line2)
      +    return line_intersects(g1, g2)
       end
      -overlaps(::PolygonTrait, mp, ::MultiPointTrait, p)::Bool = overlaps(p, mp)
      -function overlaps(t1::MultiPolygonTrait, mp, t2::Polygon, p1)::Bool
      +function overlaps(t1::MultiPolygonTrait, mp, t2::PolygonTrait, p1)::Bool
           for p2 in GI.getgeom(mp)
      -        overlaps(p1, p2)
      +        overlaps(p1, thp2) && return true
           end
       end
       function overlaps(::MultiPolygonTrait, g1, ::MultiPolygonTrait, g2)::Bool
           for p1 in GI.getgeom(g1)
      -        overlaps(PolygonTrait(), mp, PolygonTrait(), p1)
      +        overlaps(PolygonTrait(), mp, PolygonTrait(), p1) && return true
           end
      -end

      This page was generated using Literate.jl.

      +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 eb75606d8..c2b88efcd 100644 --- a/dev/source/methods/polygonize/index.html +++ b/dev/source/methods/polygonize/index.html @@ -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/methods/signed_area/index.html b/dev/source/methods/signed_area/index.html index 82edf8aa4..3714f2eb1 100644 --- a/dev/source/methods/signed_area/index.html +++ b/dev/source/methods/signed_area/index.html @@ -10,7 +10,7 @@ Returns the signed area of the geometry, based on winding order. """ -signed_area(x) = signed_area(GI.trait(x), x)

      TODOS here:

      1. This could conceivably be multithreaded. How to indicate that it should be so?
      2. What to do for corner cases (nan point, etc)?
      function signed_area(::Union{LineStringTrait, LinearRingTrait}, geom)

      Basically, we integrate the area under the line string, which gives us the signed area.

          point₁ = GI.getpoint(geom, 1)
      +signed_area(x) = signed_area(GI.trait(x), x)

      TODOS here:

      1. This could conceivably be multithreaded. How to indicate that it should be so?
      2. What to do for corner cases (nan point, etc)?
      function signed_area(::Union{GI.LineStringTrait,GI.LinearRingTrait}, geom)

      Basically, we integrate the area under the line string, which gives us the signed area.

          point₁ = GI.getpoint(geom, 1)
           point₂ = GI.getpoint(geom, 2)
           area = GI.x(point₁) * GI.y(point₂) - GI.y(point₁) * GI.x(point₂)
           for point in GI.getpoint(geom)

      Advance the point buffers by 1 point

              point₁ = point₂
      @@ -18,7 +18,7 @@
           end
           area /= 2
           return area
      -end

      This subtracts the

      function signed_area(::PolygonTrait, geom)
      +end

      This subtracts the

      function signed_area(::GI.PolygonTrait, geom)
           s_area = signed_area(GI.getexterior(geom))
           area = abs(s_area)
           for hole in GI.gethole(geom)
      @@ -27,4 +27,4 @@
           return area * sign(s_area)
       end
       
      -signed_area(::MultiPolygonTrait, geom) = sum((signed_area(poly) for poly in GI.getpolygon(geom)))

      This should theoretically work for anything, but I haven't actually tested yet!

      Below is the original GeometryBasics implementation:

      ```julia

      function signed_area(a::Point{2, T}, b::Point{2, T}, c::Point{2, T}) where T return ((b[1] - a[1]) * (c[2] - a[2]) - (c[1] - a[1]) * (b[2] - a[2])) / 2 end

      function signed_area(points::AbstractVector{<: Point{2, T}}) where {T} area = sum((points[i][1] * points[i+1][2] - points[i][2] * points[i+1][1] for i in 1:(length(points)-1))) / 2.0 end

      function signedarea(ls::GeometryBasics.LineString) # coords = GeometryBasics.decompose(Point2f, ls) return sum((p1[1] * p2[2] - p1[2] * p2[1] for (p1, p2) in ls)) / 2.0#signedarea(coords) end

      function signedarea(poly::GeometryBasics.Polygon{2}) sarea = signedarea(poly.exterior) area = abs(sarea) for hole in poly.interiors area -= abs(signedarea(hole)) end return area * sign(sarea) end

      WARNING: this may not do what you expect, since it's

      sensitive to winding order. Use GeoInterface.area instead.

      signedarea(mp::MultiPolygon) = sum(signedarea.(mp.polygons)) ```


      This page was generated using Literate.jl.

      +signed_area(::GI.MultiPolygonTrait, geom) = sum((signed_area(poly) for poly in GI.getpolygon(geom)))

      This should theoretically work for anything, but I haven't actually tested yet!

      Below is the original GeometryBasics implementation:

      ```julia

      function signed_area(a::Point{2, T}, b::Point{2, T}, c::Point{2, T}) where T return ((b[1] - a[1]) * (c[2] - a[2]) - (c[1] - a[1]) * (b[2] - a[2])) / 2 end

      function signed_area(points::AbstractVector{<: Point{2, T}}) where {T} area = sum((points[i][1] * points[i+1][2] - points[i][2] * points[i+1][1] for i in 1:(length(points)-1))) / 2.0 end

      function signedarea(ls::GeometryBasics.LineString) # coords = GeometryBasics.decompose(Point2f, ls) return sum((p1[1] * p2[2] - p1[2] * p2[1] for (p1, p2) in ls)) / 2.0#signedarea(coords) end

      function signedarea(poly::GeometryBasics.Polygon{2}) sarea = signedarea(poly.exterior) area = abs(sarea) for hole in poly.interiors area -= abs(signedarea(hole)) end return area * sign(sarea) end

      WARNING: this may not do what you expect, since it's

      sensitive to winding order. Use GeoInterface.area instead.

      signedarea(mp::MultiPolygon) = sum(signedarea.(mp.polygons)) ```


      This page was generated using Literate.jl.

      diff --git a/dev/source/methods/signed_distance/index.html b/dev/source/methods/signed_distance/index.html index 5bd627633..888254e72 100644 --- a/dev/source/methods/signed_distance/index.html +++ b/dev/source/methods/signed_distance/index.html @@ -93,4 +93,4 @@ If `geom` is a MultiPolygon, then this function returns the maximum distance to any of the polygons in `geom`. """ -signed_distance(geom, x, y) = signed_distance(GeoInterface.geomtrait(geom), geom, x, y)

      This page was generated using Literate.jl.

      +signed_distance(geom, x, y) = signed_distance(GeoInterface.geomtrait(geom), geom, x, y)

      This page was generated using Literate.jl.

      diff --git a/dev/source/methods/within/index.html b/dev/source/methods/within/index.html index 4c040e2c5..b6f9b9187 100644 --- a/dev/source/methods/within/index.html +++ b/dev/source/methods/within/index.html @@ -12,21 +12,18 @@ # Examples ```jldoctest setup=:(using GeometryOps, GeometryBasics) -julia> line = LineString([[1, 1], [1, 2], [1, 3], [1, 4]]) -LineString(Array{Float64,1}[[1.0, 1.0], [1.0, 2.0], [1.0, 3.0], [1.0, 4.0]]) +import GeometryOps as GO, GeoInterface as GI -julia> point = Point([1, 2]) -Point([1.0, 2.0]) - -julia> within(point, line) -true +line = GI.LineString([(1, 1), (1, 2), (1, 3), (1, 4)]) +point = (1, 2) +GO.within(point, line)

      output

      true
       ```
       """
       within(g1, g2)::Bool = within(trait(g1), g1, trait(g2), g2)::Bool
      -within(t1::FeatureTrait, g1, t2, g2)::Bool = within(GI.geometry(g1), g2)
      -within(t1, g1, t2::FeatureTrait, g2)::Bool = within(g1, geometry(g2))
      -within(t1::PointTrait, g1::LineStringTrait, t2, g2)::Bool = point_on_line(ft1, ft2, true)
      -within(t1::PointTrait, g1, t2::PolygonTrait, g2)::Bool = point_in_polygon(ft1, ft2, true)
      -within(t1::LineStringTrait, g1, t2::PolygonTrait, g2)::Bool = line_in_polygon(ft1, ft2)
      -within(t1::LineStringTrait, g1, t2::LineStringTrait, g2)::Bool = line_on_line(ft1, ft2)
      -within(t1::PolygonTrait, g1, t2::PolygonTrait, g2)::Bool = polygon_in_polygon(ft1, ft2, true)

      This page was generated using Literate.jl.

      +within(::GI.FeatureTrait, g1, ::Any, g2)::Bool = within(GI.geometry(g1), g2) +within(::Any, g1, t2::GI.FeatureTrait, g2)::Bool = within(g1, geometry(g2)) +within(::GI.PointTrait, g1, ::GI.LineStringTrait, 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) +within(::GI.LineStringTrait, g1, ::GI.PolygonTrait, g2)::Bool = line_in_polygon(g1, g2) +within(::GI.LineStringTrait, g1, ::GI.LineStringTrait, g2)::Bool = line_on_line(g1, g2) +within(::GI.PolygonTrait, g1, ::GI.PolygonTrait, g2)::Bool = polygon_in_polygon(g1, g2)

      This page was generated using Literate.jl.

      diff --git a/dev/source/primitives/index.html b/dev/source/primitives/index.html index 86ca9b249..00ae8e906 100644 --- a/dev/source/primitives/index.html +++ b/dev/source/primitives/index.html @@ -152,4 +152,4 @@ end function rebuild(trait::GI.PolygonTrait, geom::GB.Polygon, child_geoms; crs=nothing) Polygon(child_geoms[1], child_geoms[2:end]) -end

      This page was generated using Literate.jl.

      +end

      This page was generated using Literate.jl.

      diff --git a/dev/source/transformations/flip/index.html b/dev/source/transformations/flip/index.html index aa67b651f..260a379c7 100644 --- a/dev/source/transformations/flip/index.html +++ b/dev/source/transformations/flip/index.html @@ -16,4 +16,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 3454ccd64..037543a66 100644 --- a/dev/source/transformations/reproject/index.html +++ b/dev/source/transformations/reproject/index.html @@ -53,4 +53,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 4271bf967..6cbf17a1b 100644 --- a/dev/source/transformations/simplify/index.html +++ b/dev/source/transformations/simplify/index.html @@ -377,4 +377,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/tuples/index.html b/dev/source/transformations/tuples/index.html index 8bc04c46d..aa8affb04 100644 --- a/dev/source/transformations/tuples/index.html +++ b/dev/source/transformations/tuples/index.html @@ -14,4 +14,4 @@ (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/utils/index.html b/dev/source/utils/index.html index ef98748f6..8071fecc1 100644 --- a/dev/source/utils/index.html +++ b/dev/source/utils/index.html @@ -3,4 +3,125 @@ _is3d(::GI.AbstractGeometryTrait, geom) = GI.is3d(geom) _is3d(::GI.FeatureTrait, feature) = _is3d(GI.geometry(feature)) _is3d(::GI.FeatureCollectionTrait, fc) = _is3d(GI.getfeature(fc, 1)) -_is3d(::Nothing, geom) = _is3d(first(geom)) # Otherwise step into an itererable

      This page was generated using Literate.jl.

      +_is3d(::Nothing, geom) = _is3d(first(geom)) # Otherwise step into an itererable + +_npoint(x) = _npoint(trait(x), x) +_npoint(::Nothing, xs::AbstractArray) = sum(_npoint, xs) +_npoint(::GI.FeatureCollectionTrait, fc) = sum(_npoint, GI.getfeature(fc)) +_npoint(::GI.FeatureTrait, f) = _npoint(GI.geometry(f)) +_npoint(::GI.AbstractGeometryTrait, x) = GI.npoint(trait(x), x) + +_nedge(x) = _nedge(trait(x), x) +_nedge(::Nothing, xs::AbstractArray) = sum(_nedge, xs) +_nedge(::GI.FeatureCollectionTrait, fc) = sum(_nedge, GI.getfeature(fc)) +_nedge(::GI.FeatureTrait, f) = _nedge(GI.geometry(f)) +function _nedge(::GI.AbstractGeometryTrait, x) + n = 0 + for g in GI.getgeom(x) + n += _nedge(g) + end + return n +end +_nedge(::GI.AbstractCurveTrait, x) = GI.npoint(x) - 1 +_nedge(::GI.PointTrait, x) = error("Cant get edges from points") + + +""" + polygon_to_line(poly::Polygon) + +Converts a Polygon to LineString or MultiLineString

      Examples

      ```jldoctest
      +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)
      +```
      +"""
      +function polygon_to_line(poly)
      +    @assert GI.trait(poly) isa PolygonTrait
      +    GI.ngeom(poly) > 1 && return GI.MultiLineString(collect(GI.getgeom(poly)))
      +    return GI.LineString(collect(GI.getgeom(GI.getgeom(poly, 1))))
      +end
      +
      +
      +const TuplePoint = Tuple{Float64,Float64}
      +const Edge = Tuple{TuplePoint,TuplePoint}
      +
      +"""
      +    to_edges()
      +
      +Convert any geometry or collection of geometries into a flat
      +vector of `Tuple{Tuple{Float64,Float64},{Float64,Float64}}` edges.
      +"""
      +function to_edges(x)
      +    edges = Vector{Edge}(undef, _nedge(x))
      +    _to_edges!(edges, x, 1)
      +    return edges
      +end
      +
      +_to_edges!(edges::Vector, x, n) = _to_edges!(edges, trait(x), x, n)
      +function _to_edges!(edges::Vector, ::GI.FeatureCollectionTrait, fc, n)
      +    for f in GI.getfeature(fc)
      +        n = _to_edges!(edges, f, n)
      +    end
      +end
      +_to_edges!(edges::Vector, ::GI.FeatureTrait, f, n) = _to_edges!(edges, GI.geometry(f), n)
      +function _to_edges!(edges::Vector, ::GI.AbstractGeometryTrait, fc, n)
      +    for f in GI.getgeom(fc)
      +        n = _to_edges!(edges, f, n)
      +    end
      +end
      +function _to_edges!(edges::Vector, ::GI.AbstractCurveTrait, geom, n)
      +    p1 = GI.getpoint(geom, 1)
      +    p1x, p1y = GI.x(p1), GI.y(p1)
      +    for i in 2:GI.npoint(geom)
      +        p2 = GI.getpoint(geom, i)
      +        p2x, p2y = GI.x(p2), GI.y(p2)
      +        edges[n] = (p1x, p1y), (p2x, p2y)
      +        p1x, p1y = p2x, p2y
      +        n += 1
      +    end
      +    return n
      +end
      +
      +_tuple_point(p) = GI.x(p), GI.y(p)
      +
      +function to_extent(edges::Vector{Edge})
      +    x, y = extrema(first, edges)
      +    Extents.Extent(X=x, Y=y)
      +end
      +
      +function to_extent(edges::Vector{Edge})
      +    x, y = extrema(first, edges)
      +    Extents.Extent(X=x, Y=y)
      +end
      +
      +function to_points(xs)
      +    points = Vector{TuplePoint}(undef, _npoint(x))
      +    _to_points!(points, x, 1)
      +    return points
      +end
      +
      +_to_points!(points::Vector, x, n) = _to_points!(points, trait(x), x, n)
      +function _to_points!(points::Vector, ::FeatureCollectionTrait, fc, n)
      +    for f in GI.getfeature(fc)
      +        n = _to_points!(points, f, n)
      +    end
      +end
      +_to_points!(points::Vector, ::FeatureTrait, f, n) = _to_points!(points, GI.geometry(f), n)
      +function _to_points!(points::Vector, ::AbstractGeometryTrait, fc, n)
      +    for f in GI.getgeom(fc)
      +        n = _to_points!(points, f, n)
      +    end
      +end
      +function _to_points!(points::Vector, ::Union{AbstractCurveTrait,MultiPointTrait}, geom, n)
      +    p1 = GI.getpoint(geom, 1)
      +    p1x, p1y = GI.x(p1), GI.y(p1)
      +    for i in 2:GI.npoint(geom)
      +        p2 = GI.getpoint(geom, i)
      +        p2x, p2y = GI.x(p2), GI.y(p2)
      +        points[n] = (p1x, p1y), (p2x, p2y)
      +        p1 = p2
      +        n += 1
      +    end
      +    return n
      +end

      This page was generated using Literate.jl.