From 7d5c2185aa3b04319515c0f3ece5e4c871ca846f Mon Sep 17 00:00:00 2001 From: Maarten Pronk Date: Sun, 12 Jun 2022 23:47:34 +0200 Subject: [PATCH 1/9] Implemented GeoInterface. --- Project.toml | 4 ++- src/GeometryBasics.jl | 3 +- src/geointerface.jl | 74 +++++++++++++++++++++++++++++++++++++++++++ test/geointerface.jl | 27 ++++++++++++++++ test/runtests.jl | 5 +++ 5 files changed, 111 insertions(+), 2 deletions(-) create mode 100644 src/geointerface.jl create mode 100644 test/geointerface.jl diff --git a/Project.toml b/Project.toml index 220c5f14..46a0c572 100644 --- a/Project.toml +++ b/Project.toml @@ -1,10 +1,11 @@ name = "GeometryBasics" uuid = "5c1252a2-5f33-56bf-86c9-59e7332b4326" authors = ["SimonDanisch "] -version = "0.4.2" +version = "0.4.3" [deps] EarCut_jll = "5ae413db-bbd1-5e63-b57d-d24a61df00f5" +GeoInterface = "cf35fbd7-0cd7-5166-be24-54bfbe79505f" IterTools = "c8e1da08-722c-5040-9ed9-7db0dc04731e" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" @@ -21,6 +22,7 @@ julia = "1.3" [extras] OffsetArrays = "6fe1bfb0-de20-5000-8ca7-80f57d26f881" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" +StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] diff --git a/src/GeometryBasics.jl b/src/GeometryBasics.jl index fb60f3a8..558e7315 100644 --- a/src/GeometryBasics.jl +++ b/src/GeometryBasics.jl @@ -1,6 +1,6 @@ module GeometryBasics -using StaticArrays, Tables, StructArrays, IterTools, LinearAlgebra +using StaticArrays, Tables, StructArrays, IterTools, LinearAlgebra, GeoInterface using EarCut_jll using Base: @propagate_inbounds @@ -25,6 +25,7 @@ include("lines.jl") include("boundingboxes.jl") include("deprecated.jl") +include("geointerface.jl") export AbstractGeometry, GeometryPrimitive export Mat, Point, Vec diff --git a/src/geointerface.jl b/src/geointerface.jl new file mode 100644 index 00000000..ac19a751 --- /dev/null +++ b/src/geointerface.jl @@ -0,0 +1,74 @@ +# Implementation of trait based interface from https://github.com/JuliaGeo/GeoInterface.jl/ + +GeoInterface.isgeometry(::AbstractGeometry) = true +GeoInterface.isgeometry(::AbstractFace) = true +GeoInterface.isgeometry(::AbstractPoint) = true +GeoInterface.isgeometry(::AbstractVector{<:AbstractGeometry}) = true +GeoInterface.isgeometry(::AbstractVector{<:AbstractPoint}) = true +GeoInterface.isgeometry(::AbstractVector{<:LineString}) = true +GeoInterface.isgeometry(::AbstractVector{<:AbstractPolygon}) = true +GeoInterface.isgeometry(::AbstractVector{<:AbstractFace}) = true +GeoInterface.isgeometry(::Mesh) = true + +GeoInterface.geomtrait(::Point) = GeoInterface.PointTrait() +GeoInterface.geomtrait(::Line) = GeoInterface.LineTrait() +GeoInterface.geomtrait(::LineString) = GeoInterface.LineStringTrait() +GeoInterface.geomtrait(::Polygon) = GeoInterface.PolygonTrait() +GeoInterface.geomtrait(::MultiPoint) = GeoInterface.MultiPointTrait() +GeoInterface.geomtrait(::MultiLineString) = GeoInterface.MultiLineStringTrait() +GeoInterface.geomtrait(::MultiPolygon) = GeoInterface.MultiPolygonTrait() +GeoInterface.geomtrait(::Ngon) = GeoInterface.PolygonTrait() +GeoInterface.geomtrait(::AbstractMesh) = GeoInterface.PolyhedralSurfaceTrait() + +GeoInterface.geomtrait(::Simplex{Dim,T,1}) where {Dim,T} = GeoInterface.PointTrait() +GeoInterface.geomtrait(::Simplex{Dim,T,2}) where {Dim,T} = GeoInterface.LineStringTrait() +GeoInterface.geomtrait(::Simplex{Dim,T,3}) where {Dim,T} = GeoInterface.PolygonTrait() + +GeoInterface.ncoord(::GeoInterface.PointTrait, g::Point) = length(g) +GeoInterface.getcoord(::GeoInterface.PointTrait, g::Point, i::Int) = g[i] + +GeoInterface.ngeom(::GeoInterface.LineTrait, g::Line) = length(g) +GeoInterface.getgeom(::GeoInterface.LineTrait, g::Line, i::Int) = g[i] + +GeoInterface.ngeom(::GeoInterface.LineStringTrait, g::LineString) = length(g) +function GeoInterface.getgeom(::GeoInterface.LineStringTrait, g::LineString, i::Int) + return coordinates(g)[i] +end + +GeoInterface.ngeom(::GeoInterface.PolygonTrait, g::Polygon) = length(g.interiors) + 1 # +1 for exterior +function GeoInterface.getgeom(::GeoInterface.PolygonTrait, g::Polygon, i::Int) + return i > 1 ? g.interiors[i - 1] : g.exterior +end + +GeoInterface.ngeom(::GeoInterface.MultiPointTrait, g::MultiPoint) = length(g) +GeoInterface.getgeom(::GeoInterface.MultiPointTrait, g::MultiPoint, i::Int) = g[i] + +function GeoInterface.ngeom(::GeoInterface.MultiLineStringTrait, g::MultiLineString) + return length(g) +end +function GeoInterface.getgeom(::GeoInterface.MultiLineStringTrait, g::MultiLineString, + i::Int) + return g[i] +end + +GeoInterface.ngeom(::GeoInterface.MultiPolygonTrait, g::MultiPolygon) = length(g) +GeoInterface.getgeom(::GeoInterface.MultiPolygonTrait, g::MultiPolygon, i::Int) = g[i] + +function GeoInterface.ncoord(::GeoInterface.AbstractGeometryTrait, + ::Simplex{Dim,T,N,P}) where {Dim,T,N,P} + return Dim +end +function GeoInterface.ncoord(::GeoInterface.AbstractGeometryTrait, + ::AbstractGeometry{Dim,T}) where {Dim,T} + return Dim +end +function GeoInterface.ngeom(::GeoInterface.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) + +GeoInterface.ncoord(::PolyhedralSurfaceTrait, ::Mesh{Dim,T,E,V} where {Dim,T,E,V}) = Dim +GeoInterface.ngeom(::PolyhedralSurfaceTrait, g::AbstractMesh) = length(g) +GeoInterface.getgeom(::PolyhedralSurfaceTrait, g::AbstractMesh, i) = g[i] diff --git a/test/geointerface.jl b/test/geointerface.jl new file mode 100644 index 00000000..97182bd7 --- /dev/null +++ b/test/geointerface.jl @@ -0,0 +1,27 @@ +@testset "Basic types" begin + point = Point(2, 3) + GeoInterface.testgeometry(point) + + mp = MultiPoint([point, point]) + GeoInterface.testgeometry(mp) + + linestring = LineString(Point{2,Int}[(10, 10), (20, 20), (10, 40)]) + GeoInterface.testgeometry(linestring) + + multilinestring = MultiLineString([linestring, linestring]) + GeoInterface.testgeometry(multilinestring) + + poly = Polygon(rand(Point{2,Float32}, 5)) + GeoInterface.testgeometry(poly) + + triangle = Triangle(point, point, point) + GeoInterface.testgeometry(triangle) + + polys = MultiPolygon([poly, poly]) + GeoInterface.testgeometry(polys) +end + +@testset "Mesh" begin + mesh = triangle_mesh(Sphere(Point3f(0), 1)) + GeoInterface.testgeometry(mesh) +end diff --git a/test/runtests.jl b/test/runtests.jl index 956ebf66..2cd7f51d 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -2,6 +2,7 @@ using Test, Random, StructArrays, Tables, StaticArrays, OffsetArrays using GeometryBasics using LinearAlgebra using GeometryBasics: attributes +using GeoInterface @testset "GeometryBasics" begin @@ -715,4 +716,8 @@ end include("fixed_arrays.jl") end +@testset "GeoInterface" begin + include("geointerface.jl") +end + end # testset "GeometryBasics" From 1aafe2a19aa99da25b7b7eaf8535494bb10877be Mon Sep 17 00:00:00 2001 From: Maarten Pronk Date: Sun, 12 Jun 2022 23:50:14 +0200 Subject: [PATCH 2/9] Fix Project.toml --- Project.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Project.toml b/Project.toml index 46a0c572..4ad77c99 100644 --- a/Project.toml +++ b/Project.toml @@ -22,7 +22,6 @@ julia = "1.3" [extras] OffsetArrays = "6fe1bfb0-de20-5000-8ca7-80f57d26f881" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" -StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] From 3e35f4eb25ff825fb15cf319e0f06fadc5b6fa1e Mon Sep 17 00:00:00 2001 From: Maarten Pronk Date: Wed, 15 Jun 2022 18:35:43 +0200 Subject: [PATCH 3/9] Dispatch on Type. --- src/geointerface.jl | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/geointerface.jl b/src/geointerface.jl index ac19a751..b7c90fdb 100644 --- a/src/geointerface.jl +++ b/src/geointerface.jl @@ -1,14 +1,14 @@ # Implementation of trait based interface from https://github.com/JuliaGeo/GeoInterface.jl/ -GeoInterface.isgeometry(::AbstractGeometry) = true -GeoInterface.isgeometry(::AbstractFace) = true -GeoInterface.isgeometry(::AbstractPoint) = true -GeoInterface.isgeometry(::AbstractVector{<:AbstractGeometry}) = true -GeoInterface.isgeometry(::AbstractVector{<:AbstractPoint}) = true -GeoInterface.isgeometry(::AbstractVector{<:LineString}) = true -GeoInterface.isgeometry(::AbstractVector{<:AbstractPolygon}) = true -GeoInterface.isgeometry(::AbstractVector{<:AbstractFace}) = true -GeoInterface.isgeometry(::Mesh) = true +GeoInterface.isgeometry(::Type{<:AbstractGeometry}) = true +GeoInterface.isgeometry(::Type{<:AbstractFace}) = true +GeoInterface.isgeometry(::Type{<:AbstractPoint}) = true +GeoInterface.isgeometry(::Type{<:AbstractVector{<:AbstractGeometry}}) = true +GeoInterface.isgeometry(::Type{<:AbstractVector{<:AbstractPoint}}) = true +GeoInterface.isgeometry(::Type{<:AbstractVector{<:LineString}}) = true +GeoInterface.isgeometry(::Type{<:AbstractVector{<:AbstractPolygon}}) = true +GeoInterface.isgeometry(::Type{<:AbstractVector{<:AbstractFace}}) = true +GeoInterface.isgeometry(::Type{<:Mesh}) = true GeoInterface.geomtrait(::Point) = GeoInterface.PointTrait() GeoInterface.geomtrait(::Line) = GeoInterface.LineTrait() From 53d85c31312941eef752f402b7c6dc0fbd260166 Mon Sep 17 00:00:00 2001 From: Maarten Pronk Date: Thu, 16 Jun 2022 15:48:54 +0200 Subject: [PATCH 4/9] Upgrade Julia to 1.6 --- .github/workflows/ci.yml | 8 ++++---- Project.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b35cfb5f..6bf35910 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,7 +3,7 @@ on: push: branches: - master - tags: '*' + tags: "*" pull_request: branches: - master @@ -15,8 +15,8 @@ jobs: fail-fast: false matrix: version: - - '1.3' - - '1.7' + - "1.6" + - "1" os: - ubuntu-latest - macOS-latest @@ -54,7 +54,7 @@ jobs: - uses: actions/checkout@v2 - uses: julia-actions/setup-julia@v1 with: - version: '1.7' + version: "1.7" - run: | julia --project=docs -e ' using Pkg diff --git a/Project.toml b/Project.toml index 4ad77c99..50515dbf 100644 --- a/Project.toml +++ b/Project.toml @@ -17,7 +17,7 @@ IterTools = "1.3.0" StaticArrays = "0.12, 1.0" StructArrays = "0.6" Tables = "0.2, 1" -julia = "1.3" +julia = "1.6" [extras] OffsetArrays = "6fe1bfb0-de20-5000-8ca7-80f57d26f881" From b28b9a42ea63ebdc316817a31e8bfd7dd53fbe7e Mon Sep 17 00:00:00 2001 From: Maarten Pronk Date: Mon, 20 Jun 2022 18:36:19 +0200 Subject: [PATCH 5/9] Fixed LineString coordinates. Added more tests. --- src/GeometryBasics.jl | 3 ++- src/basic_types.jl | 4 ---- src/geointerface.jl | 21 +++++++++++++-------- test/geointerface.jl | 22 +++++++++++++++++++++- 4 files changed, 36 insertions(+), 14 deletions(-) diff --git a/src/GeometryBasics.jl b/src/GeometryBasics.jl index 558e7315..6db601eb 100644 --- a/src/GeometryBasics.jl +++ b/src/GeometryBasics.jl @@ -1,6 +1,7 @@ module GeometryBasics -using StaticArrays, Tables, StructArrays, IterTools, LinearAlgebra, GeoInterface +using StaticArrays, Tables, StructArrays, IterTools, LinearAlgebra +using GeoInterface: GeoInterface using EarCut_jll using Base: @propagate_inbounds diff --git a/src/basic_types.jl b/src/basic_types.jl index 16aa8047..03f5339e 100644 --- a/src/basic_types.jl +++ b/src/basic_types.jl @@ -57,7 +57,6 @@ Fixed Size Polygon, e.g. - ... """ struct Ngon{Dim,T<:Real,N,Point<:AbstractPoint{Dim,T}} <: AbstractPolygon{Dim,T} - points::SVector{N,Point} end @@ -137,7 +136,6 @@ It applies to infinite dimensions. The structure of this type is designed to allow embedding in higher-order spaces by parameterizing on `T`. """ struct Simplex{Dim,T<:Real,N,Point<:AbstractPoint{Dim,T}} <: Polytope{Dim,T} - points::SVector{N,Point} end @@ -328,7 +326,6 @@ Base.size(mp::MultiPolygon) = size(mp.polygons) struct MultiLineString{Dim,T<:Real,Element<:LineString{Dim,T},A<:AbstractVector{Element}} <: AbstractVector{Element} - linestrings::A end @@ -347,7 +344,6 @@ A collection of points """ struct MultiPoint{Dim,T<:Real,P<:AbstractPoint{Dim,T},A<:AbstractVector{P}} <: AbstractVector{P} - points::A end diff --git a/src/geointerface.jl b/src/geointerface.jl index b7c90fdb..729b2485 100644 --- a/src/geointerface.jl +++ b/src/geointerface.jl @@ -30,13 +30,15 @@ GeoInterface.getcoord(::GeoInterface.PointTrait, g::Point, i::Int) = g[i] GeoInterface.ngeom(::GeoInterface.LineTrait, g::Line) = length(g) GeoInterface.getgeom(::GeoInterface.LineTrait, g::Line, i::Int) = g[i] -GeoInterface.ngeom(::GeoInterface.LineStringTrait, g::LineString) = length(g) +GeoInterface.ngeom(::GeoInterface.LineStringTrait, g::LineString) = length(g) + 1 # n line segments + 1 function GeoInterface.getgeom(::GeoInterface.LineStringTrait, g::LineString, i::Int) - return coordinates(g)[i] + return GeometryBasics.coordinates(g)[i] end GeoInterface.ngeom(::GeoInterface.PolygonTrait, g::Polygon) = length(g.interiors) + 1 # +1 for exterior -function GeoInterface.getgeom(::GeoInterface.PolygonTrait, g::Polygon, i::Int) +function GeoInterface.getgeom(::GeoInterface.PolygonTrait, + g::Polygon, + i::Int)::typeof(g.exterior) return i > 1 ? g.interiors[i - 1] : g.exterior end @@ -66,9 +68,12 @@ function GeoInterface.ngeom(::GeoInterface.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) +GeoInterface.ngeom(::GeoInterface.PolygonTrait, ::Ngon) = 1 # can't have any holes +GeoInterface.getgeom(::GeoInterface.PolygonTrait, g::Ngon, _) = LineString(g.points) -GeoInterface.ncoord(::PolyhedralSurfaceTrait, ::Mesh{Dim,T,E,V} where {Dim,T,E,V}) = Dim -GeoInterface.ngeom(::PolyhedralSurfaceTrait, g::AbstractMesh) = length(g) -GeoInterface.getgeom(::PolyhedralSurfaceTrait, g::AbstractMesh, i) = g[i] +function GeoInterface.ncoord(::GeoInterface.PolyhedralSurfaceTrait, + ::Mesh{Dim,T,E,V} where {Dim,T,E,V}) + return Dim +end +GeoInterface.ngeom(::GeoInterface.PolyhedralSurfaceTrait, g::AbstractMesh) = length(g) +GeoInterface.getgeom(::GeoInterface.PolyhedralSurfaceTrait, g::AbstractMesh, i) = g[i] diff --git a/test/geointerface.jl b/test/geointerface.jl index 97182bd7..98153a61 100644 --- a/test/geointerface.jl +++ b/test/geointerface.jl @@ -1,24 +1,44 @@ @testset "Basic types" begin point = Point(2, 3) GeoInterface.testgeometry(point) + @test GeoInterface.ncoord(point) == 2 + @test GeoInterface.getcoord(point, 2) == 3 + @test GeoInterface.coordinates(point) == [2, 3] mp = MultiPoint([point, point]) GeoInterface.testgeometry(mp) + @test GeoInterface.ngeom(mp) == 2 + @test GeoInterface.getgeom(mp, 2) == point + @test GeoInterface.coordinates(mp) == [[2, 3], [2, 3]] linestring = LineString(Point{2,Int}[(10, 10), (20, 20), (10, 40)]) GeoInterface.testgeometry(linestring) + @test GeoInterface.ngeom(linestring) == 3 + @test GeoInterface.getgeom(linestring, 1) == Point(10, 10) + @test GeoInterface.getgeom(linestring, 2) == Point(20, 20) + @test GeoInterface.getgeom(linestring, 3) == Point(10, 40) + @test GeoInterface.coordinates(linestring) == [[10, 10], [20, 20], [10, 40]] multilinestring = MultiLineString([linestring, linestring]) GeoInterface.testgeometry(multilinestring) + @test GeoInterface.coordinates(multilinestring) == + [[[10, 10], [20, 20], [10, 40]], [[10, 10], [20, 20], [10, 40]]] - poly = Polygon(rand(Point{2,Float32}, 5)) + poly = Polygon(rand(Point{2,Float32}, 5), [rand(Point{2,Float32}, 5)]) GeoInterface.testgeometry(poly) + @test length(GeoInterface.coordinates(poly)) == 2 + @test length(GeoInterface.coordinates(poly)[1]) == 5 triangle = Triangle(point, point, point) GeoInterface.testgeometry(triangle) + @test length(GeoInterface.coordinates(triangle)) == 1 + @test length(GeoInterface.coordinates(triangle)[1]) == 3 polys = MultiPolygon([poly, poly]) GeoInterface.testgeometry(polys) + @test length(GeoInterface.coordinates(polys)) == 2 + @test length(GeoInterface.coordinates(polys)[1]) == 2 + @test length(GeoInterface.coordinates(polys)[1][1]) == 5 end @testset "Mesh" begin From 7aa0692aa448e69db555ddce9aaf2cf9156c9192 Mon Sep 17 00:00:00 2001 From: Martijn Visser Date: Thu, 4 Aug 2022 15:10:11 +0200 Subject: [PATCH 6/9] add GeoInterface compat --- Project.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Project.toml b/Project.toml index 46ff6cb2..570c48ce 100644 --- a/Project.toml +++ b/Project.toml @@ -14,6 +14,7 @@ Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" [compat] EarCut_jll = "2" +GeoInterface = "1.0.1" IterTools = "1.3.0" StaticArrays = "0.12, 1.0" StructArrays = "0.6" From 0a01573be160ff586e50c08568a699a3e6c09b34 Mon Sep 17 00:00:00 2001 From: Martijn Visser Date: Thu, 4 Aug 2022 15:20:36 +0200 Subject: [PATCH 7/9] using GeoInterface The trait names can get quite long and are exported, and not much else is exported. --- src/GeometryBasics.jl | 2 +- src/geointerface.jl | 68 +++++++++++++++++++++---------------------- test/geointerface.jl | 32 ++++++++++---------- 3 files changed, 51 insertions(+), 51 deletions(-) diff --git a/src/GeometryBasics.jl b/src/GeometryBasics.jl index 16d48031..8535cf1f 100644 --- a/src/GeometryBasics.jl +++ b/src/GeometryBasics.jl @@ -1,7 +1,7 @@ module GeometryBasics using StaticArrays, Tables, StructArrays, IterTools, LinearAlgebra -using GeoInterface: GeoInterface +using GeoInterface using EarCut_jll using Base: @propagate_inbounds diff --git a/src/geointerface.jl b/src/geointerface.jl index 729b2485..09351585 100644 --- a/src/geointerface.jl +++ b/src/geointerface.jl @@ -10,70 +10,70 @@ GeoInterface.isgeometry(::Type{<:AbstractVector{<:AbstractPolygon}}) = true GeoInterface.isgeometry(::Type{<:AbstractVector{<:AbstractFace}}) = true GeoInterface.isgeometry(::Type{<:Mesh}) = true -GeoInterface.geomtrait(::Point) = GeoInterface.PointTrait() -GeoInterface.geomtrait(::Line) = GeoInterface.LineTrait() -GeoInterface.geomtrait(::LineString) = GeoInterface.LineStringTrait() -GeoInterface.geomtrait(::Polygon) = GeoInterface.PolygonTrait() -GeoInterface.geomtrait(::MultiPoint) = GeoInterface.MultiPointTrait() -GeoInterface.geomtrait(::MultiLineString) = GeoInterface.MultiLineStringTrait() -GeoInterface.geomtrait(::MultiPolygon) = GeoInterface.MultiPolygonTrait() -GeoInterface.geomtrait(::Ngon) = GeoInterface.PolygonTrait() -GeoInterface.geomtrait(::AbstractMesh) = GeoInterface.PolyhedralSurfaceTrait() +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.geomtrait(::Simplex{Dim,T,1}) where {Dim,T} = GeoInterface.PointTrait() -GeoInterface.geomtrait(::Simplex{Dim,T,2}) where {Dim,T} = GeoInterface.LineStringTrait() -GeoInterface.geomtrait(::Simplex{Dim,T,3}) where {Dim,T} = GeoInterface.PolygonTrait() +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(::GeoInterface.PointTrait, g::Point) = length(g) -GeoInterface.getcoord(::GeoInterface.PointTrait, g::Point, i::Int) = g[i] +GeoInterface.ncoord(::PointTrait, g::Point) = length(g) +GeoInterface.getcoord(::PointTrait, g::Point, i::Int) = g[i] -GeoInterface.ngeom(::GeoInterface.LineTrait, g::Line) = length(g) -GeoInterface.getgeom(::GeoInterface.LineTrait, g::Line, i::Int) = g[i] +GeoInterface.ngeom(::LineTrait, g::Line) = length(g) +GeoInterface.getgeom(::LineTrait, g::Line, i::Int) = g[i] -GeoInterface.ngeom(::GeoInterface.LineStringTrait, g::LineString) = length(g) + 1 # n line segments + 1 -function GeoInterface.getgeom(::GeoInterface.LineStringTrait, g::LineString, i::Int) +GeoInterface.ngeom(::LineStringTrait, g::LineString) = length(g) + 1 # n line segments + 1 +function GeoInterface.getgeom(::LineStringTrait, g::LineString, i::Int) return GeometryBasics.coordinates(g)[i] end -GeoInterface.ngeom(::GeoInterface.PolygonTrait, g::Polygon) = length(g.interiors) + 1 # +1 for exterior -function GeoInterface.getgeom(::GeoInterface.PolygonTrait, +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(::GeoInterface.MultiPointTrait, g::MultiPoint) = length(g) -GeoInterface.getgeom(::GeoInterface.MultiPointTrait, g::MultiPoint, i::Int) = g[i] +GeoInterface.ngeom(::MultiPointTrait, g::MultiPoint) = length(g) +GeoInterface.getgeom(::MultiPointTrait, g::MultiPoint, i::Int) = g[i] -function GeoInterface.ngeom(::GeoInterface.MultiLineStringTrait, g::MultiLineString) +function GeoInterface.ngeom(::MultiLineStringTrait, g::MultiLineString) return length(g) end -function GeoInterface.getgeom(::GeoInterface.MultiLineStringTrait, g::MultiLineString, +function GeoInterface.getgeom(::MultiLineStringTrait, g::MultiLineString, i::Int) return g[i] end -GeoInterface.ngeom(::GeoInterface.MultiPolygonTrait, g::MultiPolygon) = length(g) -GeoInterface.getgeom(::GeoInterface.MultiPolygonTrait, g::MultiPolygon, i::Int) = g[i] +GeoInterface.ngeom(::MultiPolygonTrait, g::MultiPolygon) = length(g) +GeoInterface.getgeom(::MultiPolygonTrait, g::MultiPolygon, i::Int) = g[i] -function GeoInterface.ncoord(::GeoInterface.AbstractGeometryTrait, +function GeoInterface.ncoord(::AbstractGeometryTrait, ::Simplex{Dim,T,N,P}) where {Dim,T,N,P} return Dim end -function GeoInterface.ncoord(::GeoInterface.AbstractGeometryTrait, +function GeoInterface.ncoord(::AbstractGeometryTrait, ::AbstractGeometry{Dim,T}) where {Dim,T} return Dim end -function GeoInterface.ngeom(::GeoInterface.AbstractGeometryTrait, +function GeoInterface.ngeom(::AbstractGeometryTrait, ::Simplex{Dim,T,N,P}) where {Dim,T,N,P} return N end -GeoInterface.ngeom(::GeoInterface.PolygonTrait, ::Ngon) = 1 # can't have any holes -GeoInterface.getgeom(::GeoInterface.PolygonTrait, g::Ngon, _) = LineString(g.points) +GeoInterface.ngeom(::PolygonTrait, ::Ngon) = 1 # can't have any holes +GeoInterface.getgeom(::PolygonTrait, g::Ngon, _) = LineString(g.points) -function GeoInterface.ncoord(::GeoInterface.PolyhedralSurfaceTrait, +function GeoInterface.ncoord(::PolyhedralSurfaceTrait, ::Mesh{Dim,T,E,V} where {Dim,T,E,V}) return Dim end -GeoInterface.ngeom(::GeoInterface.PolyhedralSurfaceTrait, g::AbstractMesh) = length(g) -GeoInterface.getgeom(::GeoInterface.PolyhedralSurfaceTrait, g::AbstractMesh, i) = g[i] +GeoInterface.ngeom(::PolyhedralSurfaceTrait, g::AbstractMesh) = length(g) +GeoInterface.getgeom(::PolyhedralSurfaceTrait, g::AbstractMesh, i) = g[i] diff --git a/test/geointerface.jl b/test/geointerface.jl index 98153a61..548408fc 100644 --- a/test/geointerface.jl +++ b/test/geointerface.jl @@ -1,41 +1,41 @@ @testset "Basic types" begin point = Point(2, 3) - GeoInterface.testgeometry(point) - @test GeoInterface.ncoord(point) == 2 - @test GeoInterface.getcoord(point, 2) == 3 + testgeometry(point) + @test ncoord(point) == 2 + @test getcoord(point, 2) == 3 @test GeoInterface.coordinates(point) == [2, 3] mp = MultiPoint([point, point]) - GeoInterface.testgeometry(mp) - @test GeoInterface.ngeom(mp) == 2 - @test GeoInterface.getgeom(mp, 2) == point + testgeometry(mp) + @test ngeom(mp) == 2 + @test getgeom(mp, 2) == point @test GeoInterface.coordinates(mp) == [[2, 3], [2, 3]] linestring = LineString(Point{2,Int}[(10, 10), (20, 20), (10, 40)]) - GeoInterface.testgeometry(linestring) - @test GeoInterface.ngeom(linestring) == 3 - @test GeoInterface.getgeom(linestring, 1) == Point(10, 10) - @test GeoInterface.getgeom(linestring, 2) == Point(20, 20) - @test GeoInterface.getgeom(linestring, 3) == Point(10, 40) + testgeometry(linestring) + @test ngeom(linestring) == 3 + @test getgeom(linestring, 1) == Point(10, 10) + @test getgeom(linestring, 2) == Point(20, 20) + @test getgeom(linestring, 3) == Point(10, 40) @test GeoInterface.coordinates(linestring) == [[10, 10], [20, 20], [10, 40]] multilinestring = MultiLineString([linestring, linestring]) - GeoInterface.testgeometry(multilinestring) + testgeometry(multilinestring) @test GeoInterface.coordinates(multilinestring) == [[[10, 10], [20, 20], [10, 40]], [[10, 10], [20, 20], [10, 40]]] poly = Polygon(rand(Point{2,Float32}, 5), [rand(Point{2,Float32}, 5)]) - GeoInterface.testgeometry(poly) + testgeometry(poly) @test length(GeoInterface.coordinates(poly)) == 2 @test length(GeoInterface.coordinates(poly)[1]) == 5 triangle = Triangle(point, point, point) - GeoInterface.testgeometry(triangle) + testgeometry(triangle) @test length(GeoInterface.coordinates(triangle)) == 1 @test length(GeoInterface.coordinates(triangle)[1]) == 3 polys = MultiPolygon([poly, poly]) - GeoInterface.testgeometry(polys) + testgeometry(polys) @test length(GeoInterface.coordinates(polys)) == 2 @test length(GeoInterface.coordinates(polys)[1]) == 2 @test length(GeoInterface.coordinates(polys)[1][1]) == 5 @@ -43,5 +43,5 @@ end @testset "Mesh" begin mesh = triangle_mesh(Sphere(Point3f(0), 1)) - GeoInterface.testgeometry(mesh) + testgeometry(mesh) end From 839f46389805c400698d3ba3eabcc8cae306ef15 Mon Sep 17 00:00:00 2001 From: Martijn Visser Date: Thu, 4 Aug 2022 15:25:13 +0200 Subject: [PATCH 8/9] add test macro to testgeometry Not strictly needed since they trigger AssertionErrors if not ok, but good to count towards the tests since they return true if they don't fail. --- test/geointerface.jl | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test/geointerface.jl b/test/geointerface.jl index 548408fc..188d4464 100644 --- a/test/geointerface.jl +++ b/test/geointerface.jl @@ -1,18 +1,18 @@ @testset "Basic types" begin point = Point(2, 3) - testgeometry(point) + @test testgeometry(point) @test ncoord(point) == 2 @test getcoord(point, 2) == 3 @test GeoInterface.coordinates(point) == [2, 3] mp = MultiPoint([point, point]) - testgeometry(mp) + @test testgeometry(mp) @test ngeom(mp) == 2 @test getgeom(mp, 2) == point @test GeoInterface.coordinates(mp) == [[2, 3], [2, 3]] linestring = LineString(Point{2,Int}[(10, 10), (20, 20), (10, 40)]) - testgeometry(linestring) + @test testgeometry(linestring) @test ngeom(linestring) == 3 @test getgeom(linestring, 1) == Point(10, 10) @test getgeom(linestring, 2) == Point(20, 20) @@ -20,22 +20,22 @@ @test GeoInterface.coordinates(linestring) == [[10, 10], [20, 20], [10, 40]] multilinestring = MultiLineString([linestring, linestring]) - testgeometry(multilinestring) + @test testgeometry(multilinestring) @test GeoInterface.coordinates(multilinestring) == [[[10, 10], [20, 20], [10, 40]], [[10, 10], [20, 20], [10, 40]]] poly = Polygon(rand(Point{2,Float32}, 5), [rand(Point{2,Float32}, 5)]) - testgeometry(poly) + @test testgeometry(poly) @test length(GeoInterface.coordinates(poly)) == 2 @test length(GeoInterface.coordinates(poly)[1]) == 5 triangle = Triangle(point, point, point) - testgeometry(triangle) + @test testgeometry(triangle) @test length(GeoInterface.coordinates(triangle)) == 1 @test length(GeoInterface.coordinates(triangle)[1]) == 3 polys = MultiPolygon([poly, poly]) - testgeometry(polys) + @test testgeometry(polys) @test length(GeoInterface.coordinates(polys)) == 2 @test length(GeoInterface.coordinates(polys)[1]) == 2 @test length(GeoInterface.coordinates(polys)[1][1]) == 5 @@ -43,5 +43,5 @@ end @testset "Mesh" begin mesh = triangle_mesh(Sphere(Point3f(0), 1)) - testgeometry(mesh) + @test testgeometry(mesh) end From e1e1f6e7c45612851348b0c15a5ef3074346d081 Mon Sep 17 00:00:00 2001 From: Martijn Visser Date: Thu, 4 Aug 2022 18:25:20 +0200 Subject: [PATCH 9/9] add GeoInterface.convert --- Project.toml | 3 ++- src/geointerface.jl | 36 +++++++++++++++++++++++++++++ test/geointerface.jl | 54 ++++++++++++++++++++++++++++++++++++++++++++ test/runtests.jl | 1 + 4 files changed, 93 insertions(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 570c48ce..501c4bd7 100644 --- a/Project.toml +++ b/Project.toml @@ -23,9 +23,10 @@ julia = "1.6" [extras] Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" +GeoJSON = "61d90e0f-e114-555e-ac52-39dfb47a3ef9" OffsetArrays = "6fe1bfb0-de20-5000-8ca7-80f57d26f881" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] -test = ["Aqua", "Test", "Random", "OffsetArrays"] +test = ["Aqua", "GeoJSON", "Test", "Random", "OffsetArrays"] diff --git a/src/geointerface.jl b/src/geointerface.jl index 09351585..0b73b284 100644 --- a/src/geointerface.jl +++ b/src/geointerface.jl @@ -77,3 +77,39 @@ function GeoInterface.ncoord(::PolyhedralSurfaceTrait, end GeoInterface.ngeom(::PolyhedralSurfaceTrait, g::AbstractMesh) = length(g) GeoInterface.getgeom(::PolyhedralSurfaceTrait, g::AbstractMesh, i) = g[i] + +function GeoInterface.convert(::Type{Point}, type::PointTrait, geom) + dim = Int(ncoord(geom)) + return Point{dim}(GeoInterface.coordinates(geom)) +end + +function GeoInterface.convert(::Type{LineString}, type::LineStringTrait, geom) + dim = Int(ncoord(geom)) + return LineString([Point{dim}(GeoInterface.coordinates(p)) for p in getgeom(geom)]) +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 = GeoInterface.convert.(LineString, Ref(t), GeoInterface.gethole(geom)) + return Polygon(exterior, interiors) + end +end + +function GeoInterface.convert(::Type{MultiPoint}, type::MultiPointTrait, geom) + dim = Int(ncoord(geom)) + return MultiPoint([Point{dim}(GeoInterface.coordinates(p)) for p in getgeom(geom)]) +end + +function GeoInterface.convert(::Type{MultiLineString}, type::MultiLineStringTrait, geom) + t = LineStringTrait() + return MultiLineString([GeoInterface.convert(LineString, t, l) for l in getgeom(geom)]) +end + +function GeoInterface.convert(::Type{MultiPolygon}, type::MultiPolygonTrait, geom) + t = PolygonTrait() + return MultiPolygon([GeoInterface.convert(Polygon, t, poly) for poly in getgeom(geom)]) +end diff --git a/test/geointerface.jl b/test/geointerface.jl index 188d4464..b70d63fc 100644 --- a/test/geointerface.jl +++ b/test/geointerface.jl @@ -45,3 +45,57 @@ end mesh = triangle_mesh(Sphere(Point3f(0), 1)) @test testgeometry(mesh) end + +@testset "Convert" begin + # convert GeoJSON geometry types to GeometryBasics via the GeoInterface + point_str = """{"type":"Point","coordinates":[30.1,10.1]}""" + point_3d_str = """{"type":"Point","coordinates":[30.1,10.1,5.1]}""" + linestring_str = """{"type":"LineString","coordinates":[[30.1,10.1],[10.1,30.1],[40.1,40.1]]}""" + polygon_str = """{"type":"Polygon","coordinates":[[[30.1,10.1],[40.1,40.1],[20.1,40.1],[10.1,20.1],[30.1,10.1]]]}""" + polygon_hole_str = """{"type":"Polygon","coordinates":[[[35.1,10.1],[45.1,45.1],[15.1,40.1],[10.1,20.1],[35.1,10.1]],[[20.1,30.1],[35.1,35.1],[30.1,20.1],[20.1,30.1]]]}""" + multipoint_str = """{"type":"MultiPoint","coordinates":[[10.1,40.1],[40.1,30.1],[20.1,20.1],[30.1,10.1]]}""" + multilinestring_str = """{"type":"MultiLineString","coordinates":[[[10.1,10.1],[20.1,20.1],[10.1,40.1]],[[40.1,40.1],[30.1,30.1],[40.1,20.1],[30.1,10.1]]]}""" + multipolygon_str = """{"type":"MultiPolygon","coordinates":[[[[30.1,20.1],[45.1,40.1],[10.1,40.1],[30.1,20.1]]],[[[15.1,5.1],[40.1,10.1],[10.1,20.1],[5.1,10.1],[15.1,5.1]]]]}""" + multipolygon_hole_str = """{"type":"MultiPolygon","coordinates":[[[[40.1,40.1],[20.1,45.1],[45.1,30.1],[40.1,40.1]]],[[[20.1,35.1],[10.1,30.1],[10.1,10.1],[30.1,5.1],[45.1,20.1],[20.1,35.1]],[[30.1,20.1],[20.1,15.1],[20.1,25.1],[30.1,20.1]]]]}""" + + point_json = GeoJSON.read(point_str) + point_3d_json = GeoJSON.read(point_3d_str) + linestring_json = GeoJSON.read(linestring_str) + polygon_json = GeoJSON.read(polygon_str) + polygon_hole_json = GeoJSON.read(polygon_hole_str) + multipoint_json = GeoJSON.read(multipoint_str) + multilinestring_json = GeoJSON.read(multilinestring_str) + multipolygon_json = GeoJSON.read(multipolygon_str) + multipolygon_hole_json = GeoJSON.read(multipolygon_hole_str) + + point_gb = GeoInterface.convert(Point, point_json) + point_3d_gb = GeoInterface.convert(Point, point_3d_json) + linestring_gb = GeoInterface.convert(LineString, linestring_json) + polygon_gb = GeoInterface.convert(Polygon, polygon_json) + polygon_hole_gb = GeoInterface.convert(Polygon, polygon_hole_json) + multipoint_gb = GeoInterface.convert(MultiPoint, multipoint_json) + multilinestring_gb = GeoInterface.convert(MultiLineString, multilinestring_json) + multipolygon_gb = GeoInterface.convert(MultiPolygon, multipolygon_json) + multipolygon_hole_gb = GeoInterface.convert(MultiPolygon, multipolygon_hole_json) + + @test point_gb === Point{2, Float64}(30.1, 10.1) + @test point_3d_gb === Point{3, Float64}(30.1, 10.1, 5.1) + @test linestring_gb isa LineString + @test length(linestring_gb) == 2 + @test eltype(linestring_gb) == Line{2, Float64} + @test polygon_gb isa Polygon + @test isempty(polygon_gb.interiors) + @test polygon_hole_gb isa Polygon + @test length(polygon_hole_gb.interiors) == 1 + @test multipoint_gb isa MultiPoint + @test length(multipoint_gb) == 4 + @test multipoint_gb[4] === Point{2, Float64}(30.1, 10.1) + @test multilinestring_gb isa MultiLineString + @test length(multilinestring_gb) == 2 + @test multipolygon_gb isa MultiPolygon + @test length(multipolygon_gb) == 2 + @test multipolygon_hole_gb isa MultiPolygon + @test length(multipolygon_hole_gb) == 2 + @test length(multipolygon_hole_gb[1].interiors) == 0 + @test length(multipolygon_hole_gb[2].interiors) == 1 +end diff --git a/test/runtests.jl b/test/runtests.jl index 98474015..d3369239 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -3,6 +3,7 @@ using GeometryBasics using LinearAlgebra using GeometryBasics: attributes using GeoInterface +using GeoJSON @testset "GeometryBasics" begin