Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ GeoInterface = "cf35fbd7-0cd7-5166-be24-54bfbe79505f"
LibSpatialIndex_jll = "00e98e2a-4326-5239-88cb-15dcbe1c18d0"

[compat]
Aqua = "0.7"
Aqua = "0.8"
GeoInterface = "1"
LibSpatialIndex_jll = "1.9"
julia = "1.6"
Test = "1.6"
Comment thread
asinghvi17 marked this conversation as resolved.
Outdated

[extras]
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
Expand Down
26 changes: 18 additions & 8 deletions src/LibSpatialIndex.jl
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,13 @@ module LibSpatialIndex
minvalues::Vector{Float64},
maxvalues::Vector{Float64}
)
C.Index_InsertData(rtree.index, Int64(id), pointer(minvalues),
pointer(maxvalues), UInt32(length(minvalues)), Ptr{UInt8}(0), Cint(0)
length(minvalues) == rtree.ndim || throw(DimensionMismatch("Minimum values must have same length as RTree dimensions"))
length(maxvalues) == rtree.ndim || throw(DimensionMismatch("Maximum values must have same length as RTree dimensions"))

# pass C_NULL as data - it should not be dereferenced, but making it null hopefully means if it is
# it will be easier to debug
C.Index_InsertData(rtree.index, Int64(id), minvalues,
maxvalues, UInt32(rtree.ndim), C_NULL, Cint(0)
)
end
function insert!(rtree::RTree, id::Integer, extent::GI.Extent)
Expand Down Expand Up @@ -213,10 +218,13 @@ module LibSpatialIndex
minvalues::Vector{Float64},
maxvalues::Vector{Float64}
)
length(minvalues) == rtree.ndim || throw(DimensionMismatch("Minimum values must have same length as RTree dimensions"))
length(maxvalues) == rtree.ndim || throw(DimensionMismatch("Maximum values must have same length as RTree dimensions"))

items = Ref{Ptr{Int64}}()
nresults = Ref{UInt64}()
result = C.Index_Intersects_id(rtree.index, pointer(minvalues),
pointer(maxvalues), UInt32(length(minvalues)), items, nresults
result = C.Index_Intersects_id(rtree.index, minvalues,
maxvalues, UInt32(rtree.ndim), items, nresults
)
_checkresult(result, "Index_Intersects_id: Failed to evaluate")
unsafe_wrap(Array, items[], nresults[])
Expand Down Expand Up @@ -260,10 +268,13 @@ module LibSpatialIndex
maxvalues::Vector{Float64},
k::Integer
)
length(minvalues) == rtree.ndim || throw(DimensionMismatch("Minimum values must have same length as RTree dimensions"))
length(maxvalues) == rtree.ndim || throw(DimensionMismatch("Maximum values must have same length as RTree dimensions"))

items = Ref{Ptr{Int64}}()
nresults = Ref{UInt64}(k)
result = C.Index_NearestNeighbors_id(rtree.index, pointer(minvalues),
pointer(maxvalues), UInt32(length(minvalues)), items, nresults)
result = C.Index_NearestNeighbors_id(rtree.index, minvalues,
maxvalues, UInt32(rtree.ndim), items, nresults)
_checkresult(result, "Index_NearestNeighbors_id: Failed to evaluate")
unsafe_wrap(Array, items[], nresults[])
end
Expand Down Expand Up @@ -300,5 +311,4 @@ module LibSpatialIndex
end

_not_point_or_ext_error() = throw(ArgumentError("object is not a point, and does not have an extent"))

end # module
end # module
144 changes: 84 additions & 60 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,72 +4,96 @@ import GeoInterface as GI
import LibSpatialIndex as SI
import Aqua

