Skip to content

Commit 6c0d002

Browse files
authored
Refactor GeoInterface implementation. (#161)
* Refactor GeoInterface implementation. * Also fix ambiguity for current GeoInterface. * Remove latest T<:.
1 parent ff928ea commit 6c0d002

File tree

3 files changed

+98
-42
lines changed

3 files changed

+98
-42
lines changed

src/geo_interface.jl

Lines changed: 38 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -19,55 +19,67 @@ GeoInterface.geomtrait(::MultiPolygon) = MultiPolygonTrait()
1919
GeoInterface.geomtrait(::GeometryCollection) = GeometryCollectionTrait()
2020
GeoInterface.geomtrait(geom::PreparedGeometry) = GeoInterface.geomtrait(geom.ownedby)
2121

22-
GeoInterface.ngeom(::AbstractGeometryTrait, geom::AbstractGeometry) =
23-
isEmpty(geom) ? 0 : numGeometries(geom)
24-
GeoInterface.ngeom(::AbstractPointTrait, geom::AbstractGeometry) = 0
22+
GeoInterface.ngeom(::AbstractGeometryCollectionTrait, geom::AbstractMultiGeometry) =
23+
isEmpty(geom) ? 0 : Int(numGeometries(geom))
24+
GeoInterface.ngeom(::LineStringTrait, geom::LineString) = Int(numPoints(geom))
25+
GeoInterface.ngeom(::LinearRingTrait, geom::LinearRing) = Int(numPoints(geom))
26+
GeoInterface.ngeom(::PolygonTrait, geom::Polygon) = Int(numInteriorRings(geom)) + 1
27+
GeoInterface.ngeom(t::AbstractGeometryTrait, geom::PreparedGeometry) =
28+
GeoInterface.ngeom(t, geom.ownedby)
29+
GeoInterface.ngeom(::AbstractPointTrait, geom::Point) = 0
30+
GeoInterface.ngeom(::AbstractPointTrait, geom::PreparedGeometry) = 0
2531

26-
function GeoInterface.getgeom(::AbstractGeometryTrait, geom::AbstractGeometry, i)
32+
function GeoInterface.getgeom(
33+
::AbstractGeometryCollectionTrait,
34+
geom::AbstractMultiGeometry,
35+
i,
36+
)
2737
getGeometry(geom, i)
2838
end
29-
30-
GeoInterface.getgeom(::AbstractPointTrait, geom::AbstractGeometry, i) = nothing
31-
GeoInterface.ngeom(::AbstractGeometryTrait, geom::Union{LineString,LinearRing}) =
32-
numPoints(geom)
33-
GeoInterface.ngeom(t::AbstractPointTrait, geom::Union{LineString,LinearRing}) = 0
34-
GeoInterface.getgeom(::AbstractGeometryTrait, geom::Union{LineString,LinearRing}, i) =
35-
Point(getPoint(geom, i))
36-
GeoInterface.getgeom(::AbstractPointTrait, geom::Union{LineString,LinearRing}, i) = nothing
37-
38-
GeoInterface.ngeom(::AbstractGeometryTrait, geom::Polygon) = numInteriorRings(geom) + 1
39-
GeoInterface.ngeom(::AbstractPointTrait, geom::Polygon) = 0
40-
function GeoInterface.getgeom(::AbstractGeometryTrait, geom::Polygon, i)
39+
GeoInterface.getgeom(::MultiPointTrait, geom::MultiPoint, i) = getGeometry(geom, i)::Point
40+
GeoInterface.getgeom(::MultiLineStringTrait, geom::MultiLineString, i) =
41+
getGeometry(geom, i)::LineString
42+
GeoInterface.getgeom(::MultiPolygonTrait, geom::MultiPolygon, i) =
43+
getGeometry(geom, i)::Polygon
44+
GeoInterface.getgeom(
45+
::Union{LineStringTrait,LinearRingTrait},
46+
geom::Union{LineString,LinearRing},
47+
i,
48+
) = Point(getPoint(geom, i))
49+
GeoInterface.getgeom(t::AbstractPointTrait, geom::PreparedGeometry) = nothing
50+
function GeoInterface.getgeom(::PolygonTrait, geom::Polygon, i::Int)
4151
if i == 1
4252
LinearRing(exteriorRing(geom))
4353
else
4454
LinearRing(interiorRing(geom, i - 1))
4555
end
4656
end
47-
GeoInterface.getgeom(::AbstractPointTrait, geom::Polygon, i) = nothing
48-
49-
GeoInterface.ngeom(t::AbstractGeometryTrait, geom::PreparedGeometry) =
50-
GeoInterface.ngeom(t, geom.ownedby)
51-
GeoInterface.ngeom(t::AbstractPointTrait, geom::PreparedGeometry) = 0
5257
GeoInterface.getgeom(t::AbstractGeometryTrait, geom::PreparedGeometry, i) =
5358
GeoInterface.getgeom(t, geom.ownedby, i)
5459
GeoInterface.getgeom(t::AbstractPointTrait, geom::PreparedGeometry, i) = 0
5560

56-
GeoInterface.ncoord(::AbstractGeometryTrait, geom::AbstractGeometry) =
57-
isEmpty(geom) ? 0 : getCoordinateDimension(geom)
58-
GeoInterface.getcoord(::AbstractGeometryTrait, geom::AbstractGeometry, i) =
59-
getCoordinates(getCoordSeq(geom), 1)[i]
61+
GeoInterface.coordinates(t::AbstractPointTrait, geom::Point) = collect(getcoord(t, geom))
62+
GeoInterface.coordinates(t::AbstractPointTrait, geom::AbstractGeometry) = nothing
63+
GeoInterface.coordinates(t::AbstractGeometryTrait, geom::AbstractGeometry) =
64+
[GeoInterface.coordinates(x) for x in getgeom(t, geom)]
65+
GeoInterface.coordinates(t::AbstractGeometryCollectionTrait, geom::AbstractMultiGeometry) =
66+
[GeoInterface.coordinates(x) for x in getgeom(t, geom)]
6067

68+
GeoInterface.ncoord(::AbstractGeometryTrait, geom::AbstractGeometry) =
69+
isEmpty(geom) ? 0 : Int(getCoordinateDimension(geom))
6170
GeoInterface.ncoord(t::AbstractGeometryTrait, geom::PreparedGeometry) =
6271
GeoInterface.ncoord(t, geom.ownedby)
72+
73+
GeoInterface.getcoord(::AbstractGeometryTrait, geom::AbstractGeometry, i) =
74+
getCoordinates(getCoordSeq(geom), 1)[i]
6375
GeoInterface.getcoord(t::AbstractGeometryTrait, geom::PreparedGeometry, i) =
6476
GeoInterface.getcoord(t, geom.ownedby, i)
6577

6678
# FIXME this doesn't work for 3d geoms, Z is missing
6779
function GeoInterface.extent(::AbstractGeometryTrait, geom::AbstractGeometry)
6880
# minx, miny, maxx, maxy = getExtent(geom)
6981
env = envelope(geom)
70-
return Extent(X = (getXMin(env), getXMax(env)), Y = (getYMin(env), getYMax(env)))
82+
return Extent(; X = (getXMin(env), getXMax(env)), Y = (getYMin(env), getYMax(env)))
7183
end
7284

7385
GI.convert(::Type{Point}, ::PointTrait, geom::Point; context = nothing) = geom
@@ -180,7 +192,6 @@ function _geom_to_coord_seq(geom, context)
180192
return seq
181193
end
182194

183-
184195
GeoInterface.distance(
185196
::AbstractGeometryTrait,
186197
::AbstractGeometryTrait,
@@ -268,7 +279,6 @@ GeoInterface.union(
268279

269280
GeoInterfaceRecipes.@enable_geo_plots AbstractGeometry
270281

271-
272282
# -----
273283
# LibGeos operations for any GeoInterface.jl compatible geometries
274284
# -----

src/geos_types.jl

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
1+
"""All Geometries in LibGEOS are an AbstractGeometry."""
12
abstract type AbstractGeometry end
23

4+
"""
5+
All MultiGeometries in LibGEOS are an AbstractMultiGeometry.
6+
7+
Used to specialize on methods that only work on MultiGeometries, like `ngeom`.
8+
"""
9+
abstract type AbstractMultiGeometry <: AbstractGeometry end
10+
311
function Base.show(io::IO, geo::AbstractGeometry)
412
compact = get(io, :compact, false)
513
if compact
@@ -41,7 +49,7 @@ mutable struct Point <: AbstractGeometry
4149
Point(createPoint(x, y, z, context), context)
4250
end
4351

44-
mutable struct MultiPoint <: AbstractGeometry
52+
mutable struct MultiPoint <: AbstractMultiGeometry
4553
ptr::GEOSGeom
4654
context::GEOSContext
4755
# create a multipoint from a pointer - only makes sense if it is a pointer to a multipoint
@@ -111,7 +119,7 @@ mutable struct LineString <: AbstractGeometry
111119
end
112120
end
113121

114-
mutable struct MultiLineString <: AbstractGeometry
122+
mutable struct MultiLineString <: AbstractMultiGeometry
115123
ptr::GEOSGeom
116124
context::GEOSContext
117125
# create a multiline string from a multilinestring or a linestring pointer, else error
@@ -185,7 +193,6 @@ mutable struct LinearRing <: AbstractGeometry
185193
finalizer(destroyGeom, ring)
186194
ring
187195
end
188-
189196
end
190197

191198
mutable struct Polygon <: AbstractGeometry
@@ -227,7 +234,7 @@ mutable struct Polygon <: AbstractGeometry
227234
) = Polygon(createPolygon(exterior, holes, context), context)
228235
end
229236

230-
mutable struct MultiPolygon <: AbstractGeometry
237+
mutable struct MultiPolygon <: AbstractMultiGeometry
231238
ptr::GEOSGeom
232239
context::GEOSContext
233240
# create multipolygon using a multipolygon or polygon pointer, else error
@@ -274,7 +281,7 @@ mutable struct MultiPolygon <: AbstractGeometry
274281
)
275282
end
276283

277-
mutable struct GeometryCollection <: AbstractGeometry
284+
mutable struct GeometryCollection <: AbstractMultiGeometry
278285
ptr::GEOSGeom
279286
context::GEOSContext
280287
# create a geometric collection from a pointer to a geometric collection, else error
@@ -423,7 +430,7 @@ function compare(
423430
ng1 = ngeom(geo1)
424431
ng2 = ngeom(geo2)
425432
ng1 == ng2 || return false
426-
for i = 1:ng1
433+
for i in 1:ng1
427434
compare(cmp, getgeom(geo1, i), getgeom(geo2, i), ctx) || return false
428435
end
429436
end
@@ -443,7 +450,7 @@ function compare_coord_seqs(cmp, geo1, geo2, ctx)
443450
np1 == np2 || return false
444451
coords1 = Vector{Float64}(undef, ncoords1)
445452
coords2 = Vector{Float64}(undef, ncoords1)
446-
for i = 1:np1
453+
for i in 1:np1
447454
coordinates!(coords1, geo1, i, ctx)
448455
coordinates!(coords2, geo2, i, ctx)
449456
cmp(coords1, coords2) || return false
@@ -468,7 +475,7 @@ function compare_coord_seqs(cmp::IsApprox, geo1, geo2, ctx)
468475
s1 = 0.0
469476
s2 = 0.0
470477
s12 = 0.0
471-
for i = 1:np1
478+
for i in 1:np1
472479
coordinates!(coords1, geo1, i, ctx)
473480
coordinates!(coords2, geo2, i, ctx)
474481
if ncoords1 == 2
@@ -501,7 +508,7 @@ function Base.hash(geo::AbstractGeometry, h::UInt)::UInt
501508
if has_coord_seq(geo)
502509
return hash_coord_seq(geo, h)
503510
else
504-
for i = 1:ngeom(geo)
511+
for i in 1:ngeom(geo)
505512
h = hash(getgeom(geo, i), h)
506513
end
507514
end
@@ -514,14 +521,13 @@ function hash_coord_seq(geo::HasCoordSeq, h::UInt)::UInt
514521
end
515522
buf = Vector{Float64}(undef, nc)
516523
ctx = get_context(geo)
517-
for i = 1:npoints(geo)
524+
for i in 1:npoints(geo)
518525
coordinates!(buf, geo, i, ctx)
519526
h = hash(buf, h)
520527
end
521528
return h
522529
end
523530

524-
525531
# teach ccall how to get the pointer to pass to libgeos
526532
# this way the Julia compiler will track the lifetimes for us
527533
Base.unsafe_convert(::Type{Ptr{Cvoid}}, x::AbstractGeometry) = x.ptr

test/test_geo_interface.jl

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const LG = LibGEOS
1111
@test GeoInterface.ncoord(pt) == 2
1212
@test GeoInterface.getcoord(pt, 1) 1.0
1313
@test GeoInterface.testgeometry(pt)
14-
@test GeoInterface.extent(pt) == Extent(X = (1.0, 1.0), Y = (2.0, 2.0))
14+
@test GeoInterface.extent(pt) == Extent(X=(1.0, 1.0), Y=(2.0, 2.0))
1515
plot(pt)
1616

1717
pt = LibGEOS.Point(1.0, 2.0, 3.0)
@@ -32,6 +32,11 @@ const LG = LibGEOS
3232
@test GeoInterface.geomtrait(pt) == PointTrait()
3333
@test GeoInterface.testgeometry(pt)
3434

35+
@inferred GeoInterface.ncoord(pt)
36+
@inferred GeoInterface.ngeom(pt)
37+
@inferred GeoInterface.getgeom(pt)
38+
@inferred GeoInterface.coordinates(pt)
39+
3540
pt = LibGEOS.readgeom("POINT EMPTY")
3641
@test GeoInterface.coordinates(pt) Float64[] atol = 1e-5
3742
@test GeoInterface.geomtrait(pt) == PointTrait()
@@ -49,6 +54,11 @@ const LG = LibGEOS
4954
@test GeoInterface.testgeometry(mpt)
5055
plot(mpt)
5156

57+
@inferred GeoInterface.ncoord(mpt)
58+
@inferred GeoInterface.ngeom(mpt)
59+
@inferred GeoInterface.getgeom(mpt)
60+
@inferred GeoInterface.coordinates(mpt)
61+
5262
coords = Vector{Float64}[[8, 1], [9, 1], [9, 2], [8, 2]]
5363
ls = LibGEOS.LineString(coords)
5464
@test GeoInterface.coordinates(ls) == coords
@@ -60,6 +70,11 @@ const LG = LibGEOS
6070
@test GeoInterface.testgeometry(ls)
6171
plot(ls)
6272

73+
@inferred GeoInterface.ncoord(ls)
74+
@inferred GeoInterface.ngeom(ls)
75+
@inferred GeoInterface.getgeom(ls)
76+
@inferred GeoInterface.coordinates(ls)
77+
6378
ls = LibGEOS.readgeom("LINESTRING EMPTY")
6479
@test GeoInterface.coordinates(ls) == []
6580
@test GeoInterface.geomtrait(ls) == LineStringTrait()
@@ -72,6 +87,11 @@ const LG = LibGEOS
7287
@test GeoInterface.testgeometry(mls)
7388
plot(mls)
7489

90+
@inferred GeoInterface.ncoord(mls)
91+
@inferred GeoInterface.ngeom(mls)
92+
@inferred GeoInterface.getgeom(mls)
93+
@inferred GeoInterface.coordinates(mls)
94+
7595
coords = Vector{Float64}[[8, 1], [9, 1], [9, 2], [8, 2], [8, 1]]
7696
lr = LibGEOS.LinearRing(coords)
7797
@test GeoInterface.coordinates(lr) == coords
@@ -84,6 +104,11 @@ const LG = LibGEOS
84104
# Cannot convert LinearRingTrait to series data for plotting
85105
# plot(lr)
86106

107+
@inferred GeoInterface.ncoord(lr)
108+
@inferred GeoInterface.ngeom(lr)
109+
@inferred GeoInterface.getgeom(lr)
110+
@inferred GeoInterface.coordinates(lr)
111+
87112
coords = Vector{Vector{Float64}}[
88113
Vector{Float64}[[0, 0], [10, 0], [10, 10], [0, 10], [0, 0]],
89114
Vector{Float64}[[1, 8], [2, 8], [2, 9], [1, 9], [1, 8]],
@@ -99,6 +124,11 @@ const LG = LibGEOS
99124
@test GeoInterface.testgeometry(polygon)
100125
plot(polygon)
101126

127+
@inferred GeoInterface.ncoord(polygon)
128+
@inferred GeoInterface.ngeom(polygon)
129+
@inferred GeoInterface.getgeom(polygon)
130+
@inferred GeoInterface.coordinates(polygon)
131+
102132
polygon = LibGEOS.readgeom("POLYGON EMPTY")
103133
@test GeoInterface.coordinates(polygon) == [[]]
104134
@test GeoInterface.geomtrait(polygon) == PolygonTrait()
@@ -115,13 +145,18 @@ const LG = LibGEOS
115145
]]]
116146
@test GeoInterface.geomtrait(multipolygon) == MultiPolygonTrait()
117147
@test GeoInterface.testgeometry(multipolygon)
118-
@test GeoInterface.extent(multipolygon) == Extent(X = (0.0, 10.0), Y = (0.0, 10.0))
148+
@test GeoInterface.extent(multipolygon) == Extent(X=(0.0, 10.0), Y=(0.0, 10.0))
119149
plot(multipolygon)
120150

