From a67ff8c22d72c3d9348146687c97054859b24b27 Mon Sep 17 00:00:00 2001 From: Anshul Singhvi Date: Mon, 5 Feb 2024 12:05:59 -0500 Subject: [PATCH] Move the GeoInterface.jl code to an extension Removes the issue of circular dependencies for GeoInterfaceMakie --- Project.toml | 12 +- ext/GeometryBasicsGeoInterfaceExt.jl | 164 +++++++++++++++++++++++++++ src/GeometryBasics.jl | 1 - src/geointerface.jl | 159 +------------------------- 4 files changed, 177 insertions(+), 159 deletions(-) create mode 100644 ext/GeometryBasicsGeoInterfaceExt.jl diff --git a/Project.toml b/Project.toml index a4592512..dcba184c 100644 --- a/Project.toml +++ b/Project.toml @@ -1,18 +1,23 @@ name = "GeometryBasics" uuid = "5c1252a2-5f33-56bf-86c9-59e7332b4326" authors = ["SimonDanisch "] -version = "0.4.10" +version = "0.4.11" [deps] EarCut_jll = "5ae413db-bbd1-5e63-b57d-d24a61df00f5" Extents = "411431e0-e8b7-467b-b5e0-f676ba4f2910" -GeoInterface = "cf35fbd7-0cd7-5166-be24-54bfbe79505f" IterTools = "c8e1da08-722c-5040-9ed9-7db0dc04731e" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" StructArrays = "09ab397b-f2b6-538f-b94a-2f83cf4a842a" Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" +[weakdeps] +GeoInterface = "cf35fbd7-0cd7-5166-be24-54bfbe79505f" + +[extensions] +GeometryBasicsGeoInterfaceExt = "GeoInterface" + [compat] Aqua = "0.8" EarCut_jll = "2" @@ -32,9 +37,10 @@ julia = "1.6" [extras] Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" GeoJSON = "61d90e0f-e114-555e-ac52-39dfb47a3ef9" +GeoInterface = "cf35fbd7-0cd7-5166-be24-54bfbe79505f" OffsetArrays = "6fe1bfb0-de20-5000-8ca7-80f57d26f881" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] -test = ["Aqua", "GeoJSON", "Test", "Random", "OffsetArrays"] +test = ["Aqua", "GeoInterface", "GeoJSON", "Test", "Random", "OffsetArrays"] diff --git a/ext/GeometryBasicsGeoInterfaceExt.jl b/ext/GeometryBasicsGeoInterfaceExt.jl new file mode 100644 index 00000000..ce490a3d --- /dev/null +++ b/ext/GeometryBasicsGeoInterfaceExt.jl @@ -0,0 +1,164 @@ +module GeometryBasicsGeoInterfaceExt + +using GeoInterface, GeometryBasics + +import GeometryBasics: geointerface_geomtype + +# Implementation of trait based interface from https://github.com/JuliaGeo/GeoInterface.jl/ + +GeoInterface.isgeometry(::Type{<:AbstractGeometry}) = true +GeoInterface.isgeometry(::Type{<:AbstractFace}) = true +GeoInterface.isgeometry(::Type{<:AbstractPoint}) = true +GeoInterface.isgeometry(::Type{<:AbstractMesh}) = true +GeoInterface.isgeometry(::Type{<:AbstractPolygon}) = true +GeoInterface.isgeometry(::Type{<:LineString}) = true +GeoInterface.isgeometry(::Type{<:MultiPoint}) = true +GeoInterface.isgeometry(::Type{<:MultiLineString}) = true +GeoInterface.isgeometry(::Type{<:MultiPolygon}) = true +GeoInterface.isgeometry(::Type{<:Mesh}) = true + +GeoInterface.geomtrait(::Point) = PointTrait() +GeoInterface.geomtrait(::Line) = LineTrait() +GeoInterface.geomtrait(::LineString) = LineStringTrait() +GeoInterface.geomtrait(::Polygon) = PolygonTrait() +GeoInterface.geomtrait(::MultiPoint) = MultiPointTrait() +GeoInterface.geomtrait(::MultiLineString) = MultiLineStringTrait() +GeoInterface.geomtrait(::MultiPolygon) = MultiPolygonTrait() +GeoInterface.geomtrait(::GeometryBasics.Ngon) = PolygonTrait() +GeoInterface.geomtrait(::AbstractMesh) = PolyhedralSurfaceTrait() + +# GeoInterface calls this method in `GeoInterface.convert(GeometryBasics, ...)` +geointerface_geomtype(::GeoInterface.PointTrait) = Point +geointerface_geomtype(::GeoInterface.MultiPointTrait) = MultiPoint +geointerface_geomtype(::GeoInterface.LineTrait) = Line +geointerface_geomtype(::GeoInterface.LineStringTrait) = LineString +geointerface_geomtype(::GeoInterface.MultiLineStringTrait) = MultiLineString +geointerface_geomtype(::GeoInterface.PolygonTrait) = Polygon +geointerface_geomtype(::GeoInterface.MultiPolygonTrait) = MultiPolygon +geointerface_geomtype(::GeoInterface.PolyhedralSurfaceTrait) = Mesh + +GeoInterface.geomtrait(::GeometryBasics.Simplex{Dim,T,1}) where {Dim,T} = PointTrait() +GeoInterface.geomtrait(::GeometryBasics.Simplex{Dim,T,2}) where {Dim,T} = LineStringTrait() +GeoInterface.geomtrait(::GeometryBasics.Simplex{Dim,T,3}) where {Dim,T} = PolygonTrait() + +GeoInterface.ncoord(::PointTrait, g::GeometryBasics.Point) = length(g) +GeoInterface.getcoord(::PointTrait, g::GeometryBasics.Point, i::Int) = g[i] + +GeoInterface.ngeom(::LineTrait, g::GeometryBasics.Line) = length(g) +GeoInterface.getgeom(::LineTrait, g::GeometryBasics.Line, i::Int) = g[i] + +GeoInterface.ngeom(::LineStringTrait, g::GeometryBasics.LineString) = length(g) + 1 # n line segments + 1 +GeoInterface.ncoord(::LineStringTrait, g::GeometryBasics.LineString{Dim}) where {Dim} = Dim +function GeoInterface.getgeom(::LineStringTrait, g::GeometryBasics.LineString, i::Int) + return GeometryBasics.coordinates(g)[i] +end + +GeoInterface.ngeom(::PolygonTrait, g::GeometryBasics.Polygon) = length(g.interiors) + 1 # +1 for exterior +function GeoInterface.getgeom(::PolygonTrait, + g::GeometryBasics.Polygon, + i::Int)::typeof(g.exterior) + return i > 1 ? g.interiors[i - 1] : g.exterior +end + +GeoInterface.ngeom(::MultiPointTrait, g::GeometryBasics.MultiPoint) = length(g) +GeoInterface.getgeom(::MultiPointTrait, g::GeometryBasics.MultiPoint, i::Int) = g[i] + +function GeoInterface.ngeom(::MultiLineStringTrait, g::GeometryBasics.MultiLineString) + return length(g) +end +function GeoInterface.getgeom(::MultiLineStringTrait, g::GeometryBasics.MultiLineString, i::Int) + return g[i] +end +GeoInterface.ncoord(::MultiLineStringTrait, g::GeometryBasics.MultiLineString{Dim}) where {Dim} = Dim + +GeoInterface.ngeom(::MultiPolygonTrait, g::GeometryBasics.MultiPolygon) = length(g) +GeoInterface.getgeom(::MultiPolygonTrait, g::GeometryBasics.MultiPolygon, i::Int) = g[i] + +function GeoInterface.ncoord(::AbstractGeometryTrait, + ::Simplex{Dim,T,N,P}) where {Dim,T,N,P} + return Dim +end +function GeoInterface.ncoord(::AbstractGeometryTrait, + ::AbstractGeometry{Dim,T}) where {Dim,T} + return Dim +end +function GeoInterface.ngeom(::AbstractGeometryTrait, + ::Simplex{Dim,T,N,P}) where {Dim,T,N,P} + return N +end +GeoInterface.ngeom(::PolygonTrait, ::GeometryBasics.Ngon) = 1 # can't have any holes +GeoInterface.getgeom(::PolygonTrait, g::GeometryBasics.Ngon, _) = LineString(g.points) + +function GeoInterface.ncoord(::PolyhedralSurfaceTrait, + ::Mesh{Dim,T,E,V} where {Dim,T,E,V}) + return Dim +end +GeoInterface.ngeom(::PolyhedralSurfaceTrait, g::GeometryBasics.AbstractMesh) = length(g) +GeoInterface.getgeom(::PolyhedralSurfaceTrait, g::GeometryBasics.AbstractMesh, i) = g[i] + +function GeoInterface.convert(::Type{Point}, type::PointTrait, geom) + x, y = GeoInterface.x(geom), GeoInterface.y(geom) + if GeoInterface.is3d(geom) + z = GeoInterface.z(geom) + T = promote_type(typeof(x), typeof(y), typeof(z)) + return Point{3,T}(x, y, z) + else + GeoInterface.x(geom), GeoInterface.y(geom) + T = promote_type(typeof(x), typeof(y)) + return Point{2,T}(x, y) + end +end + +function GeoInterface.convert(::Type{LineString}, type::LineStringTrait, geom) + g1 = getgeom(geom, 1) + x, y = GeoInterface.x(g1), GeoInterface.y(g1) + if GeoInterface.is3d(geom) + z = GeoInterface.z(g1) + T = promote_type(typeof(x), typeof(y), typeof(z)) + return LineString([Point{3,T}(GeoInterface.x(p), GeoInterface.y(p), GeoInterface.z(p)) for p in getgeom(geom)]) + else + T = promote_type(typeof(x), typeof(y)) + return LineString([Point{2,T}(GeoInterface.x(p), GeoInterface.y(p)) for p in getgeom(geom)]) + end +end + +function GeoInterface.convert(::Type{Polygon}, type::PolygonTrait, geom) + t = LineStringTrait() + exterior = GeoInterface.convert(LineString, t, GeoInterface.getexterior(geom)) + if GeoInterface.nhole(geom) == 0 + return Polygon(exterior) + else + interiors = map(h -> GeoInterface.convert(LineString, t, h), GeoInterface.gethole(geom)) + return Polygon(exterior, interiors) + end +end + +function GeoInterface.convert(::Type{MultiPoint}, type::MultiPointTrait, geom) + g1 = getgeom(geom, 1) + x, y = GeoInterface.x(g1), GeoInterface.y(g1) + if GeoInterface.is3d(geom) + z = GeoInterface.z(g1) + T = promote_type(typeof(x), typeof(y), typeof(z)) + return MultiPoint([Point{3,T}(GeoInterface.x(p), GeoInterface.y(p), GeoInterface.z(p)) for p in getgeom(geom)]) + else + T = promote_type(typeof(x), typeof(y)) + return MultiPoint([Point{2,T}(GeoInterface.x(p), GeoInterface.y(p)) for p in getgeom(geom)]) + end +end + +function GeoInterface.convert(::Type{MultiLineString}, type::MultiLineStringTrait, geom) + t = LineStringTrait() + return MultiLineString(map(l -> GeoInterface.convert(LineString, t, l), getgeom(geom))) +end + +function GeoInterface.convert(::Type{MultiPolygon}, type::MultiPolygonTrait, geom) + t = PolygonTrait() + return MultiPolygon(map(poly -> GeoInterface.convert(Polygon, t, poly), getgeom(geom))) +end + +function GeometryBasics.Extents.extent(rect::Rect2) + (xmin, ymin), (xmax, ymax) = extrema(rect) + return GeometryBasics.Extents.Extent(X=(xmin, xmax), Y=(ymin, ymax)) +end + +end \ No newline at end of file diff --git a/src/GeometryBasics.jl b/src/GeometryBasics.jl index c375acc7..d2a12c4e 100644 --- a/src/GeometryBasics.jl +++ b/src/GeometryBasics.jl @@ -1,7 +1,6 @@ module GeometryBasics using StaticArrays, Tables, StructArrays, IterTools, LinearAlgebra -using GeoInterface import Extents using EarCut_jll diff --git a/src/geointerface.jl b/src/geointerface.jl index 63b5b71b..c7cd21e6 100644 --- a/src/geointerface.jl +++ b/src/geointerface.jl @@ -1,156 +1,5 @@ -# Implementation of trait based interface from https://github.com/JuliaGeo/GeoInterface.jl/ -GeoInterface.isgeometry(::Type{<:AbstractGeometry}) = true -GeoInterface.isgeometry(::Type{<:AbstractFace}) = true -GeoInterface.isgeometry(::Type{<:AbstractPoint}) = true -GeoInterface.isgeometry(::Type{<:AbstractMesh}) = true -GeoInterface.isgeometry(::Type{<:AbstractPolygon}) = true -GeoInterface.isgeometry(::Type{<:LineString}) = true -GeoInterface.isgeometry(::Type{<:MultiPoint}) = true -GeoInterface.isgeometry(::Type{<:MultiLineString}) = true -GeoInterface.isgeometry(::Type{<:MultiPolygon}) = true -GeoInterface.isgeometry(::Type{<:Mesh}) = true - -GeoInterface.geomtrait(::Point) = PointTrait() -GeoInterface.geomtrait(::Line) = LineTrait() -GeoInterface.geomtrait(::LineString) = LineStringTrait() -GeoInterface.geomtrait(::Polygon) = PolygonTrait() -GeoInterface.geomtrait(::MultiPoint) = MultiPointTrait() -GeoInterface.geomtrait(::MultiLineString) = MultiLineStringTrait() -GeoInterface.geomtrait(::MultiPolygon) = MultiPolygonTrait() -GeoInterface.geomtrait(::Ngon) = PolygonTrait() -GeoInterface.geomtrait(::AbstractMesh) = PolyhedralSurfaceTrait() - -# GeoInterface calls this method in `GeoInterface.convert(GeometryBasics, ...)` -geointerface_geomtype(::GeoInterface.PointTrait) = Point -geointerface_geomtype(::GeoInterface.MultiPointTrait) = MultiPoint -geointerface_geomtype(::GeoInterface.LineTrait) = Line -geointerface_geomtype(::GeoInterface.LineStringTrait) = LineString -geointerface_geomtype(::GeoInterface.MultiLineStringTrait) = MultiLineString -geointerface_geomtype(::GeoInterface.PolygonTrait) = Polygon -geointerface_geomtype(::GeoInterface.MultiPolygonTrait) = MultiPolygon -geointerface_geomtype(::GeoInterface.PolyhedralSurfaceTrait) = Mesh - -GeoInterface.geomtrait(::Simplex{Dim,T,1}) where {Dim,T} = PointTrait() -GeoInterface.geomtrait(::Simplex{Dim,T,2}) where {Dim,T} = LineStringTrait() -GeoInterface.geomtrait(::Simplex{Dim,T,3}) where {Dim,T} = PolygonTrait() - -GeoInterface.ncoord(::PointTrait, g::Point) = length(g) -GeoInterface.getcoord(::PointTrait, g::Point, i::Int) = g[i] - -GeoInterface.ngeom(::LineTrait, g::Line) = length(g) -GeoInterface.getgeom(::LineTrait, g::Line, i::Int) = g[i] - -GeoInterface.ngeom(::LineStringTrait, g::LineString) = length(g) + 1 # n line segments + 1 -GeoInterface.ncoord(::LineStringTrait, g::LineString{Dim}) where {Dim} = Dim -function GeoInterface.getgeom(::LineStringTrait, g::LineString, i::Int) - return GeometryBasics.coordinates(g)[i] -end - -GeoInterface.ngeom(::PolygonTrait, g::Polygon) = length(g.interiors) + 1 # +1 for exterior -function GeoInterface.getgeom(::PolygonTrait, - g::Polygon, - i::Int)::typeof(g.exterior) - return i > 1 ? g.interiors[i - 1] : g.exterior -end - -GeoInterface.ngeom(::MultiPointTrait, g::MultiPoint) = length(g) -GeoInterface.getgeom(::MultiPointTrait, g::MultiPoint, i::Int) = g[i] - -function GeoInterface.ngeom(::MultiLineStringTrait, g::MultiLineString) - return length(g) -end -function GeoInterface.getgeom(::MultiLineStringTrait, g::MultiLineString, i::Int) - return g[i] -end -GeoInterface.ncoord(::MultiLineStringTrait, g::MultiLineString{Dim}) where {Dim} = Dim - -GeoInterface.ngeom(::MultiPolygonTrait, g::MultiPolygon) = length(g) -GeoInterface.getgeom(::MultiPolygonTrait, g::MultiPolygon, i::Int) = g[i] - -function GeoInterface.ncoord(::AbstractGeometryTrait, - ::Simplex{Dim,T,N,P}) where {Dim,T,N,P} - return Dim -end -function GeoInterface.ncoord(::AbstractGeometryTrait, - ::AbstractGeometry{Dim,T}) where {Dim,T} - return Dim -end -function GeoInterface.ngeom(::AbstractGeometryTrait, - ::Simplex{Dim,T,N,P}) where {Dim,T,N,P} - return N -end -GeoInterface.ngeom(::PolygonTrait, ::Ngon) = 1 # can't have any holes -GeoInterface.getgeom(::PolygonTrait, g::Ngon, _) = LineString(g.points) - -function GeoInterface.ncoord(::PolyhedralSurfaceTrait, - ::Mesh{Dim,T,E,V} where {Dim,T,E,V}) - return Dim -end -GeoInterface.ngeom(::PolyhedralSurfaceTrait, g::AbstractMesh) = length(g) -GeoInterface.getgeom(::PolyhedralSurfaceTrait, g::AbstractMesh, i) = g[i] - -function GeoInterface.convert(::Type{Point}, type::PointTrait, geom) - x, y = GeoInterface.x(geom), GeoInterface.y(geom) - if GeoInterface.is3d(geom) - z = GeoInterface.z(geom) - T = promote_type(typeof(x), typeof(y), typeof(z)) - return Point{3,T}(x, y, z) - else - GeoInterface.x(geom), GeoInterface.y(geom) - T = promote_type(typeof(x), typeof(y)) - return Point{2,T}(x, y) - end -end - -function GeoInterface.convert(::Type{LineString}, type::LineStringTrait, geom) - g1 = getgeom(geom, 1) - x, y = GeoInterface.x(g1), GeoInterface.y(g1) - if GeoInterface.is3d(geom) - z = GeoInterface.z(g1) - T = promote_type(typeof(x), typeof(y), typeof(z)) - return LineString([Point{3,T}(GeoInterface.x(p), GeoInterface.y(p), GeoInterface.z(p)) for p in getgeom(geom)]) - else - T = promote_type(typeof(x), typeof(y)) - return LineString([Point{2,T}(GeoInterface.x(p), GeoInterface.y(p)) for p in getgeom(geom)]) - end -end - -function GeoInterface.convert(::Type{Polygon}, type::PolygonTrait, geom) - t = LineStringTrait() - exterior = GeoInterface.convert(LineString, t, GeoInterface.getexterior(geom)) - if GeoInterface.nhole(geom) == 0 - return Polygon(exterior) - else - interiors = map(h -> GeoInterface.convert(LineString, t, h), GeoInterface.gethole(geom)) - return Polygon(exterior, interiors) - end -end - -function GeoInterface.convert(::Type{MultiPoint}, type::MultiPointTrait, geom) - g1 = getgeom(geom, 1) - x, y = GeoInterface.x(g1), GeoInterface.y(g1) - if GeoInterface.is3d(geom) - z = GeoInterface.z(g1) - T = promote_type(typeof(x), typeof(y), typeof(z)) - return MultiPoint([Point{3,T}(GeoInterface.x(p), GeoInterface.y(p), GeoInterface.z(p)) for p in getgeom(geom)]) - else - T = promote_type(typeof(x), typeof(y)) - return MultiPoint([Point{2,T}(GeoInterface.x(p), GeoInterface.y(p)) for p in getgeom(geom)]) - end -end - -function GeoInterface.convert(::Type{MultiLineString}, type::MultiLineStringTrait, geom) - t = LineStringTrait() - return MultiLineString(map(l -> GeoInterface.convert(LineString, t, l), getgeom(geom))) -end - -function GeoInterface.convert(::Type{MultiPolygon}, type::MultiPolygonTrait, geom) - t = PolygonTrait() - return MultiPolygon(map(poly -> GeoInterface.convert(Polygon, t, poly), getgeom(geom))) -end - -function Extents.extent(rect::Rect2) - (xmin, ymin), (xmax, ymax) = extrema(rect) - return Extents.Extent(X=(xmin, xmax), Y=(ymin, ymax)) -end \ No newline at end of file +# This is a stub definition of `geointerface_geomtype` for GeometryBasics +# There are no actual methods defined here. +# All the implementation is in `ext/GeometryBasicsGeoInterfaceExt.jl` +function geointerface_geomtype end \ No newline at end of file