@testset "Simple Tutorial" begin
# based on https://github.com/libspatialindex/libspatialindex/wiki/Simple-Tutorial
println("Testing LibSpatialIndex v$(SI.version())")
props = SI.C.IndexProperty_Create()
SI.C.IndexProperty_SetIndexType(props, SI.C.RT_RTree)
SI.C.IndexProperty_SetIndexStorage(props, SI.C.RT_Memory)
idx = SI.C.Index_Create(props)
SI.C.IndexProperty_Destroy(props)
@test Bool(SI.C.Index_IsValid(idx))
@testset "LibSpatialIndex" begin
@testset "Simple Tutorial" begin
# based on https://github.com/libspatialindex/libspatialindex/wiki/Simple-Tutorial
println("Testing LibSpatialIndex v$(SI.version())")
props = SI.C.IndexProperty_Create()
SI.C.IndexProperty_SetIndexType(props, SI.C.RT_RTree)
SI.C.IndexProperty_SetIndexStorage(props, SI.C.RT_Memory)
idx = SI.C.Index_Create(props)
SI.C.IndexProperty_Destroy(props)
@test Bool(SI.C.Index_IsValid(idx))

# load()
min = [0.5, 0.5]
max = [0.5, 0.5]
SI.C.Index_InsertData(idx, 1, min, max, 2, Ptr{UInt8}(C_NULL), 0)
# load()
min = [0.5, 0.5]
max = [0.5, 0.5]
SI.C.Index_InsertData(idx, 1, min, max, 2, Ptr{UInt8}(C_NULL), 0)

# query()
min = [0.0, 0.0]
max = [1.0, 1.0]
ndims = UInt32(2)
nresults = Ref{UInt64}()
SI.C.Index_Intersects_count(idx, min, max, ndims, nresults)
@test nresults[] == 1
# query()
min = [0.0, 0.0]
max = [1.0, 1.0]
ndims = UInt32(2)
nresults = Ref{UInt64}()
SI.C.Index_Intersects_count(idx, min, max, ndims, nresults)
@test nresults[] == 1

# bounds()
pmins = zeros(2)
pmaxs = zeros(2)
ndims = Ref{UInt32}()
SI.C.Index_GetBounds(idx, pointer_from_objref(pmins), pointer_from_objref(pmaxs), ndims);
@test ndims[] == 2
@test isapprox(pmins, [0.5, 0.5])
@test isapprox(pmaxs, [0.5, 0.5])
end
# bounds()
pmins_p = Ref{Ptr{Float64}}()
pmaxs_p = Ref{Ptr{Float64}}()
ndims = Ref{UInt32}()

@testset "Simple Operations" begin
rtree = SI.RTree(2)
result = SI.insert!(rtree, 1, [0.,0.], [1.,1.])
@test result == SI.C.RT_None
result = SI.insert!(rtree, 2, [0.,0.], [2.,2.])
@test result == SI.C.RT_None
@test SI.intersects(rtree, [0.,0.],[1.,1.]) == [1,2]
@test SI.intersects(rtree, [0.,0.]) == [1,2]
@test SI.intersects(rtree, [2.,2.]) == [2]
@test SI.intersects(rtree, [1.5,1.5],[2.,2.]) == [2]
@test sort(SI.knn(rtree, [2.,2.],[2.,2.], 1)) == [2]
@test sort(SI.knn(rtree, [2.,2.],[2.,2.], 2)) == [1,2]
@test sort(SI.knn(rtree, [2.,2.],[2.,2.], 3)) == [1,2]
@test sort(SI.knn(rtree, [2.,2.], 1)) == [2]
@test sort(SI.knn(rtree, [2.,2.], 2)) == [1,2]
end
SI.C.Index_GetBounds(idx, pmins_p, pmaxs_p, ndims);

@testset "GeoInterface/Extents Operations" begin
rtree = SI.RTree(2)
result = SI.insert!(rtree, 1, GI.Extent(X=(0.0, 1.0), Y=(0.0, 1.0)))
@test result == SI.C.RT_None
@test ndims[] == 2
pmins = unsafe_wrap(Vector{Float64}, pmins_p[], ndims[], own=true)
pmaxs = unsafe_wrap(Vector{Float64}, pmaxs_p[], ndims[], own=true)

polygon = GI.Polygon([GI.LinearRing([(0.0, 0.0), (0.5, 0.0), (2.0, 0.5), (0.0, 2.0), (0.0, 0.0)])])
result = SI.insert!(rtree, 2, polygon)
@test isapprox(pmins, [0.5, 0.5])
@test isapprox(pmaxs, [0.5, 0.5])
end

