Skip to content

Commit 8778ccc

Browse files
authored
Add Julian progress functions. (#353)
* Fix macro callback. * Enable easier progress functions. * Use Ref() to pass Function. * Merge master. * Simplified macro. * Let's debug this some more... * Don't Ref things, but let ccall handle it. * Upped compat to GDAL 1.5.1. * Update docstrings.
1 parent 19ddbb4 commit 8778ccc

File tree

5 files changed

+84
-62
lines changed

5 files changed

+84
-62
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ CEnum = "0.4"
2323
ColorTypes = "0.10, 0.11"
2424
DiskArrays = "0.3"
2525
Extents = "0.1"
26-
GDAL = "1.5"
26+
GDAL = "1.5.1"
2727
GeoFormatTypes = "0.3, 0.4"
2828
GeoInterface = "1"
2929
GeoInterfaceRecipes = "1.0"

src/dataset.jl

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""
22
copywholeraster(source::AbstractDataset, dest::AbstractDataset;
3-
<keyword arguments>)
3+
options::StringList, progressfunc::Function)
44
55
Copy all dataset raster data.
66
@@ -14,6 +14,9 @@ pixel interleaved operation and `\"COMPRESSED=YES\"` to force alignment on
1414
target dataset block sizes to achieve best compression. More options may be
1515
supported in the future.
1616
17+
For progress reporting one can pass `progressfunc` function(::Float64, ::String)::Bool
18+
to call to report progress.
19+
1720
### Additional Remarks
1821
This function is primarily intended to support implementation of driver
1922
specific `createcopy()` functions. It implements efficient copying, in
@@ -24,15 +27,14 @@ function copywholeraster!(
2427
source::AbstractDataset,
2528
dest::D;
2629
options = StringList(C_NULL),
27-
progressfunc::Function = GDAL.gdaldummyprogress,
28-
progressdata::Any = C_NULL,
30+
progressfunc::Function = _dummyprogress,
2931
)::D where {D<:AbstractDataset}
3032
result = GDAL.gdaldatasetcopywholeraster(
3133
source,
3234
dest,
3335
options,
34-
@cplprogress(progressfunc),
35-
progressdata,
36+
@cfunction(_progresscallback, Cint, (Cdouble, Cstring, Ptr{Cvoid})),
37+
progressfunc,
3638
)
3739
@cplerr result "Failed to copy whole raster"
3840
return dest
@@ -56,10 +58,11 @@ provided template dataset.
5658
* `filename` the filename for the new dataset. UTF-8 encoded.
5759
* `driver` the driver to use for creating the new dataset
5860
* `strict` ``true`` if the copy must be strictly equivalent, or more
59-
normally ``false`` if the copy may adapt as needed for the output format.
61+
normally ``false`` if the copy may adapt as needed for the output format.
6062
* `options` additional format dependent options controlling creation
61-
of the output file. `The APPEND_SUBDATASET=YES` option can be specified to
62-
avoid prior destruction of existing dataset.
63+
of the output file. `The APPEND_SUBDATASET=YES` option can be specified to
64+
avoid prior destruction of existing dataset.
65+
* `progressfunc` a function(::Float64, ::String)::Bool to call to report progress
6366
6467
### Returns
6568
a pointer to the newly created dataset (may be read-only access).
@@ -93,8 +96,7 @@ function unsafe_copy(
9396
driver::Driver = getdriver(dataset),
9497
strict::Bool = false,
9598
options = StringList(C_NULL),
96-
progressfunc::Function = GDAL.gdaldummyprogress,
97-
progressdata = C_NULL,
99+
progressfunc::Function = _dummyprogress,
98100
)::Dataset
99101
return Dataset(
100102
GDAL.gdalcreatecopy(
@@ -103,8 +105,8 @@ function unsafe_copy(
103105
dataset,
104106
strict,
105107
options,
106-
@cplprogress(progressfunc),
107-
progressdata,
108+
@cfunction(_progresscallback, Cint, (Cdouble, Cstring, Ptr{Cvoid})),
109+
progressfunc,
108110
),
109111
)
110112
end
@@ -126,10 +128,11 @@ provided template dataset.
126128
* `filename` the filename for the new dataset. UTF-8 encoded.
127129
* `driver` the driver to use for creating the new dataset
128130
* `strict` ``true`` if the copy must be strictly equivalent, or more
129-
normally ``false`` if the copy may adapt as needed for the output format.
131+
normally ``false`` if the copy may adapt as needed for the output format.
130132
* `options` additional format dependent options controlling creation
131-
of the output file. `The APPEND_SUBDATASET=YES` option can be specified to
132-
avoid prior destruction of existing dataset.
133+
of the output file. `The APPEND_SUBDATASET=YES` option can be specified to
134+
avoid prior destruction of existing dataset.
135+
* `progressfunc` a function(::Float64, ::String)::Bool to call to report progress
133136
134137
### Example
135138
```
@@ -152,8 +155,7 @@ function copy(
152155
driver::Driver = getdriver(dataset),
153156
strict::Bool = false,
154157
options = StringList(C_NULL),
155-
progressfunc::Function = GDAL.gdaldummyprogress,
156-
progressdata = C_NULL,
158+
progressfunc::Function = _dummyprogress,
157159
)::IDataset
158160
return IDataset(
159161
GDAL.gdalcreatecopy(
@@ -162,8 +164,8 @@ function copy(
162164
dataset,
163165
strict,
164166
options,
165-
@cplprogress(progressfunc),
166-
progressdata,
167+
@cfunction(_progresscallback, Cint, (Cdouble, Cstring, Ptr{Cvoid})),
168+
progressfunc,
167169
),
168170
)
169171
end
@@ -931,7 +933,7 @@ end
931933

932934
"""
933935
buildoverviews!(dataset::AbstractDataset, overviewlist::Vector{Cint};
934-
bandlist, resampling="NEAREST", progressfunc, progressdata)
936+
bandlist, resampling="NEAREST", progressfunc)
935937
936938
Build raster overview(s).
937939
@@ -946,16 +948,14 @@ returned, and CPLGetLastErrorNo() will return CPLE_NotSupported.
946948
* `sampling` one of "NEAREST" (default), "GAUSS","CUBIC","AVERAGE","MODE",
947949
"AVERAGE_MAGPHASE" or "NONE" controlling the downsampling
948950
method applied.
949-
* `progressfunc` a function to call to report progress, or `NULL`.
950-
* `progressdata` application data to pass to the progress function.
951+
* `progressfunc` a function(::Float64, ::String)::Bool to call to report progress
951952
"""
952953
function buildoverviews!(
953954
dataset::T,
954955
overviewlist::Vector{Cint};
955956
bandlist::Vector{Cint} = Cint[],
956957
resampling::AbstractString = "NEAREST",
957-
progressfunc::Function = GDAL.gdaldummyprogress,
958-
progressdata = C_NULL,
958+
progressfunc::Function = _dummyprogress,
959959
)::T where {T<:AbstractDataset}
960960
result = GDAL.gdalbuildoverviews(
961961
dataset,
@@ -964,8 +964,8 @@ function buildoverviews!(
964964
overviewlist,
965965
length(bandlist),
966966
bandlist,
967-
@cplprogress(progressfunc),
968-
progressdata,
967+
@cfunction(_progresscallback, Cint, (Cdouble, Cstring, Ptr{Cvoid})),
968+
progressfunc,
969969
)
970970
@cplerr result "Failed to build overviews"
971971
return dataset

src/raster/rasterband.jl

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ end
257257

258258
"""
259259
copywholeraster!( source::AbstractRasterBand, dest::AbstractRasterBand;
260-
[options, [progressdata, [progressfunc]]])
260+
[options, [progressfunc]])
261261
262262
Copy all raster band raster data.
263263
@@ -276,22 +276,20 @@ More options may be supported in the future.
276276
* `source` the source band
277277
* `dest` the destination band
278278
* `options` transfer hints in "StringList" Name=Value format.
279-
* `progressfunc` progress reporting function.
280-
* `progressdata` callback data for progress function.
279+
* `progressfunc` a function(::Float64, ::String)::Bool to call to report progress
281280
"""
282281
function copywholeraster!(
283282
source::T,
284283
dest::AbstractRasterBand;
285284
options = StringList(C_NULL),
286-
progressdata = C_NULL,
287-
progressfunc::Function = GDAL.gdaldummyprogress,
285+
progressfunc::Function = _dummyprogress,
288286
)::T where {T<:AbstractRasterBand}
289287
result = GDAL.gdalrasterbandcopywholeraster(
290288
source,
291289
dest,
292290
options,
293-
@cplprogress(progressfunc),
294-
progressdata,
291+
@cfunction(_progresscallback, Cint, (Cdouble, Cstring, Ptr{Cvoid})),
292+
progressfunc,
295293
)
296294
@cplerr result "Failed to copy whole raster"
297295
return source
@@ -414,8 +412,7 @@ images in one file from another outside the overview architecture.
414412
### Keyword Arguments
415413
* `resampling` (optional) Resampling algorithm (eg. "AVERAGE"). default to
416414
"NEAREST".
417-
* `progressfunc` (optional) progress report function.
418-
* `progressdata` (optional) progress function callback data.
415+
* `progressfunc` (optional) a function(::Float64, ::String)::Bool to call to report progress
419416
420417
### Additional Remarks
421418
The output bands need to exist in advance.
@@ -429,18 +426,15 @@ function regenerateoverviews!(
429426
band::T,
430427
overviewbands::Vector{<:AbstractRasterBand},
431428
resampling::AbstractString = "NEAREST",
432-
# progressfunc::Function = GDAL.gdaldummyprogress,
433-
progressdata = C_NULL,
429+
progressfunc::Function = _dummyprogress,
434430
)::T where {T<:AbstractRasterBand}
435-
cfunc =
436-
@cfunction(GDAL.gdaldummyprogress, Cint, (Cdouble, Cstring, Ptr{Cvoid}))
437431
result = GDAL.gdalregenerateoverviews(
438432
band,
439433
length(overviewbands),
440434
overviewbands,
441435
resampling,
442-
cfunc,
443-
progressdata,
436+
@cfunction(_progresscallback, Cint, (Cdouble, Cstring, Ptr{Cvoid})),
437+
progressfunc,
444438
)
445439
@cplerr result "Failed to regenerate overviews"
446440
return band

src/utils.jl

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -228,25 +228,41 @@ macro cplwarn(code, message)
228228
end
229229
end
230230

231-
macro cplprogress(progressfunc)
232-
@static if Sys.ARCH == :aarch64
233-
@warn "User provided progress functions are unsupported on this architecture."
234-
quote
235-
@cfunction(
236-
$(Expr(:$, esc(GDAL.gdaldummyprogress))),
237-
Cint,
238-
(Cdouble, Cstring, Ptr{Cvoid})
239-
)
240-
end
241-
else
242-
quote
243-
@cfunction(
244-
$(Expr(:$, esc(progressfunc))),
245-
Cint,
246-
(Cdouble, Cstring, Ptr{Cvoid})
247-
)
248-
end
249-
end
231+
"""
232+
Default progress function, which logs no progress.
233+
A progress function should return true to continue, or false to abort.
234+
"""
235+
function _dummyprogress(progress::Cdouble, message = "")::Bool
236+
return true
237+
end
238+
239+
"""
240+
_progresscallback(dfComplete::Cdouble, pszMessage::Cstring, pProgressArg::Ptr{Cvoid})::Cint
241+
242+
The progress callback function to be passed to GDAL. Users can provide their own progress function by
243+
passing a function of the form `f(dfComplete::Cdouble, pszMessage::Cstring)::Bool` to the
244+
`progressfunc` keyword argument of the functions that implement it, which should be wrapped in a `Ref`.
245+
246+
In essence, we pass a pointer to the (user-provided) progress function to GDAL, and GDAL passes it back to us,
247+
including the progress ratio and message. We then call the user-provided function with these arguments.
248+
249+
### Parameters
250+
* `dfComplete` – completion ratio from 0.0 to 1.0.
251+
* `pszMessage` – optional message (made available as an argument to the user-defined progress function).
252+
* `pProgressArg` – callback data argument (i.e. user-defined progress function, see `_dummyprogress`).
253+
"""
254+
function _progresscallback(
255+
dfComplete::Cdouble,
256+
pszMessage::Cstring,
257+
pProgressArg::Ptr{Cvoid}, # A pointer to Ref(_dummyprogress) by default
258+
)::Cint
259+
pProgressArg == C_NULL && return true
260+
# User provided functions are wrapped in a Ref so the conversion to Ptr{Cvoid} works
261+
# Here we do the reverse, load the pointer and unwrap the ref, so we get the function.
262+
f = unsafe_pointer_to_objref(pProgressArg)
263+
isa(f, Function) || return true
264+
pszMessage == C_NULL && return f(dfComplete)::Bool
265+
return f(dfComplete, unsafe_string(pszMessage))::Bool
250266
end
251267

252268
# """

test/test_dataset.jl

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ end
2929
@testset "Test methods for raster dataset" begin
3030
AG.read("data/utmsmall.tif") do dataset
3131
@testset "Method 1" begin
32+
io = IOBuffer()
33+
function showprogress(progress, message = "")
34+
print(io, round(Int, progress*100))
35+
return true
36+
end
37+
3238
AG.copy(
3339
dataset,
3440
filename = "/vsimem/utmcopy.tif",
@@ -38,7 +44,13 @@ end
3844
@test AG.noverview(band) == 0
3945
AG.buildoverviews!(copydataset, Cint[2, 4, 8])
4046
@test AG.noverview(band) == 3
41-
AG.copywholeraster!(dataset, copydataset)
47+
AG.copywholeraster!(
48+
dataset,
49+
copydataset,
50+
progressfunc = showprogress,
51+
)
52+
seek(io, 0)
53+
@test occursin("100", String(readavailable(io)))
4254
return nothing
4355
end
4456
end

0 commit comments

Comments
 (0)