From 889239a5c4c84a8c4194c1892f87c2b72e447548 Mon Sep 17 00:00:00 2001 From: Martijn Visser Date: Sun, 17 Nov 2019 13:07:56 +0100 Subject: [PATCH] create PROJError and raise it in custom error handler This handler, `log_func`, is registered in `__init__`. It simply throws when the level is at error, and ignores otherwise. Based on https://github.com/pyproj4/pyproj/pull/215. --- src/Proj4.jl | 26 +++++++++++++++----------- src/error.jl | 22 ++++++++++++++++++++++ test/proj6api.jl | 27 ++++++++++++++++++++++++--- test/runtests.jl | 4 ++-- 4 files changed, 63 insertions(+), 16 deletions(-) create mode 100644 src/error.jl diff --git a/src/Proj4.jl b/src/Proj4.jl index c92aa93..0fbdd8e 100644 --- a/src/Proj4.jl +++ b/src/Proj4.jl @@ -15,21 +15,11 @@ if !isfile(depsjl_path) end include(depsjl_path) -const PROJ_LIB = Ref{String}() - -# Module initialization function -function __init__() - # Always check your dependencies from `deps.jl` - check_deps() - - PROJ_LIB[] = abspath(@__DIR__, "..", "deps", "usr", "share", "proj") - proj_context_set_search_paths(1, [PROJ_LIB[]]) -end - include("projection_codes.jl") # ESRI and EPSG projection strings include("proj_capi.jl") # low-level C-facing functions (corresponding to src/proj_api.h) include("proj_common.jl") include("proj_c.jl") +include("error.jl") function _version() m = match(r"(\d+).(\d+).(\d+),.+", _get_release()) @@ -71,5 +61,19 @@ function unsafe_loadstringlist(ptr::Ptr{Cstring}) strings end +const PROJ_LIB = Ref{String}() + +# Module initialization function +function __init__() + # Always check your dependencies from `deps.jl` + check_deps() + + # register custom error handler + funcptr = @cfunction(log_func, Ptr{Cvoid}, (Ptr{Cvoid}, Cint, Cstring)) + proj_log_func(C_NULL, funcptr) + + PROJ_LIB[] = abspath(@__DIR__, "..", "deps", "usr", "share", "proj") + proj_context_set_search_paths(1, [PROJ_LIB[]]) +end end # module diff --git a/src/error.jl b/src/error.jl new file mode 100644 index 0000000..dbf8b88 --- /dev/null +++ b/src/error.jl @@ -0,0 +1,22 @@ +"Exception type based on PROJ error handling" +struct PROJError <: Exception + msg::String + # reset PROJ's error stack on construction + function PROJError(msg) + proj_errno_reset(C_NULL) + new(msg) + end +end + +function Base.showerror(io::IO, err::PROJError) + err = string("PROJError: ", err.msg) + println(io, err) +end + +"Custom error handler, automatically set with `proj_log_func`" +function log_func(user_data::Ptr{Cvoid}, level::Cint, msg::Cstring) + if level == PJ_LOG_ERROR + throw(PROJError(unsafe_string(msg))) + end + return C_NULL +end diff --git a/test/proj6api.jl b/test/proj6api.jl index d36d6e9..e70e53c 100644 --- a/test/proj6api.jl +++ b/test/proj6api.jl @@ -1,6 +1,27 @@ using Test import Proj4 +@testset "Error handling" begin + @test_throws ArgumentError Proj4.proj_errno_string(0) + @test Proj4.proj_errno_string(1) == "Operation not permitted" + @test Proj4.proj_errno_string(2) == "No such file or directory" + + # this throws an internal error that is handled by our log_func + @test_throws Proj4.PROJError Proj4.proj_create("+proj=bobbyjoe") + # ensure we have reset the error state + @test Proj4.proj_context_errno() == 0 + +end + +@testset "Database" begin + crs = Proj4.proj_create_from_database("EPSG", "4326", Proj4.PJ_CATEGORY_CRS, false, C_NULL) + Proj4.proj_errno(crs) + Proj4.proj_get_id_code(crs, 0) + # The following is wrong, but unfortunately segfaults. How to fix other than adding + # a null check before the ccall in this and other functions? + # Proj4.proj_get_id_code(C_NULL, 0) +end + @testset "New tests" begin pj_latlon = Proj4.proj_create("EPSG:4326") @@ -15,8 +36,8 @@ end @testset "Transformation between CRS" begin # taken from http://osgeo-org.1560.x6.nabble.com/PROJ-PROJ-6-0-0-proj-create-operation-factory-context-behavior-td5403470.html - src = Proj4.proj_create("IGNF:REUN47GAUSSL") # area : 55.17,-21.42,55.92,-20.76 - tgt = Proj4.proj_create("IGNF:RGAF09UTM20") # area : -63.2,14.25,-60.73,18.2 + src = Proj4.proj_create("IGNF:REUN47GAUSSL") # area : 55.17,-21.42,55.92,-20.76 + tgt = Proj4.proj_create("IGNF:RGAF09UTM20") # area : -63.2,14.25,-60.73,18.2 factory = Proj4.proj_create_operation_factory_context(C_NULL) @test factory != C_NULL @test Proj4.proj_context_errno() == 0 @@ -49,4 +70,4 @@ end Proj4.proj_destroy(src) Proj4.proj_destroy(tgt) # proj_context_destroy(c) -end \ No newline at end of file +end diff --git a/test/runtests.jl b/test/runtests.jl index 360bc38..faefbed 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,8 +1,6 @@ using Proj4 using Test -include("proj6api.jl") - @testset "Proj4" begin println(""" @@ -10,6 +8,8 @@ C library version: $(Proj4.version) [\"$(Proj4._get_release())\"] geodesic support: $(Proj4.has_geodesic_support) """) +include("proj6api.jl") + # Some very basic sanity checking wgs84 = Projection("+proj=longlat +datum=WGS84 +no_defs") utm56 = Projection("+proj=utm +zone=56 +south +datum=WGS84 +units=m +no_defs")