@test result == SI.C.RT_None
@test SI.intersects(rtree, GI.LineString([(0.0, 0.0), (1.0, 1.0)])) == [1, 2]
@test SI.intersects(rtree, GI.Point(0.0, 0.0)) == [1, 2]
@test SI.intersects(rtree, (X=2.0, Y=2.0)) == [2]
@test sort(SI.knn(rtree, GI.Extent(X=(2.0, 2.0), Y=(2.0, 2.0)), 1)) == [2]
@test sort(SI.knn(rtree, (2.0, 2.0), 1)) == [2]
end
@testset "Simple Operations" begin
rtree = SI.RTree(2)
result = SI.insert!(rtree, 1, [0.,0.], [1.,1.])
@test result == SI.C.RT_None
result = SI.insert!(rtree, 2, [0.,0.], [2.,2.])
@test result == SI.C.RT_None
@test SI.intersects(rtree, [0.,0.],[1.,1.]) == [1,2]
@test SI.intersects(rtree, [0.,0.]) == [1,2]
@test SI.intersects(rtree, [2.,2.]) == [2]
@test SI.intersects(rtree, [1.5,1.5],[2.,2.]) == [2]
@test sort(SI.knn(rtree, [2.,2.],[2.,2.], 1)) == [2]
@test sort(SI.knn(rtree, [2.,2.],[2.,2.], 2)) == [1,2]
@test sort(SI.knn(rtree, [2.,2.],[2.,2.], 3)) == [1,2]
@test sort(SI.knn(rtree, [2.,2.], 1)) == [2]
@test sort(SI.knn(rtree, [2.,2.], 2)) == [1,2]
end

@testset "GeoInterface/Extents Operations" begin
rtree = SI.RTree(2)
result = SI.insert!(rtree, 1, GI.Extent(X=(0.0, 1.0), Y=(0.0, 1.0)))
@test result == SI.C.RT_None

polygon = GI.Polygon([GI.LinearRing([(0.0, 0.0), (0.5, 0.0), (2.0, 0.5), (0.0, 2.0), (0.0, 0.0)])])
result = SI.insert!(rtree, 2, polygon)

@test result == SI.C.RT_None
@test SI.intersects(rtree, GI.LineString([(0.0, 0.0), (1.0, 1.0)])) == [1, 2]
@test SI.intersects(rtree, GI.Point(0.0, 0.0)) == [1, 2]
@test SI.intersects(rtree, (X=2.0, Y=2.0)) == [2]
@test sort(SI.knn(rtree, GI.Extent(X=(2.0, 2.0), Y=(2.0, 2.0)), 1)) == [2]
@test sort(SI.knn(rtree, (2.0, 2.0), 1)) == [2]
end

@testset "Bounds checks" begin
# confirm that bounds checks on the Julia side are working
tree = SI.RTree(2)
@test_throws DimensionMismatch SI.insert!(tree, 0, [0.0], [0.0, 0.1])
@test_throws DimensionMismatch SI.insert!(tree, 0, [0.0, 1.0], [0.1])
# should throw even if they're the same length if they don't match dimensions
@test_throws DimensionMismatch SI.insert!(tree, 0, [0.0, 0.1, 1.1], [0.0, 0.1, 1.1])

@test_throws DimensionMismatch SI.intersects(tree, [0.0], [0.0, 0.1])
@test_throws DimensionMismatch SI.intersects(tree, [0.0, 1.0], [0.1])
@test_throws DimensionMismatch SI.intersects(tree, [0.0, 0.1, 1.1], [0.0, 0.1, 1.1])

@test_throws DimensionMismatch SI.knn(tree, [0.0], [0.0, 0.1], 1)
@test_throws DimensionMismatch SI.knn(tree, [0.0, 1.0], [0.1], 1)
@test_throws DimensionMismatch SI.knn(tree, [0.0, 0.1, 1.1], [0.0, 0.1, 1.1], 1)
end

@testset "Aqua" begin
Aqua.test_all(LibSpatialIndex)
@testset "Aqua" begin
Aqua.test_all(LibSpatialIndex)
end
end
Loading