Skip to content

Commit c21a0a7

Browse files
authored
Add Polygon constructors and createPolygon functionality (#134)
* Add createPolygon with single argument and update Polygon constructor New createPolygon function allows creation of polygons without user specified holes. Polygon constructor added to convert a LinearRing to Polygon. Polygon constructor that takes a pointer limited by type. * Add tests for Polygon constructors - ERROR thrown Create a test file for GEOSGeom constructors found in geos_types.jl. Tests pass for Polygon constructed from vectors, but an "illegal instruction" error is thrown when one is constructed from a pointer. The error messages mentions destroyGeom. There are more tests below for constructing from a linear ring but these depend on the pointer constructor and thus cause a seg fault. * Add cloneGeom around pointers in Polygon constructor and finish tests Adding cloneGeom around pointers sent to Polygon constructor that takes in a pointer has stopped the tests from seg faulting. All desired Polygon constructors and createPolygon functionality added and tested. * Fix requested pull request changes Typo in comments, remove magic numbers, change variable name
1 parent 2c83c28 commit c21a0a7

File tree

6 files changed

+119
-586
lines changed

6 files changed

+119
-586
lines changed

examples/readme_examples.ipynb

Lines changed: 7 additions & 583 deletions
Large diffs are not rendered by default.

src/geos_functions.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,9 @@ function createPolygon(
443443
end
444444
result
445445
end
446+
# convenience function to create polygon without holes
447+
createPolygon(shell::GEOSGeom, context::GEOSContext = _context) =
448+
createPolygon(shell, GEOSGeom[], context)
446449

447450
function createCollection(
448451
geomtype::GEOSGeomTypes,

src/geos_types.jl

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,20 +74,34 @@ end
7474

7575
mutable struct Polygon <: AbstractGeometry
7676
ptr::GEOSGeom
77-
77+
# create polygon using GEOSGeom pointer - only makes sense if pointer points to a polygon or a linear ring to start with.
7878
function Polygon(ptr::GEOSGeom)
79-
polygon = new(ptr)
79+
id = LibGEOS.geomTypeId(ptr)
80+
if id == GEOS_POLYGON
81+
polygon = new(cloneGeom(ptr))
82+
elseif id == GEOS_LINEARRING
83+
polygon = new(cloneGeom(createPolygon(ptr)))
84+
else
85+
error("LibGEOS: Can't convert a pointer to an element with a GeomType ID of $id to a polygon (yet). Please open an issue if you think this conversion makes sense. ")
86+
end
8087
finalizer(destroyGeom, polygon)
8188
polygon
8289
end
90+
# using vector of coordinates in following form:
91+
# [[exterior], [hole1], [hole2], ...] where exterior and holeN are coordinates where the first and last point are the same
8392
function Polygon(coords::Vector{Vector{Vector{Float64}}})
8493
exterior = createLinearRing(coords[1])
8594
interiors = GEOSGeom[createLinearRing(lr) for lr in coords[2:end]]
8695
polygon = new(createPolygon(exterior, interiors))
8796
finalizer(destroyGeom, polygon)
8897
polygon
8998
end
90-
99+
# using 1 linear ring to form polygon with no holes - linear ring will be outer boundary of polygon
100+
Polygon(ring::LinearRing) = Polygon(ring.ptr)
101+
# using multiple linear rings to form polygon with holes - exterior linear ring will be polygon boundary and list of interior linear rings will form holes
102+
Polygon(exterior::LinearRing, holes::Vector{LinearRing}) =
103+
Polygon(createPolygon(exterior.ptr,
104+
GEOSGeom[ring.ptr for ring in holes]))
91105
end
92106

93107
mutable struct MultiPolygon <: AbstractGeometry

test/runtests.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ if version != LibGEOS.GEOS_CAPI_VERSION
1414
end
1515

1616
@testset "LibGEOS" begin
17+
include("test_geos_types.jl")
1718
include("test_geos_functions.jl")
1819
include("test_geos_operations.jl")
1920
include("test_geo_interface.jl")

test/test_geos_functions.jl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,14 @@ end
9090
@test LibGEOS.getCoordinates(LibGEOS.getCoordSeq(interiors[2])) ==
9191
Vector{Float64}[[8, 1], [9, 1], [9, 2], [8, 2], [8, 1]]
9292

93+
exterior_poly = LibGEOS.createPolygon(exterior)
94+
@test LibGEOS.getGeomDimensions(exterior_poly) == 2
95+
@test LibGEOS.geomTypeId(exterior_poly) == LibGEOS.GEOS_POLYGON
96+
@test LibGEOS.geomArea(exterior_poly) 100.0 atol = 1e-5
97+
@test_throws ErrorException LibGEOS.interiorRing(exterior_poly, 1)
98+
@test LibGEOS.equals(LibGEOS.exteriorRing(exterior_poly), exterior)
99+
100+
93101
# Interpolation and Projection
94102
ls = LibGEOS.createLineString(Vector{Float64}[[8, 1], [9, 1], [9, 2], [8, 2]])
95103
pt = LibGEOS.interpolate(ls, 2.5)

test/test_geos_types.jl

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
@testset "GEOS types" begin
2+
@testset "Polygon" begin
3+
# Test polygon made from vectors
4+
poly_vec = LibGEOS.Polygon([
5+
[[-2.0, -2.0], [2.0, -2.0], [2.0, 2.0], [-2.0,2.0], [-2.0, -2.0]],
6+
[[0.0, 0.0], [1.0, 1.0], [1.0,0.0], [0.0, 0.0]]])
7+
@test LibGEOS.isValid(poly_vec)
8+
@test LibGEOS.area(poly_vec) == 15.5
9+
@test !LibGEOS.isEmpty(poly_vec)
10+
@test LibGEOS.geomTypeId(poly_vec.ptr) == 3
11+
@test LibGEOS.getGeomDimensions(poly_vec.ptr) == 2
12+
@test LibGEOS.GeoInterface.coordinates(poly_vec) ==
13+
[[[-2.0, -2.0], [2.0, -2.0], [2.0, 2.0], [-2.0,2.0], [-2.0, -2.0]],
14+
[[0.0, 0.0], [1.0, 1.0], [1.0,0.0], [0.0, 0.0]]]
15+
@test length(LibGEOS.interiorRings(poly_vec)) == 1
16+
17+
# Test polygon made from polygon pointer
18+
poly_ptr = LibGEOS.Polygon(poly_vec.ptr)
19+
@test LibGEOS.isValid(poly_ptr)
20+
@test LibGEOS.equals(poly_vec, poly_ptr) # same area and coordinates
21+
@test LibGEOS.geomTypeId(poly_vec.ptr) == 3
22+
@test LibGEOS.getGeomDimensions(poly_vec.ptr) == 2
23+
@test length(LibGEOS.interiorRings(poly_vec)) == 1
24+
25+
# Test polygon made from linear ring pointer
26+
ring_ext = LibGEOS.LinearRing([[-2.0, -2.0], [2.0, -2.0], [2.0, 2.0],
27+
[-2.0, 2.0], [-2.0, -2.0]])
28+
poly_ringptr = LibGEOS.Polygon(ring_ext.ptr)
29+
# These change when made into polygon - compare below
30+
@test LibGEOS.geomTypeId(ring_ext.ptr) == 2
31+
@test LibGEOS.getGeomDimensions(ring_ext.ptr) == 1
32+
33+
@test LibGEOS.isValid(poly_ringptr)
34+
@test LibGEOS.area(poly_ringptr) == 16
35+
@test !LibGEOS.isEmpty(poly_ringptr)
36+
@test LibGEOS.geomTypeId(poly_ringptr.ptr) == 3
37+
@test LibGEOS.getGeomDimensions(poly_ringptr.ptr) == 2
38+
39+
# Test polygon made from point pointer
40+
point = LibGEOS.Point(1.0, 2.0)
41+
@test_throws ErrorException LibGEOS.Polygon(point.ptr)
42+
43+
# Test polygon made from 1 linear ring
44+
poly_ring = LibGEOS.Polygon(ring_ext)
45+
@test LibGEOS.isValid(poly_ring)
46+
@test LibGEOS.area(poly_ring) == 16
47+
@test !LibGEOS.isEmpty(poly_ring)
48+
@test LibGEOS.geomTypeId(poly_ring.ptr) == 3
49+
@test LibGEOS.getGeomDimensions(poly_ring.ptr) == 2
50+
@test LibGEOS.GeoInterface.coordinates(poly_ring) ==
51+
[[[-2.0, -2.0], [2.0, -2.0], [2.0, 2.0], [-2.0, 2.0], [-2.0, -2.0]]]
52+
@test length(LibGEOS.interiorRings(poly_ring)) == 0
53+
54+
# Test polygon made from multiple linear rings (exterior and interior)
55+
ring_int1 = LibGEOS.LinearRing([[0.0, 0.0], [1.0, 1.0], [1.0,0.0],
56+
[0.0, 0.0]])
57+
ring_int2 = LibGEOS.LinearRing([[0.0, 0.0], [0.0, -1.0], [-1.0, -1.0],
58+
[-1.0, 0.0], [0.0, 0.0]])
59+
60+
#1 ring
61+
poly_rings1 = LibGEOS.Polygon(ring_ext, [ring_int1])
62+
@test LibGEOS.isValid(poly_rings1)
63+
@test LibGEOS.geomTypeId(poly_rings1.ptr) == 3
64+
@test LibGEOS.getGeomDimensions(poly_rings1.ptr) == 2
65+
@test length(LibGEOS.interiorRings(poly_rings1)) == 1
66+
@test LibGEOS.equals(poly_rings1, poly_vec)
67+
68+
#2 rings
69+
poly_rings2 = LibGEOS.Polygon(ring_ext, [ring_int1, ring_int2])
70+
71+
@test LibGEOS.isValid(poly_rings2)
72+
@test LibGEOS.area(poly_rings2) == 14.5
73+
@test !LibGEOS.isEmpty(poly_rings2)
74+
@test LibGEOS.geomTypeId(poly_rings2.ptr) == 3
75+
@test LibGEOS.getGeomDimensions(poly_rings2.ptr) == 2
76+
@test LibGEOS.GeoInterface.coordinates(poly_rings2) ==
77+
[[[-2.0, -2.0], [2.0, -2.0], [2.0, 2.0], [-2.0, 2.0], [-2.0, -2.0]],
78+
[[0.0, 0.0], [1.0, 1.0], [1.0,0.0],[0.0, 0.0]],
79+
[[0.0, 0.0], [0.0, -1.0], [-1.0, -1.0],[-1.0, 0.0], [0.0, 0.0]]]
80+
@test length(LibGEOS.interiorRings(poly_rings2)) == 2
81+
end
82+
83+
end

0 commit comments

Comments
 (0)