151+
@inferred GeoInterface.ncoord(multipolygon)
152+
@inferred GeoInterface.ngeom(multipolygon)
153+
@inferred GeoInterface.getgeom(multipolygon)
154+
@inferred GeoInterface.coordinates(multipolygon)
155+
121156
pmultipolygon = LibGEOS.prepareGeom(multipolygon)
122157
@test GeoInterface.geomtrait(pmultipolygon) == MultiPolygonTrait()
123158
@test GeoInterface.testgeometry(pmultipolygon)
124-
@test GeoInterface.extent(pmultipolygon) == Extent(X = (0.0, 10.0), Y = (0.0, 10.0))
159+
@test GeoInterface.extent(pmultipolygon) == Extent(X=(0.0, 10.0), Y=(0.0, 10.0))
125160
LibGEOS.destroyGeom(pmultipolygon)
126161

127162
geomcollection = LibGEOS.readgeom(
@@ -196,6 +231,11 @@ const LG = LibGEOS
196231
@test GeoInterface.testgeometry(geomcollection)
197232
plot(geomcollection)
198233

234+
@inferred GeoInterface.ncoord(geomcollection)
235+
@inferred GeoInterface.ngeom(geomcollection)
236+
@inferred GeoInterface.getgeom(geomcollection)
237+
# @inferred GeoInterface.coordinates(geomcollection) # can't be inferred
238+
199239
geomcollection = LibGEOS.readgeom(
200240
"GEOMETRYCOLLECTION(MULTIPOINT(0 0, 0 0, 1 1),LINESTRING(1 1, 2 2, 2 2, 0 0),POLYGON((5 5, 0 0, 0 2, 2 2, 5 5)))",
201241
)

0 commit comments

Comments
 (0)