ArchGDAL provides two approaches for working with GDAL objects.
The first approach is through Scoped Objects, which uses
do-blocks as context managers. The problem with using do-blocks to manage context is that they are difficult to work with in an interactive way:
ArchGDAL.read(filename) do dataset # dataset exists within this scope end # we do not have access to dataset from here on
In the example above, we do not get to see information about
dataset unless we write code to display information within the scope of the do-block. This makes it difficult to work with it in an exploratory "depth-first" manner.
The second approach is through Interactive Objects, which are designed for use at the REPL.
dataset = ArchGDAL.read(filename) # work with dataset
A potential drawback of the second approach is that the objects are managed by Julia's garbage collector. This requires ArchGDAL to keep track of objects that interactive objects have a relationship with so that the interactive objects are not prematurely destroyed. For safety, ArchGDAL might make clones/copies of the underlying data, and only allow a subset of GDAL's objects to be created in this way.
Unlike the design of fiona,
ArchGDAL does not immediately make copies from data sources. This introduces concerns about memory management (whether objects should be managed by Julia's garbage collector, or by other means of destroying GDAL object when they are out of scope).
For scoped objects, they are often created within the context of a do-block. As an example, the following code block
ArchGDAL.getband(dataset, i) do rasterband # do something with rasterband end
corresponds to the following sequence of function calls:
rasterband = ArchGDAL.unsafe_getband(dataset, i) try # do something with rasterband finally ArchGDAL.destroy(rasterband) end
under the hood (see
src/context.jl). Therefore, the objects themselves do not have a finalizer registered:
mutable struct RasterBand <: AbstractRasterBand ptr::GDAL.GDALRasterBandH end unsafe_getband(dataset::AbstractDataset, i::Integer) = RasterBand(GDAL.getrasterband(dataset.ptr, i)) function destroy(rb::AbstractRasterBand) rb.ptr = C_NULL end
We use the
unsafe_ prefix to indicate those methods that return scoped objects. These methods should not be used by users directly.
By contrast, the following code
rasterband = ArchGDAL.getband(dataset, i) # do something with rasterband
returns an interactive rasterband that has
destroy() registered with its finalizer.
mutable struct IRasterBand <: AbstractRasterBand ptr::GDAL.GDALRasterBandH ownedby::AbstractDataset function IRasterBand( ptr::GDAL.GDALRasterBandH = C_NULL; ownedby::AbstractDataset = Dataset() ) rasterband = new(ptr, ownedby) finalizer(destroy, rasterband) return rasterband end end getband(dataset::AbstractDataset, i::Integer) = IRasterBand(GDAL.getrasterband(dataset.ptr, i), ownedby = dataset) function destroy(rasterband::IRasterBand) rasterband.ptr = C_NULL rasterband.ownedby = Dataset() return rasterband end
IRasterBand indicates that it is an [i]nteractive type. Other interactive types include
ArchGDAL requires all interactive types to have a finalizer that calls
destroy() on them. All objects that have a relationship with an interactive object are required to hold a reference to the interactive object. For example, objects of type
IRasterBand might have a relationship with an
IDataset, therefore they have an
ownedby attribute which might refer to such a dataset.
Sometimes, it is helpful to work with objects that are "internal references" that have restrictions on the types of methods that they support. As an example
layerdefn(featurelayer) returns a feature definition that is internal to the feature layer, and does not support methods such as
write!(featuredefn, fielddefn) and
deletegeomdefn!(featuredefn, i). To indicate that they might have restrictions, some types have
View as a postfix. Such types include
ArchGDAL.unsafe_<method>(args...)will return a scoped object. The proper way of using them is within the setting of a do-block:
ArchGDAL.<method>(args...) do result # result is a scoped object end
ArchGDAL.<method>(args...)will return an interactive object.
result = ArchGDAL.<method>(args...) # result is an interactive object
Users are allowed to mix both "interactive" and "scoped" objects. As long as they do not manually call
ArchGDAL.destroy() on any object, ArchGDAL is designed to avoid the pitfalls of GDAL memory management (e.g. in Python Gotchas).
Here's a collection of references for developers who are interested.