diff --git a/.JuliaFormatter.toml b/.JuliaFormatter.toml new file mode 100644 index 00000000..a700a076 --- /dev/null +++ b/.JuliaFormatter.toml @@ -0,0 +1,8 @@ +# Configuration file for JuliaFormatter.jl +# For more information, see: https://domluna.github.io/JuliaFormatter.jl/stable/config/ + +always_for_in = true +always_use_return = true +margin = 80 +remove_extra_newlines = true +short_to_long_function_def = true diff --git a/.github/workflows/format_pr.yml b/.github/workflows/format_pr.yml new file mode 100644 index 00000000..95fc1b0b --- /dev/null +++ b/.github/workflows/format_pr.yml @@ -0,0 +1,30 @@ +name: format-pr +on: + schedule: + - cron: '0 0 * * *' +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Install JuliaFormatter and format + run: | + julia -e 'import Pkg; Pkg.add("JuliaFormatter")' + julia -e 'using JuliaFormatter; format(".")' + + # https://github.com/marketplace/actions/create-pull-request + # https://github.com/peter-evans/create-pull-request#reference-example + - name: Create Pull Request + id: cpr + uses: peter-evans/create-pull-request@v3 + with: + token: ${{ secrets.GITHUB_TOKEN }} + commit-message: Format .jl files + title: 'Automatic JuliaFormatter.jl run' + branch: auto-juliaformatter-pr + delete-branch: true + labels: formatting, automated pr, no changelog + - name: Check outputs + run: | + echo "Pull Request Number - ${{ steps.cpr.outputs.pull-request-number }}" + echo "Pull Request URL - ${{ steps.cpr.outputs.pull-request-url }}" diff --git a/.gitignore b/.gitignore index 120d0431..a110aa2e 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,6 @@ test/gdalworkshop/ test/ospy/ test/pyrasterio/ test/spatialite/ +.DS_Store +Manifest.toml +*.tif diff --git a/Project.toml b/Project.toml index 3faa6b2b..f86f75b4 100644 --- a/Project.toml +++ b/Project.toml @@ -6,26 +6,21 @@ desc = "A high level API for GDAL - Geospatial Data Abstraction Library" version = "0.6.0" [deps] +ColorTypes = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" DiskArrays = "3c3547ce-8d99-4f5e-a174-61eb10b00ae3" GDAL = "add2ef01-049f-52c4-9ee2-e494f65e021a" GeoFormatTypes = "68eda718-8dee-11e9-39e7-89f7f65f511f" GeoInterface = "cf35fbd7-0cd7-5166-be24-54bfbe79505f" +ImageCore = "a09fc81d-aa75-5fe9-8630-4744c3626534" Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" [compat] +ColorTypes = "0.10, 0.11" DiskArrays = "0.2.4" GDAL = "1.1.3" GeoFormatTypes = "0.3" GeoInterface = "0.4, 0.5" +ImageCore = "0.8, 0.9" Tables = "1" julia = "1.3" - -[extras] -Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" -Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" -Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" -Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" - -[targets] -test = ["Dates", "Pkg", "Statistics", "Test"] diff --git a/README.md b/README.md index 6d77bf95..51c48344 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,15 @@ # ArchGDAL [![CI](https://github.com/yeesian/ArchGDAL.jl/workflows/CI/badge.svg)](https://github.com/yeesian/ArchGDAL.jl/actions?query=workflow%3ACI) [![Coverage Status](https://coveralls.io/repos/github/yeesian/ArchGDAL.jl/badge.svg?branch=master)](https://coveralls.io/github/yeesian/ArchGDAL.jl?branch=master) +[![version](https://juliahub.com/docs/ArchGDAL/version.svg)](https://juliahub.com/ui/Packages/ArchGDAL/MCIiB) [![Stable](https://img.shields.io/badge/docs-stable-blue.svg)](https://yeesian.com/ArchGDAL.jl/stable) [![Dev](https://img.shields.io/badge/docs-dev-blue.svg)](https://yeesian.com/ArchGDAL.jl/dev) +[![deps](https://juliahub.com/docs/ArchGDAL/deps.svg)](https://juliahub.com/ui/Packages/ArchGDAL/MCIiB?t=2) +[![ColPrac: Contributor's Guide](https://img.shields.io/badge/ColPrac-Contributor's%20Guide-blueviolet)](https://github.com/SciML/ColPrac) [GDAL](http://gdal.org/) is a translator library for raster and vector geospatial data formats that is released under an [X/MIT](https://trac.osgeo.org/gdal/wiki/FAQGeneral#WhatlicensedoesGDALOGRuse) license by the [Open Source Geospatial Foundation](http://www.osgeo.org/). As a library, it presents an abstract data model to drivers for various [raster](http://www.gdal.org/formats_list.html) and [vector](http://www.gdal.org/ogr_formats.html) formats. -This package aims to be a complete solution for working with GDAL in Julia, similar in scope to [the SWIG bindings for Python](https://pypi.python.org/pypi/GDAL/). It builds on top of [GDAL.jl](https://github.com/JuliaGeo/GDAL.jl), and provides a high level API for GDAL, espousing the following principles. +This package aims to be a complete solution for working with GDAL in Julia, similar in scope to [the SWIG bindings for Python](https://pypi.python.org/pypi/GDAL/) and the user-friendliness of [Fiona](https://github.com/Toblerity/Fiona) and [Rasterio](https://github.com/mapbox/rasterio). It builds on top of [GDAL.jl](https://github.com/JuliaGeo/GDAL.jl), and provides a high level API for GDAL, espousing the following principles. ## Principles (The Arch Way) (adapted from: https://wiki.archlinux.org/index.php/Arch_Linux#Principles) @@ -29,3 +32,76 @@ To test if it is installed correctly, ```julia pkg> test ArchGDAL ``` + +## Getting Involved + +### Community + +This package will not be possible without [JuliaLang](https://julialang.org/), [GDAL](https://github.com/OSGeo/gdal) and [GDAL.jl](https://github.com/JuliaGeo/GDAL.jl). They are maintained by https://julialang.org/community/, https://www.osgeo.org/ and https://juliageo.org/ respectively. In case of any contention for support and involvement, we encourage participation and contributions to those projects and communities over this package. + +### Style Guide + +ArchGDAL.jl uses [JuliaFormatter.jl](https://github.com/domluna/JuliaFormatter.jl) as +an autoformatting tool, and uses the options in [`.JuliaFormatter.toml`](https://github.com/yeesian/ArchGDAL.jl/blob/master/.JuliaFormatter.toml). + +If you wish to format code, `cd` to the ArchGDAL.jl directory, then run: +```julia +] add JuliaFormatter +using JuliaFormatter +format(".") +``` + +### Dependencies +To manage the dependencies of this package, we work with [environments](https://pkgdocs.julialang.org/v1/environments/): + +1. Navigate to the directory corresponding to the package: + +```julia +$ cd /Users/yeesian/.julia/dev/ArchGDAL +/Users/yeesian/.julia/dev/ArchGDAL +``` + +2. Start a session: + +```shell +$ julia --project +``` + +3. Activate the environment corresponding to `Project.toml`): + +```julia +(@v1.6) pkg> activate . + Activating environment at `~/.julia/environments/v1.6/Project.toml` +``` + +4. Manage the dependencies using Pkg in https://pkgdocs.julialang.org/v1.6/managing-packages/, e.g. + +```julia +(ArchGDAL) pkg> st + Project ArchGDAL v0.6.0 + Status `~/.julia/dev/ArchGDAL/Project.toml` + [3c3547ce] DiskArrays + [add2ef01] GDAL + [68eda718] GeoFormatTypes + [cf35fbd7] GeoInterface + [bd369af6] Tables + [ade2ca70] Dates + +(ArchGDAL) pkg> add CEnum + Resolving package versions... + Updating `~/.julia/dev/ArchGDAL/Project.toml` + [fa961155] + CEnum v0.4.1 + [3c3547ce] + DiskArrays v0.2.7 + [add2ef01] + GDAL v1.2.1 + [68eda718] + GeoFormatTypes v0.3.0 + [cf35fbd7] + GeoInterface v0.5.5 + [bd369af6] + Tables v1.4.2 +``` + +5. Update the `[compat]` section of `Project.toml` so that julia can resolve the versions, e.g. + +``` +[compat] +... +CEnum = "0.4" +``` diff --git a/docs/Project.toml b/docs/Project.toml index 5b55e0dd..0b775dab 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -3,6 +3,7 @@ ArchGDAL = "c9ce4bd3-c3d5-55b8-8973-c0e20141b8c3" DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" DiskArrays = "3c3547ce-8d99-4f5e-a174-61eb10b00ae3" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" +GDAL = "add2ef01-049f-52c4-9ee2-e494f65e021a" [compat] Documenter = "0.26.2" diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..15e6dae3 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,9 @@ +# ArchGDAL.jl documentation + +The latest stable version of the documentation can be viewed at https://yeesian.com/ArchGDAL.jl/stable/. To build a local version of the documentation, navigate to this directory and run the following command: + +```shell +$ julia --project make.jl +``` + +It should create files in a subdirectory named `build/`. If it builds successfully, you should be able to preview the documentation in a browser by opening `build/index.html` in a browser. diff --git a/docs/make.jl b/docs/make.jl index 2618ddda..664222fb 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -1,5 +1,12 @@ using Documenter, ArchGDAL +DocMeta.setdocmeta!( + ArchGDAL, + :DocTestSetup, + :(using ArchGDAL, GDAL); + recursive = true, +) + # make sure you have run the tests before such that the test files are present makedocs( modules = [ArchGDAL], @@ -17,15 +24,17 @@ makedocs( "GDAL Datasets" => "datasets.md", "Feature Data" => "features.md", "Raster Data" => "rasters.md", + "Working with Images" => "images.md", "Tables Interface" => "tables.md", "Geometric Operations" => "geometries.md", "Spatial Projections" => "projections.md", + # TODO: Uncomment the following line once we support Spatialite + # See https://github.com/JuliaGeo/GDAL.jl/issues/65#issuecomment-493890448. # "Working with Spatialite" => "spatialite.md", "Interactive versus Scoped Objects" => "memory.md", "Design Considerations" => "considerations.md", "API Reference" => "reference.md", - # "Naming Conventions" => "conventions.md", # table between GDAL, GDAL.jl, and ArchGDAL.jl - ] + ], ) deploydocs(; repo = "github.com/yeesian/ArchGDAL.jl.git") diff --git a/docs/src/considerations.md b/docs/src/considerations.md index 7ab87838..8b9ab4e0 100644 --- a/docs/src/considerations.md +++ b/docs/src/considerations.md @@ -15,3 +15,84 @@ Here's a collection of references for developers who are interested: - [https://github.com/mapbox/rasterio/pull/665](https://github.com/mapbox/rasterio/pull/665) - [https://github.com/mapbox/rasterio/issues/875](https://github.com/mapbox/rasterio/issues/875) - [https://rasterio.readthedocs.io/en/latest/topics/configuration.html](https://rasterio.readthedocs.io/en/latest/topics/configuration.html) + +## GDAL Enum Values + +[GDAL.jl](https://github.com/JuliaGeo/GDAL.jl) uses [CEnum.jl](https://github.com/JuliaInterop/CEnum.jl), which is a C-compatible enum, this is the default in [Clang.jl](https://github.com/JuliaInterop/Clang.jl). This is useful when the underlying values are of interest, for example the following snippets from [`src/types.jl`](https://github.com/yeesian/ArchGDAL.jl/blob/master/src/types.jl): + +```julia +import Base.| + +for T in (GDALOpenFlag, FieldValidation) + eval(quote + |(x::$T, y::UInt8) = UInt8(x) | y + |(x::UInt8, y::$T) = x | UInt8(y) + |(x::$T, y::$T) = UInt8(x) | UInt8(y) + end) +end +``` + +and + +```julia +function basetype(gt::OGRwkbGeometryType)::OGRwkbGeometryType + wkbGeomType = convert(GDAL.OGRwkbGeometryType, gt) + wkbGeomType &= (~0x80000000) # Remove 2.5D flag. + wkbGeomType %= 1000 # Normalize Z, M, and ZM types. + return GDAL.OGRwkbGeometryType(wkbGeomType) +end +``` + +However, the use of CEnum.jl allows for multiple enums to have the same underlying value, resulting in unintuitive behavior if they are used as keys in a dictionary. For example, in the following code: + +```julia +julia> Dict(GDAL.GCI_YCbCr_CrBand => "a", GDAL.GCI_Max => "b") +Dict{GDAL.GDALColorInterp, String} with 1 entry: + GCI_YCbCr_CrBand => "b" +``` + +the entry for `GDAL.GCI_YCbCr_CrBand => "a"` got overwritten by `GDAL.GCI_Max => "b"` because both `GDAL.GCI_YCbCr_CrBand` and `GDAL.GCI_Max` corresponded to the same value. + +To avoid such forms of behavior, this package uses [`Base.Enums`](https://docs.julialang.org/en/v1/base/base/#Base.Enums.Enum) instead, so the above example would result in the following behavior: + + +```julia +julia> Dict(ArchGDAL.GCI_YCbCr_CrBand => "a", ArchGDAL.GCI_Max => "b") +Dict{ArchGDAL.GDALColorInterp, String} with 2 entries: + GCI_YCbCr_CrBand => "a" + GCI_Max => "b" +``` + +To maintain parity with GDAL behavior, ArchGDAL.jl provides conversion methods to map from the enums in ArchGDAL to the corresponding cenums from GDAL.jl when calling the corresponding GDAL functions. + +## Colors + +Rather than encouraging [operations on colortables](https://gdal.org/python/osgeo.gdal.ColorTable-class.html) (with very limited functionality from GDAL), users are better served by arrays of [ColorTypes](https://github.com/JuliaGraphics/ColorTypes.jl) using [Colors.jl](https://github.com/JuliaGraphics/Colors.jl), [for example](http://juliagraphics.github.io/Colors.jl/stable/colormapsandcolorscales/#Generating-a-range-of-colors) + +```julia +range(startcolor, stop=endcolor, length=15) +``` + +instead of + +```julia +createcolorramp!(colortable, startindex, startcolor, startindex+15, endcolor) +``` + +## Images + +To differentiate 2d arrays of colors from 3d arrays with band as the third dimension: + +* For 2D arrays (a single rasterband), if they have a color interpretation, we use the color interpretation. If they don't have a color interpretation, ArchGDAL.jl defaults to "Grey". +* For >2D arrays (multiple rasterbands), if they have a palette interpretation (or combination of color interpretations) that resolves to a valid colortype, ArchGDAL.jl uses the palette interpretation. If they don't have a palette interpretation, we throw an error. + +In general, `read()` will return `Array{UInt8}`, and `imread()` will return `Array{<:Colorant}`. + +## Tables.jl Interface + +The interface is implemented in [`src/tables.jl`](https://github.com/yeesian/ArchGDAL.jl/blob/master/src/tables.jl), and is only for feature and geometries in OGR (and not for images and rasters). The current API from GDAL makes it row-based in the conventions of Tables.jl. Therefore, + +* `ArchGDAL.Feature` meets the criteria for an [`AbstractRow`](https://tables.juliadata.org/dev/#Tables.AbstractRow-1) based on https://github.com/yeesian/ArchGDAL.jl/blob/a665f3407930b8221269f8949c246db022c3a85c/src/tables.jl#L31-L58. +* `ArchGDAL.FeatureLayer` meets the criteria for an `AbstractRow`-iterator based on the previous bullet and meeting the criteria for [`Iteration`](https://docs.julialang.org/en/v1/manual/interfaces/#man-interface-iteration) in [`base/iterators.jl`](https://github.com/yeesian/ArchGDAL.jl/blob/a665f3407930b8221269f8949c246db022c3a85c/src/base/iterators.jl#L1-L18). +* `ArchGDAL.AbstractDataset` might contain multiple layers, and might correspond to multiple tables. The way to construct tables would be to get the layers before forming the corresponding tables. + diff --git a/docs/src/conventions.md b/docs/src/conventions.md deleted file mode 100644 index e69de29b..00000000 diff --git a/docs/src/geometries.md b/docs/src/geometries.md index aef12ab4..383aefa1 100644 --- a/docs/src/geometries.md +++ b/docs/src/geometries.md @@ -2,7 +2,6 @@ ```@setup geometries using ArchGDAL -const AG = ArchGDAL ``` In this section, we consider some of the common kinds of geometries that arises in applications. These include `Point`, `LineString`, `Polygon`, `GeometryCollection`, `MultiPolygon`, `MultiPoint`, and `MultiLineString`. For brevity in the examples, we will use the prefix `const AG = ArchGDAL`. @@ -11,53 +10,53 @@ In this section, we consider some of the common kinds of geometries that arises To create geometries of different types, ```@example geometries -point = AG.createpoint(1.0, 2.0) -linestring = AG.createlinestring([(i,i+1) for i in 1.0:3.0]) -linearring = AG.createlinearring([(0.,0.), (0.,1.), (1.,1.)]) -simplepolygon = AG.createpolygon([(0.,0.), (0.,1.), (1.,1.)]) -complexpolygon = AG.createpolygon([[(0.,0.), (0.,j), (j,j)] for j in 1.0:-0.1:0.9]) -multipoint = AG.createlinearring([(0.,0.), (0.,1.), (1.,1.)]) -multilinestring = AG.createmultilinestring([[(i,i+1) for i in j:j+3] for j in 1.0:5.0:6.0]) -multipolygon = AG.createmultipolygon([[[(0.,0.), (0.,j), (j,j)]] for j in 1.0:-0.1:0.9]) +point = ArchGDAL.createpoint(1.0, 2.0) +linestring = ArchGDAL.createlinestring([(i,i+1) for i in 1.0:3.0]) +linearring = ArchGDAL.createlinearring([(0.,0.), (0.,1.), (1.,1.)]) +simplepolygon = ArchGDAL.createpolygon([(0.,0.), (0.,1.), (1.,1.)]) +complexpolygon = ArchGDAL.createpolygon([[(0.,0.), (0.,j), (j,j)] for j in 1.0:-0.1:0.9]) +multipoint = ArchGDAL.createlinearring([(0.,0.), (0.,1.), (1.,1.)]) +multilinestring = ArchGDAL.createmultilinestring([[(i,i+1) for i in j:j+3] for j in 1.0:5.0:6.0]) +multipolygon = ArchGDAL.createmultipolygon([[[(0.,0.), (0.,j), (j,j)]] for j in 1.0:-0.1:0.9]) ``` Alternatively, they can be assembled from their components. ```@example geometries -point = AG.createpoint() -AG.addpoint!(point, 1.0, 2.0) +point = ArchGDAL.createpoint() +ArchGDAL.addpoint!(point, 1.0, 2.0) -linestring = AG.createlinestring() +linestring = ArchGDAL.createlinestring() for i in 1.0:3.0 - AG.addpoint!(linestring, i, i+1) + ArchGDAL.addpoint!(linestring, i, i+1) end -linearring = AG.createlinearring() +linearring = ArchGDAL.createlinearring() for i in 1.0:3.0 - AG.addpoint!(linearring, i, i+1) + ArchGDAL.addpoint!(linearring, i, i+1) end -polygon = AG.createpolygon() +polygon = ArchGDAL.createpolygon() for j in 1.0:-0.1:0.9 - ring = AG.createlinearring([(0.,0.), (0.,j), (j,j)]) - AG.addgeom!(polygon, ring) + ring = ArchGDAL.createlinearring([(0.,0.), (0.,j), (j,j)]) + ArchGDAL.addgeom!(polygon, ring) end -multipoint = AG.createmultipoint() +multipoint = ArchGDAL.createmultipoint() for i in 1.0:3.0 - pt = AG.createpoint(i, i+1) - AG.addgeom!(multipoint, pt) + pt = ArchGDAL.createpoint(i, i+1) + ArchGDAL.addgeom!(multipoint, pt) end -multilinestring = AG.createmultilinestring() +multilinestring = ArchGDAL.createmultilinestring() for j in 1.0:5.0:6.0 - line = AG.createlinestring([(i,i+1) for i in j:j+3]) - AG.addgeom!(multilinestring, line) + line = ArchGDAL.createlinestring([(i,i+1) for i in j:j+3]) + ArchGDAL.addgeom!(multilinestring, line) end -multipolygon = AG.createmultipolygon() +multipolygon = ArchGDAL.createmultipolygon() for j in 1.0:-0.1:0.9 - poly = AG.createpolygon([(0.,0.), (0.,j), (j,j)]) - AG.addgeom!(multipolygon, poly) + poly = ArchGDAL.createpolygon([(0.,0.), (0.,j), (j,j)]) + ArchGDAL.addgeom!(multipolygon, poly) end ``` diff --git a/docs/src/images.md b/docs/src/images.md new file mode 100644 index 00000000..69c1c647 --- /dev/null +++ b/docs/src/images.md @@ -0,0 +1,52 @@ +# Images + +```@setup rasters +using ArchGDAL +``` + +In this section, we revisit the [`gdalworkshop/world.tif`](https://github.com/yeesian/ArchGDALDatasets/blob/307f8f0e584a39a050c042849004e6a2bd674f99/gdalworkshop/world.tif) dataset. +```@example rasters +dataset = ArchGDAL.read("gdalworkshop/world.tif") +``` +A description of the display is available in [Raster Datasets](@ref). + +## Reading from Datasets +We can construct an image from it in the following way: +```@example rasters +ArchGDAL.imread(dataset) +``` + +## Reading from Files +We can read the file as an image instead: +```@example rasters +ArchGDAL.imread("gdalworkshop/world.tif") +``` + +## Reading from Rasterbands +We can also read from individual raster bands: +```@example rasters +ArchGDAL.imread(ArchGDAL.getband(dataset, 1)) +``` +Or equivalently, +```@example rasters +ArchGDAL.imread(dataset, 1) +``` +It will interpret the color channel (for RGB) correctly if there is one. E.g. +```@example rasters +ArchGDAL.imread(dataset, 2) +``` +and +```@example rasters +ArchGDAL.imread(dataset, 3) +``` + +## Working with Colors +Operations on colors behave as you think they might: +```@example rasters +ArchGDAL.imread(dataset, 2) + ArchGDAL.imread(dataset, 3) +``` +and +```@example rasters +0.5 * ArchGDAL.imread(dataset, 1) + ArchGDAL.imread(dataset, 3) +``` +See [Colors.jl](https://juliagraphics.github.io/Colors.jl/stable/) for more on what you can do. diff --git a/docs/src/memory.md b/docs/src/memory.md index e3709f57..d3c6e9eb 100644 --- a/docs/src/memory.md +++ b/docs/src/memory.md @@ -41,14 +41,14 @@ end under the hood (see `src/context.jl`). Therefore, the objects themselves do not have a finalizer registered: ```julia mutable struct RasterBand <: AbstractRasterBand - ptr::GDALRasterBand + ptr::GDAL.GDALRasterBandH end unsafe_getband(dataset::AbstractDataset, i::Integer) = RasterBand(GDAL.getrasterband(dataset.ptr, i)) function destroy(rb::AbstractRasterBand) - rb.ptr = GDALRasterBand(C_NULL) + rb.ptr = C_NULL end ``` @@ -65,11 +65,11 @@ rasterband = ArchGDAL.getband(dataset, i) returns an interactive rasterband that has `destroy()` registered with its finalizer. ```julia mutable struct IRasterBand <: AbstractRasterBand - ptr::GDALRasterBand + ptr::GDAL.GDALRasterBandH ownedby::AbstractDataset function IRasterBand( - ptr::GDALRasterBand = GDALRasterBand(C_NULL); + ptr::GDAL.GDALRasterBandH = C_NULL; ownedby::AbstractDataset = Dataset() ) rasterband = new(ptr, ownedby) @@ -82,7 +82,7 @@ getband(dataset::AbstractDataset, i::Integer) = IRasterBand(GDAL.getrasterband(dataset.ptr, i), ownedby = dataset) function destroy(rasterband::IRasterBand) - rasterband.ptr = GDALRasterBand(C_NULL) + rasterband.ptr = C_NULL rasterband.ownedby = Dataset() return rasterband end diff --git a/docs/src/projections.md b/docs/src/projections.md index 738896ca..49f56d39 100644 --- a/docs/src/projections.md +++ b/docs/src/projections.md @@ -2,7 +2,6 @@ ```@setup projections using ArchGDAL -const AG = ArchGDAL ``` (This is based entirely on the [GDAL/OSR Tutorial](http://www.gdal.org/osr_tutorial.html) and [Python GDAL/OGR Cookbook](https://pcjericks.github.io/py-gdalogr-cookbook/projection.html).) diff --git a/docs/src/reference.md b/docs/src/reference.md index 0c16580e..908d6856 100644 --- a/docs/src/reference.md +++ b/docs/src/reference.md @@ -4,7 +4,14 @@ ```@autodocs Modules = [ArchGDAL] -Pages = ["ArchGDAL.jl", "display.jl", "iterators.jl", "gcp.jl", "types.jl", "utils.jl"] +Pages = ["ArchGDAL.jl", "display.jl", "iterators.jl", "types.jl", "utils.jl"] +``` + +## [GDAL Constants](@id API-GDAL-Constants) + +```@autodocs +Modules = [ArchGDAL] +Pages = ["constants.jl"] ``` ## [GDAL Datasets](@id API-GDAL-Datasets) @@ -34,13 +41,20 @@ Modules = [ArchGDAL] Pages = ["array.jl", "colortable.jl", "rasterattributetable.jl", "rasterband.jl", "rasterio.jl"] ``` -## [Spatial projections](@id API-Spatial-projections) +## [Spatial Projections](@id API-Spatial-Projections) ```@autodocs Modules = [ArchGDAL] Pages = ["spatialref.jl"] ``` +## [Geo Transformations](@id API-GeoTransforms) + +```@autodocs +Modules = [ArchGDAL] +Pages = ["geotransform.jl"] +``` + ## [Utilities](@id API-Utilities) ```@autodocs diff --git a/docs/src/spatialite.md b/docs/src/spatialite.md index 063be83c..9534f8b5 100644 --- a/docs/src/spatialite.md +++ b/docs/src/spatialite.md @@ -12,7 +12,6 @@ We will work with the following database: ```@example spatialite import ArchGDAL -const AG = ArchGDAL filepath = download("https://github.com/yeesian/ArchGDALDatasets/raw/e0b15dca5ad493c5ebe8111688c5d14b031b7305/spatialite/test-2.3.sqlite", "test.sqlite") ``` @@ -20,7 +19,7 @@ filepath = download("https://github.com/yeesian/ArchGDALDatasets/raw/e0b15dca5ad Here's a quick summary of `test.sqlite`: ```@example spatialite -AG.read(filepath) do dataset +ArchGDAL.read(filepath) do dataset print(dataset) end ``` @@ -29,8 +28,8 @@ We will display the results of running `query` on the dataset using the followin ```@example spatialite function inspect(query, filename=filepath) - AG.read(filename) do dataset - AG.executesql(dataset, query) do results + ArchGDAL.read(filename) do dataset + ArchGDAL.executesql(dataset, query) do results print(results) end end diff --git a/docs/src/tables.md b/docs/src/tables.md index 0f8ae9f6..bd80000b 100644 --- a/docs/src/tables.md +++ b/docs/src/tables.md @@ -1,72 +1,28 @@ # Tabular Interface ```@setup tables -using ArchGDAL -using DataFrames +using ArchGDAL, DataFrames ``` -ArchGDAL now brings in greater flexibilty in terms of raster data handling via the -[Tables.jl](https://github.com/JuliaData/Tables.jl) API, that aims to provide a fast and -responsive tabular interface to data. +ArchGDAL now brings in greater flexibilty in terms of vector data handling via the +[Tables.jl](https://github.com/JuliaData/Tables.jl) API. In general, tables are modelled based on feature layers and support multiple geometries per layer. Namely, the layer(s) of a dataset can be converted to DataFrame(s) to perform miscellaneous spatial operations. -In this section, we revisit the +Here is a quick example based on the [`data/point.geojson`](https://github.com/yeesian/ArchGDALDatasets/blob/307f8f0e584a39a050c042849004e6a2bd674f99/data/point.geojson) -dataset. +dataset: ```@example tables dataset = ArchGDAL.read("data/point.geojson") -``` - -Each layer can be represented as a separate Table. - -```@example tables -layer = ArchGDAL.getlayer(dataset, 0) -``` - -The [`ArchGDAL.Table`](@ref) method accepts an `ArchGDAL.FeatureLayer`. -```@example tables -table = ArchGDAL.Table(layer) -``` - -Individual rows can be retrieved using the `Base.getindex(t::ArchGDAL.Table, idx::Int)` method or simply `table[idx]`. -```@example tables -row = table[1] +DataFrames.DataFrame(ArchGDAL.getlayer(dataset, 0)) ``` -Layers are retrievable! -One can get back the layer that a Table is made up of. -```@example tables -lyr = ArchGDAL.getlayer(table) -``` - -The Tables interface also support multiple geometries per layer. - -Here, we visit the +To illustrate multiple geometries, here is a second example based on the [`data/multi_geom.csv`](https://github.com/yeesian/ArchGDALDatasets/blob/master/data/multi_geom.csv) -dataset. +dataset: ```@example tables dataset1 = ArchGDAL.read("data/multi_geom.csv", options = ["GEOM_POSSIBLE_NAMES=point,linestring", "KEEP_GEOM_COLUMNS=NO"]) -layer = ArchGDAL.getlayer(dataset, 0) -table = ArchGDAL.Table(layer) -``` - -Exatracting a row from the table, we see that the row/feature is made up of two geometries -viz. `point` and `linestring`. -```@example tables -row = table[1] -``` - -Finally layers can be converted to DataFrames to perform miscellaneous spatial operations. -```@example tables -df = DataFrame(table) -``` -In some cases the `nextfeature` might become a bit tedious to use. In which case the `ArchGDAL.nextnamedtuple()` method comes in handy. Though built upon `nextfeature`, simply calling it, yields the `feature` as a `NamedTuple`. Though one might have to use `ArchGDAL.resetreading!(layer)` method to reset the layer reading to the start. - -```@example tables -ArchGDAL.resetreading!(layer) -feat1 = ArchGDAL.nextnamedtuple(layer) -feat2 = ArchGDAL.nextnamedtuple(layer) +DataFrames.DataFrame(ArchGDAL.getlayer(dataset1, 0)) ``` diff --git a/src/ArchGDAL.jl b/src/ArchGDAL.jl index 1bb05c96..fa85eb6b 100644 --- a/src/ArchGDAL.jl +++ b/src/ArchGDAL.jl @@ -1,51 +1,56 @@ module ArchGDAL - using Dates - using GDAL: GDAL - using GeoFormatTypes: GeoFormatTypes - using GeoInterface: GeoInterface - using Tables: Tables - - const GFT = GeoFormatTypes +using Dates +using GDAL: GDAL +using GeoFormatTypes: GeoFormatTypes +using GeoInterface: GeoInterface +using Tables: Tables +using ImageCore: ImageCore +using ColorTypes: ColorTypes - include("utils.jl") - include("types.jl") - include("driver.jl") - include("gcp.jl") - include("spatialref.jl") - include("dataset.jl") - include("raster/rasterband.jl") - include("raster/rasterio.jl") - include("raster/array.jl") - include("raster/rasterattributetable.jl") - include("raster/colortable.jl") - include("ogr/geometry.jl") - include("ogr/feature.jl") - include("ogr/featurelayer.jl") - include("ogr/featuredefn.jl") - include("ogr/fielddefn.jl") - include("ogr/styletable.jl") - include("utilities.jl") - include("context.jl") - include("base/iterators.jl") - include("base/display.jl") - include("tables.jl") - include("geointerface.jl") - include("convert.jl") +const GFT = GeoFormatTypes - mutable struct DriverManager - function DriverManager() - drivermanager = new() - GDAL.gdalallregister() - finalizer((dm,) -> GDAL.gdaldestroydrivermanager(), drivermanager) - return drivermanager - end +include("constants.jl") +include("utils.jl") +include("types.jl") +include("driver.jl") +include("geotransform.jl") +include("spatialref.jl") +include("dataset.jl") +include("raster/rasterband.jl") +include("raster/rasterio.jl") +include("raster/array.jl") +include("raster/rasterattributetable.jl") +include("raster/colortable.jl") +include("raster/images.jl") +include("ogr/geometry.jl") +include("ogr/feature.jl") +include("ogr/featurelayer.jl") +include("ogr/featuredefn.jl") +include("ogr/fielddefn.jl") +include("ogr/styletable.jl") +include("utilities.jl") +include("context.jl") +include("base/iterators.jl") +include("base/display.jl") +include("tables.jl") +include("geointerface.jl") +include("convert.jl") + +mutable struct DriverManager + function DriverManager() + drivermanager = new() + GDAL.gdalallregister() + finalizer((dm,) -> GDAL.gdaldestroydrivermanager(), drivermanager) + return drivermanager end +end - const DRIVER_MANAGER = Ref{DriverManager}() +const DRIVER_MANAGER = Ref{DriverManager}() - function __init__() - DRIVER_MANAGER[] = DriverManager() - end +function __init__() + DRIVER_MANAGER[] = DriverManager() + return nothing +end end # module diff --git a/src/base/display.jl b/src/base/display.jl index 43b0225f..64b6e435 100644 --- a/src/base/display.jl +++ b/src/base/display.jl @@ -1,13 +1,20 @@ -function Base.show(io::IO, drv::Driver) - drv.ptr == C_NULL && (return print(io, "NULL Driver")) - print(io, "Driver: $(shortname(drv))/$(longname(drv))") +function Base.show(io::IO, drv::Driver)::Nothing + if drv.ptr == C_NULL + print(io, "NULL Driver") + else + print(io, "Driver: $(shortname(drv))/$(longname(drv))") + end + return nothing end -function Base.show(io::IO, dataset::AbstractDataset) - dataset.ptr == C_NULL && (return print(io, "NULL Dataset")) +function Base.show(io::IO, dataset::AbstractDataset)::Nothing + if dataset.ptr == C_NULL + print(io, "NULL Dataset") + return nothing + end println(io, "GDAL Dataset ($(getdriver(dataset)))") println(io, "File(s): ") - for (i,filename) in enumerate(filelist(dataset)) + for (i, filename) in enumerate(filelist(dataset)) println(io, " $filename") if i > 5 println(io, " ...") @@ -31,28 +38,35 @@ function Base.show(io::IO, dataset::AbstractDataset) println(io, "\nNumber of feature layers: $nlayers") ndisplay = min(nlayers, 5) # display up to 5 layers for i in 1:ndisplay - layer = getlayer(dataset, i-1) + layer = getlayer(dataset, i - 1) layergeomtype = getgeomtype(layer) - println(io, " Layer $(i-1): $(getname(layer)) ($layergeomtype)") + println(io, " Layer $(i - 1): $(getname(layer)) ($layergeomtype)") end if nlayers > 5 - print(io, " Remaining layers: ") + print(io, " Remaining layers:\n ") for i in 6:nlayers - print(io, "$(getname(getlayer(dataset, i-1))) ") + print(io, "$(getname(getlayer(dataset, i - 1))), ") # display up to 5 layer names per line - if i % 5 == 0 println() end + if i % 5 == 0 && i < nlayers + print(io, "\n ") + end end end end + return nothing end #Add method to avoid show from DiskArrays -Base.show(io::IO, raster::RasterDataset) = show(io, raster.ds) +Base.show(io::IO, raster::RasterDataset)::Nothing = show(io, raster.ds) -Base.show(io::IO, ::MIME"text/plain", raster::RasterDataset) = show(io, raster.ds) +Base.show(io::IO, ::MIME"text/plain", raster::RasterDataset)::Nothing = + show(io, raster.ds) -function summarize(io::IO, rasterband::AbstractRasterBand) - rasterband.ptr == C_NULL && (return print(io, "NULL RasterBand")) +function summarize(io::IO, rasterband::AbstractRasterBand)::Nothing + if rasterband.ptr == C_NULL + print(io, "NULL RasterBand") + return nothing + end access = accessflag(rasterband) color = getname(getcolorinterp(rasterband)) xsize = width(rasterband) @@ -60,14 +74,22 @@ function summarize(io::IO, rasterband::AbstractRasterBand) i = indexof(rasterband) pxtype = pixeltype(rasterband) println(io, "[$access] Band $i ($color): $xsize x $ysize ($pxtype)") + return nothing end -Base.show(io::IO, rasterband::AbstractRasterBand) = show(io, "text/plain", rasterband) +Base.show(io::IO, rasterband::AbstractRasterBand)::Nothing = + show(io, "text/plain", rasterband) -function Base.show(io::IO, ::MIME"text/plain", rasterband::AbstractRasterBand) - rasterband.ptr == C_NULL && (return print(io, "NULL RasterBand")) +function Base.show( + io::IO, + ::MIME"text/plain", + rasterband::AbstractRasterBand, +)::Nothing summarize(io, rasterband) - (x,y) = blocksize(rasterband) + if rasterband.ptr == C_NULL + return nothing + end + (x, y) = blocksize(rasterband) sc = getscale(rasterband) ofs = getoffset(rasterband) norvw = noverview(rasterband) @@ -77,15 +99,19 @@ function Base.show(io::IO, ::MIME"text/plain", rasterband::AbstractRasterBand) println(io, "units: $(sc)px + $ofs$ut") print(io, " overviews: ") for i in 1:norvw - ovr_band = getoverview(rasterband, i-1) - print(io, "($(i-1)) $(width(ovr_band))x$(height(ovr_band)) ") + ovr_band = getoverview(rasterband, i - 1) + print(io, "($(i - 1)) $(width(ovr_band))x$(height(ovr_band)) ") i % 3 == 0 && print(io, "\n ") end + return nothing end # assumes that the layer is reset, and will reset it after display -function Base.show(io::IO, layer::AbstractFeatureLayer) - layer.ptr == C_NULL && (return println(io, "NULL Layer")) +function Base.show(io::IO, layer::AbstractFeatureLayer)::Nothing + if layer.ptr == C_NULL + print(io, "NULL FeatureLayer") + return nothing + end layergeomtype = getgeomtype(layer) println(io, "Layer: $(getname(layer))") featuredefn = layerdefn(layer) @@ -94,8 +120,8 @@ function Base.show(io::IO, layer::AbstractFeatureLayer) n = ngeom(featuredefn) ngeomdisplay = min(n, 3) for i in 1:ngeomdisplay - gfd = getgeomdefn(featuredefn, i-1) - display = " Geometry $(i-1) ($(getname(gfd))): [$(gettype(gfd))]" + gfd = getgeomdefn(featuredefn, i - 1) + display = " Geometry $(i - 1) ($(getname(gfd))): [$(gettype(gfd))]" if length(display) > 75 println(io, "$display[1:70]...") continue @@ -122,14 +148,14 @@ function Base.show(io::IO, layer::AbstractFeatureLayer) n = nfield(featuredefn) nfielddisplay = min(n, 5) for i in 1:nfielddisplay - fd = getfielddefn(featuredefn, i-1) - display = " Field $(i-1) ($(getname(fd))): [$(gettype(fd))]" + fd = getfielddefn(featuredefn, i - 1) + display = " Field $(i - 1) ($(getname(fd))): [$(gettype(fd))]" if length(display) > 75 println(io, "$display[1:70]...") continue end for f in layer - field = string(getfield(f, i-1)) + field = string(getfield(f, i - 1)) length(field) > 25 && (field = "$(field[1:20])...") newdisplay = "$display, $field" if length(newdisplay) > 75 @@ -143,80 +169,117 @@ function Base.show(io::IO, layer::AbstractFeatureLayer) resetreading!(layer) end n > 5 && print(io, "...\n Number of Fields: $n") + return nothing end -function Base.show(io::IO, featuredefn::AbstractFeatureDefn) - featuredefn.ptr == C_NULL && (return print(io, "NULL FeatureDefn")) +function Base.show(io::IO, featuredefn::AbstractFeatureDefn)::Nothing + if featuredefn.ptr == C_NULL + print(io, "NULL FeatureDefn") + return nothing + end n = ngeom(featuredefn) ngeomdisplay = min(n, 3) for i in 1:ngeomdisplay - gfd = getgeomdefn(featuredefn, i-1) - println(io, " Geometry (index $(i-1)): $gfd") + gfd = getgeomdefn(featuredefn, i - 1) + println(io, " Geometry (index $(i - 1)): $gfd") end n > 3 && println(io, " ...\n Number of Geometries: $n") n = nfield(featuredefn) nfielddisplay = min(n, 5) for i in 1:nfielddisplay - fd = getfielddefn(featuredefn, i-1) - println(io, " Field (index $(i-1)): $fd") + fd = getfielddefn(featuredefn, i - 1) + println(io, " Field (index $(i - 1)): $fd") end n > 5 && print(io, "...\n Number of Fields: $n") + return nothing end -function Base.show(io::IO, fd::AbstractFieldDefn) - fd.ptr == C_NULL && (return print(io, "NULL FieldDefn")) +function Base.show(io::IO, fd::AbstractFieldDefn)::Nothing + if fd.ptr == C_NULL + print(io, "NULL FieldDefn") + return nothing + end print(io, "$(getname(fd)) ($(gettype(fd)))") + return nothing end -function Base.show(io::IO, gfd::AbstractGeomFieldDefn) - gfd.ptr == C_NULL && (return print(io, "NULL GeomFieldDefn")) +function Base.show(io::IO, gfd::AbstractGeomFieldDefn)::Nothing + if gfd.ptr == C_NULL + print(io, "NULL GeomFieldDefn") + return nothing + end print(io, "$(getname(gfd)) ($(gettype(gfd)))") + return nothing end -function Base.show(io::IO, feature::Feature) - feature.ptr == C_NULL && (return println(io, "NULL Feature")) +function Base.show(io::IO, feature::Feature)::Nothing + if feature.ptr == C_NULL + print(io, "NULL Feature") + return nothing + end println(io, "Feature") n = ngeom(feature) for i in 1:min(n, 3) - displayname = geomname(getgeom(feature, i-1)) - println(io, " (index $(i-1)) geom => $displayname") + displayname = geomname(getgeom(feature, i - 1)) + println(io, " (index $(i - 1)) geom => $displayname") end n > 3 && println(io, "...\n Number of geometries: $n") n = nfield(feature) for i in 1:min(n, 10) - displayname = getname(getfielddefn(feature, i-1)) - print(io, " (index $(i-1)) $displayname => ") - println(io, "$(getfield(feature, i-1))") + displayname = getname(getfielddefn(feature, i - 1)) + print(io, " (index $(i - 1)) $displayname => ") + println(io, "$(getfield(feature, i - 1))") end n > 10 && print(io, "...\n Number of Fields: $n") + return nothing end -function Base.show(io::IO, spref::AbstractSpatialRef) - spref.ptr == C_NULL && (return print(io, "NULL Spatial Reference System")) +function Base.show(io::IO, spref::AbstractSpatialRef)::Nothing + if spref.ptr == C_NULL + print(io, "NULL Spatial Reference System") + return nothing + end projstr = toPROJ4(spref) if length(projstr) > 45 projstart = projstr[1:35] - projend = projstr[end-4:end] + projend = projstr[(end-4):end] print(io, "Spatial Reference System: $projstart ... $projend") else print(io, "Spatial Reference System: $projstr") end + return nothing end -function Base.show(io::IO, geom::AbstractGeometry) - geom.ptr == C_NULL && (return print(io, "NULL Geometry")) +function Base.show(io::IO, geom::AbstractGeometry)::Nothing + if geom.ptr == C_NULL + print(io, "NULL Geometry") + return nothing + end compact = get(io, :compact, false) if !compact print(io, "Geometry: ") geomwkt = toWKT(geom) if length(geomwkt) > 60 - print(io, "$(geomwkt[1:50]) ... $(geomwkt[end-4:end])") + print(io, "$(geomwkt[1:50]) ... $(geomwkt[(end - 4):end])") else print(io, "$geomwkt") end else print(io, "Geometry: $(getgeomtype(geom))") end + return nothing end + +function Base.show(io::IO, ct::ColorTable)::Nothing + if ct.ptr == C_NULL + print(io, "NULL ColorTable") + return nothing + end + palette = paletteinterp(ct) + print(io, "ColorTable[$palette]") + return nothing +end + +Base.show(io::IO, ::MIME"text/plain", ct::ColorTable)::Nothing = show(io, ct) diff --git a/src/base/iterators.jl b/src/base/iterators.jl index fe563610..2d8348b6 100644 --- a/src/base/iterators.jl +++ b/src/base/iterators.jl @@ -1,39 +1,52 @@ -function Base.iterate(layer::AbstractFeatureLayer, state::Int=0) +function Base.iterate( + layer::AbstractFeatureLayer, + state::Integer = 0, +)::Union{Nothing,Tuple{Feature,Int64}} layer.ptr == C_NULL && return nothing state == 0 && resetreading!(layer) ptr = GDAL.ogr_l_getnextfeature(layer.ptr) - if ptr == C_NULL + return if ptr == C_NULL resetreading!(layer) - return nothing + nothing else - return (Feature(ptr), state+1) + (Feature(ptr), state + 1) end end -Base.eltype(layer::AbstractFeatureLayer) = Feature +Base.eltype(layer::AbstractFeatureLayer)::DataType = Feature -Base.length(layer::AbstractFeatureLayer) = nfeature(layer, true) +Base.length(layer::AbstractFeatureLayer)::Integer = nfeature(layer, true) -struct BlockIterator - rows::Cint - cols::Cint - ni::Cint - nj::Cint - n::Cint - xbsize::Cint - ybsize::Cint +struct BlockIterator{T<:Integer} + rows::T + cols::T + ni::T + nj::T + n::T + xbsize::T + ybsize::T end -function blocks(raster::AbstractRasterBand) +function blocks( + ::Type{T}, + raster::AbstractRasterBand, +)::BlockIterator{T} where {T<:Integer} (xbsize, ybsize) = blocksize(raster) - rows = height(raster) - cols = width(raster) - ni = ceil(Cint, rows / ybsize) - nj = ceil(Cint, cols / xbsize) - BlockIterator(rows, cols, ni, nj, ni * nj, xbsize, ybsize) + rows = T(height(raster)) + cols = T(width(raster)) + ni = ceil(T, rows / ybsize) + nj = ceil(T, cols / xbsize) + return BlockIterator{T}(rows, cols, ni, nj, ni * nj, xbsize, ybsize) end -function Base.iterate(obj::BlockIterator, iter::Int=0) +function blocks(raster::AbstractRasterBand)::BlockIterator{Int64} + return blocks(Int64, raster) +end + +function Base.iterate( + obj::BlockIterator{T}, + iter::T = 0, +)::Union{Nothing,Tuple{Tuple{Tuple{T,T},Tuple{T,T}},T}} where {T} iter == obj.n && return nothing j = floor(Int, iter / obj.ni) i = iter % obj.ni @@ -47,43 +60,78 @@ function Base.iterate(obj::BlockIterator, iter::Int=0) else obj.cols - j * obj.xbsize end - (((i, j), (nrows, ncols)), iter+1) + return (((i, j), (nrows, ncols)), iter + 1) end -struct WindowIterator - blockiter::BlockIterator +struct WindowIterator{T<:Integer} + blockiter::BlockIterator{T} end Base.size(i::WindowIterator) = (i.blockiter.ni, i.blockiter.nj) Base.length(i::WindowIterator) = i.blockiter.n -Base.IteratorSize(::Type{WindowIterator}) = Base.HasShape{2}() -Base.IteratorEltype(::Type{WindowIterator}) = Base.HasEltype() -Base.eltype(::WindowIterator) = Tuple{UnitRange{Int}, UnitRange{Int}} -windows(raster::AbstractRasterBand) = WindowIterator(blocks(raster)) +function Base.IteratorSize(::Type{WindowIterator{T}}) where {T<:Integer} + return Base.HasShape{2}() +end + +function Base.IteratorEltype(::Type{WindowIterator{T}}) where {T<:Integer} + return Base.HasEltype() +end + +function Base.eltype(::WindowIterator{T})::DataType where {T<:Integer} + return Tuple{UnitRange{T},UnitRange{T}} +end -function Base.iterate(obj::WindowIterator, iter::Int=0) +function windows( + ::Type{T}, + raster::AbstractRasterBand, +)::WindowIterator{T} where {T<:Integer} + return WindowIterator{T}(blocks(T, raster)) +end + +windows(raster::AbstractRasterBand)::WindowIterator{Int64} = + windows(Int64, raster) + +function Base.iterate( + obj::WindowIterator{T}, + iter::T = 0, +)::Union{Nothing,Tuple{NTuple{2,UnitRange{T}},T}} where {T<:Integer} handle = obj.blockiter next = Base.iterate(handle, iter) next == nothing && return nothing (((i, j), (nrows, ncols)), iter) = next - (((1:ncols) .+ j * handle.xbsize, (1:nrows) .+ i * handle.ybsize), iter) + return ( + ((1:ncols) .+ j * handle.xbsize, (1:nrows) .+ i * handle.ybsize), + iter, + ) end -mutable struct BufferIterator{T <: Real} +mutable struct BufferIterator{R<:Real,T<:Integer} raster::AbstractRasterBand - w::WindowIterator - buffer::Array{T, 2} + w::WindowIterator{T} + buffer::Matrix{R} end -function bufferwindows(raster::AbstractRasterBand) - BufferIterator( +function bufferwindows( + ::Type{T}, + raster::AbstractRasterBand, +)::BufferIterator{pixeltype(raster),T} where {T<:Integer} + return BufferIterator{pixeltype(raster),T}( raster, - windows(raster), - Array{pixeltype(raster)}(undef, blocksize(raster)...) + windows(T, raster), + Matrix{pixeltype(raster)}(undef, blocksize(raster)...), ) end -function Base.iterate(obj::BufferIterator, iter::Int=0) +function bufferwindows( + raster::AbstractRasterBand, +)::BufferIterator{pixeltype(raster),Int64} + return bufferwindows(Int64, raster) +end + +function Base.iterate( + obj::BufferIterator{R,T}, + iter::T = 0, +)::Union{Nothing,Tuple{Matrix{R},T}} where {R<:Real,T<:Integer} next = Base.iterate(obj.w, iter) next == nothing && return nothing ((cols, rows), iter) = next diff --git a/src/constants.jl b/src/constants.jl new file mode 100644 index 00000000..8ef6fe5d --- /dev/null +++ b/src/constants.jl @@ -0,0 +1,416 @@ +const StringList = Ptr{Cstring} + +""" +The value of `GDALDataType` could be different from `GDAL.GDALDataType`. + +It maps correctly to `GDAL.GDALDataType` if you do e.g. + +```jldoctest; output = false +convert(GDAL.GDALDataType, ArchGDAL.GDT_Unknown) + +# output +GDT_Unknown::GDALDataType = 0x00000000 +``` +""" +@enum( + GDALDataType, + GDT_Unknown = 0, + GDT_Byte = 1, + GDT_UInt16 = 2, + GDT_Int16 = 3, + GDT_UInt32 = 4, + GDT_Int32 = 5, + GDT_Float32 = 6, + GDT_Float64 = 7, + GDT_CInt16 = 8, + GDT_CInt32 = 9, + GDT_CFloat32 = 10, + GDT_CFloat64 = 11, + GDT_TypeCount = 12, +) + +""" +The value of `OGRFieldType` could be different from `GDAL.OGRFieldType`. + +It maps correctly to `GDAL.OGRFieldType` if you do e.g. + +```jldoctest; output = false +convert(GDAL.OGRFieldType, ArchGDAL.OFTInteger) + +# output +OFTInteger::OGRFieldType = 0x00000000 +``` +""" +@enum( + OGRFieldType, + OFTInteger = 0, + OFTIntegerList = 1, + OFTReal = 2, + OFTRealList = 3, + OFTString = 4, + OFTStringList = 5, + OFTWideString = 6, + OFTWideStringList = 7, + OFTBinary = 8, + OFTDate = 9, + OFTTime = 10, + OFTDateTime = 11, + OFTInteger64 = 12, + OFTInteger64List = 13, + OFTMaxType = 14, # 13 +) + +""" +The value of `OGRFieldSubType` could be different from `GDAL.OGRFieldSubType`. + +It maps correctly to `GDAL.OGRFieldSubType` if you do e.g. + +```jldoctest; output = false +convert(GDAL.OGRFieldSubType, ArchGDAL.OFSTNone) + +# output +OFSTNone::OGRFieldSubType = 0x00000000 +``` +""" +@enum( + OGRFieldSubType, + OFSTNone = 0, + OFSTBoolean = 1, + OFSTInt16 = 2, + OFSTFloat32 = 3, + OFSTJSON = 4, + OFSTMaxSubType = 5, # 4 +) + +""" +The value of `OGRJustification` could be different from `GDAL.OGRJustification`. + +It maps correctly to `GDAL.OGRJustification` if you do e.g. + +```jldoctest; output = false +convert(GDAL.OGRJustification, ArchGDAL.OJUndefined) + +# output +OJUndefined::OGRJustification = 0x00000000 +``` +""" +@enum(OGRJustification, OJUndefined = 0, OJLeft = 1, OJRight = 2,) + +""" +The value of `GDALRATFieldType` could be different from `GDAL.GDALRATFieldType`. + +It maps correctly to `GDAL.GDALRATFieldType` if you do e.g. + +```jldoctest; output = false +convert(GDAL.GDALRATFieldType, ArchGDAL.GFT_Integer) + +# output +GFT_Integer::GDALRATFieldType = 0x00000000 +``` +""" +@enum(GDALRATFieldType, GFT_Integer = 0, GFT_Real = 1, GFT_String = 2,) + +""" +The value of `GDALRATFieldUsage` could be different from `GDAL.GDALRATFieldUsage`. + +It maps correctly to `GDAL.GDALRATFieldUsage` if you do e.g. + +```jldoctest; output = false +convert(GDAL.GDALRATFieldUsage, ArchGDAL.GFU_Generic) + +# output +GFU_Generic::GDALRATFieldUsage = 0x00000000 +``` +""" +@enum( + GDALRATFieldUsage, + GFU_Generic = 0, + GFU_PixelCount = 1, + GFU_Name = 2, + GFU_Min = 3, + GFU_Max = 4, + GFU_MinMax = 5, + GFU_Red = 6, + GFU_Green = 7, + GFU_Blue = 8, + GFU_Alpha = 9, + GFU_RedMin = 10, + GFU_GreenMin = 11, + GFU_BlueMin = 12, + GFU_AlphaMin = 13, + GFU_RedMax = 14, + GFU_GreenMax = 15, + GFU_BlueMax = 16, + GFU_AlphaMax = 17, + GFU_MaxCount = 18, +) + +""" +The value of `GDALAccess` could be different from `GDAL.GDALAccess`. + +It maps correctly to `GDAL.GDALAccess` if you do e.g. + +```jldoctest; output = false +convert(GDAL.GDALAccess, ArchGDAL.GA_ReadOnly) + +# output +GA_ReadOnly::GDALAccess = 0x00000000 +``` +""" +@enum(GDALAccess, GA_ReadOnly = 0, GA_Update = 1,) + +""" +The value of `GDALRWFlag` could be different from `GDAL.GDALRWFlag`. + +It maps correctly to `GDAL.GDALRWFlag` if you do e.g. + +```jldoctest; output = false +convert(GDAL.GDALRWFlag, ArchGDAL.GF_Read) + +# output +GF_Read::GDALRWFlag = 0x00000000 +``` +""" +@enum(GDALRWFlag, GF_Read = 0, GF_Write = 1,) + +""" +The value of `GDALPaletteInterp` could be different from `GDAL.GDALPaletteInterp`. + +It maps correctly to `GDAL.GDALPaletteInterp` if you do e.g. + +```jldoctest; output = false +convert(GDAL.GDALPaletteInterp, ArchGDAL.GPI_Gray) + +# output +GPI_Gray::GDALPaletteInterp = 0x00000000 +``` +""" +@enum(GDALPaletteInterp, GPI_Gray = 0, GPI_RGB = 1, GPI_CMYK = 2, GPI_HLS = 3,) + +""" +The value of `GDALColorInterp` could be different from `GDAL.GDALColorInterp`. + +It maps correctly to `GDAL.GDALColorInterp` if you do e.g. + +```jldoctest; output = false +convert(GDAL.GDALColorInterp, ArchGDAL.GCI_Undefined) + +# output +GCI_Undefined::GDALColorInterp = 0x00000000 +``` +""" +@enum( + GDALColorInterp, + GCI_Undefined = 0, + GCI_GrayIndex = 1, # GreyScale + GCI_PaletteIndex = 2, # Paletted (see associated color table) + GCI_RedBand = 3, # Red band of RGBA image + GCI_GreenBand = 4, # Green band of RGBA image + GCI_BlueBand = 5, # Blue band of RGBA image + GCI_AlphaBand = 6, # Alpha (0=transparent, 255=opaque) + GCI_HueBand = 7, # Hue band of HLS image + GCI_SaturationBand = 8, # Saturation band of HLS image + GCI_LightnessBand = 9, # Lightness band of HLS image + GCI_CyanBand = 10, # Cyan band of CMYK image + GCI_MagentaBand = 11, # Magenta band of CMYK image + GCI_YellowBand = 12, # Yellow band of CMYK image + GCI_BlackBand = 13, # Black band of CMYK image + GCI_YCbCr_YBand = 14, # Y Luminance + GCI_YCbCr_CbBand = 15, # Cb Chroma + GCI_YCbCr_CrBand = 16, # Cr Chroma + GCI_Max = 17, # Max current value = 16 +) + +""" +The value of `GDALAsyncStatusType` could be different from `GDAL.GDALAsyncStatusType`. + +It maps correctly to `GDAL.GDALAsyncStatusType` if you do e.g. + +```jldoctest; output = false +convert(GDAL.GDALAsyncStatusType, ArchGDAL.GARIO_PENDING) + +# output +GARIO_PENDING::GDALAsyncStatusType = 0x00000000 +``` +""" +@enum( + GDALAsyncStatusType, + GARIO_PENDING = 0, + GARIO_UPDATE = 1, + GARIO_ERROR = 2, + GARIO_COMPLETE = 3, + GARIO_TypeCount = 4, +) + +""" +The value of `OGRSTClassId` could be different from `GDAL.OGRSTClassId`. + +It maps correctly to `GDAL.OGRSTClassId` if you do e.g. + +```jldoctest; output = false +convert(GDAL.OGRSTClassId, ArchGDAL.OGRSTCNone) + +# output +OGRSTCNone::ogr_style_tool_class_id = 0x00000000 +``` +""" +@enum( + OGRSTClassId, + OGRSTCNone = 0, + OGRSTCPen = 1, + OGRSTCBrush = 2, + OGRSTCSymbol = 3, + OGRSTCLabel = 4, + OGRSTCVector = 5, +) + +""" +The value of `OGRSTUnitId` could be different from `GDAL.OGRSTUnitId`. + +It maps correctly to `GDAL.OGRSTUnitId` if you do e.g. + +```jldoctest; output = false +convert(GDAL.OGRSTUnitId, ArchGDAL.OGRSTUGround) + +# output +OGRSTUGround::ogr_style_tool_units_id = 0x00000000 +``` +""" +@enum( + OGRSTUnitId, + OGRSTUGround = 0, + OGRSTUPixel = 1, + OGRSTUPoints = 2, + OGRSTUMM = 3, + OGRSTUCM = 4, + OGRSTUInches = 5, +) + +""" +The value of `OGRwkbGeometryType` could be different from `GDAL.OGRwkbGeometryType`. + +It maps correctly to `GDAL.OGRwkbGeometryType` if you do e.g. + +```jldoctest; output = false +convert(GDAL.OGRwkbGeometryType, ArchGDAL.wkbUnknown) + +# output +wkbUnknown::OGRwkbGeometryType = 0x00000000 +``` +""" +@enum( + OGRwkbGeometryType, + wkbUnknown = 0, + wkbPoint = 1, + wkbLineString = 2, + wkbPolygon = 3, + wkbMultiPoint = 4, + wkbMultiLineString = 5, + wkbMultiPolygon = 6, + wkbGeometryCollection = 7, + wkbCircularString = 8, + wkbCompoundCurve = 9, + wkbCurvePolygon = 10, + wkbMultiCurve = 11, + wkbMultiSurface = 12, + wkbCurve = 13, + wkbSurface = 14, + wkbPolyhedralSurface = 15, + wkbTIN = 16, + wkbTriangle = 17, + wkbNone = 18, + wkbLinearRing = 19, + wkbCircularStringZ = 20, + wkbCompoundCurveZ = 21, + wkbCurvePolygonZ = 22, + wkbMultiCurveZ = 23, + wkbMultiSurfaceZ = 24, + wkbCurveZ = 25, + wkbSurfaceZ = 26, + wkbPolyhedralSurfaceZ = 27, + wkbTINZ = 28, + wkbTriangleZ = 29, + wkbPointM = 30, + wkbLineStringM = 31, + wkbPolygonM = 32, + wkbMultiPointM = 33, + wkbMultiLineStringM = 34, + wkbMultiPolygonM = 35, + wkbGeometryCollectionM = 36, + wkbCircularStringM = 37, + wkbCompoundCurveM = 38, + wkbCurvePolygonM = 39, + wkbMultiCurveM = 40, + wkbMultiSurfaceM = 41, + wkbCurveM = 42, + wkbSurfaceM = 43, + wkbPolyhedralSurfaceM = 44, + wkbTINM = 45, + wkbTriangleM = 46, + wkbPointZM = 47, + wkbLineStringZM = 48, + wkbPolygonZM = 49, + wkbMultiPointZM = 50, + wkbMultiLineStringZM = 51, + wkbMultiPolygonZM = 52, + wkbGeometryCollectionZM = 53, + wkbCircularStringZM = 54, + wkbCompoundCurveZM = 55, + wkbCurvePolygonZM = 56, + wkbMultiCurveZM = 57, + wkbMultiSurfaceZM = 58, + wkbCurveZM = 59, + wkbSurfaceZM = 60, + wkbPolyhedralSurfaceZM = 61, + wkbTINZM = 62, + wkbTriangleZM = 63, + wkbPoint25D = 64, + wkbLineString25D = 65, + wkbPolygon25D = 66, + wkbMultiPoint25D = 67, + wkbMultiLineString25D = 68, + wkbMultiPolygon25D = 69, + wkbGeometryCollection25D = 70, +) + +""" +The value of `OGRwkbByteOrder` could be different from `GDAL.OGRwkbByteOrder`. + +It maps correctly to `GDAL.OGRwkbByteOrder` if you do e.g. + +```jldoctest; output = false +convert(GDAL.OGRwkbByteOrder, ArchGDAL.wkbXDR) + +# output +wkbXDR::OGRwkbByteOrder = 0x00000000 +``` +""" +@enum(OGRwkbByteOrder, wkbXDR = 0, wkbNDR = 1,) + +@enum( + GDALOpenFlag, + OF_READONLY = GDAL.GDAL_OF_READONLY, # 0x00 + OF_UPDATE = GDAL.GDAL_OF_UPDATE, # 0x01 + # OF_All = GDAL.GDAL_OF_ALL, # 0x00 + OF_RASTER = GDAL.GDAL_OF_RASTER, # 0x02 + OF_VECTOR = GDAL.GDAL_OF_VECTOR, # 0x04 + OF_GNM = GDAL.GDAL_OF_GNM, # 0x08 + OF_KIND_MASK = GDAL.GDAL_OF_KIND_MASK, # 0x1e + OF_SHARED = GDAL.GDAL_OF_SHARED, # 0x20 + OF_VERBOSE_ERROR = GDAL.GDAL_OF_VERBOSE_ERROR, # 0x40 + OF_INTERNAL = GDAL.GDAL_OF_INTERNAL, # 0x80 + # OF_DEFAULT_BLOCK_ACCESS = GDAL.GDAL_OF_DEFAULT_BLOCK_ACCESS, # 0 + OF_ARRAY_BLOCK_ACCESS = GDAL.GDAL_OF_ARRAY_BLOCK_ACCESS, # 0x0100 + OF_HASHSET_BLOCK_ACCESS = GDAL.GDAL_OF_HASHSET_BLOCK_ACCESS, # 0x0200 + # OF_RESERVED_1 = GDAL.GDAL_OF_RESERVED_1, # 0x0300 + OF_BLOCK_ACCESS_MASK = GDAL.GDAL_OF_BLOCK_ACCESS_MASK, # 0x0300 +) + +@enum( + FieldValidation, + F_VAL_NULL = GDAL.OGR_F_VAL_NULL, # 0x0001 + F_VAL_GEOM_TYPE = GDAL.OGR_F_VAL_GEOM_TYPE, # 0x0002 + F_VAL_WIDTH = GDAL.OGR_F_VAL_WIDTH, # 0x0004 + F_VAL_ALLOW_NULL_WHEN_DEFAULT = GDAL.OGR_F_VAL_ALLOW_NULL_WHEN_DEFAULT, # 0x0008 + F_VAL_ALLOW_DIFFERENT_GEOM_DIM = GDAL.OGR_F_VAL_ALLOW_DIFFERENT_GEOM_DIM, # 0x0010 +) diff --git a/src/context.jl b/src/context.jl index 2fbb150b..483de305 100644 --- a/src/context.jl +++ b/src/context.jl @@ -1,8 +1,8 @@ function environment( - f::Function; - globalconfig::Vector=[], - threadconfig::Vector=[] - ) + f::Function; + globalconfig::Vector = [], + threadconfig::Vector = [], +) # Save the current settings # # CPLGetConfigOption() will return the value of the config option, be it @@ -13,23 +13,28 @@ function environment( # CPLSetThreadLocalConfigOption() # # (ref https://github.com/mapbox/rasterio/pull/997#issuecomment-287117289) - globalsettings = Dict(k => getconfigoption(k) for (k,v) in globalconfig) - localsettings = Dict(k => getthreadconfigoption(k) for (k,v) in threadconfig) - for (k,v) in threadconfig; setthreadconfigoption(k, v) end - for (k,v) in globalconfig; setconfigoption(k, v) end + globalsettings = Dict(k => getconfigoption(k) for (k, v) in globalconfig) + localsettings = + Dict(k => getthreadconfigoption(k) for (k, v) in threadconfig) + for (k, v) in threadconfig + setthreadconfigoption(k, v) + end + for (k, v) in globalconfig + setconfigoption(k, v) + end - try + return try f() finally # Restore previous settings - for (k,v) in globalsettings + for (k, v) in globalsettings if v == "" clearconfigoption(k) else setconfigoption(k, v) end end - for (k,v) in localsettings + for (k, v) in localsettings if v == "" clearthreadconfigoption(k) else @@ -41,7 +46,7 @@ end function executesql(f::Function, dataset::Dataset, args...) result = unsafe_executesql(dataset, args...) - try + return try f(result) finally releaseresultset(dataset, result) @@ -57,7 +62,7 @@ function createfeature(f::Function, featuredefn::FeatureDefn) # if we do not artificially increase the reference, then destroy(feature) # will release the featuredefn, when we're going to handle it ourselves # later. Therefore we dereference (rather than release) the featuredefn. - try + return try f(feature) finally destroy(feature) @@ -66,7 +71,8 @@ function createfeature(f::Function, featuredefn::FeatureDefn) end """ - addfielddefn!(layer::AbstractFeatureLayer, name, etype::OGRFieldType; ) + addfielddefn!(layer::AbstractFeatureLayer, name, etype::OGRFieldType; + ) Create a new field on a layer. @@ -97,45 +103,31 @@ to the layer. format driver. """ function addfielddefn!( - layer::AbstractFeatureLayer, - name::AbstractString, - etype::OGRFieldType; - nwidth::Integer = 0, - nprecision::Integer = 0, - justify::OGRJustification = GDAL.OJUndefined, - approx::Bool = false - ) + layer::T, + name::AbstractString, + etype::OGRFieldType; + nwidth::Integer = 0, + nprecision::Integer = 0, + justify::OGRJustification = OJUndefined, + approx::Bool = false, +)::T where {T<:AbstractFeatureLayer} fielddefn = unsafe_createfielddefn(name, etype) - setparams!(fielddefn, name, etype, nwidth = nwidth, nprecision = nprecision, - justify = justify) + setparams!( + fielddefn, + name, + etype, + nwidth = nwidth, + nprecision = nprecision, + justify = justify, + ) addfielddefn!(layer, fielddefn) destroy(fielddefn) - layer -end - -function addfielddefn( - f::Function, - layer::AbstractFeatureLayer, - name::AbstractString, - etype::OGRFieldType; - nwidth::Integer = 0, - nprecision::Integer = 0, - justify::OGRJustification = GDAL.OJUndefined, - approx::Bool = false - ) - fielddefn = unsafe_createfielddefn(name, etype) - setparams!(fielddefn, name, etype, nwidth = nwidth, nprecision = nprecision, - justify = justify) - try - f(fielddefn) - addfielddefn!(layer, fielddefn) - finally - destroy(fielddefn) - end + return layer end """ - writegeomdefn!(layer::AbstractFeatureLayer, name, etype::OGRwkbGeometryType, approx=false) + writegeomdefn!(layer::AbstractFeatureLayer, name, etype::OGRwkbGeometryType, + approx=false) Write a new geometry field on a layer. @@ -162,11 +154,11 @@ to the layer. slightly different form depending on the limitations of the driver. """ function writegeomdefn!( - layer::AbstractFeatureLayer, - name::AbstractString, - etype::OGRwkbGeometryType; - approx::Bool = false - ) + layer::T, + name::AbstractString, + etype::OGRwkbGeometryType; + approx::Bool = false, +)::T where {T<:AbstractFeatureLayer} geomdefn = unsafe_creategeomdefn(name, etype) addgeomdefn!(layer, geomdefn) destroy(geomdefn) @@ -174,14 +166,14 @@ function writegeomdefn!( end function writegeomdefn( - f::Function, - layer::AbstractFeatureLayer, - name::AbstractString, - etype::OGRwkbGeometryType; - approx::Bool = false - ) + f::Function, + layer::AbstractFeatureLayer, + name::AbstractString, + etype::OGRwkbGeometryType; + approx::Bool = false, +) geomdefn = unsafe_creategeomdefn(name, etype) - try + return try f(geomdefn) addgeomdefn!(layer, geomdefn) finally @@ -190,29 +182,88 @@ function writegeomdefn( end for gdalfunc in ( - :boundary, :buffer, :centroid, :clone, :convexhull, :create, - :createcolortable, :createcoordtrans, :copy, :createfeaturedefn, - :createfielddefn, :creategeom, :creategeomcollection, - :creategeomfieldcollection, :creategeomdefn, :createlayer, - :createlinearring, :createlinestring, :createmultilinestring, - :createmultipoint, :createmultipolygon, :createmultipolygon_noholes, - :createpoint, :createpolygon, :createRAT, :createstylemanager, - :createstyletable, :createstyletool, :curvegeom, :delaunaytriangulation, - :difference, :forceto, :fromGML, :fromJSON, :fromWKB, :fromWKT, - :gdalbuildvrt, :gdaldem, :gdalgrid, :gdalnearblack, :gdalrasterize, - :gdaltranslate, :gdalvectortranslate, :gdalwarp, :getband, - :getcolortable, :getfeature, :getgeom, :getlayer, :getmaskband, - :getoverview, :getpart, :getspatialref, :importCRS, :intersection, :importEPSG, - :importEPSGA, :importESRI, :importPROJ4, :importWKT, :importXML, - :importURL, :lineargeom, :newspatialref, :nextfeature, :pointalongline, - :pointonsurface, :polygonfromedges, :polygonize, :read, :sampleoverview, - :simplify, :simplifypreservetopology, :symdifference, :union, :update, - :readraster, - ) + :boundary, + :buffer, + :centroid, + :clone, + :convexhull, + :create, + :createcolortable, + :createcoordtrans, + :copy, + :createfeaturedefn, + :createfielddefn, + :creategeom, + :creategeomcollection, + :creategeomfieldcollection, + :creategeomdefn, + :createlayer, + :createlinearring, + :createlinestring, + :createmultilinestring, + :createmultipoint, + :createmultipolygon, + :createmultipolygon_noholes, + :createpoint, + :createpolygon, + :createRAT, + :createstylemanager, + :createstyletable, + :createstyletool, + :curvegeom, + :delaunaytriangulation, + :difference, + :forceto, + :fromGML, + :fromJSON, + :fromWKB, + :fromWKT, + :gdalbuildvrt, + :gdaldem, + :gdalgrid, + :gdalnearblack, + :gdalrasterize, + :gdaltranslate, + :gdalvectortranslate, + :gdalwarp, + :getband, + :getcolortable, + :getfeature, + :getgeom, + :getlayer, + :getmaskband, + :getoverview, + :getpart, + :getspatialref, + :importCRS, + :intersection, + :importEPSG, + :importEPSGA, + :importESRI, + :importPROJ4, + :importWKT, + :importXML, + :importURL, + :lineargeom, + :newspatialref, + :nextfeature, + :pointalongline, + :pointonsurface, + :polygonfromedges, + :polygonize, + :read, + :sampleoverview, + :simplify, + :simplifypreservetopology, + :symdifference, + :union, + :update, + :readraster, +) eval(quote function $(gdalfunc)(f::Function, args...; kwargs...) obj = $(Symbol("unsafe_$gdalfunc"))(args...; kwargs...) - try + return try f(obj) finally destroy(obj) diff --git a/src/convert.jl b/src/convert.jl index 260f3d6d..1d58acdc 100644 --- a/src/convert.jl +++ b/src/convert.jl @@ -7,20 +7,25 @@ source::GeoFormatTypes.GeoFormat ) -Convert a `GeoFormatTypes.GeoFormat` object to Geometry, then to the target format. -The Geom trait is needed to separate out convert for CRS for WellKnownText -and GML, which may contain both. +Convert a `GeoFormatTypes.GeoFormat` object to Geometry, then to the target +format. The Geom trait is needed to separate out convert for CRS for +WellKnownText and GML, which may contain both. Both `Geom` and `Mixed` formats are converted to Geometries by default. To convert a `Mixed` format to crs, `CRS` must be explicitly passed for `mode`. """ -Base.convert(target::Type{<:GFT.GeoFormat}, mode::Union{GFT.FormatMode,Type{GFT.FormatMode}}, - source::GFT.GeoFormat) = - convert(target, convert(AbstractGeometry, source)) +function Base.convert( + target::Type{<:GFT.GeoFormat}, + mode::Union{GFT.FormatMode,Type{GFT.FormatMode}}, + source::GFT.GeoFormat, +) + return convert(target, convert(AbstractGeometry, source)) +end """ - convert(::Type{<:AbstractGeometry}, source::GeoFormatTypes.AbstractWellKnownText) + convert(::Type{<:AbstractGeometry}, + source::GeoFormatTypes.AbstractWellKnownText) convert(::Type{<:AbstractGeometry}, source::GeoFormatTypes.WellKnownBinary) convert(::Type{<:AbstractGeometry}, source::GeoFormatTypes.GeoJSON) convert(::Type{<:AbstractGeometry}, source::GeoFormatTypes.GML) @@ -28,17 +33,31 @@ Base.convert(target::Type{<:GFT.GeoFormat}, mode::Union{GFT.FormatMode,Type{GFT. Convert `GeoFormat` geometry data to an ArchGDAL `Geometry` type """ -Base.convert(::Type{<:AbstractGeometry}, source::GFT.AbstractWellKnownText) = - fromWKT(GFT.val(source)) -Base.convert(::Type{<:AbstractGeometry}, source::GFT.WellKnownBinary) = - fromWKB(GFT.val(source)) -Base.convert(::Type{<:AbstractGeometry}, source::GFT.GeoJSON) = - fromJSON(GFT.val(source)) -Base.convert(::Type{<:AbstractGeometry}, source::GFT.GML) = - fromGML(GFT.val(source)) +function Base.convert( + ::Type{<:AbstractGeometry}, + source::GFT.AbstractWellKnownText, +) + return fromWKT(GFT.val(source)) +end +function Base.convert(::Type{<:AbstractGeometry}, source::GFT.WellKnownBinary) + return fromWKB(GFT.val(source)) +end +function Base.convert(::Type{<:AbstractGeometry}, source::GFT.GeoJSON) + return fromJSON(GFT.val(source)) +end +function Base.convert(::Type{<:AbstractGeometry}, source::GFT.GML) + return fromGML(GFT.val(source)) +end + +function Base.convert(::Type{IGeometry{wkbUnknown}}, source::AbstractGeometry) + result = IGeometry(C_NULL) + result.ptr = unsafe_clone(source).ptr + return result +end """ - convert(::Type{<:GeoFormatTypes.AbstractWellKnownText}, source::AbstractGeometry) + convert(::Type{<:GeoFormatTypes.AbstractWellKnownText}, + source::AbstractGeometry) convert(::Type{<:GeoFormatTypes.WellKnownBinary}, source::AbstractGeometry) convert(::Type{<:GeoFormatTypes.GeoJSON}, source::AbstractGeometry) convert(::Type{<:GeoFormatTypes.GML}, source::AbstractGeometry) @@ -47,35 +66,50 @@ Base.convert(::Type{<:AbstractGeometry}, source::GFT.GML) = Convert `AbstractGeometry` data to any geometry `GeoFormat`. """ -Base.convert(::Type{<:GFT.AbstractWellKnownText}, source::AbstractGeometry) = - GFT.WellKnownText(GFT.Geom(), toWKT(source)) -Base.convert(::Type{<:GFT.WellKnownBinary}, source::AbstractGeometry) = - GFT.WellKnownBinary(GFT.Geom(), toWKB(source)) -Base.convert(::Type{<:GFT.GeoJSON}, source::AbstractGeometry) = - GFT.GeoJSON(toJSON(source)) -Base.convert(::Type{<:GFT.GML}, source::AbstractGeometry) = - GFT.GML(GFT.Geom(), toGML(source)) -Base.convert(::Type{<:GFT.KML}, source::AbstractGeometry) = - GFT.KML(toKML(source)) +function Base.convert( + ::Type{<:GFT.AbstractWellKnownText}, + source::AbstractGeometry, +) + return GFT.WellKnownText(GFT.Geom(), toWKT(source)) +end +function Base.convert(::Type{<:GFT.WellKnownBinary}, source::AbstractGeometry) + return GFT.WellKnownBinary(GFT.Geom(), toWKB(source)) +end +function Base.convert(::Type{<:GFT.GeoJSON}, source::AbstractGeometry) + return GFT.GeoJSON(toJSON(source)) +end +function Base.convert(::Type{<:GFT.GML}, source::AbstractGeometry) + return GFT.GML(GFT.Geom(), toGML(source)) +end +function Base.convert(::Type{<:GFT.KML}, source::AbstractGeometry) + return GFT.KML(toKML(source)) +end """ - convert(target::Type{<:GeoFormatTypes.GeoFormat}, mode::CRS, source::GeoFormat) + convert(target::Type{<:GeoFormatTypes.GeoFormat}, mode::CRS, + source::GeoFormat) Convert `GeoFormat` CRS data to another `GeoFormat` CRS type. """ -Base.convert(target::Type{<:GFT.GeoFormat}, mode::Union{GFT.CRS,Type{GFT.CRS}}, - source::GFT.GeoFormat) = - unsafe_convertcrs(target, importCRS(source)) - -unsafe_convertcrs(::Type{<:GFT.CoordSys}, crsref) = - GFT.CoordSys(toMICoordSys(crsref)) -unsafe_convertcrs(::Type{<:GFT.ProjString}, crsref) = - GFT.ProjString(toPROJ4(crsref)) -unsafe_convertcrs(::Type{<:GFT.WellKnownText}, crsref) = - GFT.WellKnownText(GFT.CRS(), toWKT(crsref)) -unsafe_convertcrs(::Type{<:GFT.ESRIWellKnownText}, crsref) = - GFT.ESRIWellKnownText(GFT.CRS(), toWKT(morphtoESRI!(crsref))) -unsafe_convertcrs(::Type{<:GFT.GML}, crsref) = - GFT.GML(toXML(crsref)) +function Base.convert( + target::Type{<:GFT.GeoFormat}, + mode::Union{GFT.CRS,Type{GFT.CRS}}, + source::GFT.GeoFormat, +) + return unsafe_convertcrs(target, importCRS(source)) +end +function unsafe_convertcrs(::Type{<:GFT.CoordSys}, crsref) + return GFT.CoordSys(toMICoordSys(crsref)) +end +function unsafe_convertcrs(::Type{<:GFT.ProjString}, crsref) + return GFT.ProjString(toPROJ4(crsref)) +end +function unsafe_convertcrs(::Type{<:GFT.WellKnownText}, crsref) + return GFT.WellKnownText(GFT.CRS(), toWKT(crsref)) +end +function unsafe_convertcrs(::Type{<:GFT.ESRIWellKnownText}, crsref) + return GFT.ESRIWellKnownText(GFT.CRS(), toWKT(morphtoESRI!(crsref))) +end +unsafe_convertcrs(::Type{<:GFT.GML}, crsref) = GFT.GML(toXML(crsref)) diff --git a/src/dataset.jl b/src/dataset.jl index 25a9c3ea..06f6f58e 100644 --- a/src/dataset.jl +++ b/src/dataset.jl @@ -1,5 +1,6 @@ """ - copywholeraster(source::AbstractDataset, dest::AbstractDataset; ) + copywholeraster(source::AbstractDataset, dest::AbstractDataset; + ) Copy all dataset raster data. @@ -8,8 +9,8 @@ similarly configured dataset. The source and destination dataset must have the same number of bands, and the same width and height. The bands do not have to have the same data type. -Currently the only `options` supported are : `\"INTERLEAVE=PIXEL\"` to -force pixel interleaved operation and `\"COMPRESSED=YES\"` to force alignment on +Currently the only `options` supported are : `\"INTERLEAVE=PIXEL\"` to force +pixel interleaved operation and `\"COMPRESSED=YES\"` to force alignment on target dataset block sizes to achieve best compression. More options may be supported in the future. @@ -19,20 +20,27 @@ specific `createcopy()` functions. It implements efficient copying, in particular \"chunking\" the copy in substantial blocks and, if appropriate, performing the transfer in a pixel interleaved fashion. """ -function copywholeraster( - source::AbstractDataset, - dest::AbstractDataset; - options = StringList(C_NULL), - progressfunc::Function = GDAL.gdaldummyprogress, - progressdata::Any = C_NULL +function copywholeraster!( + source::AbstractDataset, + dest::D; + options = StringList(C_NULL), + progressfunc::Function = GDAL.gdaldummyprogress, + progressdata::Any = C_NULL, +)::D where {D<:AbstractDataset} + result = GDAL.gdaldatasetcopywholeraster( + source.ptr, + dest.ptr, + options, + @cplprogress(progressfunc), + progressdata, ) - result = GDAL.gdaldatasetcopywholeraster(source.ptr, dest.ptr, options, - @cplprogress(progressfunc), progressdata) @cplerr result "Failed to copy whole raster" + return dest end """ - unsafe_copy(dataset::AbstractDataset; [filename, [driver, []]]) + unsafe_copy(dataset::AbstractDataset; [filename, [driver, + []]]) Create a copy of a dataset. @@ -80,23 +88,25 @@ In some situations, the new dataset can be created in another process through the GDAL API Proxy mechanism. """ function unsafe_copy( - dataset::AbstractDataset; - filename::AbstractString = string("/vsimem/$(gensym())"), - driver::Driver = getdriver(dataset), - strict::Bool = false, - options = StringList(C_NULL), - progressfunc::Function = GDAL.gdaldummyprogress, - progressdata = C_NULL + dataset::AbstractDataset; + filename::AbstractString = string("/vsimem/$(gensym())"), + driver::Driver = getdriver(dataset), + strict::Bool = false, + options = StringList(C_NULL), + progressfunc::Function = GDAL.gdaldummyprogress, + progressdata = C_NULL, +)::Dataset + return Dataset( + GDAL.gdalcreatecopy( + driver.ptr, + filename, + dataset.ptr, + strict, + options, + @cplprogress(progressfunc), + progressdata, + ), ) - return Dataset(GDAL.gdalcreatecopy( - driver.ptr, - filename, - dataset.ptr, - strict, - options, - @cplprogress(progressfunc), - progressdata - )) end """ @@ -137,23 +147,25 @@ end The newly created dataset. """ function copy( - dataset::AbstractDataset; - filename::AbstractString = string("/vsimem/$(gensym())"), - driver::Driver = getdriver(dataset), - strict::Bool = false, - options = StringList(C_NULL), - progressfunc::Function = GDAL.gdaldummyprogress, - progressdata = C_NULL + dataset::AbstractDataset; + filename::AbstractString = string("/vsimem/$(gensym())"), + driver::Driver = getdriver(dataset), + strict::Bool = false, + options = StringList(C_NULL), + progressfunc::Function = GDAL.gdaldummyprogress, + progressdata = C_NULL, +)::IDataset + return IDataset( + GDAL.gdalcreatecopy( + driver.ptr, + filename, + dataset.ptr, + strict, + options, + @cplprogress(progressfunc), + progressdata, + ), ) - return IDataset(GDAL.gdalcreatecopy( - driver.ptr, - filename, - dataset.ptr, - strict, - options, - @cplprogress(progressfunc), - progressdata - )) end """ @@ -161,12 +173,18 @@ end Writes the dataset to the designated filename. """ -function write(dataset::AbstractDataset, filename::AbstractString; kwargs...) - return destroy(unsafe_copy(dataset, filename = filename; kwargs...)) +function write( + dataset::AbstractDataset, + filename::AbstractString; + kwargs..., +)::Nothing + destroy(unsafe_copy(dataset, filename = filename; kwargs...)) + return nothing end """ - unsafe_create(filename::AbstractString; driver, width, height, nbands, dtype, options) + unsafe_create(filename::AbstractString; driver, width, height, nbands, + dtype, options) Create a new dataset. @@ -189,35 +207,50 @@ In GDAL 2, the arguments nXSize, nYSize and nBands can be passed to 0 when creating a vector-only dataset for a compatible driver. """ function unsafe_create( - filename::AbstractString; - driver::Driver = identifydriver(filename), - width::Integer = 0, - height::Integer = 0, - nbands::Integer = 0, - dtype::DataType = Any, - options = StringList(C_NULL) + filename::AbstractString; + driver::Driver = identifydriver(filename), + width::Integer = 0, + height::Integer = 0, + nbands::Integer = 0, + dtype::DataType = Any, + options = StringList(C_NULL), +)::Dataset + result = GDAL.gdalcreate( + driver.ptr, + filename, + width, + height, + nbands, + convert(GDALDataType, dtype), + options, ) - result = GDAL.gdalcreate(driver.ptr, filename, width, height, nbands, - _GDALTYPE[dtype], options) return Dataset(result) end function unsafe_create( - driver::Driver; - filename::AbstractString = string("/vsimem/$(gensym())"), - width::Integer = 0, - height::Integer = 0, - nbands::Integer = 0, - dtype::DataType = Any, - options = StringList(C_NULL) + driver::Driver; + filename::AbstractString = string("/vsimem/$(gensym())"), + width::Integer = 0, + height::Integer = 0, + nbands::Integer = 0, + dtype::DataType = Any, + options = StringList(C_NULL), +)::Dataset + result = GDAL.gdalcreate( + driver.ptr, + filename, + width, + height, + nbands, + convert(GDALDataType, dtype), + options, ) - result = GDAL.gdalcreate(driver.ptr, filename, width, height, nbands, - _GDALTYPE[dtype], options) return Dataset(result) end """ - create(filename::AbstractString; driver, width, height, nbands, dtype, options) + create(filename::AbstractString; driver, width, height, nbands, dtype, + options) Create a new dataset. @@ -247,40 +280,55 @@ end The newly created dataset. """ function create( - filename::AbstractString; - driver::Driver = identifydriver(filename), - width::Integer = 0, - height::Integer = 0, - nbands::Integer = 0, - dtype::DataType = Any, - options = StringList(C_NULL) + filename::AbstractString; + driver::Driver = identifydriver(filename), + width::Integer = 0, + height::Integer = 0, + nbands::Integer = 0, + dtype::DataType = Any, + options = StringList(C_NULL), +)::IDataset + result = GDAL.gdalcreate( + driver.ptr, + filename, + width, + height, + nbands, + convert(GDALDataType, dtype), + options, ) - result = GDAL.gdalcreate(driver.ptr, filename, width, height, nbands, - _GDALTYPE[dtype], options) return IDataset(result) end function create( - driver::Driver; - filename::AbstractString = string("/vsimem/$(gensym())"), - width::Integer = 0, - height::Integer = 0, - nbands::Integer = 0, - dtype::DataType = Any, - options = StringList(C_NULL) + driver::Driver; + filename::AbstractString = string("/vsimem/$(gensym())"), + width::Integer = 0, + height::Integer = 0, + nbands::Integer = 0, + dtype::DataType = Any, + options = StringList(C_NULL), +)::IDataset + result = GDAL.gdalcreate( + driver.ptr, + filename, + width, + height, + nbands, + convert(GDALDataType, dtype), + options, ) - result = GDAL.gdalcreate(driver.ptr, filename, width, height, nbands, - _GDALTYPE[dtype], options) return IDataset(result) end """ - unsafe_read(filename; flags=OF_ReadOnly, alloweddrivers, options, siblingfiles) + unsafe_read(filename; flags=OF_READONLY, alloweddrivers, options, + siblingfiles) -Open a raster file as a GDALDataset. +Open a raster file as a `Dataset`. This function will try to open the passed file, or virtual dataset name by -invoking the Open method of each registered `GDALDriver` in turn. The first +invoking the Open method of each registered `Driver` in turn. The first successful open will result in a returned dataset. If all drivers fail then `NULL` is returned and an error is issued. @@ -293,11 +341,11 @@ driver on how to access a dataset. It should be in UTF-8 encoding. - Driver kind: GDAL_OF_RASTER for raster drivers, GDAL_OF_VECTOR for vector drivers. If none of the value is specified, both are implied. - - Access mode: `OF_ReadOnly` (exclusive) or `OF_Update`. + - Access mode: `OF_READONLY` (exclusive) or `OF_UPDATE`. - Shared mode: `GDAL_OF_SHARED`. If set, it allows the sharing of - GDALDataset handles for a dataset with other callers that + `Dataset` handles for a dataset with other callers that have set GDAL_OF_SHARED. In particular, GDALOpenEx() will - consult its list of currently open and shared GDALDataset's, + consult its list of currently open and shared `Dataset`'s, and if the GetDescription() name for one exactly matches the pszFilename passed to GDALOpenEx() it will be referenced and returned, if GDALOpenEx() is called from the same thread. @@ -332,19 +380,24 @@ archive (see `VSIInstallTarFileHandler()`), or a HTTP / FTP server (see `VSIInstallCurlFileHandler()`) """ function unsafe_read( - filename::AbstractString; - flags = OF_ReadOnly, - alloweddrivers = StringList(C_NULL), - options = StringList(C_NULL), - siblingfiles = StringList(C_NULL) + filename::AbstractString; + flags = OF_READONLY, + alloweddrivers = StringList(C_NULL), + options = StringList(C_NULL), + siblingfiles = StringList(C_NULL), +)::Dataset + result = GDAL.gdalopenex( + filename, + Int(flags), + alloweddrivers, + options, + siblingfiles, ) - result = GDAL.gdalopenex(filename, Int(flags), alloweddrivers, options, - siblingfiles) return Dataset(result) end """ - read(filename; flags=OF_ReadOnly, alloweddrivers, options, siblingfiles) + read(filename; flags=OF_READONLY, alloweddrivers, options, siblingfiles) Open a raster file @@ -353,13 +406,13 @@ Open a raster file ### Keyword Arguments * `flags`: a combination of `OF_*` flags (listed below) that may be - combined through the logical `|` operator. It defaults to `OF_ReadOnly`. + combined through the logical `|` operator. It defaults to `OF_READONLY`. - Driver kind: `OF_Raster` for raster drivers, `OF_Vector` for vector drivers. If none of the value is specified, both are implied. - - Access mode: `OF_ReadOnly` (exclusive) or `OF_Update`. + - Access mode: `OF_READONLY` (exclusive) or `OF_UPDATE`. - Shared mode: `OF_Shared`. If set, it allows the sharing of handles for a dataset with other callers that have set `OF_Shared`. - - Verbose error: `OF_Verbose_Error`. If set, a failed attempt to open the + - Verbose error: `OF_VERBOSE_ERROR`. If set, a failed attempt to open the file will lead to an error message to be reported. * `options`: additional format dependent options. @@ -379,54 +432,61 @@ end The corresponding dataset. """ function read( - filename::AbstractString; - flags = OF_ReadOnly | OF_Verbose_Error, - alloweddrivers = StringList(C_NULL), - options = StringList(C_NULL), - siblingfiles = StringList(C_NULL) + filename::AbstractString; + flags = OF_READONLY | OF_VERBOSE_ERROR, + alloweddrivers = StringList(C_NULL), + options = StringList(C_NULL), + siblingfiles = StringList(C_NULL), +)::IDataset + result = GDAL.gdalopenex( + filename, + Int(flags), + alloweddrivers, + options, + siblingfiles, ) - result = GDAL.gdalopenex(filename, Int(flags), alloweddrivers, options, - siblingfiles) return IDataset(result) end -unsafe_update(filename::AbstractString; flags = OF_Update, kwargs...) = - unsafe_read(filename; flags = OF_Update | flags, kwargs...) +unsafe_update(filename::AbstractString; flags = OF_UPDATE, kwargs...)::Dataset = + unsafe_read(filename; flags = OF_UPDATE | flags, kwargs...) """ width(dataset::AbstractDataset) Fetch raster width in pixels. """ -width(dataset::AbstractDataset) = GDAL.gdalgetrasterxsize(dataset.ptr) +width(dataset::AbstractDataset)::Integer = GDAL.gdalgetrasterxsize(dataset.ptr) """ height(dataset::AbstractDataset) Fetch raster height in pixels. """ -height(dataset::AbstractDataset) = GDAL.gdalgetrasterysize(dataset.ptr) +height(dataset::AbstractDataset)::Integer = GDAL.gdalgetrasterysize(dataset.ptr) """ nraster(dataset::AbstractDataset) Fetch the number of raster bands on this dataset. """ -nraster(dataset::AbstractDataset) = GDAL.gdalgetrastercount(dataset.ptr) +nraster(dataset::AbstractDataset)::Integer = + GDAL.gdalgetrastercount(dataset.ptr) """ nlayer(dataset::AbstractDataset) Fetch the number of feature layers on this dataset. """ -nlayer(dataset::AbstractDataset) = GDAL.gdaldatasetgetlayercount(dataset.ptr) +nlayer(dataset::AbstractDataset)::Integer = + GDAL.gdaldatasetgetlayercount(dataset.ptr) """ getdriver(dataset::AbstractDataset) Fetch the driver that the dataset was created with """ -getdriver(dataset::AbstractDataset) = +getdriver(dataset::AbstractDataset)::Driver = Driver(GDAL.gdalgetdatasetdriver(dataset.ptr)) """ @@ -442,40 +502,41 @@ list is owned by the caller and should be deallocated with `CSLDestroy()`. The returned filenames will normally be relative or absolute paths depending on the path used to originally open the dataset. The strings will be UTF-8 encoded """ -filelist(dataset::AbstractDataset) = GDAL.gdalgetfilelist(dataset.ptr) +filelist(dataset::AbstractDataset)::Vector{String} = + GDAL.gdalgetfilelist(dataset.ptr) """ getlayer(dataset::AbstractDataset, i::Integer) Fetch the layer at index `i` (between `0` and `nlayer(dataset)-1`) -The returned layer remains owned by the GDALDataset and should not be deleted by +The returned layer remains owned by the `Dataset` and should not be deleted by the application. """ -getlayer(dataset::AbstractDataset, i::Integer) = +getlayer(dataset::AbstractDataset, i::Integer)::IFeatureLayer = IFeatureLayer(GDAL.gdaldatasetgetlayer(dataset.ptr, i), ownedby = dataset) -unsafe_getlayer(dataset::AbstractDataset, i::Integer) = +unsafe_getlayer(dataset::AbstractDataset, i::Integer)::FeatureLayer = FeatureLayer(GDAL.gdaldatasetgetlayer(dataset.ptr, i)) """ getlayer(dataset::AbstractDataset, name::AbstractString) getlayer(table::Table) -Fetch the feature layer corresponding to the given name. If it is called on a Table, which -supports only one layer, a name is not needed. +Fetch the feature layer corresponding to the given name. If it is called on a +Table, which supports only one layer, a name is not needed. -The returned layer remains owned by the GDALDataset and should not be deleted by +The returned layer remains owned by the `Dataset` and should not be deleted by the application. """ -function getlayer(dataset::AbstractDataset, name::AbstractString) +function getlayer(dataset::AbstractDataset, name::AbstractString)::IFeatureLayer return IFeatureLayer( GDAL.gdaldatasetgetlayerbyname(dataset.ptr, name), - ownedby = dataset + ownedby = dataset, ) end -unsafe_getlayer(dataset::AbstractDataset, name::AbstractString) = +unsafe_getlayer(dataset::AbstractDataset, name::AbstractString)::FeatureLayer = FeatureLayer(GDAL.gdaldatasetgetlayerbyname(dataset.ptr, name)) """ @@ -487,7 +548,7 @@ Delete the indicated layer (at index i; between `0` to `nlayer()-1`) `OGRERR_NONE` on success, or `OGRERR_UNSUPPORTED_OPERATION` if deleting layers is not supported for this dataset. """ -function deletelayer!(dataset::AbstractDataset, i::Integer) +function deletelayer!(dataset::T, i::Integer)::T where {T<:AbstractDataset} result = GDAL.gdaldatasetdeletelayer(dataset.ptr, i) @ogrerr result "Failed to delete layer" return dataset @@ -496,7 +557,8 @@ end """ testcapability(dataset::AbstractDataset, capability::AbstractString) -Test if capability is available. `true` if capability available otherwise `false`. +Test if capability is available. `true` if capability available otherwise +`false`. One of the following dataset capability names can be passed into this function, and a `true` or `false` value will be returned indicating whether or not the @@ -518,25 +580,28 @@ the strings themselves to avoid misspelling. * `dataset`: the dataset handle. * `capability`: the capability to test. """ -testcapability(dataset::AbstractDataset, capability::AbstractString) = +testcapability(dataset::AbstractDataset, capability::AbstractString)::Bool = Bool(GDAL.gdaldatasettestcapability(dataset.ptr, capability)) function listcapability( - dataset::AbstractDataset, - capabilities = (GDAL.ODsCCreateLayer, - GDAL.ODsCDeleteLayer, - GDAL.ODsCCreateGeomFieldAfterCreateLayer, - GDAL.ODsCCurveGeometries, - GDAL.ODsCTransactions, - GDAL.ODsCEmulatedTransactions) - ) - return Dict{String, Bool}( + dataset::AbstractDataset, + capabilities = ( + GDAL.ODsCCreateLayer, + GDAL.ODsCDeleteLayer, + GDAL.ODsCCreateGeomFieldAfterCreateLayer, + GDAL.ODsCCurveGeometries, + GDAL.ODsCTransactions, + GDAL.ODsCEmulatedTransactions, + ), +)::Dict{String,Bool} + return Dict{String,Bool}( c => testcapability(dataset, c) for c in capabilities ) end """ - unsafe_executesql(dataset::AbstractDataset, query::AbstractString; dialect, spatialfilter) + unsafe_executesql(dataset::AbstractDataset, query::AbstractString; dialect, + spatialfilter) Execute an SQL statement against the data store. @@ -566,17 +631,19 @@ an OGRLayer containing the results of the query. Deallocate with ReleaseResultSet(). """ function unsafe_executesql( - dataset::AbstractDataset, - query::AbstractString; - dialect::AbstractString = "", - spatialfilter::Geometry = Geometry(GDALGeometry(C_NULL)) + dataset::AbstractDataset, + query::AbstractString; + dialect::AbstractString = "", + spatialfilter::Geometry = Geometry(C_NULL), +)::FeatureLayer + return FeatureLayer( + GDAL.gdaldatasetexecutesql( + dataset.ptr, + query, + spatialfilter.ptr, + dialect, + ), ) - return FeatureLayer(GDALFeatureLayer(GDAL.gdaldatasetexecutesql( - dataset.ptr, - query, - spatialfilter.ptr, - dialect - ))) end """ @@ -585,16 +652,20 @@ end Release results of ExecuteSQL(). This function should only be used to deallocate OGRLayers resulting from an -ExecuteSQL() call on the same GDALDataset. Failure to deallocate a results set -before destroying the GDALDataset may cause errors. +ExecuteSQL() call on the same `Dataset`. Failure to deallocate a results set +before destroying the `Dataset` may cause errors. ### Parameters * `dataset`: the dataset handle. * `layer`: the result of a previous ExecuteSQL() call. """ -function releaseresultset(dataset::AbstractDataset, layer::FeatureLayer) +function releaseresultset( + dataset::AbstractDataset, + layer::FeatureLayer, +)::Nothing GDAL.gdaldatasetreleaseresultset(dataset.ptr, layer.ptr) destroy(layer) + return nothing end """ @@ -603,10 +674,10 @@ end Fetch a band object for a dataset from its index. """ -getband(dataset::AbstractDataset, i::Integer) = +getband(dataset::AbstractDataset, i::Integer)::IRasterBand = IRasterBand(GDAL.gdalgetrasterband(dataset.ptr, i), ownedby = dataset) -unsafe_getband(dataset::AbstractDataset, i::Integer) = +unsafe_getband(dataset::AbstractDataset, i::Integer)::RasterBand = RasterBand(GDAL.gdalgetrasterband(dataset.ptr, i)) """ @@ -636,22 +707,28 @@ transformation to projection coordinates. ### Returns `CE_None` on success, or `CE_Failure` if no transform can be fetched. """ -function getgeotransform!(dataset::AbstractDataset, transform::Vector{Cdouble}) +function getgeotransform!( + dataset::AbstractDataset, + transform::Vector{Cdouble}, +)::Vector{Cdouble} @assert length(transform) == 6 result = GDAL.gdalgetgeotransform(dataset.ptr, pointer(transform)) @cplerr result "Failed to get geotransform" return transform end -getgeotransform(dataset::AbstractDataset) = - getgeotransform!(dataset, Array{Cdouble}(undef, 6)) +getgeotransform(dataset::AbstractDataset)::Vector{Cdouble} = + getgeotransform!(dataset, Vector{Cdouble}(undef, 6)) """ setgeotransform!(dataset::AbstractDataset, transform::Vector{Cdouble}) Set the affine transformation coefficients. """ -function setgeotransform!(dataset::AbstractDataset, transform::Vector{Cdouble}) +function setgeotransform!( + dataset::T, + transform::Vector{Cdouble}, +)::T where {T<:AbstractDataset} @assert length(transform) == 6 result = GDAL.gdalsetgeotransform(dataset.ptr, pointer(transform)) @cplerr result "Failed to transform raster dataset" @@ -663,7 +740,7 @@ end Get number of GCPs for this dataset. Zero if there are none. """ -ngcp(dataset::AbstractDataset) = GDAL.gdalgetgcpcount(dataset.ptr) +ngcp(dataset::AbstractDataset)::Integer = GDAL.gdalgetgcpcount(dataset.ptr) """ getproj(dataset::AbstractDataset) @@ -674,23 +751,28 @@ It should be suitable for use with the OGRSpatialReference class. When a projection definition is not available an empty (but not `NULL`) string is returned. """ -getproj(dataset::AbstractDataset) = GDAL.gdalgetprojectionref(dataset.ptr) +getproj(dataset::AbstractDataset)::String = + GDAL.gdalgetprojectionref(dataset.ptr) """ setproj!(dataset::AbstractDataset, projstring::AbstractString) Set the projection reference string for this dataset. """ -function setproj!(dataset::AbstractDataset, projstring::AbstractString) +function setproj!( + dataset::T, + projstring::AbstractString, +)::T where {T<:AbstractDataset} result = GDAL.gdalsetprojection(dataset.ptr, projstring) @cplerr result "Could not set projection" return dataset end """ - buildoverviews!(dataset::AbstractDataset, overviewlist::Vector{Cint}; bandlist, resampling="NEAREST", - progressfunc, progressdata) - Build raster overview(s). + buildoverviews!(dataset::AbstractDataset, overviewlist::Vector{Cint}; + bandlist, resampling="NEAREST", progressfunc, progressdata) + +Build raster overview(s). If the operation is unsupported for the indicated dataset, then CE_Failure is returned, and CPLGetLastErrorNo() will return CPLE_NotSupported. @@ -707,13 +789,13 @@ returned, and CPLGetLastErrorNo() will return CPLE_NotSupported. * `progressdata` application data to pass to the progress function. """ function buildoverviews!( - dataset::AbstractDataset, - overviewlist::Vector{Cint}; - bandlist::Vector{Cint} = Cint[], - resampling::AbstractString = "NEAREST", - progressfunc::Function = GDAL.gdaldummyprogress, - progressdata = C_NULL - ) + dataset::T, + overviewlist::Vector{Cint}; + bandlist::Vector{Cint} = Cint[], + resampling::AbstractString = "NEAREST", + progressfunc::Function = GDAL.gdaldummyprogress, + progressdata = C_NULL, +)::T where {T<:AbstractDataset} result = GDAL.gdalbuildoverviews( dataset.ptr, resampling, @@ -722,13 +804,27 @@ function buildoverviews!( length(bandlist), bandlist, @cplprogress(progressfunc), - progressdata + progressdata, ) @cplerr result "Failed to build overviews" return dataset end -function destroy(dataset::AbstractDataset) +function destroy(dataset::AbstractDataset)::Nothing GDAL.gdalclose(dataset.ptr) dataset.ptr = C_NULL + return nothing +end + +""" + pixeltype(ds::AbstractDataset) + +Tries to determine a common dataset type for all the bands +in a raster dataset. +""" +function pixeltype(ds::AbstractDataset)::DataType + alldatatypes = map(1:nraster(ds)) do i + return pixeltype(getband(ds, i)) + end + return reduce(promote_type, alldatatypes) end diff --git a/src/driver.jl b/src/driver.jl index 97f7ca3e..a313d5cb 100644 --- a/src/driver.jl +++ b/src/driver.jl @@ -3,81 +3,73 @@ Fetch driver by index. """ -getdriver(i::Integer) = Driver(GDAL.gdalgetdriver(i)) +getdriver(i::Integer)::Driver = Driver(GDAL.gdalgetdriver(i)) """ getdriver(name::AbstractString) Fetch a driver based on the short name (such as `GTiff`). """ -getdriver(name::AbstractString) = Driver(GDAL.gdalgetdriverbyname(name)) - -""" - destroy(drv::Driver) - -Destroy a `GDALDriver`. - -This is roughly equivalent to deleting the driver, but is guaranteed to take -place in the GDAL heap. It is important this that function not be called on a -driver that is registered with the `GDALDriverManager`. -""" -function destroy(drv::Driver) - GDAL.gdaldestroydriver(drv.ptr) - drv.ptr = C_NULL -end +getdriver(name::AbstractString)::Driver = Driver(GDAL.gdalgetdriverbyname(name)) """ register(drv::Driver) Register a driver for use. """ -register(drv::Driver) = GDAL.gdalregisterdriver(drv.ptr) +function register(drv::Driver)::Nothing + GDAL.gdalregisterdriver(drv.ptr) + return nothing +end """ deregister(drv::Driver) Deregister the passed driver. """ -deregister(drv::Driver) = GDAL.gdalderegisterdriver(drv.ptr) +function deregister(drv::Driver)::Nothing + GDAL.gdalderegisterdriver(drv.ptr) + return nothing +end """ options(drv::Driver) Return the list of creation options of the driver [an XML string]. """ -options(drv::Driver) = GDAL.gdalgetdrivercreationoptionlist(drv.ptr) +options(drv::Driver)::String = GDAL.gdalgetdrivercreationoptionlist(drv.ptr) -driveroptions(name::AbstractString) = options(getdriver(name)) +driveroptions(name::AbstractString)::String = options(getdriver(name)) """ shortname(drv::Driver) Return the short name of a driver (e.g. `GTiff`). """ -shortname(drv::Driver) = GDAL.gdalgetdrivershortname(drv.ptr) +shortname(drv::Driver)::String = GDAL.gdalgetdrivershortname(drv.ptr) """ longname(drv::Driver) Return the long name of a driver (e.g. `GeoTIFF`), or empty string. """ -longname(drv::Driver) = GDAL.gdalgetdriverlongname(drv.ptr) +longname(drv::Driver)::String = GDAL.gdalgetdriverlongname(drv.ptr) """ ndriver() Fetch the number of registered drivers. """ -ndriver() = GDAL.gdalgetdrivercount() +ndriver()::Integer = GDAL.gdalgetdrivercount() """ listdrivers() Returns a listing of all registered drivers. """ -listdrivers() = Dict{String,String}([ - shortname(getdriver(i)) => longname(getdriver(i)) for i in 0:(ndriver()-1) -]) +listdrivers()::Dict{String,String} = Dict{String,String}( + [shortname(getdriver(i)) => longname(getdriver(i)) for i in 0:(ndriver()-1)], +) """ identifydriver(filename::AbstractString) @@ -85,11 +77,11 @@ listdrivers() = Dict{String,String}([ Identify the driver that can open a raster file. This function will try to identify the driver that can open the passed filename -by invoking the Identify method of each registered `GDALDriver` in turn. The +by invoking the Identify method of each registered `Driver` in turn. The first driver that successful identifies the file name will be returned. If all drivers fail then `NULL` is returned. """ -identifydriver(filename::AbstractString) = +identifydriver(filename::AbstractString)::Driver = Driver(GDAL.gdalidentifydriver(filename, C_NULL)) """ @@ -120,8 +112,12 @@ in the list of creation options are compatible with the capabilities declared by the `GDAL_DMD_CREATIONOPTIONLIST` metadata item. In case of incompatibility a (non fatal) warning will be emited and ``false`` will be returned. """ -validate(drv::Driver, options::Vector{T}) where {T <: AbstractString} = - Bool(GDAL.gdalvalidatecreationoptions(drv.ptr, options)) +function validate( + drv::Driver, + options::Vector{T}, +)::Bool where {T<:AbstractString} + return Bool(GDAL.gdalvalidatecreationoptions(drv.ptr, options)) +end """ copyfiles(drv::Driver, new::AbstractString, old::AbstractString) @@ -131,21 +127,32 @@ Copy all the files associated with a dataset. """ function copyfiles end -function copyfiles(drv::Driver, new::AbstractString, old::AbstractString) +function copyfiles( + drv::Driver, + new::AbstractString, + old::AbstractString, +)::Nothing result = GDAL.gdalcopydatasetfiles(drv.ptr, new, old) @cplerr result "Failed to copy dataset files" + return nothing end -copyfiles(drvname::AbstractString, new::AbstractString, old::AbstractString) = +function copyfiles( + drvname::AbstractString, + new::AbstractString, + old::AbstractString, +)::Nothing copyfiles(getdriver(drvname), new, old) + return nothing +end """ extensions() -Returns a `Dict{String,String}` of all of the file extensions that can be read by GDAL, -with their respective drivers shortname. +Returns a `Dict{String,String}` of all of the file extensions that can be read +by GDAL, with their respective drivers' `shortname`s. """ -function extensions() +function extensions()::Dict{String,String} extdict = Dict{String,String}() for i in 1:ndriver() driver = getdriver(i) @@ -166,7 +173,7 @@ Returns a driver shortname that matches the filename extension. So `extensiondriver("/my/file.tif") == "GTiff"`. """ -function extensiondriver(filename::AbstractString) +function extensiondriver(filename::AbstractString)::String split = splitext(filename) extensiondict = extensions() ext = split[2] == "" ? split[1] : split[2] diff --git a/src/geointerface.jl b/src/geointerface.jl index e39dd7b5..46936d23 100644 --- a/src/geointerface.jl +++ b/src/geointerface.jl @@ -1,72 +1,79 @@ -let pointtypes = (GDAL.wkbPoint, GDAL.wkbPoint25D, GDAL.wkbPointM, - GDAL.wkbPointZM), - multipointtypes = (GDAL.wkbMultiPoint, GDAL.wkbMultiPoint25D, - GDAL.wkbMultiPointM, GDAL.wkbMultiPointZM), - linetypes = (GDAL.wkbLineString, GDAL.wkbLineString25D, GDAL.wkbLineStringM, - GDAL.wkbLineStringZM), - multilinetypes = (GDAL.wkbMultiLineString, GDAL.wkbMultiLineString25D, - GDAL.wkbMultiLineStringM, GDAL.wkbMultiLineStringZM), - polygontypes = (GDAL.wkbPolygon, GDAL.wkbPolygon25D, GDAL.wkbPolygonM, - GDAL.wkbPolygonZM), - multipolygontypes = (GDAL.wkbMultiPolygon, GDAL.wkbMultiPolygon25D, - GDAL.wkbMultiPolygonM, GDAL.wkbMultiPolygonZM), - collectiontypes = (GDAL.wkbGeometryCollection, - GDAL.wkbGeometryCollection25D, GDAL.wkbGeometryCollectionM, - GDAL.wkbGeometryCollectionZM) +let pointtypes = (wkbPoint, wkbPoint25D, wkbPointM, wkbPointZM), + multipointtypes = + (wkbMultiPoint, wkbMultiPoint25D, wkbMultiPointM, wkbMultiPointZM), + linetypes = + (wkbLineString, wkbLineString25D, wkbLineStringM, wkbLineStringZM), + multilinetypes = ( + wkbMultiLineString, + wkbMultiLineString25D, + wkbMultiLineStringM, + wkbMultiLineStringZM, + ), + polygontypes = (wkbPolygon, wkbPolygon25D, wkbPolygonM, wkbPolygonZM), + multipolygontypes = ( + wkbMultiPolygon, + wkbMultiPolygon25D, + wkbMultiPolygonM, + wkbMultiPolygonZM, + ), + collectiontypes = ( + wkbGeometryCollection, + wkbGeometryCollection25D, + wkbGeometryCollectionM, + wkbGeometryCollectionZM, + ) - function GeoInterface.geotype(g::AbstractGeometry) + function GeoInterface.geotype(g::AbstractGeometry)::Symbol gtype = getgeomtype(g) - if gtype in pointtypes - return :Point + return if gtype in pointtypes + :Point elseif gtype in multipointtypes - return :MultiPoint + :MultiPoint elseif gtype in linetypes - return :LineString - elseif gtype == GDAL.wkbLinearRing - return :LinearRing + :LineString + elseif gtype == wkbLinearRing + :LinearRing elseif gtype in multilinetypes - return :MultiLineString + :MultiLineString elseif gtype in polygontypes - return :Polygon + :Polygon elseif gtype in multipolygontypes - return :MultiPolygon + :MultiPolygon elseif gtype in collectiontypes - return :GeometryCollection + :GeometryCollection else @warn "unknown geometry type" gtype - return :Unknown + :Unknown end end function GeoInterface.coordinates(g::AbstractGeometry) gtype = getgeomtype(g) ndim = getcoorddim(g) - if gtype in pointtypes + return if gtype in pointtypes if ndim == 2 - return Float64[getx(g,0), gety(g,0)] + Float64[getx(g, 0), gety(g, 0)] elseif ndim == 3 - return Float64[getx(g,0), gety(g,0), getz(g,0)] + Float64[getx(g, 0), gety(g, 0), getz(g, 0)] else - @assert ndim == 0 - @warn("Empty Point") + error("getcoorddim($g) returned $ndim: expected 2 or 3") end elseif gtype in multipointtypes - return Vector{Float64}[ - GeoInterface.coordinates(getgeom(g,i-1)) for i in 1:ngeom(g) + Vector{Float64}[ + GeoInterface.coordinates(getgeom(g, i - 1)) for i in 1:ngeom(g) ] - elseif gtype in linetypes || gtype == GDAL.wkbLinearRing - return Vector{Float64}[ - collect(getpoint(g,i-1)[1:ndim]) for i in 1:ngeom(g) + elseif gtype in linetypes || gtype == wkbLinearRing + Vector{Float64}[ + collect(getpoint(g, i - 1)[1:ndim]) for i in 1:ngeom(g) ] elseif gtype in multilinetypes || gtype in polygontypes - return Vector{Vector{Float64}}[ - GeoInterface.coordinates(getgeom(g,i-1)) for i in 1:ngeom(g) + Vector{Vector{Float64}}[ + GeoInterface.coordinates(getgeom(g, i - 1)) for i in 1:ngeom(g) ] elseif gtype in multipolygontypes - return Vector{Vector{Vector{Float64}}}[ - GeoInterface.coordinates(getgeom(g,i-1)) for i in 1:ngeom(g) + Vector{Vector{Vector{Float64}}}[ + GeoInterface.coordinates(getgeom(g, i - 1)) for i in 1:ngeom(g) ] end end - -end \ No newline at end of file +end diff --git a/src/gcp.jl b/src/geotransform.jl similarity index 56% rename from src/gcp.jl rename to src/geotransform.jl index cf1fed23..96434c1a 100644 --- a/src/gcp.jl +++ b/src/geotransform.jl @@ -1,5 +1,5 @@ """ - invgeotransform!(gt_in::Vector{Cdouble}, gt_out::Vector{Cdouble}) + invgeotransform!(gt_in::Vector{Float64}, gt_out::Vector{Float64}) Invert Geotransform. @@ -13,17 +13,21 @@ converts the equation from being pixel to geo to being geo to pixel. ### Returns `gt_out` """ -function invgeotransform!(gt_in::Vector{Cdouble}, gt_out::Vector{Cdouble}) +function invgeotransform!( + gt_in::Vector{Float64}, + gt_out::Vector{Float64}, +)::Vector{Float64} result = Bool(GDAL.gdalinvgeotransform(pointer(gt_in), pointer(gt_out))) result || error("Geotransform coefficients is uninvertable") - gt_out + return gt_out end -invgeotransform(gt_in::Vector{Cdouble}) = - invgeotransform!(gt_in, Array{Cdouble}(undef, 6)) +invgeotransform(gt_in::Vector{Float64})::Vector{Float64} = + invgeotransform!(gt_in, Array{Float64}(undef, 6)) """ - applygeotransform(geotransform::Vector{Cdouble}, pixel::Cdouble, line::Cdouble) + applygeotransform(geotransform::Vector{Float64}, pixel::Float64, + line::Float64) Apply GeoTransform to x/y coordinate. @@ -39,39 +43,43 @@ georeferenced `(geo_x,geo_y)` location. * `line` input line position. """ function applygeotransform( - geotransform::Vector{Cdouble}, - pixel::Cdouble, - line::Cdouble - ) - geo_xy = Array{Cdouble}(undef, 2) + geotransform::Vector{Float64}, + pixel::Float64, + line::Float64, +)::Vector{Float64} + geo_xy = Vector{Float64}(undef, 2) geo_x = pointer(geo_xy) - geo_y = geo_x + sizeof(Cdouble) + geo_y = geo_x + sizeof(Float64) GDAL.gdalapplygeotransform(pointer(geotransform), pixel, line, geo_x, geo_y) - geo_xy + return geo_xy end """ - composegeotransform!(gt1::Vector{Float64}, gt2::Vector{Float64}, gtout::Vector{Float64}) + composegeotransform!(gt1::Vector{Float64}, gt2::Vector{Float64}, + gtout::Vector{Float64}) Compose two geotransforms. -The resulting geotransform is the equivelent to `padfGT1` and then `padfGT2` +The resulting geotransform is the equivalent to `padfGT1` and then `padfGT2` being applied to a point. ### Parameters * `gt1` the first geotransform, six values. * `gt2` the second geotransform, six values. -* `gtout` the output geotransform, six values, may safely be the same -array as `gt1` or `gt2`. +* `gtout` the output geotransform, six values. """ function composegeotransform!( - gt1::Vector{Cdouble}, - gt2::Vector{Cdouble}, - gtout::Vector{Cdouble} - ) - GDAL.gdalcomposegeotransform(pointer(gt1), pointer(gt2), pointer(gtout)) - gtout + gt1::Vector{Float64}, + gt2::Vector{Float64}, + gtout::Vector{Float64}, +)::Vector{Float64} + GDAL.gdalcomposegeotransforms(pointer(gt1), pointer(gt2), pointer(gtout)) + return gtout end -composegeotransform(gt1::Vector{Cdouble}, gt2::Vector{Cdouble}) = - composegeotransform!(gt1, gt2, Array{Cdouble}(undef, 6)) +function composegeotransform( + gt1::Vector{Float64}, + gt2::Vector{Float64}, +)::Vector{Float64} + return composegeotransform!(gt1, gt2, Vector{Float64}(undef, 6)) +end diff --git a/src/ogr/feature.jl b/src/ogr/feature.jl index 6d0f0a7b..2b4c225a 100644 --- a/src/ogr/feature.jl +++ b/src/ogr/feature.jl @@ -6,7 +6,7 @@ Duplicate feature. The newly created feature is owned by the caller, and will have its own reference to the OGRFeatureDefn. """ -unsafe_clone(feature::Feature) = Feature(GDAL.ogr_f_clone(feature.ptr)) +unsafe_clone(feature::Feature)::Feature = Feature(GDAL.ogr_f_clone(feature.ptr)) """ destroy(feature::Feature) @@ -19,9 +19,10 @@ to delete a feature created within the DLL. If the delete is done in the calling application the memory will be freed onto the application heap which is inappropriate. """ -function destroy(feature::Feature) +function destroy(feature::Feature)::Nothing GDAL.ogr_f_destroy(feature.ptr) feature.ptr = C_NULL + return nothing end """ @@ -41,9 +42,10 @@ passed geometry, but instead makes a copy of it. `OGRERR_NONE` if successful, or `OGR_UNSUPPORTED_GEOMETRY_TYPE` if the geometry type is illegal for the `OGRFeatureDefn` (checking not yet implemented). """ -function setgeom!(feature::Feature, geom::AbstractGeometry) +function setgeom!(feature::Feature, geom::AbstractGeometry)::Feature result = GDAL.ogr_f_setgeometry(feature.ptr, geom.ptr) @ogrerr result "OGRErr $result: Failed to set feature geometry." + return feature end """ @@ -51,21 +53,21 @@ end Returns a clone of the geometry corresponding to the feature. """ -function getgeom(feature::Feature) +function getgeom(feature::Feature)::IGeometry result = GDAL.ogr_f_getgeometryref(feature.ptr) - if result == C_NULL - return IGeometry() + return if result == C_NULL + IGeometry() else - return IGeometry(GDAL.ogr_g_clone(result)) + IGeometry(GDAL.ogr_g_clone(result)) end end -function unsafe_getgeom(feature::Feature) +function unsafe_getgeom(feature::Feature)::Geometry result = GDAL.ogr_f_getgeometryref(feature.ptr) - if result == C_NULL - return Geometry() + return if result == C_NULL + Geometry() else - return Geometry(GDAL.ogr_g_clone(result)) + Geometry(GDAL.ogr_g_clone(result)) end end @@ -76,7 +78,7 @@ Fetch number of fields on this feature. This will always be the same as the field count for the OGRFeatureDefn. """ -nfield(feature::Feature) = GDAL.ogr_f_getfieldcount(feature.ptr) +nfield(feature::Feature)::Integer = GDAL.ogr_f_getfieldcount(feature.ptr) """ getfielddefn(feature::Feature, i::Integer) @@ -91,7 +93,7 @@ Fetch definition for this field. an handle to the field definition (from the `FeatureDefn`). This is an internal reference, and should not be deleted or modified. """ -getfielddefn(feature::Feature, i::Integer) = +getfielddefn(feature::Feature, i::Integer)::IFieldDefnView = IFieldDefnView(GDAL.ogr_f_getfielddefnref(feature.ptr, i)) """ @@ -104,13 +106,22 @@ Fetch the field index given field name. * `name`: the name of the field to search for. ### Returns -the field index, or -1 if no matching field is found. +the field index, or `nothing` if no matching field is found. ### Remarks This is a cover for the `OGRFeatureDefn::GetFieldIndex()` method. """ -findfieldindex(feature::Feature, name::Union{AbstractString, Symbol}) = - GDAL.ogr_f_getfieldindex(feature.ptr, name) +function findfieldindex( + feature::Feature, + name::Union{AbstractString,Symbol}, +)::Union{Integer,Nothing} + i = GDAL.ogr_f_getfieldindex(feature.ptr, name) + return if i == -1 + nothing + else + i + end +end """ isfieldset(feature::Feature, i::Integer) @@ -121,7 +132,7 @@ Test if a field has ever been assigned a value or not. * `feature`: the feature that owned the field. * `i`: the field to fetch, from 0 to GetFieldCount()-1. """ -isfieldset(feature::Feature, i::Integer) = +isfieldset(feature::Feature, i::Integer)::Bool = Bool(GDAL.ogr_f_isfieldset(feature.ptr, i)) """ @@ -133,8 +144,10 @@ Clear a field, marking it as unset. * `feature`: the feature that owned the field. * `i`: the field to fetch, from 0 to GetFieldCount()-1. """ -unsetfield!(feature::Feature, i::Integer) = - (GDAL.ogr_f_unsetfield(feature.ptr, i); feature) +function unsetfield!(feature::Feature, i::Integer)::Feature + GDAL.ogr_f_unsetfield(feature.ptr, i) + return feature +end # """ # OGR_F_GetRawFieldRef(OGRFeatureH hFeat, @@ -161,7 +174,7 @@ Fetch field value as integer. * `feature`: the feature that owned the field. * `i`: the field to fetch, from 0 to GetFieldCount()-1. """ -asint(feature::Feature, i::Integer) = +asint(feature::Feature, i::Integer)::Int32 = GDAL.ogr_f_getfieldasinteger(feature.ptr, i) """ @@ -173,7 +186,7 @@ Fetch field value as integer 64 bit. * `feature`: the feature that owned the field. * `i`: the field to fetch, from 0 to GetFieldCount()-1. """ -asint64(feature::Feature, i::Integer) = +asint64(feature::Feature, i::Integer)::Int64 = GDAL.ogr_f_getfieldasinteger64(feature.ptr, i) """ @@ -185,7 +198,7 @@ Fetch field value as a double. * `feature`: the feature that owned the field. * `i`: the field to fetch, from 0 to GetFieldCount()-1. """ -asdouble(feature::Feature, i::Integer) = +asdouble(feature::Feature, i::Integer)::Float64 = GDAL.ogr_f_getfieldasdouble(feature.ptr, i) """ @@ -197,7 +210,7 @@ Fetch field value as a string. * `feature`: the feature that owned the field. * `i`: the field to fetch, from 0 to GetFieldCount()-1. """ -asstring(feature::Feature, i::Integer) = +asstring(feature::Feature, i::Integer)::String = GDAL.ogr_f_getfieldasstring(feature.ptr, i) """ @@ -215,10 +228,10 @@ the field value. This list is internal, and should not be modified, or freed. Its lifetime may be very brief. If *pnCount is zero on return the returned pointer may be NULL or non-NULL. """ -function asintlist(feature::Feature, i::Integer) +function asintlist(feature::Feature, i::Integer)::Vector{Int32} n = Ref{Cint}() ptr = GDAL.ogr_f_getfieldasintegerlist(feature.ptr, i, n) - return (n.x == 0) ? Int32[] : unsafe_wrap(Array{Int32}, ptr, n.x) + return (n.x == 0) ? Int32[] : unsafe_wrap(Vector{Int32}, ptr, n.x) end """ @@ -236,10 +249,10 @@ the field value. This list is internal, and should not be modified, or freed. Its lifetime may be very brief. If *pnCount is zero on return the returned pointer may be NULL or non-NULL. """ -function asint64list(feature::Feature, i::Integer) +function asint64list(feature::Feature, i::Integer)::Vector{Int64} n = Ref{Cint}() ptr = GDAL.ogr_f_getfieldasinteger64list(feature.ptr, i, n) - return (n.x == 0) ? Int64[] : unsafe_wrap(Array{Int64}, ptr, n.x) + return (n.x == 0) ? Int64[] : unsafe_wrap(Vector{Int64}, ptr, n.x) end """ @@ -257,10 +270,10 @@ the field value. This list is internal, and should not be modified, or freed. Its lifetime may be very brief. If *pnCount is zero on return the returned pointer may be NULL or non-NULL. """ -function asdoublelist(feature::Feature, i::Integer) +function asdoublelist(feature::Feature, i::Integer)::Vector{Float64} n = Ref{Cint}() ptr = GDAL.ogr_f_getfieldasdoublelist(feature.ptr, i, n) - return (n.x == 0) ? Float64[] : unsafe_wrap(Array{Float64}, ptr, n.x) + return (n.x == 0) ? Float64[] : unsafe_wrap(Vector{Float64}, ptr, n.x) end """ @@ -276,7 +289,7 @@ Fetch field value as a list of strings. the field value. This list is internal, and should not be modified, or freed. Its lifetime may be very brief. """ -asstringlist(feature::Feature, i::Integer) = +asstringlist(feature::Feature, i::Integer)::Vector{String} = GDAL.ogr_f_getfieldasstringlist(feature.ptr, i) """ @@ -292,16 +305,17 @@ Fetch field value as binary. the field value. This list is internal, and should not be modified, or freed. Its lifetime may be very brief. """ -function asbinary(feature::Feature, i::Integer) +function asbinary(feature::Feature, i::Integer)::Vector{UInt8} n = Ref{Cint}() ptr = GDAL.ogr_f_getfieldasbinary(feature.ptr, i, n) - return (n.x == 0) ? UInt8[] : unsafe_wrap(Array{UInt8}, ptr, n.x) + return (n.x == 0) ? UInt8[] : unsafe_wrap(Vector{UInt8}, ptr, n.x) end """ asdatetime(feature::Feature, i::Integer) -Fetch field value as date and time. +Fetch field value as date and time. Currently this method only works for +OFTDate, OFTTime and OFTDateTime fields. ### Parameters * `hFeat`: handle to the feature that owned the field. @@ -310,11 +324,27 @@ Fetch field value as date and time. ### Returns `true` on success or `false` on failure. """ -function asdatetime(feature::Feature, i::Integer) - pyr = Ref{Cint}(); pmth = Ref{Cint}(); pday = Ref{Cint}() - phr = Ref{Cint}(); pmin = Ref{Cint}(); psec = Ref{Cint}(); ptz=Ref{Cint}() - result = Bool(GDAL.ogr_f_getfieldasdatetime(feature.ptr, i, pyr, pmth, pday, - phr, pmin, psec, ptz)) +function asdatetime(feature::Feature, i::Integer)::DateTime + pyr = Ref{Cint}() + pmth = Ref{Cint}() + pday = Ref{Cint}() + phr = Ref{Cint}() + pmin = Ref{Cint}() + psec = Ref{Cint}() + ptz = Ref{Cint}() + result = Bool( + GDAL.ogr_f_getfieldasdatetime( + feature.ptr, + i, + pyr, + pmth, + pday, + phr, + pmin, + psec, + ptz, + ), + ) (result == false) && error("Failed to fetch datetime at index $i") return DateTime(pyr[], pmth[], pday[], phr[], pmin[], psec[]) end @@ -351,38 +381,41 @@ end # pfSecond,pnTZFlag) # end -getdefault(feature::Feature, i::Integer) = getdefault(getfielddefn(feature, i)) - -const _FETCHFIELD = Dict{GDAL.OGRFieldType, Function}( - GDAL.OFTInteger => asint, #0 - GDAL.OFTIntegerList => asintlist, #1 - GDAL.OFTReal => asdouble, #2 - GDAL.OFTRealList => asdoublelist, #3 - GDAL.OFTString => asstring, #4 - GDAL.OFTStringList => asstringlist, #5 - # const OFTWideString = (UInt32)(6) - # const OFTWideStringList = (UInt32)(7) - GDAL.OFTBinary => asbinary, #8 - # const OFTDate = (UInt32)(9) - # const OFTTime = (UInt32)(10) - GDAL.OFTDateTime => asdatetime, #11 - GDAL.OFTInteger64 => asint64, #12 - GDAL.OFTInteger64List => asint64list #13 - # const OFTMaxType = (UInt32)(13) - ) +function getdefault(feature::Feature, i::Integer)::String + return getdefault(getfielddefn(feature, i)) +end + +getfield(feature::Feature, i::Nothing)::Missing = missing function getfield(feature::Feature, i::Integer) - if isfieldset(feature, i) + _FETCHFIELD = Dict{OGRFieldType,Function}( + OFTInteger => asint, #0 + OFTIntegerList => asintlist, #1 + OFTReal => asdouble, #2 + OFTRealList => asdoublelist, #3 + OFTString => asstring, #4 + OFTStringList => asstringlist, #5 + # const OFTWideString = (UInt32)(6) + # const OFTWideStringList = (UInt32)(7) + OFTBinary => asbinary, #8 + OFTDate => asdatetime, #9 + OFTTime => asdatetime, #10 + OFTDateTime => asdatetime, #11 + OFTInteger64 => asint64, #12 + OFTInteger64List => asint64list, #13 + ) + return if isfieldset(feature, i) _fieldtype = gettype(getfielddefn(feature, i)) _fetchfield = get(_FETCHFIELD, _fieldtype, getdefault) - return _fetchfield(feature, i) + _fetchfield(feature, i) else - return getdefault(feature, i) + getdefault(feature, i) end end -getfield(feature::Feature, name::Union{AbstractString, Symbol}) = - getfield(feature, findfieldindex(feature, name)) +function getfield(feature::Feature, name::Union{AbstractString,Symbol}) + return getfield(feature, findfieldindex(feature, name)) +end """ setfield!(feature::Feature, i::Integer, value) @@ -390,10 +423,10 @@ getfield(feature::Feature, name::Union{AbstractString, Symbol}) = Set a feature's `i`-th field to `value`. -The following types for `value` are accepted: `Int32`, `Int64`, `Float64`, `AbstractString`, -or a `Vector` with those in it, as well as `Vector{UInt8}`. For `DateTime` values, an -additional keyword argument `tzflag` is accepted (0=unknown, 1=localtime, 100=GMT, see data -model for details). +The following types for `value` are accepted: `Int32`, `Int64`, `Float64`, +`AbstractString`, or a `Vector` with those in it, as well as `Vector{UInt8}`. +For `DateTime` values, an additional keyword argument `tzflag` is accepted +(0=unknown, 1=localtime, 100=GMT, see data model for details). OFTInteger, OFTInteger64 and OFTReal fields will be set directly. OFTString fields will be assigned a string representation of the value, but not @@ -407,46 +440,50 @@ field types may be unaffected. """ function setfield! end -function setfield!(feature::Feature, i::Integer, value::Cint) +function setfield!(feature::Feature, i::Integer, value::Int32)::Feature GDAL.ogr_f_setfieldinteger(feature.ptr, i, value) return feature end -function setfield!(feature::Feature, i::Integer, value::Int64) +function setfield!(feature::Feature, i::Integer, value::Int64)::Feature GDAL.ogr_f_setfieldinteger64(feature.ptr, i, value) return feature end -function setfield!(feature::Feature, i::Integer, value::Cdouble) +function setfield!(feature::Feature, i::Integer, value::Float64)::Feature GDAL.ogr_f_setfielddouble(feature.ptr, i, value) return feature end -function setfield!(feature::Feature, i::Integer, value::AbstractString) +function setfield!(feature::Feature, i::Integer, value::AbstractString)::Feature GDAL.ogr_f_setfieldstring(feature.ptr, i, value) return feature end -function setfield!(feature::Feature, i::Integer, value::Vector{Cint}) +function setfield!(feature::Feature, i::Integer, value::Vector{Int32})::Feature GDAL.ogr_f_setfieldintegerlist(feature.ptr, i, length(value), value) return feature end -function setfield!(feature::Feature, i::Integer, value::Vector{GDAL.GIntBig}) +function setfield!(feature::Feature, i::Integer, value::Vector{Int64})::Feature GDAL.ogr_f_setfieldinteger64list(feature.ptr, i, length(value), value) return feature end -function setfield!(feature::Feature, i::Integer, value::Vector{Cdouble}) +function setfield!( + feature::Feature, + i::Integer, + value::Vector{Float64}, +)::Feature GDAL.ogr_f_setfielddoublelist(feature.ptr, i, length(value), value) return feature end function setfield!( - feature::Feature, - i::Integer, - value::Vector{T} - ) where T <: AbstractString + feature::Feature, + i::Integer, + value::Vector{T}, +)::Feature where {T<:AbstractString} GDAL.ogr_f_setfieldstringlist(feature.ptr, i, value) return feature end @@ -466,12 +503,17 @@ end # Ptr{OGRField}),arg1,arg2,arg3) # end -function setfield!(feature::Feature, i::Integer, value::Vector{GDAL.GByte}) +function setfield!(feature::Feature, i::Integer, value::Vector{UInt8})::Feature GDAL.ogr_f_setfieldbinary(feature.ptr, i, sizeof(value), value) return feature end -function setfield!(feature::Feature, i::Integer, dt::DateTime, tzflag::Int = 0) +function setfield!( + feature::Feature, + i::Integer, + dt::DateTime, + tzflag::Int = 0, +)::Feature GDAL.ogr_f_setfielddatetime( feature.ptr, i, @@ -481,7 +523,7 @@ function setfield!(feature::Feature, i::Integer, dt::DateTime, tzflag::Int = 0) Dates.hour(dt), Dates.minute(dt), Dates.second(dt), - tzflag + tzflag, ) return feature end @@ -493,7 +535,7 @@ Fetch number of geometry fields on this feature. This will always be the same as the geometry field count for OGRFeatureDefn. """ -ngeom(feature::Feature) = GDAL.ogr_f_getgeomfieldcount(feature.ptr) +ngeom(feature::Feature)::Integer = GDAL.ogr_f_getgeomfieldcount(feature.ptr) """ getgeomdefn(feature::Feature, i::Integer) @@ -508,11 +550,12 @@ Fetch definition for this geometry field. The field definition (from the OGRFeatureDefn). This is an internal reference, and should not be deleted or modified. """ -getgeomdefn(feature::Feature, i::Integer) = - IGeomFieldDefnView(GDAL.ogr_f_getgeomfielddefnref(feature.ptr, i)) +function getgeomdefn(feature::Feature, i::Integer)::IGeomFieldDefnView + return IGeomFieldDefnView(GDAL.ogr_f_getgeomfielddefnref(feature.ptr, i)) +end """ - findgeomindex(feature::Feature, name::AbstractString="") + findgeomindex(feature::Feature, name::Union{AbstractString, Symbol} = "") Fetch the geometry field index given geometry field name. @@ -526,8 +569,12 @@ the geometry field index, or -1 if no matching geometry field is found. ### Remarks This is a cover for the `OGRFeatureDefn::GetGeomFieldIndex()` method. """ -findgeomindex(feature::Feature, name::AbstractString="") = - GDAL.ogr_f_getgeomfieldindex(feature.ptr, name) +function findgeomindex( + feature::Feature, + name::Union{AbstractString,Symbol} = "", +)::Integer + return GDAL.ogr_f_getgeomfieldindex(feature.ptr, name) +end """ getgeom(feature::Feature, i::Integer) @@ -538,21 +585,45 @@ Returns a clone of the feature geometry at index `i`. * `feature`: the feature to get geometry from. * `i`: geometry field to get. """ -function getgeom(feature::Feature, i::Integer) +function getgeom(feature::Feature, i::Integer)::IGeometry result = GDAL.ogr_f_getgeomfieldref(feature.ptr, i) - if result == C_NULL - return IGeometry() + return if result == C_NULL + IGeometry() else - return IGeometry(GDAL.ogr_g_clone(result)) + IGeometry(GDAL.ogr_g_clone(result)) end end -function unsafe_getgeom(feature::Feature, i::Integer) +function unsafe_getgeom(feature::Feature, i::Integer)::Geometry result = GDAL.ogr_f_getgeomfieldref(feature.ptr, i) - if result == C_NULL - return Geometry() + return if result == C_NULL + Geometry() else - return Geometry(GDAL.ogr_g_clone(result)) + Geometry(GDAL.ogr_g_clone(result)) + end +end + +function getgeom( + feature::Feature, + name::Union{AbstractString,Symbol}, +)::IGeometry + i = findgeomindex(feature, name) + return if i == -1 + IGeometry() + else + getgeom(feature, i) + end +end + +function unsafe_getgeom( + feature::Feature, + name::Union{AbstractString,Symbol}, +)::Geometry + i = findgeomindex(feature, name) + return if i == -1 + Geometry() + else + unsafe_getgeom(feature, i) end end @@ -574,7 +645,7 @@ the passed geometry, but instead makes a copy of it. `OGRERR_NONE` if successful, or `OGR_UNSUPPORTED_GEOMETRY_TYPE` if the geometry type is illegal for the `OGRFeatureDefn` (checking not yet implemented). """ -function setgeom!(feature::Feature, i::Integer, geom::AbstractGeometry) +function setgeom!(feature::Feature, i::Integer, geom::AbstractGeometry)::Feature result = GDAL.ogr_f_setgeomfield(feature.ptr, i, geom.ptr) @ogrerr result "OGRErr $result: Failed to set feature geometry" return feature @@ -588,7 +659,7 @@ Get feature identifier. ### Returns feature id or `OGRNullFID` (`-1`) if none has been assigned. """ -getfid(feature::Feature) = GDAL.ogr_f_getfid(feature.ptr) +getfid(feature::Feature) = GDAL.ogr_f_getfid(feature.ptr)::Integer """ setfid!(feature::Feature, i::Integer) @@ -602,7 +673,7 @@ Set the feature identifier. ### Returns On success OGRERR_NONE, or on failure some other value. """ -function setfid!(feature::Feature, i::Integer) +function setfid!(feature::Feature, i::Integer)::Feature result = GDAL.ogr_f_setfid(feature.ptr, i) @ogrerr result "OGRErr $result: Failed to set FID $i" return feature @@ -610,7 +681,8 @@ end """ setfrom!(feature1::Feature, feature2::Feature, forgiving::Bool = false) - setfrom!(feature1::Feature, feature2::Feature, indices::Vector{Cint}, forgiving::Bool = false) + setfrom!(feature1::Feature, feature2::Feature, indices::Vector{Cint}, + forgiving::Bool = false) Set one feature from another. @@ -631,31 +703,39 @@ otherwise an error code. """ function setfrom! end -function setfrom!(feature1::Feature, feature2::Feature, forgiving::Bool = false) +function setfrom!( + feature1::Feature, + feature2::Feature, + forgiving::Bool = false, +)::Feature result = GDAL.ogr_f_setfrom(feature1.ptr, feature2.ptr, forgiving) @ogrerr result "OGRErr $result: Failed to set feature" return feature1 end function setfrom!( - feature1::Feature, - feature2::Feature, - indices::Vector{Cint}, - forgiving::Bool = false + feature1::Feature, + feature2::Feature, + indices::Vector{Cint}, + forgiving::Bool = false, +)::Feature + result = GDAL.ogr_f_setfromwithmap( + feature1.ptr, + feature2.ptr, + forgiving, + indices, ) - result = GDAL.ogr_f_setfromwithmap(feature1.ptr, feature2.ptr, forgiving, - indices) @ogrerr result "OGRErr $result: Failed to set feature with map" return feature1 end - """ getstylestring(feature::Feature) Fetch style string for this feature. """ -getstylestring(feature::Feature) = GDAL.ogr_f_getstylestring(feature.ptr) +getstylestring(feature::Feature)::String = + GDAL.ogr_f_getstylestring(feature.ptr) """ setstylestring!(feature::Feature, style::AbstractString) @@ -665,7 +745,7 @@ Set feature style string. This method operate exactly as `setstylestringdirectly!()` except that it doesn't assume ownership of the passed string, but makes a copy of it. """ -function setstylestring!(feature::Feature, style::AbstractString) +function setstylestring!(feature::Feature, style::AbstractString)::Feature GDAL.ogr_f_setstylestring(feature.ptr, style) return feature end @@ -675,7 +755,7 @@ end Fetch style table for this feature. """ -getstyletable(feature::Feature) = +getstyletable(feature::Feature)::StyleTable = StyleTable(GDAL.ogr_f_getstyletable(feature.ptr)) """ @@ -683,7 +763,7 @@ getstyletable(feature::Feature) = Set the style table for this feature. """ -function setstyletable!(feature::Feature, styletable::StyleTable) +function setstyletable!(feature::Feature, styletable::StyleTable)::Feature GDAL.ogr_f_setstyletable(feature.ptr, styletable.ptr) return feature end @@ -707,7 +787,7 @@ than what can be obtained with the rest of the API, but it may be useful in round-tripping scenarios where some characteristics of the underlying format are not captured otherwise by the OGR abstraction. """ -getnativedata(feature::Feature) = GDAL.ogr_f_getnativedata(feature.ptr) +getnativedata(feature::Feature)::String = GDAL.ogr_f_getnativedata(feature.ptr) """ setnativedata!(feature::Feature, data::AbstractString) @@ -719,7 +799,7 @@ driver that created this feature, or that is aimed at an output driver. The native data may be in different format, which is indicated by GetNativeMediaType(). """ -function setnativedata!(feature::Feature, data::AbstractString) +function setnativedata!(feature::Feature, data::AbstractString)::Feature GDAL.ogr_f_setnativedata(feature.ptr, data) return feature end @@ -733,7 +813,8 @@ The native media type is the identifier for the format of the native data. It follows the IANA RFC 2045 (see https://en.wikipedia.org/wiki/Media_type), e.g. \"application/vnd.geo+json\" for JSON. """ -getmediatype(feature::Feature) = GDAL.ogr_f_getnativemediatype(feature.ptr) +getmediatype(feature::Feature)::String = + GDAL.ogr_f_getnativemediatype(feature.ptr) """ setmediatype!(feature::Feature, mediatype::AbstractString) @@ -744,13 +825,14 @@ The native media type is the identifier for the format of the native data. It follows the IANA RFC 2045 (see https://en.wikipedia.org/wiki/Media_type), e.g. \"application/vnd.geo+json\" for JSON. """ -function setmediatype!(feature::Feature, mediatype::AbstractString) +function setmediatype!(feature::Feature, mediatype::AbstractString)::Feature GDAL.ogr_f_setnativemediatype(feature.ptr, mediatype) return feature end """ - fillunsetwithdefault!(feature::Feature; notnull = true, options = StringList(C_NULL)) + fillunsetwithdefault!(feature::Feature; notnull = true, + options = StringList(C_NULL)) Fill unset fields with default values that might be defined. @@ -760,11 +842,12 @@ Fill unset fields with default values that might be defined. * `papszOptions`: unused currently. Must be set to `NULL`. """ function fillunsetwithdefault!( - feature::Feature; - notnull::Bool = true, - options = StringList(C_NULL) - ) + feature::Feature; + notnull::Bool = true, + options = StringList(C_NULL), +)::Feature GDAL.ogr_f_fillunsetwithdefault(feature.ptr, notnull, options) + return feature end """ @@ -789,5 +872,5 @@ fails, then it will fail for all interpretations). ### Returns `true` if all enabled validation tests pass. """ -validate(feature::Feature, flags::Integer, emiterror::Bool) = +validate(feature::Feature, flags::FieldValidation, emiterror::Bool)::Bool = Bool(GDAL.ogr_f_validate(feature.ptr, flags, emiterror)) diff --git a/src/ogr/featuredefn.jl b/src/ogr/featuredefn.jl index 3d3a792a..bc7e40ec 100644 --- a/src/ogr/featuredefn.jl +++ b/src/ogr/featuredefn.jl @@ -6,7 +6,7 @@ Create a new feature definition object to hold field definitions. The `FeatureDefn` maintains a reference count, but this starts at zero, and should normally be incremented by the owner. """ -unsafe_createfeaturedefn(name::AbstractString) = +unsafe_createfeaturedefn(name::AbstractString)::FeatureDefn = FeatureDefn(GDAL.ogr_fd_create(name)) """ @@ -19,34 +19,36 @@ The count is used to track the number of `Feature`s referencing this definition. ### Returns The updated reference count. """ -reference(featuredefn::FeatureDefn) = GDAL.ogr_fd_reference(featuredefn.ptr) +reference(featuredefn::FeatureDefn)::Integer = + GDAL.ogr_fd_reference(featuredefn.ptr) """ dereference(featuredefn::FeatureDefn) Decrements the reference count by one, and returns the updated count. """ -dereference(featuredefn::FeatureDefn) = GDAL.ogr_fd_dereference(featuredefn.ptr) +dereference(featuredefn::FeatureDefn)::Integer = + GDAL.ogr_fd_dereference(featuredefn.ptr) """ nreference(featuredefn::AbstractFeatureDefn) Fetch the current reference count. """ -nreference(featuredefn::AbstractFeatureDefn) = +nreference(featuredefn::AbstractFeatureDefn)::Integer = GDAL.ogr_fd_getreferencecount(featuredefn.ptr) "Destroy a feature definition object and release all memory associated with it" -function destroy(featuredefn::FeatureDefn) +function destroy(featuredefn::FeatureDefn)::Nothing GDAL.ogr_fd_destroy(featuredefn.ptr) featuredefn.ptr = C_NULL - return featuredefn + return nothing end "Destroy a feature definition view" -function destroy(featuredefn::IFeatureDefnView) +function destroy(featuredefn::IFeatureDefnView)::Nothing featuredefn.ptr = C_NULL - return featuredefn + return nothing end """ @@ -54,21 +56,25 @@ end Drop a reference, and destroy if unreferenced. """ -release(featuredefn::FeatureDefn) = GDAL.ogr_fd_release(featuredefn.ptr) +function release(featuredefn::FeatureDefn)::Nothing + GDAL.ogr_fd_release(featuredefn.ptr) + return nothing +end """ getname(featuredefn::AbstractFeatureDefn) Get name of the OGRFeatureDefn passed as an argument. """ -getname(featuredefn::AbstractFeatureDefn) = GDAL.ogr_fd_getname(featuredefn.ptr) +getname(featuredefn::AbstractFeatureDefn)::String = + GDAL.ogr_fd_getname(featuredefn.ptr) """ nfield(featuredefn::AbstractFeatureDefn) Fetch number of fields on the passed feature definition. """ -nfield(featuredefn::AbstractFeatureDefn) = +nfield(featuredefn::AbstractFeatureDefn)::Integer = GDAL.ogr_fd_getfieldcount(featuredefn.ptr) """ @@ -84,14 +90,15 @@ Fetch field definition of the passed feature definition. an handle to an internal field definition object or NULL if invalid index. This object should not be modified or freed by the application. """ -getfielddefn(featuredefn::FeatureDefn, i::Integer) = +getfielddefn(featuredefn::FeatureDefn, i::Integer)::FieldDefn = FieldDefn(GDAL.ogr_fd_getfielddefn(featuredefn.ptr, i)) -getfielddefn(featuredefn::IFeatureDefnView, i::Integer) = +getfielddefn(featuredefn::IFeatureDefnView, i::Integer)::IFieldDefnView = IFieldDefnView(GDAL.ogr_fd_getfielddefn(featuredefn.ptr, i)) """ - findfieldindex(featuredefn::AbstractFeatureDefn, name::Union{AbstractString, Symbol}) + findfieldindex(featuredefn::AbstractFeatureDefn, + name::Union{AbstractString, Symbol}) Find field by name. @@ -101,8 +108,12 @@ the field index, or -1 if no match found. ### Remarks This uses the OGRFeatureDefn::GetFieldIndex() method. """ -findfieldindex(featuredefn::AbstractFeatureDefn, name::Union{AbstractString, Symbol}) = - GDAL.ogr_fd_getfieldindex(featuredefn.ptr, name) +function findfieldindex( + featuredefn::AbstractFeatureDefn, + name::Union{AbstractString,Symbol}, +)::Integer + return GDAL.ogr_fd_getfieldindex(featuredefn.ptr, name) +end """ addfielddefn!(featuredefn::FeatureDefn, fielddefn::FieldDefn) @@ -116,7 +127,10 @@ This function should only be called while there are no OGRFeature objects in existence based on this OGRFeatureDefn. The OGRFieldDefn passed in is copied, and remains the responsibility of the caller. """ -function addfielddefn!(featuredefn::FeatureDefn, fielddefn::FieldDefn) +function addfielddefn!( + featuredefn::FeatureDefn, + fielddefn::FieldDefn, +)::FeatureDefn GDAL.ogr_fd_addfielddefn(featuredefn.ptr, fielddefn.ptr) return featuredefn end @@ -132,7 +146,7 @@ function directly, but use `OGR_L_DeleteField()` instead. This method should only be called while there are no OGRFeature objects in existence based on this OGRFeatureDefn. """ -function deletefielddefn!(featuredefn::FeatureDefn, i::Integer) +function deletefielddefn!(featuredefn::FeatureDefn, i::Integer)::FeatureDefn result = GDAL.ogr_fd_deletefielddefn(featuredefn.ptr, i) @ogrerr result "Failed to delete field $i in the feature definition" return featuredefn @@ -156,13 +170,15 @@ existence based on this OGRFeatureDefn. at position `i` after reordering, its position before reordering was `indices[i]`. """ -function reorderfielddefns!(featuredefn::FeatureDefn, indices::Vector{Cint}) +function reorderfielddefns!( + featuredefn::FeatureDefn, + indices::Vector{Cint}, +)::FeatureDefn result = GDAL.ogr_fd_reorderfielddefns(featuredefn.ptr, indices) @ogrerr result "Failed to reorder $indices in the feature definition" return featuredefn end - """ getgeomtype(featuredefn::AbstractFeatureDefn) @@ -178,7 +194,7 @@ For layers with multiple geometry fields, this method only returns the geometry type of the first geometry column. For other columns, use `OGR_GFld_GetType(OGR_FD_GetGeomFieldDefn(OGR_L_GetLayerDefn(hLayer), i))`. """ -getgeomtype(featuredefn::AbstractFeatureDefn) = +getgeomtype(featuredefn::AbstractFeatureDefn)::OGRwkbGeometryType = GDAL.ogr_fd_getgeomtype(featuredefn.ptr) """ @@ -191,7 +207,10 @@ type. The default upon creation is `wkbUnknown` which allows for any geometry type. The geometry type should generally not be changed after any OGRFeatures have been created against this definition. """ -function setgeomtype!(featuredefn::FeatureDefn, etype::OGRwkbGeometryType) +function setgeomtype!( + featuredefn::FeatureDefn, + etype::OGRwkbGeometryType, +)::FeatureDefn GDAL.ogr_fd_setgeomtype(featuredefn.ptr, etype) return featuredefn end @@ -201,7 +220,7 @@ end Determine whether the geometry can be omitted when fetching features. """ -isgeomignored(featuredefn::AbstractFeatureDefn) = +isgeomignored(featuredefn::AbstractFeatureDefn)::Bool = Bool(GDAL.ogr_fd_isgeometryignored(featuredefn.ptr)) """ @@ -209,7 +228,7 @@ isgeomignored(featuredefn::AbstractFeatureDefn) = Set whether the geometry can be omitted when fetching features. """ -function setgeomignored!(featuredefn::FeatureDefn, ignore::Bool) +function setgeomignored!(featuredefn::FeatureDefn, ignore::Bool)::FeatureDefn GDAL.ogr_fd_setgeometryignored(featuredefn.ptr, ignore) return featuredefn end @@ -219,7 +238,7 @@ end Determine whether the style can be omitted when fetching features. """ -isstyleignored(featuredefn::AbstractFeatureDefn) = +isstyleignored(featuredefn::AbstractFeatureDefn)::Bool = Bool(GDAL.ogr_fd_isstyleignored(featuredefn.ptr)) """ @@ -227,7 +246,7 @@ isstyleignored(featuredefn::AbstractFeatureDefn) = Set whether the style can be omitted when fetching features. """ -function setstyleignored!(featuredefn::FeatureDefn, ignore::Bool) +function setstyleignored!(featuredefn::FeatureDefn, ignore::Bool)::FeatureDefn GDAL.ogr_fd_setstyleignored(featuredefn.ptr, ignore) return featuredefn end @@ -237,7 +256,7 @@ end Fetch number of geometry fields on the passed feature definition. """ -ngeom(featuredefn::AbstractFeatureDefn) = +ngeom(featuredefn::AbstractFeatureDefn)::Integer = GDAL.ogr_fd_getgeomfieldcount(featuredefn.ptr) """ @@ -252,10 +271,10 @@ Fetch geometry field definition of the passed feature definition. an internal field definition object or `NULL` if invalid index. This object should not be modified or freed by the application. """ -getgeomdefn(featuredefn::FeatureDefn, i::Integer = 0) = +getgeomdefn(featuredefn::FeatureDefn, i::Integer = 0)::GeomFieldDefn = GeomFieldDefn(GDAL.ogr_fd_getgeomfielddefn(featuredefn.ptr, i)) -getgeomdefn(featuredefn::IFeatureDefnView, i::Integer = 0) = +getgeomdefn(featuredefn::IFeatureDefnView, i::Integer = 0)::IGeomFieldDefnView = IGeomFieldDefnView(GDAL.ogr_fd_getgeomfielddefn(featuredefn.ptr, i)) """ @@ -269,8 +288,12 @@ name (case insensitively) is returned. ### Returns the geometry field index, or -1 if no match found. """ -findgeomindex(featuredefn::AbstractFeatureDefn, name::AbstractString = "") = - GDAL.ogr_fd_getgeomfieldindex(featuredefn.ptr, name) +function findgeomindex( + featuredefn::AbstractFeatureDefn, + name::AbstractString = "", +)::Integer + return GDAL.ogr_fd_getgeomfieldindex(featuredefn.ptr, name) +end """ addgeomdefn!(featuredefn::FeatureDefn, geomfielddefn::AbstractGeomFieldDefn) @@ -288,9 +311,9 @@ This method should only be called while there are no OGRFeature objects in existence based on this OGRFeatureDefn. """ function addgeomdefn!( - featuredefn::FeatureDefn, - geomfielddefn::AbstractGeomFieldDefn - ) + featuredefn::FeatureDefn, + geomfielddefn::AbstractGeomFieldDefn, +)::FeatureDefn # `geomfielddefn` is copied, and remains the responsibility of the caller. GDAL.ogr_fd_addgeomfielddefn(featuredefn.ptr, geomfielddefn.ptr) return featuredefn @@ -307,7 +330,7 @@ function directly, but use OGRLayer::DeleteGeomField() instead. This method should only be called while there are no OGRFeature objects in existence based on this OGRFeatureDefn. """ -function deletegeomdefn!(featuredefn::FeatureDefn, i::Integer) +function deletegeomdefn!(featuredefn::FeatureDefn, i::Integer)::FeatureDefn result = GDAL.ogr_fd_deletegeomfielddefn(featuredefn.ptr, i) @ogrerr result "Failed to delete geom field $i in the feature definition" return featuredefn @@ -319,9 +342,9 @@ end Test if the feature definition is identical to the other one. """ function issame( - featuredefn1::AbstractFeatureDefn, - featuredefn2::AbstractFeatureDefn - ) + featuredefn1::AbstractFeatureDefn, + featuredefn2::AbstractFeatureDefn, +)::Bool return Bool(GDAL.ogr_fd_issame(featuredefn1.ptr, featuredefn2.ptr)) end @@ -336,8 +359,8 @@ OGRFeatures that depend on it is likely to result in a crash. Starting with GDAL 2.1, returns NULL in case out of memory situation. """ -function unsafe_createfeature(featuredefn::AbstractFeatureDefn) - return Feature(GDALFeature(GDAL.ogr_f_create(featuredefn.ptr))) +function unsafe_createfeature(featuredefn::AbstractFeatureDefn)::Feature + return Feature(GDAL.ogr_f_create(featuredefn.ptr)) end """ @@ -345,5 +368,5 @@ end Fetch feature definition. """ -getfeaturedefn(feature::Feature) = +getfeaturedefn(feature::Feature)::IFeatureDefnView = IFeatureDefnView(GDAL.ogr_f_getdefnref(feature.ptr)) diff --git a/src/ogr/featurelayer.jl b/src/ogr/featurelayer.jl index aa48dc8d..cb94db29 100644 --- a/src/ogr/featurelayer.jl +++ b/src/ogr/featurelayer.jl @@ -1,14 +1,14 @@ -function destroy(layer::AbstractFeatureLayer) - layer.ptr = GDALFeatureLayer(C_NULL) - return layer +function destroy(layer::AbstractFeatureLayer)::Nothing + layer.ptr = C_NULL + return nothing end -function destroy(layer::IFeatureLayer) - layer.ptr = GDALFeatureLayer(C_NULL) +function destroy(layer::IFeatureLayer)::Nothing + layer.ptr = C_NULL layer.ownedby = Dataset() layer.spatialref = SpatialRef() - return layer + return nothing end """ @@ -21,35 +21,47 @@ This function attempts to create a new layer on the dataset with the indicated * `name`: the name for the new layer. This should ideally not match any existing layer on the datasource. Defaults to an empty string. * `dataset`: the dataset. Defaults to creating a new in memory dataset. -* `geom`: the geometry type for the layer. Use wkbUnknown (default) if +* `geom`: the geometry type for the layer. Use `wkbUnknown` (default) if there are no constraints on the types geometry to be written. * `spatialref`: the coordinate system to use for the new layer. * `options`: a StringList of name=value (driver-specific) options. """ function createlayer(; - name::AbstractString = "", - dataset::AbstractDataset = create(getdriver("Memory")), - geom::OGRwkbGeometryType = GDAL.wkbUnknown, - spatialref::AbstractSpatialRef = SpatialRef(), - options = StringList(C_NULL) - ) + name::AbstractString = "", + dataset::AbstractDataset = create(getdriver("Memory")), + geom::OGRwkbGeometryType = wkbUnknown, + spatialref::AbstractSpatialRef = SpatialRef(), + options = StringList(C_NULL), +)::IFeatureLayer return IFeatureLayer( - GDAL.gdaldatasetcreatelayer(dataset.ptr, name, spatialref.ptr, geom, - options), + GDAL.gdaldatasetcreatelayer( + dataset.ptr, + name, + spatialref.ptr, + geom, + options, + ), ownedby = dataset, - spatialref = spatialref + spatialref = spatialref, ) end function unsafe_createlayer(; - name::AbstractString = "", - dataset::AbstractDataset = create(getdriver("Memory")), - geom::OGRwkbGeometryType = GDAL.wkbUnknown, - spatialref::AbstractSpatialRef = SpatialRef(), - options = StringList(C_NULL) + name::AbstractString = "", + dataset::AbstractDataset = create(getdriver("Memory")), + geom::OGRwkbGeometryType = wkbUnknown, + spatialref::AbstractSpatialRef = SpatialRef(), + options = StringList(C_NULL), +)::FeatureLayer + return FeatureLayer( + GDAL.gdaldatasetcreatelayer( + dataset.ptr, + name, + spatialref.ptr, + geom, + options, + ), ) - return FeatureLayer(GDAL.gdaldatasetcreatelayer(dataset.ptr, name, - spatialref.ptr, geom, options)) end """ @@ -69,25 +81,26 @@ layer, and then duplicates each feature of the source layer. * `options`: a StringList of name=value (driver-specific) options. """ function copy( - layer::AbstractFeatureLayer; - dataset::AbstractDataset = create(getdriver("Memory")), - name::AbstractString = "copy($(getname(layer)))", - options = StringList(C_NULL) - ) + layer::AbstractFeatureLayer; + dataset::AbstractDataset = create(getdriver("Memory")), + name::AbstractString = "copy($(getname(layer)))", + options = StringList(C_NULL), +)::IFeatureLayer return IFeatureLayer( GDAL.gdaldatasetcopylayer(dataset.ptr, layer.ptr, name, options), - ownedby = dataset + ownedby = dataset, ) end function unsafe_copy( - layer::AbstractFeatureLayer; - dataset::AbstractDataset = create(getdriver("Memory")), - name::AbstractString = "copy($(getname(layer)))", - options = StringList(C_NULL) + layer::AbstractFeatureLayer; + dataset::AbstractDataset = create(getdriver("Memory")), + name::AbstractString = "copy($(getname(layer)))", + options = StringList(C_NULL), +)::FeatureLayer + return FeatureLayer( + GDAL.gdaldatasetcopylayer(dataset.ptr, layer.ptr, name, options), ) - FeatureLayer(GDAL.gdaldatasetcopylayer(dataset.ptr, layer.ptr, name, - options)) end """ @@ -95,28 +108,29 @@ end Return the layer name. """ -getname(layer::AbstractFeatureLayer) = GDAL.ogr_l_getname(layer.ptr) +getname(layer::AbstractFeatureLayer)::String = GDAL.ogr_l_getname(layer.ptr) """ getgeomtype(layer::AbstractFeatureLayer) Return the layer geometry type. """ -getgeomtype(layer::AbstractFeatureLayer) = GDAL.ogr_l_getgeomtype(layer.ptr) +getgeomtype(layer::AbstractFeatureLayer)::OGRwkbGeometryType = + GDAL.ogr_l_getgeomtype(layer.ptr) """ getspatialfilter(layer::AbstractFeatureLayer) Returns the current spatial filter for this layer. """ -function getspatialfilter(layer::AbstractFeatureLayer) - result = GDALGeometry(GDAL.ogr_l_getspatialfilter(Ptr{Cvoid}(layer.ptr))) - if result == C_NULL - return IGeometry(result) +function getspatialfilter(layer::AbstractFeatureLayer)::IGeometry + result = GDAL.ogr_l_getspatialfilter(Ptr{Cvoid}(layer.ptr)) + return if result == C_NULL + IGeometry(result) else # NOTE(yeesian): we make a clone here so that the geometry does not # depend on the FeatureLayer. - return IGeometry(GDALGeometry(GDAL.ogr_g_clone(result))) + IGeometry(GDAL.ogr_g_clone(result)) end end @@ -125,25 +139,25 @@ end Returns a clone of the spatial reference system for this layer. """ -function getspatialref(layer::AbstractFeatureLayer) +function getspatialref(layer::AbstractFeatureLayer)::ISpatialRef result = GDAL.ogr_l_getspatialref(layer.ptr) - if result == C_NULL - return ISpatialRef() + return if result == C_NULL + ISpatialRef() else # NOTE(yeesian): we make a clone here so that the spatialref does not # depend on the FeatureLayer/Dataset. - return ISpatialRef(GDAL.osrclone(result)) + ISpatialRef(GDAL.osrclone(result)) end end -function unsafe_getspatialref(layer::AbstractFeatureLayer) +function unsafe_getspatialref(layer::AbstractFeatureLayer)::SpatialRef result = GDAL.ogr_l_getspatialref(layer.ptr) - if result == C_NULL - return SpatialRef() + return if result == C_NULL + SpatialRef() else # NOTE(yeesian): we make a clone here so that the spatialref does not # depend on the FeatureLayer/Dataset. - return SpatialRef(GDAL.osrclone(result)) + SpatialRef(GDAL.osrclone(result)) end end @@ -176,15 +190,18 @@ In the future this may be generalized. Note that only the last spatial filter set is applied, even if several successive calls are done with different iGeomField values. """ -function setspatialfilter!(layer::AbstractFeatureLayer, geom::Geometry) +function setspatialfilter!( + layer::L, + geom::Geometry, +)::L where {L<:AbstractFeatureLayer} # This method makes an internal copy of `geom`. The input `geom` remains # the responsibility of the caller, and may be safely destroyed. GDAL.ogr_l_setspatialfilter(layer.ptr, geom.ptr) return layer end -function clearspatialfilter!(layer::AbstractFeatureLayer) - GDAL.ogr_l_setspatialfilter(layer.ptr, GDALGeometry(C_NULL)) +function clearspatialfilter!(layer::L)::L where {L<:AbstractFeatureLayer} + GDAL.ogr_l_setspatialfilter(layer.ptr, C_NULL) return layer end @@ -206,18 +223,19 @@ The only way to clear a spatial filter set with this method is to call `OGRLayer::SetSpatialFilter(NULL)`. """ function setspatialfilter!( - layer::AbstractFeatureLayer, - xmin::Real, - ymin::Real, - xmax::Real, - ymax::Real - ) + layer::L, + xmin::Real, + ymin::Real, + xmax::Real, + ymax::Real, +)::L where {L<:AbstractFeatureLayer} GDAL.ogr_l_setspatialfilterrect(layer.ptr, xmin, ymin, xmax, ymax) return layer end """ - setspatialfilter!(layer::AbstractFeatureLayer, i::Integer, geom::AbstractGeometry) + setspatialfilter!(layer::AbstractFeatureLayer, i::Integer, + geom::AbstractGeometry) Set a new spatial filter. @@ -243,23 +261,27 @@ layer (as returned by OGRLayer::GetSpatialRef()). In the future this may be generalized. """ function setspatialfilter!( - layer::AbstractFeatureLayer, - i::Integer, - geom::AbstractGeometry - ) + layer::L, + i::Integer, + geom::AbstractGeometry, +)::L where {L<:AbstractFeatureLayer} # This method makes an internal copy of `geom`. The input `geom` remains # the responsibility of the caller, and may be safely destroyed. GDAL.ogr_l_setspatialfilterex(layer.ptr, i, geom.ptr) return layer end -function clearspatialfilter!(layer::AbstractFeatureLayer, i::Integer) - GDAL.ogr_l_setspatialfilterex(layer.ptr, i, GDALGeometry(C_NULL)) +function clearspatialfilter!( + layer::L, + i::Integer, +)::L where {L<:AbstractFeatureLayer} + GDAL.ogr_l_setspatialfilterex(layer.ptr, i, C_NULL) return layer end """ - setspatialfilter!(layer::AbstractFeatureLayer, i::Integer, xmin, ymin, xmax, ymax) + setspatialfilter!(layer::AbstractFeatureLayer, i::Integer, xmin, ymin, xmax, + ymax) Set a new rectangular spatial filter. @@ -272,13 +294,13 @@ Set a new rectangular spatial filter. * `ymax`: the maximum Y coordinate for the rectangular region. """ function setspatialfilter!( - layer::AbstractFeatureLayer, - i::Integer, - xmin::Real, - ymin::Real, - xmax::Real, - ymax::Real - ) + layer::L, + i::Integer, + xmin::Real, + ymin::Real, + xmax::Real, + ymax::Real, +)::L where {L<:AbstractFeatureLayer} GDAL.ogr_l_setspatialfilterrectex(layer.ptr, i, xmin, ymin, xmax, ymax) return layer end @@ -308,14 +330,17 @@ broader than those of OGR SQL. Note that installing a query string will generally result in resetting the current reading position (ala `resetreading!()`). """ -function setattributefilter!(layer::AbstractFeatureLayer, query::AbstractString) +function setattributefilter!( + layer::L, + query::AbstractString, +)::L where {L<:AbstractFeatureLayer} result = GDAL.ogr_l_setattributefilter(layer.ptr, query) @ogrerr result """Failed to set a new attribute query. The query expression might be in error.""" return layer end -function clearattributefilter!(layer::AbstractFeatureLayer) +function clearattributefilter!(layer::L)::L where {L<:AbstractFeatureLayer} result = GDAL.ogr_l_setattributefilter(layer.ptr, C_NULL) @ogrerr result "OGRErr $result: Failed to clear attribute query." return layer @@ -328,7 +353,7 @@ Reset feature reading to start on the first feature. This affects `nextfeature()`. """ -function resetreading!(layer::AbstractFeatureLayer) +function resetreading!(layer::L)::L where {L<:AbstractFeatureLayer} GDAL.ogr_l_resetreading(layer.ptr) return layer end @@ -360,8 +385,8 @@ on drivers. If a transaction is committed/aborted, the current sequential reading may or may not be valid after that operation and a call to `resetreading!()` might be needed. """ -function unsafe_nextfeature(layer::AbstractFeatureLayer) - return Feature(GDALFeature(GDAL.ogr_l_getnextfeature(layer.ptr))) +function unsafe_nextfeature(layer::AbstractFeatureLayer)::Feature + return Feature(GDAL.ogr_l_getnextfeature(layer.ptr)) end """ @@ -387,7 +412,10 @@ then calls `nextfeature()` `i` times is used. To determine if fast seeking is available on the layer, use the `testcapability()` method with a value of `OLCFastSetNextByIndex`. """ -function setnextbyindex!(layer::AbstractFeatureLayer, i::Integer) +function setnextbyindex!( + layer::L, + i::Integer, +)::L where {L<:AbstractFeatureLayer} result = GDAL.ogr_l_setnextbyindex(layer.ptr, i) @ogrerr result "Failed to move the cursor to index $i" return layer @@ -419,10 +447,11 @@ the features in the layer looking for the desired feature. Sequential reads (with OGR_L_GetNextFeature()) are generally considered interrupted by a OGR_L_GetFeature() call. -The returned feature is now owned by the caller, and should be freed with `destroy()`. +The returned feature is now owned by the caller, and should be freed with +`destroy()`. """ -unsafe_getfeature(layer::AbstractFeatureLayer, i::Integer) = - Feature(GDALFeature(GDAL.ogr_l_getfeature(layer.ptr, i))) +unsafe_getfeature(layer::AbstractFeatureLayer, i::Integer)::Feature = + Feature(GDAL.ogr_l_getfeature(layer.ptr, i)) """ setfeature!(layer::AbstractFeatureLayer, feature::Feature) @@ -456,15 +485,18 @@ OGRNullFID, then the native implementation may use that as the feature id of the new feature, but not necessarily. Upon successful return the passed feature will have been updated with the new feature id. """ -function addfeature!(layer::AbstractFeatureLayer, feature::Feature) +function addfeature!( + layer::L, + feature::Feature, +)::L where {L<:AbstractFeatureLayer} result = GDAL.ogr_l_createfeature(layer.ptr, feature.ptr) @ogrerr result "Failed to create and write feature in layer." return layer end -function addfeature(f::Function, layer::AbstractFeatureLayer) +function addfeature(f::Function, layer::L)::L where {L<:AbstractFeatureLayer} feature = unsafe_createfeature(layer) - try + return try f(feature) addfeature!(layer, feature) finally @@ -480,12 +512,12 @@ Create and returns a new feature based on the layer definition. The newly feature is owned by the layer (it will increase the number of features the layer by one), but the feature has not been written to the layer yet. """ -unsafe_createfeature(layer::AbstractFeatureLayer) = +unsafe_createfeature(layer::AbstractFeatureLayer)::Feature = unsafe_createfeature(layerdefn(layer)) -function createfeature(f::Function, layer::AbstractFeatureLayer) +function createfeature(f::Function, layer::L)::L where {L<:AbstractFeatureLayer} feature = unsafe_createfeature(layer) - try + return try f(feature) setfeature!(layer, feature) finally @@ -504,7 +536,7 @@ by the driver. Most drivers do not support feature deletion, and will return OGRERR_UNSUPPORTED_OPERATION. The OGR_L_TestCapability() function may be called with OLCDeleteFeature to check if the driver supports feature deletion. """ -function deletefeature!(layer::AbstractFeatureLayer, i::Integer) +function deletefeature!(layer::L, i::Integer)::L where {L<:AbstractFeatureLayer} result = GDAL.ogr_l_deletefeature(layer.ptr, i) # OGRERR_NONE if the operation works, otherwise an appropriate error code # (e.g OGRERR_NON_EXISTING_FEATURE if the feature does not exist). @@ -520,11 +552,12 @@ Returns a view of the schema information for this layer. ### Remarks The `featuredefn` is owned by the `layer` and should not be modified. """ -layerdefn(layer::AbstractFeatureLayer) = +layerdefn(layer::AbstractFeatureLayer)::IFeatureDefnView = IFeatureDefnView(GDAL.ogr_l_getlayerdefn(layer.ptr)) """ - findfieldindex(layer::AbstractFeatureLayer, field::Union{AbstractString, Symbol}, exactmatch::Bool) + findfieldindex(layer::AbstractFeatureLayer, + field::Union{AbstractString, Symbol}, exactmatch::Bool) Find the index of the field in a layer, or -1 if the field doesn't exist. @@ -533,10 +566,10 @@ the driver might apply some changes to make it match, like those it might do if the layer was created (eg. like `LAUNDER` in the OCI driver). """ function findfieldindex( - layer::AbstractFeatureLayer, - field::Union{AbstractString, Symbol}, - exactmatch::Bool - ) + layer::AbstractFeatureLayer, + field::Union{AbstractString,Symbol}, + exactmatch::Bool, +)::Integer return GDAL.ogr_l_findfieldindex(layer.ptr, field, exactmatch) end @@ -550,7 +583,7 @@ Fetch the feature count in this layer, or `-1` if the count is not known. * `force`: flag indicating whether the count should be computed even if it is expensive. (`false` by default.) """ -nfeature(layer::AbstractFeatureLayer, force::Bool = false) = +nfeature(layer::AbstractFeatureLayer, force::Bool = false)::Integer = GDAL.ogr_l_getfeaturecount(layer.ptr, force) """ @@ -558,14 +591,14 @@ nfeature(layer::AbstractFeatureLayer, force::Bool = false) = Fetch number of geometry fields on the feature layer. """ -ngeom(layer::AbstractFeatureLayer) = ngeom(layerdefn(layer)) +ngeom(layer::AbstractFeatureLayer)::Integer = ngeom(layerdefn(layer)) """ nfield(layer::AbstractFeatureLayer) Fetch number of fields on the feature layer. """ -nfield(layer::AbstractFeatureLayer) = nfield(layerdefn(layer)) +nfield(layer::AbstractFeatureLayer)::Integer = nfield(layerdefn(layer)) """ envelope(layer::AbstractFeatureLayer, force::Bool = false) @@ -596,14 +629,21 @@ meaningful extents could be collected. Note that some implementations of this method may alter the read cursor of the layer. """ -function envelope(layer::AbstractFeatureLayer, i::Integer, force::Bool = false) +function envelope( + layer::AbstractFeatureLayer, + i::Integer, + force::Bool = false, +)::GDAL.OGREnvelope envelope = Ref{GDAL.OGREnvelope}(GDAL.OGREnvelope(0, 0, 0, 0)) result = GDAL.ogr_l_getextentex(layer.ptr, i, envelope, force) @ogrerr result "Extent not known" return envelope[] end -function envelope(layer::AbstractFeatureLayer, force::Bool = false) +function envelope( + layer::AbstractFeatureLayer, + force::Bool = false, +)::GDAL.OGREnvelope envelope = Ref{GDAL.OGREnvelope}(GDAL.OGREnvelope(0, 0, 0, 0)) result = GDAL.ogr_l_getextent(layer.ptr, envelope, force) @ogrerr result "Extent not known" @@ -633,8 +673,8 @@ the caller. implementation using `resetreading!()` and `nextfeature()` to find the requested feature id. -* `OLCSequentialWrite` / \"SequentialWrite\": `true` if the CreateFeature() method - works for this layer. Note this means that this particular layer is +* `OLCSequentialWrite` / \"SequentialWrite\": `true` if the CreateFeature() + method works for this layer. Note this means that this particular layer is writable. The same OGRLayer class may returned `false` for other layer instances that are effectively read-only. @@ -643,19 +683,19 @@ the caller. writable. The same OGRLayer class may returned `false` for other layer instances that are effectively read-only. -* `OLCFastSpatialFilter` / \"FastSpatialFilter\": `true` if this layer implements - spatial filtering efficiently. Layers that effectively read all features, - and test them with the OGRFeature intersection methods should return `false`. - This can be used as a clue by the application whether it should build and - maintain its own spatial index for features in this layer. +* `OLCFastSpatialFilter` / \"FastSpatialFilter\": `true` if this layer + implements spatial filtering efficiently. Layers that effectively read all + features, and test them with the OGRFeature intersection methods should + return `false`. This can be used as a clue by the application whether it + should build and maintain its own spatial index for features in this layer. -* `OLCFastFeatureCount` / \"FastFeatureCount\": `true` if this layer can return a - feature count (via GetFeatureCount()) efficiently. i.e. without counting the - features. In some cases this will return `true` until a spatial filter is - installed after which it will return `false`. +* `OLCFastFeatureCount` / \"FastFeatureCount\": `true` if this layer can return + a feature count (via GetFeatureCount()) efficiently. i.e. without counting + the features. In some cases this will return `true` until a spatial filter + is installed after which it will return `false`. -* `OLCFastGetExtent` / \"FastGetExtent\": `true` if this layer can return its data - extent (via GetExtent()) efficiently, i.e. without scanning all the +* `OLCFastGetExtent` / \"FastGetExtent\": `true` if this layer can return its + data extent (via GetExtent()) efficiently, i.e. without scanning all the features. In some cases this will return `true` until a spatial filter is installed after which it will return `false`. @@ -665,9 +705,9 @@ the caller. * `OLCCreateField` / \"CreateField\": `true` if this layer can create new fields on the current layer using CreateField(), otherwise `false`. -* `OLCCreateGeomField` / \"CreateGeomField\": (GDAL >= 1.11) `true` if this layer - can create new geometry fields on the current layer using CreateGeomField(), - otherwise `false`. +* `OLCCreateGeomField` / \"CreateGeomField\": (GDAL >= 1.11) `true` if this + layer can create new geometry fields on the current layer using + CreateGeomField(), otherwise `false`. * `OLCDeleteField` / \"DeleteField\": `true` if this layer can delete existing fields on the current layer using DeleteField(), otherwise `false`. @@ -680,50 +720,52 @@ the caller. definition of an existing field on the current layer using AlterFieldDefn(), otherwise `false`. -* `OLCDeleteFeature` / \"DeleteFeature\": `true` if the DeleteFeature() method is - supported on this layer, otherwise `false`. +* `OLCDeleteFeature` / \"DeleteFeature\": `true` if the DeleteFeature() method + is supported on this layer, otherwise `false`. -* `OLCStringsAsUTF8` / \"StringsAsUTF8\": `true` if values of OFTString fields are - assured to be in UTF-8 format. If `false` the encoding of fields is uncertain, - though it might still be UTF-8. +* `OLCStringsAsUTF8` / \"StringsAsUTF8\": `true` if values of OFTString fields + are assured to be in UTF-8 format. If `false` the encoding of fields is + uncertain, though it might still be UTF-8. * `OLCTransactions` / \"Transactions\": `true` if the StartTransaction(), CommitTransaction() and RollbackTransaction() methods work in a meaningful way, otherwise `false`. -* `OLCIgnoreFields` / \"IgnoreFields\": `true` if fields, geometry and style will - be omitted when fetching features as set by SetIgnoredFields() method. +* `OLCIgnoreFields` / \"IgnoreFields\": `true` if fields, geometry and style + will be omitted when fetching features as set by SetIgnoredFields() method. * `OLCCurveGeometries` / \"CurveGeometries\": `true` if this layer supports writing curve geometries or may return such geometries. (GDAL 2.0). """ -testcapability(layer::AbstractFeatureLayer, capability::AbstractString) = +testcapability(layer::AbstractFeatureLayer, capability::AbstractString)::Bool = Bool(GDAL.ogr_l_testcapability(layer.ptr, capability)) function listcapability( - layer::AbstractFeatureLayer, - capabilities = (GDAL.OLCRandomRead, - GDAL.OLCSequentialWrite, - GDAL.OLCRandomWrite, - GDAL.OLCFastSpatialFilter, - GDAL.OLCFastFeatureCount, - GDAL.OLCFastGetExtent, - GDAL.OLCCreateField, - GDAL.OLCDeleteField, - GDAL.OLCReorderFields, - GDAL.OLCAlterFieldDefn, - GDAL.OLCTransactions, - GDAL.OLCDeleteFeature, - GDAL.OLCFastSetNextByIndex, - GDAL.OLCStringsAsUTF8, - GDAL.OLCIgnoreFields, - GDAL.OLCCreateGeomField, - GDAL.OLCCurveGeometries, - GDAL.OLCMeasuredGeometries) + layer::AbstractFeatureLayer, + capabilities = ( + GDAL.OLCRandomRead, + GDAL.OLCSequentialWrite, + GDAL.OLCRandomWrite, + GDAL.OLCFastSpatialFilter, + GDAL.OLCFastFeatureCount, + GDAL.OLCFastGetExtent, + GDAL.OLCCreateField, + GDAL.OLCDeleteField, + GDAL.OLCReorderFields, + GDAL.OLCAlterFieldDefn, + GDAL.OLCTransactions, + GDAL.OLCDeleteFeature, + GDAL.OLCFastSetNextByIndex, + GDAL.OLCStringsAsUTF8, + GDAL.OLCIgnoreFields, + GDAL.OLCCreateGeomField, + GDAL.OLCCurveGeometries, + GDAL.OLCMeasuredGeometries, + ), +)::Dict{String,Bool} + return Dict{String,Bool}( + [c => testcapability(layer, c) for c in capabilities], ) - Dict{String, Bool}([ - c => testcapability(layer,c) for c in capabilities - ]) end # TODO use syntax below once v0.4 support is dropped (not in Compat.jl) @@ -739,7 +781,8 @@ end # ) """ - addfielddefn!(layer::AbstractFeatureLayer, field::AbstractFieldDefn, approx = false) + addfielddefn!(layer::AbstractFeatureLayer, field::AbstractFieldDefn, + approx = false) Create a new field on a layer. @@ -768,17 +811,18 @@ fields with not-null constraints, this is generally before creating any feature to the layer. """ function addfielddefn!( - layer::AbstractFeatureLayer, - field::AbstractFieldDefn, - approx::Bool = false - ) + layer::L, + field::AbstractFieldDefn, + approx::Bool = false, +)::L where {L<:AbstractFeatureLayer} result = GDAL.ogr_l_createfield(layer.ptr, field.ptr, approx) @ogrerr result "Failed to create new field" return layer end """ - addgeomdefn!(layer::AbstractFeatureLayer, field::AbstractGeomFieldDefn, approx = false) + addgeomdefn!(layer::AbstractFeatureLayer, field::AbstractGeomFieldDefn, + approx = false) Create a new geometry field on a layer. @@ -807,10 +851,10 @@ fields with not-null constraints, this is generally before creating any feature to the layer. """ function addgeomdefn!( - layer::AbstractFeatureLayer, - field::AbstractGeomFieldDefn, - approx::Bool = false - ) + layer::L, + field::AbstractGeomFieldDefn, + approx::Bool = false, +)::L where {L<:AbstractFeatureLayer} result = GDAL.ogr_l_creategeomfield(layer.ptr, field.ptr, approx) # OGRERR_NONE on success. @ogrerr result "Failed to create new geometry field" @@ -996,7 +1040,8 @@ Increment layer reference count. ### Returns The reference count after incrementing. """ -reference(layer::AbstractFeatureLayer) = GDAL.ogr_l_reference(layer.ptr) +reference(layer::AbstractFeatureLayer)::Integer = + GDAL.ogr_l_reference(layer.ptr) """ dereference(layer::AbstractFeatureLayer) @@ -1006,14 +1051,16 @@ Decrement layer reference count. ### Returns The reference count after decrementing. """ -dereference(layer::AbstractFeatureLayer) = GDAL.ogr_l_dereference(layer.ptr) +dereference(layer::AbstractFeatureLayer)::Integer = + GDAL.ogr_l_dereference(layer.ptr) """ nreference(layer::AbstractFeatureLayer) The current reference count for the layer object itself. """ -nreference(layer::AbstractFeatureLayer) = GDAL.ogr_l_getrefcount(layer.ptr) +nreference(layer::AbstractFeatureLayer)::Integer = + GDAL.ogr_l_getrefcount(layer.ptr) # """ # Flush pending changes to disk. @@ -1035,7 +1082,7 @@ nreference(layer::AbstractFeatureLayer) = GDAL.ogr_l_getrefcount(layer.ptr) # function synctodisk!(layer::AbstractFeatureLayer) # result = GDAL.ogr_l_synctodisk(layer.ptr) # @ogrerr result "Failed to flush pending changes to disk" -# layer.ptr = GDALFeatureLayer(C_NULL) +# layer.ptr = C_NULL # end # """ @@ -1051,14 +1098,15 @@ nreference(layer::AbstractFeatureLayer) = GDAL.ogr_l_getrefcount(layer.ptr) The name of the FID column in the database, or \"\" if not supported. """ -fidcolumnname(layer::AbstractFeatureLayer) = GDAL.ogr_l_getfidcolumn(layer.ptr) +fidcolumnname(layer::AbstractFeatureLayer)::String = + GDAL.ogr_l_getfidcolumn(layer.ptr) """ geomcolumnname(layer::AbstractFeatureLayer) The name of the geometry column in the database, or \"\" if not supported. """ -geomcolumnname(layer::AbstractFeatureLayer) = +geomcolumnname(layer::AbstractFeatureLayer)::String = GDAL.ogr_l_getgeometrycolumn(layer.ptr) """ @@ -1081,7 +1129,10 @@ Besides field names of the layers, the following special fields can be passed: By default, no fields are ignored. """ -function setignoredfields!(layer::AbstractFeatureLayer, fieldnames) +function setignoredfields!( + layer::L, + fieldnames, +)::L where {L<:AbstractFeatureLayer} result = GDAL.ogr_l_setignoredfields(layer.ptr, fieldnames) # OGRERR_NONE if all field names have been resolved (even if the driver # does not support this method) @@ -1089,7 +1140,6 @@ function setignoredfields!(layer::AbstractFeatureLayer, fieldnames) return layer end - # """ # Intersection of two layers. diff --git a/src/ogr/fielddefn.jl b/src/ogr/fielddefn.jl index 959d38b3..5f60bdb3 100644 --- a/src/ogr/fielddefn.jl +++ b/src/ogr/fielddefn.jl @@ -5,35 +5,37 @@ Create a new field definition. By default, fields have no width, precision, are nullable and not ignored. """ -unsafe_createfielddefn(name::AbstractString, etype::OGRFieldType) = +unsafe_createfielddefn(name::AbstractString, etype::OGRFieldType)::FieldDefn = FieldDefn(GDAL.ogr_fld_create(name, etype)) "Destroy a field definition." -function destroy(fielddefn::FieldDefn) +function destroy(fielddefn::FieldDefn)::Nothing GDAL.ogr_fld_destroy(fielddefn.ptr) fielddefn.ptr = C_NULL - return fielddefn + return nothing end -function destroy(fielddefn::IFieldDefnView) +function destroy(fielddefn::IFieldDefnView)::Nothing fielddefn.ptr = C_NULL - return fielddefn + return nothing end "Set the name of this field." -function setname!(fielddefn::FieldDefn, name::AbstractString) +function setname!(fielddefn::FieldDefn, name::AbstractString)::FieldDefn GDAL.ogr_fld_setname(fielddefn.ptr, name) return fielddefn end "Fetch the name of this field." -getname(fielddefn::AbstractFieldDefn) = GDAL.ogr_fld_getnameref(fielddefn.ptr) +getname(fielddefn::AbstractFieldDefn)::String = + GDAL.ogr_fld_getnameref(fielddefn.ptr) "Fetch the type of this field." -gettype(fielddefn::AbstractFieldDefn) = GDAL.ogr_fld_gettype(fielddefn.ptr) +gettype(fielddefn::AbstractFieldDefn)::OGRFieldType = + GDAL.ogr_fld_gettype(fielddefn.ptr) "Set the type of this field." -function settype!(fielddefn::FieldDefn, etype::OGRFieldType) +function settype!(fielddefn::FieldDefn, etype::OGRFieldType)::FieldDefn GDAL.ogr_fld_settype(fielddefn.ptr, etype) return fielddefn end @@ -49,7 +51,7 @@ Fetch subtype of this field. ### Returns field subtype. """ -getsubtype(fielddefn::AbstractFieldDefn) = +getsubtype(fielddefn::AbstractFieldDefn)::OGRFieldSubType = GDAL.ogr_fld_getsubtype(fielddefn.ptr) """ @@ -64,7 +66,7 @@ OGRFeatureDefn. * `fielddefn`: handle to the field definition to set type to. * `subtype`: the new field subtype. """ -function setsubtype!(fielddefn::FieldDefn, subtype::OGRFieldSubType) +function setsubtype!(fielddefn::FieldDefn, subtype::OGRFieldSubType)::FieldDefn GDAL.ogr_fld_setsubtype(fielddefn.ptr, subtype) return fielddefn end @@ -76,7 +78,7 @@ Get the justification for this field. Note: no driver is know to use the concept of field justification. """ -getjustify(fielddefn::AbstractFieldDefn) = +getjustify(fielddefn::AbstractFieldDefn)::OGRJustification = GDAL.ogr_fld_getjustify(fielddefn.ptr) """ @@ -86,7 +88,10 @@ Set the justification for this field. Note: no driver is know to use the concept of field justification. """ -function setjustify!(fielddefn::FieldDefn, ejustify::OGRJustification) +function setjustify!( + fielddefn::FieldDefn, + ejustify::OGRJustification, +)::FieldDefn GDAL.ogr_fld_setjustify(fielddefn.ptr, ejustify) return fielddefn end @@ -99,7 +104,8 @@ Get the formatting width for this field. ### Returns the width, zero means no specified width. """ -getwidth(fielddefn::AbstractFieldDefn) = GDAL.ogr_fld_getwidth(fielddefn.ptr) +getwidth(fielddefn::AbstractFieldDefn)::Integer = + GDAL.ogr_fld_getwidth(fielddefn.ptr) """ setwidth!(fielddefn::FieldDefn, width::Integer) @@ -109,7 +115,7 @@ Set the formatting width for this field in characters. This should never be done to an OGRFieldDefn that is already part of an OGRFeatureDefn. """ -function setwidth!(fielddefn::FieldDefn, width::Integer) +function setwidth!(fielddefn::FieldDefn, width::Integer)::FieldDefn GDAL.ogr_fld_setwidth(fielddefn.ptr, width) return fielddefn end @@ -121,7 +127,7 @@ Get the formatting precision for this field. This should normally be zero for fields of types other than OFTReal. """ -getprecision(fielddefn::AbstractFieldDefn) = +getprecision(fielddefn::AbstractFieldDefn)::Integer = GDAL.ogr_fld_getprecision(fielddefn.ptr) """ @@ -131,7 +137,7 @@ Set the formatting precision for this field in characters. This should normally be zero for fields of types other than OFTReal. """ -function setprecision!(fielddefn::FieldDefn, precision::Integer) +function setprecision!(fielddefn::FieldDefn, precision::Integer)::FieldDefn GDAL.ogr_fld_setprecision(fielddefn.ptr, precision) return fielddefn end @@ -150,13 +156,13 @@ Set defining parameters for a field in one call. * `justify`: the formatting justification ([OJUndefined], OJLeft or OJRight) """ function setparams!( - fielddefn::FieldDefn, - name::AbstractString, - etype::OGRFieldType; - nwidth::Integer = 0, - nprecision::Integer = 0, - justify::OGRJustification = GDAL.OJUndefined - ) + fielddefn::FieldDefn, + name::AbstractString, + etype::OGRFieldType; + nwidth::Integer = 0, + nprecision::Integer = 0, + justify::OGRJustification = OJUndefined, +)::FieldDefn GDAL.ogr_fld_set(fielddefn.ptr, name, etype, nwidth, nprecision, justify) return fielddefn end @@ -166,7 +172,7 @@ end Return whether this field should be omitted when fetching features. """ -isignored(fielddefn::AbstractFieldDefn) = +isignored(fielddefn::AbstractFieldDefn)::Bool = Bool(GDAL.ogr_fld_isignored(fielddefn.ptr)) """ @@ -174,7 +180,7 @@ isignored(fielddefn::AbstractFieldDefn) = Set whether this field should be omitted when fetching features. """ -function setignored!(fielddefn::FieldDefn, ignore::Bool) +function setignored!(fielddefn::FieldDefn, ignore::Bool)::FieldDefn GDAL.ogr_fld_setignored(fielddefn.ptr, ignore) return fielddefn end @@ -186,12 +192,12 @@ Return whether this field can receive null values. By default, fields are nullable. -Even if this method returns `false` (i.e not-nullable field), it doesn't mean that -OGRFeature::IsFieldSet() will necessary return `true`, as fields can be temporary -unset and null/not-null validation is usually done when +Even if this method returns `false` (i.e not-nullable field), it doesn't mean +that OGRFeature::IsFieldSet() will necessarily return `true`, as fields can be +temporarily unset and null/not-null validation is usually done when OGRLayer::CreateFeature()/SetFeature() is called. """ -isnullable(fielddefn::AbstractFieldDefn) = +isnullable(fielddefn::AbstractFieldDefn)::Bool = Bool(GDAL.ogr_fld_isnullable(fielddefn.ptr)) """ @@ -205,7 +211,7 @@ to set a not-null constraint. Drivers that support writing not-null constraint will advertize the GDAL_DCAP_NOTNULL_FIELDS driver metadata item. """ -function setnullable!(fielddefn::FieldDefn, nullable::Bool) +function setnullable!(fielddefn::FieldDefn, nullable::Bool)::FieldDefn GDAL.ogr_fld_setnullable(fielddefn.ptr, nullable) return fielddefn end @@ -215,12 +221,13 @@ end Get default field value """ -function getdefault(fielddefn::AbstractFieldDefn) - result = @gdal(OGR_Fld_GetDefault::Cstring, fielddefn.ptr::GDALFieldDefn) - if result == C_NULL - return "" +function getdefault(fielddefn::AbstractFieldDefn)::String + result = + @gdal(OGR_Fld_GetDefault::Cstring, fielddefn.ptr::GDAL.OGRFieldDefnH) + return if result == C_NULL + "" else - return unsafe_string(result) + unsafe_string(result) end end @@ -246,7 +253,7 @@ datetime literal value, format should be 'YYYY/MM/DD HH:MM:SS[.sss]' Drivers that support writing DEFAULT clauses will advertize the GDAL_DCAP_DEFAULT_FIELDS driver metadata item. """ -function setdefault!(fielddefn::AbstractFieldDefn, default) +function setdefault!(fielddefn::T, default)::T where {T<:AbstractFieldDefn} GDAL.ogr_fld_setdefault(fielddefn.ptr, default) return fielddefn end @@ -260,7 +267,7 @@ Driver specific default values are those that are not NULL, a numeric value, a literal value enclosed between single quote characters, CURRENT_TIMESTAMP, CURRENT_TIME, CURRENT_DATE or datetime literal value. """ -isdefaultdriverspecific(fielddefn::AbstractFieldDefn) = +isdefaultdriverspecific(fielddefn::AbstractFieldDefn)::Bool = Bool(GDAL.ogr_fld_isdefaultdriverspecific(fielddefn.ptr)) """ @@ -268,38 +275,46 @@ isdefaultdriverspecific(fielddefn::AbstractFieldDefn) = Create a new field geometry definition. """ -unsafe_creategeomdefn(name::AbstractString, etype::OGRwkbGeometryType) = - GeomFieldDefn(GDAL.ogr_gfld_create(name, etype)) +function unsafe_creategeomdefn( + name::AbstractString, + etype::OGRwkbGeometryType, +)::GeomFieldDefn + return GeomFieldDefn(GDAL.ogr_gfld_create(name, etype)) +end "Destroy a geometry field definition." -function destroy(geomdefn::GeomFieldDefn) +function destroy(geomdefn::GeomFieldDefn)::Nothing GDAL.ogr_gfld_destroy(geomdefn.ptr) geomdefn.ptr = C_NULL geomdefn.spatialref = SpatialRef() - return geomdefn + return nothing end "Destroy a geometry field definition." -function destroy(geomdefn::IGeomFieldDefnView) +function destroy(geomdefn::IGeomFieldDefnView)::Nothing geomdefn.ptr = C_NULL - return geomdefn + return nothing end "Set the name of this field." -function setname!(geomdefn::GeomFieldDefn, name::AbstractString) +function setname!(geomdefn::GeomFieldDefn, name::AbstractString)::GeomFieldDefn GDAL.ogr_gfld_setname(geomdefn.ptr, name) return geomdefn end "Fetch name of this field." -getname(geomdefn::AbstractGeomFieldDefn) = +getname(geomdefn::AbstractGeomFieldDefn)::String = GDAL.ogr_gfld_getnameref(geomdefn.ptr) "Fetch geometry type of this field." -gettype(geomdefn::AbstractGeomFieldDefn) = GDAL.ogr_gfld_gettype(geomdefn.ptr) +gettype(geomdefn::AbstractGeomFieldDefn)::OGRwkbGeometryType = + GDAL.ogr_gfld_gettype(geomdefn.ptr) "Set the geometry type of this field." -function settype!(geomdefn::GeomFieldDefn, etype::OGRwkbGeometryType) +function settype!( + geomdefn::GeomFieldDefn, + etype::OGRwkbGeometryType, +)::GeomFieldDefn GDAL.ogr_gfld_settype(geomdefn.ptr, etype) return geomdefn end @@ -309,7 +324,7 @@ end Returns a clone of the spatial reference system for this field. May be NULL. """ -function getspatialref(geomdefn::AbstractGeomFieldDefn) +function getspatialref(geomdefn::AbstractGeomFieldDefn)::ISpatialRef result = GDAL.ogr_gfld_getspatialref(geomdefn.ptr) if result == C_NULL return ISpatialRef() @@ -320,7 +335,7 @@ function getspatialref(geomdefn::AbstractGeomFieldDefn) end end -function unsafe_getspatialref(geomdefn::AbstractGeomFieldDefn) +function unsafe_getspatialref(geomdefn::AbstractGeomFieldDefn)::SpatialRef result = GDAL.ogr_gfld_getspatialref(geomdefn.ptr) if result == C_NULL return SpatialRef() @@ -340,9 +355,9 @@ This function drops the reference of the previously set SRS object and acquires a new reference on the passed object (if non-NULL). """ function setspatialref!( - geomdefn::GeomFieldDefn, - spatialref::AbstractSpatialRef - ) + geomdefn::GeomFieldDefn, + spatialref::AbstractSpatialRef, +)::GeomFieldDefn clonespatialref = clone(spatialref) GDAL.ogr_gfld_setspatialref(geomdefn.ptr, clonespatialref.ptr) geomdefn.spatialref = clonespatialref @@ -356,14 +371,14 @@ Return whether this geometry field can receive null values. By default, fields are nullable. -Even if this method returns `false` (i.e not-nullable field), it doesn't mean that -OGRFeature::IsFieldSet() will necessary return `true`, as fields can be temporary -unset and null/not-null validation is usually done when +Even if this method returns `false` (i.e not-nullable field), it doesn't mean +that OGRFeature::IsFieldSet() will necessary return `true`, as fields can be +temporarily unset and null/not-null validation is usually done when OGRLayer::CreateFeature()/SetFeature() is called. Note that not-nullable geometry fields might also contain 'empty' geometries. """ -isnullable(geomdefn::AbstractGeomFieldDefn) = +isnullable(geomdefn::AbstractGeomFieldDefn)::Bool = Bool(GDAL.ogr_gfld_isnullable(geomdefn.ptr)) """ @@ -377,7 +392,7 @@ to set a not-null constraint. Drivers that support writing not-null constraint will advertize the GDAL_DCAP_NOTNULL_GEOMFIELDS driver metadata item. """ -function setnullable!(geomdefn::GeomFieldDefn, nullable::Bool) +function setnullable!(geomdefn::GeomFieldDefn, nullable::Bool)::GeomFieldDefn GDAL.ogr_gfld_setnullable(geomdefn.ptr, nullable) return geomdefn end @@ -387,7 +402,7 @@ end Return whether this field should be omitted when fetching features. """ -isignored(geomdefn::AbstractGeomFieldDefn) = +isignored(geomdefn::AbstractGeomFieldDefn)::Bool = Bool(GDAL.ogr_gfld_isignored(geomdefn.ptr)) """ @@ -395,7 +410,7 @@ isignored(geomdefn::AbstractGeomFieldDefn) = Set whether this field should be omitted when fetching features. """ -function setignored!(geomdefn::GeomFieldDefn, ignore::Bool) +function setignored!(geomdefn::GeomFieldDefn, ignore::Bool)::GeomFieldDefn GDAL.ogr_gfld_setignored(geomdefn.ptr, ignore) return geomdefn end diff --git a/src/ogr/geometry.jl b/src/ogr/geometry.jl index 8532592f..74a94956 100644 --- a/src/ogr/geometry.jl +++ b/src/ogr/geometry.jl @@ -8,24 +8,26 @@ binary (WKB) representation. ### Parameters * `data`: pointer to the input BLOB data. """ -function fromWKB(data) - geom = Ref{GDALGeometry}() - result = @gdal(OGR_G_CreateFromWkb::GDAL.OGRErr, +function fromWKB(data)::IGeometry + geom = Ref{GDAL.OGRGeometryH}() + result = @gdal( + OGR_G_CreateFromWkb::GDAL.OGRErr, data::Ptr{Cuchar}, - C_NULL::GDALSpatialRef, - geom::Ptr{GDALGeometry}, + C_NULL::GDAL.OGRSpatialReferenceH, + geom::Ptr{GDAL.OGRGeometryH}, sizeof(data)::Cint ) @ogrerr result "Failed to create geometry from WKB" return IGeometry(geom[]) end -function unsafe_fromWKB(data) - geom = Ref{GDALGeometry}() - result = @gdal(OGR_G_CreateFromWkb::GDAL.OGRErr, +function unsafe_fromWKB(data)::Geometry + geom = Ref{GDAL.OGRGeometryH}() + result = @gdal( + OGR_G_CreateFromWkb::GDAL.OGRErr, data::Ptr{Cuchar}, - C_NULL::GDALSpatialRef, - geom::Ptr{GDALGeometry}, + C_NULL::GDAL.OGRSpatialReferenceH, + geom::Ptr{GDAL.OGRGeometryH}, sizeof(data)::Cint ) @ogrerr result "Failed to create geometry from WKB" @@ -43,31 +45,34 @@ Create a geometry object of the appropriate type from its well known text geometry to be created. The pointer is updated to point just beyond that last character consumed. """ -function fromWKT(data::Vector{String}) - geom = Ref{GDALGeometry}() - result = @gdal(OGR_G_CreateFromWkt::GDAL.OGRErr, +function fromWKT(data::Vector{String})::IGeometry + geom = Ref{GDAL.OGRGeometryH}() + result = @gdal( + OGR_G_CreateFromWkt::GDAL.OGRErr, data::StringList, - C_NULL::GDALSpatialRef, - geom::Ptr{GDALGeometry} + C_NULL::GDAL.OGRSpatialReferenceH, + geom::Ptr{GDAL.OGRGeometryH} ) @ogrerr result "Failed to create geometry from WKT" return IGeometry(geom[]) end -function unsafe_fromWKT(data::Vector{String}) - geom = Ref{GDALGeometry}() - result = @gdal(OGR_G_CreateFromWkt::GDAL.OGRErr, +function unsafe_fromWKT(data::Vector{String})::Geometry + geom = Ref{GDAL.OGRGeometryH}() + result = @gdal( + OGR_G_CreateFromWkt::GDAL.OGRErr, data::StringList, - C_NULL::GDALSpatialRef, - geom::Ptr{GDALGeometry} + C_NULL::GDAL.OGRSpatialReferenceH, + geom::Ptr{GDAL.OGRGeometryH} ) @ogrerr result "Failed to create geometry from WKT" return Geometry(geom[]) end -fromWKT(data::String, args...) = fromWKT([data], args...) +fromWKT(data::String, args...)::IGeometry = fromWKT([data], args...) -unsafe_fromWKT(data::String, args...) = unsafe_fromWKT([data], args...) +unsafe_fromWKT(data::String, args...)::Geometry = + unsafe_fromWKT([data], args...) """ Destroy geometry object. @@ -75,9 +80,10 @@ Destroy geometry object. Equivalent to invoking delete on a geometry, but it guaranteed to take place within the context of the GDAL/OGR heap. """ -function destroy(geom::AbstractGeometry) +function destroy(geom::AbstractGeometry)::Nothing GDAL.ogr_g_destroygeometry(geom.ptr) geom.ptr = C_NULL + return nothing end """ @@ -85,7 +91,7 @@ end Returns a copy of the geometry with the original spatial reference system. """ -function clone(geom::AbstractGeometry) +function clone(geom::AbstractGeometry)::IGeometry if geom.ptr == C_NULL return IGeometry() else @@ -93,7 +99,7 @@ function clone(geom::AbstractGeometry) end end -function unsafe_clone(geom::AbstractGeometry) +function unsafe_clone(geom::AbstractGeometry)::Geometry if geom.ptr == C_NULL return Geometry() else @@ -109,10 +115,10 @@ Create an empty geometry of desired type. This is equivalent to allocating the desired geometry with new, but the allocation is guaranteed to take place in the context of the GDAL/OGR heap. """ -creategeom(geomtype::OGRwkbGeometryType) = +creategeom(geomtype::OGRwkbGeometryType)::IGeometry = IGeometry(GDAL.ogr_g_creategeometry(geomtype)) -unsafe_creategeom(geomtype::OGRwkbGeometryType) = +unsafe_creategeom(geomtype::OGRwkbGeometryType)::Geometry = Geometry(GDAL.ogr_g_creategeometry(geomtype)) """ @@ -136,21 +142,23 @@ For that, OGRGeometry::getCurveGeometry() can be used. The passed in geometry is cloned and a new one returned. """ function forceto( - geom::AbstractGeometry, - targettype::OGRwkbGeometryType, - options = StringList(C_NULL) + geom::AbstractGeometry, + targettype::OGRwkbGeometryType, + options = StringList(C_NULL), +)::IGeometry + return IGeometry( + GDAL.ogr_g_forceto(unsafe_clone(geom).ptr, targettype, options), ) - return IGeometry(GDAL.ogr_g_forceto(unsafe_clone(geom).ptr, targettype, - options)) end function unsafe_forceto( - geom::AbstractGeometry, - targettype::OGRwkbGeometryType, - options = StringList(C_NULL) + geom::AbstractGeometry, + targettype::OGRwkbGeometryType, + options = StringList(C_NULL), +)::Geometry + return Geometry( + GDAL.ogr_g_forceto(unsafe_clone(geom).ptr, targettype, options), ) - return Geometry(GDAL.ogr_g_forceto(unsafe_clone(geom).ptr, targettype, - options)) end """ @@ -162,7 +170,7 @@ This function corresponds to the SFCOM IGeometry::GetDimension() method. It indicates the dimension of the geometry, but does not indicate the dimension of the underlying space (as indicated by OGR_G_GetCoordinateDimension() function). """ -geomdim(geom::AbstractGeometry) = GDAL.ogr_g_getdimension(geom.ptr) +geomdim(geom::AbstractGeometry)::Integer = GDAL.ogr_g_getdimension(geom.ptr) """ getcoorddim(geom::AbstractGeometry) @@ -170,10 +178,9 @@ geomdim(geom::AbstractGeometry) = GDAL.ogr_g_getdimension(geom.ptr) Get the dimension of the coordinates in this geometry. ### Returns -In practice this will return 2 or 3. It can also return 0 in the case of an -empty point. +This will return 2 or 3. """ -getcoorddim(geom::AbstractGeometry) = +getcoorddim(geom::AbstractGeometry)::Integer = GDAL.ogr_g_getcoordinatedimension(geom.ptr) """ @@ -187,8 +194,8 @@ dimension of a geometry collection, a compound curve, a polygon, etc. will affect the children geometries. This will also remove the M dimension if present before this call. """ -function setcoorddim!(geom::AbstractGeometry, dim::Integer) - GDAL.ogr_g_setcoordinatedimension(geom.ptr ,dim) +function setcoorddim!(geom::G, dim::Integer)::G where {G<:AbstractGeometry} + GDAL.ogr_g_setcoordinatedimension(geom.ptr, dim) return geom end @@ -197,7 +204,7 @@ end Computes and returns the bounding envelope for this geometry. """ -function envelope(geom::AbstractGeometry) +function envelope(geom::AbstractGeometry)::GDAL.OGREnvelope envelope = Ref{GDAL.OGREnvelope}(GDAL.OGREnvelope(0, 0, 0, 0)) GDAL.ogr_g_getenvelope(geom.ptr, envelope) return envelope[] @@ -208,7 +215,7 @@ end Computes and returns the bounding envelope (3D) for this geometry """ -function envelope3d(geom::AbstractGeometry) +function envelope3d(geom::AbstractGeometry)::GDAL.OGREnvelope3D envelope = Ref{GDAL.OGREnvelope3D}(GDAL.OGREnvelope3D(0, 0, 0, 0, 0, 0)) GDAL.ogr_g_getenvelope3d(geom.ptr, envelope) return envelope[] @@ -219,15 +226,22 @@ end Returns a bounding box polygon (CW) built from envelope coordinates """ -function boundingbox(geom::AbstractGeometry) +function boundingbox(geom::AbstractGeometry)::IGeometry coordinates = envelope(geom) - MinX, MaxX, MinY, MaxY = coordinates.MinX, coordinates.MaxX, coordinates.MinY, coordinates.MaxY + MinX, MaxX = coordinates.MinX, coordinates.MaxX + MinY, MaxY = coordinates.MinY, coordinates.MaxY # creates a CW closed ring polygon - return createpolygon([[MaxY, MinX], [MaxY, MaxX], [MinY, MaxX], [MinY, MinX], [MaxY, MinX]]) + return createpolygon([ + [MaxY, MinX], + [MaxY, MaxX], + [MinY, MaxX], + [MinY, MinX], + [MaxY, MinX], + ]) end """ - toWKB(geom::AbstractGeometry, order::OGRwkbByteOrder = GDAL.wkbNDR) + toWKB(geom::AbstractGeometry, order::OGRwkbByteOrder = wkbNDR) Convert a geometry well known binary format. @@ -235,15 +249,18 @@ Convert a geometry well known binary format. * `geom`: handle on the geometry to convert to a well know binary data from. * `order`: One of wkbXDR or [wkbNDR] indicating MSB or LSB byte order resp. """ -function toWKB(geom::AbstractGeometry, order::OGRwkbByteOrder = GDAL.wkbNDR) - buffer = Array{Cuchar}(undef, wkbsize(geom)) +function toWKB( + geom::AbstractGeometry, + order::OGRwkbByteOrder = wkbNDR, +)::Vector{Cuchar} + buffer = Vector{Cuchar}(undef, wkbsize(geom)) result = GDAL.ogr_g_exporttowkb(geom.ptr, order, buffer) @ogrerr result "Failed to export geometry to WKB" return buffer end """ - toISOWKB(geom::AbstractGeometry, order::OGRwkbByteOrder = GDAL.wkbNDR) + toISOWKB(geom::AbstractGeometry, order::OGRwkbByteOrder = wkbNDR) Convert a geometry into SFSQL 1.2 / ISO SQL/MM Part 3 well known binary format. @@ -251,7 +268,10 @@ Convert a geometry into SFSQL 1.2 / ISO SQL/MM Part 3 well known binary format. * `geom`: handle on the geometry to convert to a well know binary data from. * `order`: One of wkbXDR or [wkbNDR] indicating MSB or LSB byte order resp. """ -function toISOWKB(geom::AbstractGeometry, order::OGRwkbByteOrder = GDAL.wkbNDR) +function toISOWKB( + geom::AbstractGeometry, + order::OGRwkbByteOrder = wkbNDR, +)::Vector{Cuchar} buffer = Array{Cuchar}(undef, wkbsize(geom)) result = GDAL.ogr_g_exporttoisowkb(geom.ptr, order, buffer) @ogrerr result "Failed to export geometry to ISO WKB" @@ -263,14 +283,14 @@ end Returns size (in bytes) of related binary representation. """ -wkbsize(geom::AbstractGeometry) = GDAL.ogr_g_wkbsize(geom.ptr) +wkbsize(geom::AbstractGeometry)::Integer = GDAL.ogr_g_wkbsize(geom.ptr) """ toWKT(geom::AbstractGeometry) Convert a geometry into well known text format. """ -function toWKT(geom::AbstractGeometry) +function toWKT(geom::AbstractGeometry)::String wkt_ptr = Ref(Cstring(C_NULL)) result = GDAL.ogr_g_exporttowkt(geom.ptr, wkt_ptr) @ogrerr result "OGRErr $result: failed to export geometry to WKT" @@ -284,7 +304,7 @@ end Convert a geometry into SFSQL 1.2 / ISO SQL/MM Part 3 well known text format. """ -function toISOWKT(geom::AbstractGeometry) +function toISOWKT(geom::AbstractGeometry)::String isowkt_ptr = Ref(Cstring(C_NULL)) result = GDAL.ogr_g_exporttoisowkt(geom.ptr, isowkt_ptr) @ogrerr result "OGRErr $result: failed to export geometry to ISOWKT" @@ -298,21 +318,21 @@ end Fetch geometry type code """ -getgeomtype(geom::AbstractGeometry) = _geomtype(geom) +getgeomtype(geom::AbstractGeometry)::OGRwkbGeometryType = _geomtype(geom) """ geomname(geom::AbstractGeometry) Fetch WKT name for geometry type. """ -geomname(geom::AbstractGeometry) = GDAL.ogr_g_getgeometryname(geom.ptr) +geomname(geom::AbstractGeometry)::String = GDAL.ogr_g_getgeometryname(geom.ptr) """ flattento2d!(geom::AbstractGeometry) Convert geometry to strictly 2D. """ -function flattento2d!(geom::AbstractGeometry) +function flattento2d!(geom::G)::G where {G<:AbstractGeometry} GDAL.ogr_g_flattento2d(geom.ptr) return geom end @@ -325,7 +345,7 @@ Force rings to be closed. If this geometry, or any contained geometries has polygon rings that are not closed, they will be closed by adding the starting point at the end. """ -function closerings!(geom::AbstractGeometry) +function closerings!(geom::G)::G where {G<:AbstractGeometry} GDAL.ogr_g_closerings(geom.ptr) return geom end @@ -342,46 +362,50 @@ geometries supported by this parser, but they are too numerous to list here. The following GML2 elements are parsed : Point, LineString, Polygon, MultiPoint, MultiLineString, MultiPolygon, MultiGeometry. """ -fromGML(data) = IGeometry(GDAL.ogr_g_createfromgml(data)) +fromGML(data)::IGeometry = IGeometry(GDAL.ogr_g_createfromgml(data)) -unsafe_fromGML(data) = Geometry(GDAL.ogr_g_createfromgml(data)) +unsafe_fromGML(data)::Geometry = Geometry(GDAL.ogr_g_createfromgml(data)) """ toGML(geom::AbstractGeometry) Convert a geometry into GML format. """ -toGML(geom::AbstractGeometry) = GDAL.ogr_g_exporttogml(geom.ptr) +toGML(geom::AbstractGeometry)::String = GDAL.ogr_g_exporttogml(geom.ptr) """ toKML(geom::AbstractGeometry, altitudemode = C_NULL) Convert a geometry into KML format. """ -toKML(geom::AbstractGeometry, altitudemode = C_NULL) = +toKML(geom::AbstractGeometry, altitudemode = C_NULL)::String = GDAL.ogr_g_exporttokml(geom.ptr, altitudemode) # ↑ * `altitudemode`: value to write in altitudeMode element, or NULL. """ - toJSON(geom::AbstractGeometry) + toJSON(geom::AbstractGeometry; kwargs...) Convert a geometry into GeoJSON format. -""" -toJSON(geom::AbstractGeometry) = GDAL.ogr_g_exporttojson(geom.ptr) -""" - toJSON(geom::AbstractGeometry, options) - -Convert a geometry into GeoJSON format. + * The following options are supported : + * `COORDINATE_PRECISION=number`: maximum number of figures after decimal + separator to write in coordinates. + * `SIGNIFICANT_FIGURES=number`: maximum number of significant figures. + * + * If COORDINATE_PRECISION is defined, SIGNIFICANT_FIGURES will be ignored if + * specified. + * When none are defined, the default is COORDINATE_PRECISION=15. ### Parameters * `geom`: handle to the geometry. -* `options`: a list of options. ### Returns A GeoJSON fragment or NULL in case of error. """ -toJSON(geom::AbstractGeometry, options) = +toJSON(geom::AbstractGeometry; kwargs...)::String = + GDAL.ogr_g_exporttojsonex(geom.ptr, String["$k=$v" for (k, v) in kwargs]) + +toJSON(geom::AbstractGeometry, options::Vector{String})::String = GDAL.ogr_g_exporttojsonex(geom.ptr, options) """ @@ -389,9 +413,10 @@ toJSON(geom::AbstractGeometry, options) = Create a geometry object from its GeoJSON representation. """ -fromJSON(data::String) = IGeometry(GDAL.ogr_g_creategeometryfromjson(data)) +fromJSON(data::String)::IGeometry = + IGeometry(GDAL.ogr_g_creategeometryfromjson(data)) -unsafe_fromJSON(data::String) = +unsafe_fromJSON(data::String)::Geometry = Geometry(GDAL.ogr_g_creategeometryfromjson(data)) # """ @@ -420,27 +445,27 @@ Returns a clone of the spatial reference system for the geometry. (The original SRS may be shared with many objects, and should not be modified.) """ -function getspatialref(geom::AbstractGeometry) +function getspatialref(geom::AbstractGeometry)::ISpatialRef if geom.ptr == C_NULL return ISpatialRef() end result = GDAL.ogr_g_getspatialreference(geom.ptr) - if result == C_NULL - return ISpatialRef() + return if result == C_NULL + ISpatialRef() else - return ISpatialRef(GDAL.osrclone(result)) + ISpatialRef(GDAL.osrclone(result)) end end -function unsafe_getspatialref(geom::AbstractGeometry) +function unsafe_getspatialref(geom::AbstractGeometry)::SpatialRef if geom.ptr == C_NULL return SpatialRef() end result = GDAL.ogr_g_getspatialreference(geom.ptr) - if result == C_NULL - return SpatialRef() + return if result == C_NULL + SpatialRef() else - return SpatialRef(GDAL.osrclone(result)) + SpatialRef(GDAL.osrclone(result)) end end @@ -453,7 +478,10 @@ Apply arbitrary coordinate transformation to geometry. * `geom`: handle on the geometry to apply the transform to. * `coordtransform`: handle on the transformation to apply. """ -function transform!(geom::AbstractGeometry, coordtransform::CoordTransform) +function transform!( + geom::G, + coordtransform::CoordTransform, +)::G where {G<:AbstractGeometry} result = GDAL.ogr_g_transform(geom.ptr, coordtransform.ptr) @ogrerr result "Failed to transform geometry" return geom @@ -497,10 +525,10 @@ Compute a simplified geometry. * `geom`: the geometry. * `tol`: the distance tolerance for the simplification. """ -simplify(geom::AbstractGeometry, tol::Real) = +simplify(geom::AbstractGeometry, tol::Real)::IGeometry = IGeometry(GDAL.ogr_g_simplify(geom.ptr, tol)) -unsafe_simplify(geom::AbstractGeometry, tol::Real) = +unsafe_simplify(geom::AbstractGeometry, tol::Real)::Geometry = Geometry(GDAL.ogr_g_simplify(geom.ptr, tol)) """ @@ -512,10 +540,10 @@ Simplify the geometry while preserving topology. * `geom`: the geometry. * `tol`: the distance tolerance for the simplification. """ -simplifypreservetopology(geom::AbstractGeometry, tol::Real) = +simplifypreservetopology(geom::AbstractGeometry, tol::Real)::IGeometry = IGeometry(GDAL.ogr_g_simplifypreservetopology(geom.ptr, tol)) -unsafe_simplifypreservetopology(geom::AbstractGeometry, tol::Real) = +unsafe_simplifypreservetopology(geom::AbstractGeometry, tol::Real)::Geometry = Geometry(GDAL.ogr_g_simplifypreservetopology(geom.ptr, tol)) """ @@ -529,14 +557,19 @@ Return a Delaunay triangulation of the vertices of the geometry. * `onlyedges`: if `true`, will return a MULTILINESTRING, otherwise it will return a GEOMETRYCOLLECTION containing triangular POLYGONs. """ -delaunaytriangulation(geom::AbstractGeometry, tol::Real, onlyedges::Bool) = - IGeometry(GDAL.ogr_g_delaunaytriangulation(geom.ptr, tol, onlyedges)) +function delaunaytriangulation( + geom::AbstractGeometry, + tol::Real, + onlyedges::Bool, +)::IGeometry + return IGeometry(GDAL.ogr_g_delaunaytriangulation(geom.ptr, tol, onlyedges)) +end function unsafe_delaunaytriangulation( - geom::AbstractGeometry, - tol::Real, - onlyedges::Bool - ) + geom::AbstractGeometry, + tol::Real, + onlyedges::Bool, +)::Geometry return Geometry(GDAL.ogr_g_delaunaytriangulation(geom.ptr, tol, onlyedges)) end @@ -552,7 +585,7 @@ computation is performed in 2d only * `geom`: the geometry to segmentize * `maxlength`: the maximum distance between 2 points after segmentization """ -function segmentize!(geom::AbstractGeometry, maxlength::Real) +function segmentize!(geom::G, maxlength::Real)::G where {G<:AbstractGeometry} GDAL.ogr_g_segmentize(geom.ptr, maxlength) return geom end @@ -566,7 +599,7 @@ Determines whether two geometries intersect. If GEOS is enabled, then this is done in rigorous fashion otherwise `true` is returned if the envelopes (bounding boxes) of the two geometries overlap. """ -intersects(g1::AbstractGeometry, g2::AbstractGeometry) = +intersects(g1::AbstractGeometry, g2::AbstractGeometry)::Bool = Bool(GDAL.ogr_g_intersects(g1.ptr, g2.ptr)) """ @@ -574,7 +607,7 @@ intersects(g1::AbstractGeometry, g2::AbstractGeometry) = Returns `true` if the geometries are equivalent. """ -equals(g1::AbstractGeometry, g2::AbstractGeometry) = +equals(g1::AbstractGeometry, g2::AbstractGeometry)::Bool = Bool(GDAL.ogr_g_equals(g1.ptr, g2.ptr)) """ @@ -582,7 +615,7 @@ equals(g1::AbstractGeometry, g2::AbstractGeometry) = Returns `true` if the geometries are disjoint. """ -disjoint(g1::AbstractGeometry, g2::AbstractGeometry) = +disjoint(g1::AbstractGeometry, g2::AbstractGeometry)::Bool = Bool(GDAL.ogr_g_disjoint(g1.ptr, g2.ptr)) """ @@ -590,7 +623,7 @@ disjoint(g1::AbstractGeometry, g2::AbstractGeometry) = Returns `true` if the geometries are touching. """ -touches(g1::AbstractGeometry, g2::AbstractGeometry) = +touches(g1::AbstractGeometry, g2::AbstractGeometry)::Bool = Bool(GDAL.ogr_g_touches(g1.ptr, g2.ptr)) """ @@ -598,7 +631,7 @@ touches(g1::AbstractGeometry, g2::AbstractGeometry) = Returns `true` if the geometries are crossing. """ -crosses(g1::AbstractGeometry, g2::AbstractGeometry) = +crosses(g1::AbstractGeometry, g2::AbstractGeometry)::Bool = Bool(GDAL.ogr_g_crosses(g1.ptr, g2.ptr)) """ @@ -606,7 +639,7 @@ crosses(g1::AbstractGeometry, g2::AbstractGeometry) = Returns `true` if g1 is contained within g2. """ -within(g1::AbstractGeometry, g2::AbstractGeometry) = +within(g1::AbstractGeometry, g2::AbstractGeometry)::Bool = Bool(GDAL.ogr_g_within(g1.ptr, g2.ptr)) """ @@ -614,7 +647,7 @@ within(g1::AbstractGeometry, g2::AbstractGeometry) = Returns `true` if g1 contains g2. """ -contains(g1::AbstractGeometry, g2::AbstractGeometry) = +contains(g1::AbstractGeometry, g2::AbstractGeometry)::Bool = Bool(GDAL.ogr_g_contains(g1.ptr, g2.ptr)) """ @@ -622,7 +655,7 @@ contains(g1::AbstractGeometry, g2::AbstractGeometry) = Returns `true` if the geometries overlap. """ -overlaps(g1::AbstractGeometry, g2::AbstractGeometry) = +overlaps(g1::AbstractGeometry, g2::AbstractGeometry)::Bool = Bool(GDAL.ogr_g_overlaps(g1.ptr, g2.ptr)) """ @@ -633,9 +666,10 @@ Returns the boundary of the geometry. A new geometry object is created and returned containing the boundary of the geometry on which the method is invoked. """ -boundary(geom::AbstractGeometry) = IGeometry(GDAL.ogr_g_boundary(geom.ptr)) +boundary(geom::AbstractGeometry)::IGeometry = + IGeometry(GDAL.ogr_g_boundary(geom.ptr)) -unsafe_boundary(geom::AbstractGeometry) = +unsafe_boundary(geom::AbstractGeometry)::Geometry = Geometry(GDAL.ogr_g_boundary(geom.ptr)) """ @@ -646,9 +680,10 @@ Returns the convex hull of the geometry. A new geometry object is created and returned containing the convex hull of the geometry on which the method is invoked. """ -convexhull(geom::AbstractGeometry) = IGeometry(GDAL.ogr_g_convexhull(geom.ptr)) +convexhull(geom::AbstractGeometry)::IGeometry = + IGeometry(GDAL.ogr_g_convexhull(geom.ptr)) -unsafe_convexhull(geom::AbstractGeometry) = +unsafe_convexhull(geom::AbstractGeometry)::Geometry = Geometry(GDAL.ogr_g_convexhull(geom.ptr)) """ @@ -674,11 +709,16 @@ accuracy of the result. * `quadsegs`: the number of segments used to approximate a 90 degree (quadrant) of curvature. """ -buffer(geom::AbstractGeometry, dist::Real, quadsegs::Integer = 30) = +buffer(geom::AbstractGeometry, dist::Real, quadsegs::Integer = 30)::IGeometry = IGeometry(GDAL.ogr_g_buffer(geom.ptr, dist, quadsegs)) -unsafe_buffer(geom::AbstractGeometry, dist::Real, quadsegs::Integer = 30) = - Geometry(GDAL.ogr_g_buffer(geom.ptr, dist, quadsegs)) +function unsafe_buffer( + geom::AbstractGeometry, + dist::Real, + quadsegs::Integer = 30, +)::Geometry + return Geometry(GDAL.ogr_g_buffer(geom.ptr, dist, quadsegs)) +end """ intersection(g1::AbstractGeometry, g2::AbstractGeometry) @@ -690,10 +730,10 @@ Generates a new geometry which is the region of intersection of the two geometries operated on. The OGR_G_Intersects() function can be used to test if two geometries intersect. """ -intersection(g1::AbstractGeometry, g2::AbstractGeometry) = +intersection(g1::AbstractGeometry, g2::AbstractGeometry)::IGeometry = IGeometry(GDAL.ogr_g_intersection(g1.ptr, g2.ptr)) -unsafe_intersection(g1::AbstractGeometry, g2::AbstractGeometry) = +unsafe_intersection(g1::AbstractGeometry, g2::AbstractGeometry)::Geometry = Geometry(GDAL.ogr_g_intersection(g1.ptr, g2.ptr)) """ @@ -701,10 +741,10 @@ unsafe_intersection(g1::AbstractGeometry, g2::AbstractGeometry) = Returns a new geometry representing the union of the geometries. """ -union(g1::AbstractGeometry, g2::AbstractGeometry) = +union(g1::AbstractGeometry, g2::AbstractGeometry)::IGeometry = IGeometry(GDAL.ogr_g_union(g1.ptr, g2.ptr)) -unsafe_union(g1::AbstractGeometry, g2::AbstractGeometry) = +unsafe_union(g1::AbstractGeometry, g2::AbstractGeometry)::Geometry = Geometry(GDAL.ogr_g_union(g1.ptr, g2.ptr)) """ @@ -717,10 +757,10 @@ the current implementation based on GEOS can operate on other geometry types than the types that are supported by SQL/MM-Part 3 : surfaces (polygons) and multisurfaces (multipolygons). """ -pointonsurface(geom::AbstractGeometry) = +pointonsurface(geom::AbstractGeometry)::IGeometry = IGeometry(GDAL.ogr_g_pointonsurface(geom.ptr)) -unsafe_pointonsurface(geom::AbstractGeometry) = +unsafe_pointonsurface(geom::AbstractGeometry)::Geometry = Geometry(GDAL.ogr_g_pointonsurface(geom.ptr)) """ @@ -733,10 +773,10 @@ the other geometry removed. A new geometry representing the difference of the geometries, or NULL if the difference is empty. """ -difference(g1::AbstractGeometry, g2::AbstractGeometry) = +difference(g1::AbstractGeometry, g2::AbstractGeometry)::IGeometry = IGeometry(GDAL.ogr_g_difference(g1.ptr, g2.ptr)) -unsafe_difference(g1::AbstractGeometry, g2::AbstractGeometry) = +unsafe_difference(g1::AbstractGeometry, g2::AbstractGeometry)::Geometry = Geometry(GDAL.ogr_g_difference(g1.ptr, g2.ptr)) """ @@ -745,10 +785,10 @@ unsafe_difference(g1::AbstractGeometry, g2::AbstractGeometry) = Returns a new geometry representing the symmetric difference of the geometries or NULL if the difference is empty or an error occurs. """ -symdifference(g1::AbstractGeometry, g2::AbstractGeometry) = +symdifference(g1::AbstractGeometry, g2::AbstractGeometry)::IGeometry = IGeometry(GDAL.ogr_g_symdifference(g1.ptr, g2.ptr)) -unsafe_symdifference(g1::AbstractGeometry, g2::AbstractGeometry) = +unsafe_symdifference(g1::AbstractGeometry, g2::AbstractGeometry)::Geometry = Geometry(GDAL.ogr_g_symdifference(g1.ptr, g2.ptr)) """ @@ -756,7 +796,7 @@ unsafe_symdifference(g1::AbstractGeometry, g2::AbstractGeometry) = Returns the distance between the geometries or -1 if an error occurs. """ -distance(g1::AbstractGeometry, g2::AbstractGeometry) = +distance(g1::AbstractGeometry, g2::AbstractGeometry)::Float64 = GDAL.ogr_g_distance(g1.ptr, g2.ptr) """ @@ -764,14 +804,14 @@ distance(g1::AbstractGeometry, g2::AbstractGeometry) = Returns the length of the geometry, or 0.0 for unsupported geometry types. """ -geomlength(geom::AbstractGeometry) = GDAL.ogr_g_length(geom.ptr) +geomlength(geom::AbstractGeometry)::Float64 = GDAL.ogr_g_length(geom.ptr) """ geomarea(geom::AbstractGeometry) Returns the area of the geometry or 0.0 for unsupported geometry types. """ -geomarea(geom::AbstractGeometry) = GDAL.ogr_g_area(geom.ptr) +geomarea(geom::AbstractGeometry)::Float64 = GDAL.ogr_g_area(geom.ptr) """ centroid!(geom::AbstractGeometry, centroid::AbstractGeometry) @@ -787,7 +827,10 @@ multipoint, linestring, geometrycollection such as multipolygons. OGC SF SQL 1.1 defines the operation for surfaces (polygons). SQL/MM-Part 3 defines the operation for surfaces and multisurfaces (multipolygons). """ -function centroid!(geom::AbstractGeometry, centroid::AbstractGeometry) +function centroid!( + geom::AbstractGeometry, + centroid::G, +)::G where {G<:AbstractGeometry} result = GDAL.ogr_g_centroid(geom.ptr, centroid.ptr) @ogrerr result "Failed to compute the geometry centroid" return centroid @@ -806,13 +849,13 @@ multipoint, linestring, geometrycollection such as multipolygons. OGC SF SQL 1.1 defines the operation for surfaces (polygons). SQL/MM-Part 3 defines the operation for surfaces and multisurfaces (multipolygons).) """ -function centroid(geom::AbstractGeometry) +function centroid(geom::AbstractGeometry)::IGeometry point = createpoint() centroid!(geom, point) return point end -function unsafe_centroid(geom::AbstractGeometry) +function unsafe_centroid(geom::AbstractGeometry)::Geometry point = unsafe_createpoint() centroid!(geom, point) return point @@ -831,10 +874,10 @@ Fetch point at given distance along curve. ### Returns a point or NULL. """ -pointalongline(geom::AbstractGeometry, distance::Real) = +pointalongline(geom::AbstractGeometry, distance::Real)::IGeometry = IGeometry(GDAL.ogr_g_value(geom.ptr, distance)) -unsafe_pointalongline(geom::AbstractGeometry, distance::Real) = +unsafe_pointalongline(geom::AbstractGeometry, distance::Real)::Geometry = Geometry(GDAL.ogr_g_value(geom.ptr, distance)) """ @@ -845,7 +888,7 @@ Clear geometry information. This restores the geometry to its initial state after construction, and before assignment of actual geometry. """ -function empty!(geom::AbstractGeometry) +function empty!(geom::G)::G where {G<:AbstractGeometry} GDAL.ogr_g_empty(geom.ptr) return geom end @@ -855,28 +898,28 @@ end Returns `true` if the geometry has no points, otherwise `false`. """ -isempty(geom::AbstractGeometry) = Bool(GDAL.ogr_g_isempty(geom.ptr)) +isempty(geom::AbstractGeometry)::Bool = Bool(GDAL.ogr_g_isempty(geom.ptr)) """ isvalid(geom::AbstractGeometry) Returns `true` if the geometry is valid, otherwise `false`. """ -isvalid(geom::AbstractGeometry) = Bool(GDAL.ogr_g_isvalid(geom.ptr)) +isvalid(geom::AbstractGeometry)::Bool = Bool(GDAL.ogr_g_isvalid(geom.ptr)) """ issimple(geom::AbstractGeometry) Returns `true` if the geometry is simple, otherwise `false`. """ -issimple(geom::AbstractGeometry) = Bool(GDAL.ogr_g_issimple(geom.ptr)) +issimple(geom::AbstractGeometry)::Bool = Bool(GDAL.ogr_g_issimple(geom.ptr)) """ isring(geom::AbstractGeometry) Returns `true` if the geometry is a ring, otherwise `false`. """ -isring(geom::AbstractGeometry) = Bool(GDAL.ogr_g_isring(geom.ptr)) +isring(geom::AbstractGeometry)::Bool = Bool(GDAL.ogr_g_isring(geom.ptr)) """ polygonize(geom::AbstractGeometry) @@ -888,9 +931,10 @@ reassembled Polygons: NULL will be returned if the input collection doesn't correspond to a MultiLinestring, or when reassembling Edges into Polygons is impossible due to topological inconsistencies. """ -polygonize(geom::AbstractGeometry) = IGeometry(GDAL.ogr_g_polygonize(geom.ptr)) +polygonize(geom::AbstractGeometry)::IGeometry = + IGeometry(GDAL.ogr_g_polygonize(geom.ptr)) -unsafe_polygonize(geom::AbstractGeometry) = +unsafe_polygonize(geom::AbstractGeometry)::Geometry = Geometry(GDAL.ogr_g_polygonize(geom.ptr)) # """ @@ -923,27 +967,26 @@ unsafe_polygonize(geom::AbstractGeometry) = # nZStride) # end - """ getx(geom::AbstractGeometry, i::Integer) Fetch the x coordinate of a point from a geometry, at index i. """ -getx(geom::AbstractGeometry, i::Integer) = GDAL.ogr_g_getx(geom.ptr, i) +getx(geom::AbstractGeometry, i::Integer)::Float64 = GDAL.ogr_g_getx(geom.ptr, i) """ gety(geom::AbstractGeometry, i::Integer) Fetch the y coordinate of a point from a geometry, at index i. """ -gety(geom::AbstractGeometry, i::Integer) = GDAL.ogr_g_gety(geom.ptr, i) +gety(geom::AbstractGeometry, i::Integer)::Float64 = GDAL.ogr_g_gety(geom.ptr, i) """ getz(geom::AbstractGeometry, i::Integer) Fetch the z coordinate of a point from a geometry, at index i. """ -getz(geom::AbstractGeometry, i::Integer) = GDAL.ogr_g_getz(geom.ptr, i) +getz(geom::AbstractGeometry, i::Integer)::Float64 = GDAL.ogr_g_getz(geom.ptr, i) """ getpoint(geom::AbstractGeometry, i::Integer) @@ -953,8 +996,8 @@ Fetch a point in line string or a point geometry, at index i. ### Parameters * `i`: the vertex to fetch, from 0 to getNumPoints()-1, zero for a point. """ -getpoint(geom::AbstractGeometry, i::Integer) = - getpoint!(geom, i, Ref{Cdouble}(), Ref{Cdouble}(), Ref{Cdouble}()) +getpoint(geom::AbstractGeometry, i::Integer)::Tuple{Float64,Float64,Float64} = + getpoint!(geom, i, Ref{Float64}(), Ref{Float64}(), Ref{Float64}()) function getpoint!(geom::AbstractGeometry, i::Integer, x, y, z) GDAL.ogr_g_getpoint(geom.ptr, i, x, y, z) @@ -970,7 +1013,7 @@ Set number of points in a geometry. * `geom`: the geometry. * `n`: the new number of points for geometry. """ -function setpointcount!(geom::AbstractGeometry, n::Integer) +function setpointcount!(geom::G, n::Integer)::G where {G<:AbstractGeometry} GDAL.ogr_g_setpointcount(geom.ptr, n) return geom end @@ -991,17 +1034,22 @@ Set the location of a vertex in a point or linestring geometry. function setpoint! end function setpoint!( - geom::AbstractGeometry, - i::Integer, - x::Real, - y::Real, - z::Real - ) + geom::G, + i::Integer, + x::Real, + y::Real, + z::Real, +)::G where {G<:AbstractGeometry} GDAL.ogr_g_setpoint(geom.ptr, i, x, y, z) return geom end -function setpoint!(geom::AbstractGeometry, i::Integer, x::Real, y::Real) +function setpoint!( + geom::G, + i::Integer, + x::Real, + y::Real, +)::G where {G<:AbstractGeometry} GDAL.ogr_g_setpoint_2d(geom.ptr, i, x, y) return geom end @@ -1020,12 +1068,17 @@ Add a point to a geometry (line string or point). """ function addpoint! end -function addpoint!(geom::AbstractGeometry, x::Real, y::Real, z::Real) +function addpoint!( + geom::G, + x::Real, + y::Real, + z::Real, +)::G where {G<:AbstractGeometry} GDAL.ogr_g_addpoint(geom.ptr, x, y, z) return geom end -function addpoint!(geom::AbstractGeometry, x::Real, y::Real) +function addpoint!(geom::G, x::Real, y::Real)::G where {G<:AbstractGeometry} GDAL.ogr_g_addpoint_2d(geom.ptr, x, y) return geom end @@ -1058,7 +1111,6 @@ end # nYStride,pabyZ,nZStride) # end - """ ngeom(geom::AbstractGeometry) @@ -1072,9 +1124,9 @@ This corresponds to wkbGeometryCollection[25D], and * `0` for other geometry types. """ -function ngeom(geom::AbstractGeometry) +function ngeom(geom::AbstractGeometry)::Integer n = GDAL.ogr_g_getpointcount(geom.ptr) - n == 0 ? GDAL.ogr_g_getgeometrycount(geom.ptr) : n + return n == 0 ? GDAL.ogr_g_getgeometrycount(geom.ptr) : n end """ @@ -1089,13 +1141,13 @@ For a polygon, `getgeom(polygon,i)` returns the exterior ring if * `geom`: the geometry container from which to get a geometry from. * `i`: index of the geometry to fetch, between 0 and getNumGeometries() - 1. """ -function getgeom(geom::AbstractGeometry, i::Integer) +function getgeom(geom::AbstractGeometry, i::Integer)::IGeometry # NOTE(yeesian): GDAL.ogr_g_getgeometryref(geom, i) returns an handle to a # geometry within the container. The returned geometry remains owned by the # container, and should not be modified. The handle is only valid until the # next change to the geometry container. Use OGR_G_Clone() to make a copy. if geom.ptr == C_NULL - return Geometry() + return IGeometry() end result = GDAL.ogr_g_getgeometryref(geom.ptr, i) if result == C_NULL @@ -1105,7 +1157,7 @@ function getgeom(geom::AbstractGeometry, i::Integer) end end -function unsafe_getgeom(geom::AbstractGeometry, i::Integer) +function unsafe_getgeom(geom::AbstractGeometry, i::Integer)::Geometry # NOTE(yeesian): GDAL.ogr_g_getgeometryref(geom, i) returns an handle to a # geometry within the container. The returned geometry remains owned by the # container, and should not be modified. The handle is only valid until the @@ -1138,7 +1190,10 @@ interior rings. * `geomcontainer`: existing geometry. * `subgeom`: geometry to add to the existing geometry. """ -function addgeom!(geomcontainer::AbstractGeometry, subgeom::AbstractGeometry) +function addgeom!( + geomcontainer::G, + subgeom::AbstractGeometry, +)::G where {G<:AbstractGeometry} result = GDAL.ogr_g_addgeometry(geomcontainer.ptr, subgeom.ptr) @ogrerr result "Failed to add geometry. The geometry type could be illegal" return geomcontainer @@ -1181,7 +1236,11 @@ Remove a geometry from an exiting geometry container. The default is `true` as the existing geometry is considered to own the geometries in it. """ -function removegeom!(geom::AbstractGeometry, i::Integer, todelete::Bool = true) +function removegeom!( + geom::G, + i::Integer, + todelete::Bool = true, +)::G where {G<:AbstractGeometry} result = GDAL.ogr_g_removegeometry(geom.ptr, i, todelete) @ogrerr result "Failed to remove geometry. The index could be out of range." return geom @@ -1198,7 +1257,10 @@ Remove all geometries from an exiting geometry container. The default is `true` as the existing geometry is considered to own the geometries in it. """ -function removeallgeoms!(geom::AbstractGeometry, todelete::Bool = true) +function removeallgeoms!( + geom::G, + todelete::Bool = true, +)::G where {G<:AbstractGeometry} result = GDAL.ogr_g_removegeometry(geom.ptr, -1, todelete) @ogrerr result "Failed to remove all geometries." return geom @@ -1214,7 +1276,7 @@ Returns if this geometry is or has curve geometry. * `nonlinear`: set it to `true` to check if the geometry is or contains a CIRCULARSTRING. """ -hascurvegeom(geom::AbstractGeometry, nonlinear::Bool) = +hascurvegeom(geom::AbstractGeometry, nonlinear::Bool)::Bool = Bool(GDAL.ogr_g_hascurvegeometry(geom.ptr, nonlinear)) """ @@ -1232,20 +1294,40 @@ MULTICURVE or MULTISURFACE in it, by approximating curve geometries. * `options`: options as a null-terminated list of strings or NULL. See OGRGeometryFactory::curveToLineString() for valid options. """ -lineargeom(geom::AbstractGeometry, stepsize::Real = 0) = - IGeometry(GDAL.ogr_g_getlineargeometry(geom.ptr, stepsize, C_NULL)) -unsafe_lineargeom(geom::AbstractGeometry, stepsize::Real = 0) = - Geometry(GDAL.ogr_g_getlineargeometry(geom.ptr, stepsize, C_NULL)) - -lineargeom(geom::AbstractGeometry, options::Vector, stepsize::Real = 0) = - IGeometry(GDAL.ogr_g_getlineargeometry(geom.ptr, stepsize, options)) +function lineargeom( + geom::AbstractGeometry, + stepsize::Real = 0; + kwargs..., +)::IGeometry + return lineargeom(geom, String["$k=$v" for (k, v) in kwargs], stepsize) +end function unsafe_lineargeom( - geom::AbstractGeometry, - options::Vector, - stepsize::Real = 0 + geom::AbstractGeometry, + stepsize::Real = 0; + kwargs..., +)::Geometry + return unsafe_lineargeom( + geom, + String["$k=$v" for (k, v) in kwargs], + stepsize, ) +end + +function lineargeom( + geom::AbstractGeometry, + options::Vector{String}, + stepsize::Real = 0, +)::IGeometry + return IGeometry(GDAL.ogr_g_getlineargeometry(geom.ptr, stepsize, options)) +end + +function unsafe_lineargeom( + geom::AbstractGeometry, + options::Vector{String}, + stepsize::Real = 0, +)::Geometry return Geometry(GDAL.ogr_g_getlineargeometry(geom.ptr, stepsize, options)) end @@ -1262,14 +1344,15 @@ If the geometry has no curve portion, the returned geometry will be a clone. The reverse function is OGR_G_GetLinearGeometry(). """ -curvegeom(geom::AbstractGeometry) = +curvegeom(geom::AbstractGeometry)::IGeometry = IGeometry(GDAL.ogr_g_getcurvegeometry(geom.ptr, C_NULL)) -unsafe_curvegeom(geom::AbstractGeometry) = +unsafe_curvegeom(geom::AbstractGeometry)::Geometry = Geometry(GDAL.ogr_g_getcurvegeometry(geom.ptr, C_NULL)) """ - polygonfromedges(lines::AbstractGeometry, tol::Real; besteffort = false, autoclose = false) + polygonfromedges(lines::AbstractGeometry, tol::Real; besteffort = false, + autoclose = false) Build a ring from a bunch of arcs. @@ -1284,27 +1367,37 @@ Build a ring from a bunch of arcs. points of the ring are the same. (defaults to `false`) """ function polygonfromedges( - lines::AbstractGeometry, - tol::Real; - besteffort::Bool = false, - autoclose::Bool = false - ) + lines::AbstractGeometry, + tol::Real; + besteffort::Bool = false, + autoclose::Bool = false, +)::IGeometry perr = Ref{GDAL.OGRErr}() - result = GDAL.ogrbuildpolygonfromedges(lines.ptr, besteffort, autoclose, - tol, perr) + result = GDAL.ogrbuildpolygonfromedges( + lines.ptr, + besteffort, + autoclose, + tol, + perr, + ) @ogrerr perr[] "Failed to build polygon from edges." return IGeometry(result) end function unsafe_polygonfromedges( - lines::AbstractGeometry, - tol::Real; - besteffort::Bool = false, - autoclose::Bool = false - ) + lines::AbstractGeometry, + tol::Real; + besteffort::Bool = false, + autoclose::Bool = false, +)::Geometry perr = Ref{GDAL.OGRErr}() - result = GDAL.ogrbuildpolygonfromedges(lines.ptr, besteffort, autoclose, - tol, perr) + result = GDAL.ogrbuildpolygonfromedges( + lines.ptr, + besteffort, + autoclose, + tol, + perr, + ) @ogrerr perr[] "Failed to build polygon from edges." return Geometry(result) end @@ -1333,38 +1426,45 @@ other libraries or applications. ### Returns a point or NULL. """ -setnonlineargeomflag!(flag::Bool) = +function setnonlineargeomflag!(flag::Bool)::Nothing GDAL.ogrsetnonlineargeometriesenabledflag(flag) + return nothing +end """ getnonlineargeomflag() Get flag to enable/disable returning non-linear geometries in the C API. """ -getnonlineargeomflag() = Bool(GDAL.ogrgetnonlineargeometriesenabledflag()) - -for (geom, wkbgeom) in ((:geomcollection, GDAL.wkbGeometryCollection), - (:linestring, GDAL.wkbLineString), - (:linearring, GDAL.wkbLinearRing), - (:multilinestring, GDAL.wkbMultiLineString), - (:multipoint, GDAL.wkbMultiPoint), - (:multipolygon, GDAL.wkbMultiPolygon), - (:multipolygon_noholes, GDAL.wkbMultiPolygon), - (:point, GDAL.wkbPoint), - (:polygon, GDAL.wkbPolygon)) - eval(quote - $(Symbol("create$geom"))() = creategeom($wkbgeom) - $(Symbol("unsafe_create$geom"))() = unsafe_creategeom($wkbgeom) - end) +getnonlineargeomflag()::Bool = Bool(GDAL.ogrgetnonlineargeometriesenabledflag()) + +for (geom, wkbgeom) in ( + (:geomcollection, wkbGeometryCollection), + (:linestring, wkbLineString), + (:linearring, wkbLinearRing), + (:multilinestring, wkbMultiLineString), + (:multipoint, wkbMultiPoint), + (:multipolygon, wkbMultiPolygon), + (:multipolygon_noholes, wkbMultiPolygon), + (:point, wkbPoint), + (:polygon, wkbPolygon), +) + eval( + quote + $(Symbol("create$geom"))()::IGeometry = creategeom($wkbgeom) + $(Symbol("unsafe_create$geom"))()::Geometry = + unsafe_creategeom($wkbgeom) + end, + ) end -for f in (:create, :unsafe_create) +for (f, rt) in ((:create, :IGeometry), (:unsafe_create, :Geometry)) for (args, typedargs) in ( - ((:x,:y), (:(x::Real),:(y::Real))), - ((:x,:y,:z), (:(x::Real),:(y::Real),:(z::Real))) - ) + ((:x, :y), (:(x::Real), :(y::Real))), + ((:x, :y, :z), (:(x::Real), :(y::Real), :(z::Real))), + ) eval(quote - function $(Symbol("$(f)point"))($(typedargs...)) + function $(Symbol("$(f)point"))($(typedargs...))::$rt geom = $(Symbol("$(f)point"))() addpoint!(geom, $(args...)) return geom @@ -1373,15 +1473,19 @@ for f in (:create, :unsafe_create) end for (args, typedargs) in ( - ((:xs,:ys), (:(xs::Vector{Cdouble}), - :(ys::Vector{Cdouble}))), - ((:xs,:ys,:zs), (:(xs::Vector{Cdouble}), - :(ys::Vector{Cdouble}), - :(zs::Vector{Cdouble}))) - ) + ((:xs, :ys), (:(xs::Vector{Cdouble}), :(ys::Vector{Cdouble}))), + ( + (:xs, :ys, :zs), + ( + :(xs::Vector{Cdouble}), + :(ys::Vector{Cdouble}), + :(zs::Vector{Cdouble}), + ), + ), + ) for geom in (:linestring, :linearring) eval(quote - function $(Symbol("$f$geom"))($(typedargs...)) + function $(Symbol("$f$geom"))($(typedargs...))::$rt geom = $(Symbol("$f$geom"))() for pt in zip($(args...)) addpoint!(geom, pt...) @@ -1391,44 +1495,48 @@ for f in (:create, :unsafe_create) end) end - for (geom,component) in ((:polygon, :linearring),) - eval(quote - function $(Symbol("$f$geom"))($(typedargs...)) - geom = $(Symbol("$f$geom"))() - subgeom = $(Symbol("unsafe_create$component"))($(args...)) - result = GDAL.ogr_g_addgeometrydirectly( - geom.ptr, - subgeom.ptr - ) - @ogrerr result "Failed to add $component." - return geom - end - end) - end - - for (geom,component) in ((:multipoint, :point),) - eval(quote - function $(Symbol("$f$geom"))($(typedargs...)) - geom = $(Symbol("$f$geom"))() - for pt in zip($(args...)) - subgeom = $(Symbol("unsafe_create$component"))(pt) + for (geom, component) in ((:polygon, :linearring),) + eval( + quote + function $(Symbol("$f$geom"))($(typedargs...))::$rt + geom = $(Symbol("$f$geom"))() + subgeom = + $(Symbol("unsafe_create$component"))($(args...)) result = GDAL.ogr_g_addgeometrydirectly( geom.ptr, - subgeom.ptr + subgeom.ptr, ) - @ogrerr result "Failed to add point." + @ogrerr result "Failed to add $component." + return geom end - return geom - end - end) + end, + ) + end + + for (geom, component) in ((:multipoint, :point),) + eval( + quote + function $(Symbol("$f$geom"))($(typedargs...))::$rt + geom = $(Symbol("$f$geom"))() + for pt in zip($(args...)) + subgeom = $(Symbol("unsafe_create$component"))(pt) + result = GDAL.ogr_g_addgeometrydirectly( + geom.ptr, + subgeom.ptr, + ) + @ogrerr result "Failed to add point." + end + return geom + end + end, + ) end end - for typeargs in (Vector{<:Real}, - Tuple{<:Real,<:Real}, - Tuple{<:Real,<:Real,<:Real}) + for typeargs in + (Vector{<:Real}, Tuple{<:Real,<:Real}, Tuple{<:Real,<:Real,<:Real}) eval(quote - function $(Symbol("$(f)point"))(coords::$typeargs) + function $(Symbol("$(f)point"))(coords::$typeargs)::$rt geom = $(Symbol("$(f)point"))() addpoint!(geom, coords...) return geom @@ -1436,12 +1544,14 @@ for f in (:create, :unsafe_create) end) end - for typeargs in (Vector{Tuple{Cdouble,Cdouble}}, - Vector{Tuple{Cdouble,Cdouble,Cdouble}}, - Vector{Vector{Cdouble}}) + for typeargs in ( + Vector{Tuple{Cdouble,Cdouble}}, + Vector{Tuple{Cdouble,Cdouble,Cdouble}}, + Vector{Vector{Cdouble}}, + ) for geom in (:linestring, :linearring) eval(quote - function $(Symbol("$f$geom"))(coords::$typeargs) + function $(Symbol("$f$geom"))(coords::$typeargs)::$rt geom = $(Symbol("$f$geom"))() for coord in coords addpoint!(geom, coord...) @@ -1451,55 +1561,72 @@ for f in (:create, :unsafe_create) end) end - for (geom,component) in ((:polygon, :linearring),) - eval(quote - function $(Symbol("$f$geom"))(coords::$typeargs) - geom = $(Symbol("$f$geom"))() - subgeom = $(Symbol("unsafe_create$component"))(coords) - result = GDAL.ogr_g_addgeometrydirectly( - geom.ptr, - subgeom.ptr - ) - @ogrerr result "Failed to add $component." - return geom - end - end) - end - end - - for (variants,typeargs) in ( - (((:multipoint, :point),), - (Vector{Tuple{Cdouble,Cdouble}}, - Vector{Tuple{Cdouble,Cdouble,Cdouble}}, - Vector{Vector{Cdouble}})), - - (((:polygon, :linearring), - (:multilinestring, :linestring), - (:multipolygon_noholes, :polygon)), - (Vector{Vector{Tuple{Cdouble,Cdouble}}}, - Vector{Vector{Tuple{Cdouble,Cdouble,Cdouble}}}, - Vector{Vector{Vector{Cdouble}}})), - - (((:multipolygon, :polygon),), - (Vector{Vector{Vector{Tuple{Cdouble,Cdouble}}}}, - Vector{Vector{Vector{Tuple{Cdouble,Cdouble,Cdouble}}}}, - Vector{Vector{Vector{Vector{Cdouble}}}})) - ) - for typearg in typeargs, (geom, component) in variants - eval(quote - function $(Symbol("$f$geom"))(coords::$typearg) - geom = $(Symbol("$f$geom"))() - for coord in coords - subgeom = $(Symbol("unsafe_create$component"))(coord) + for (geom, component) in ((:polygon, :linearring),) + eval( + quote + function $(Symbol("$f$geom"))(coords::$typeargs)::$rt + geom = $(Symbol("$f$geom"))() + subgeom = $(Symbol("unsafe_create$component"))(coords) result = GDAL.ogr_g_addgeometrydirectly( geom.ptr, - subgeom.ptr + subgeom.ptr, ) @ogrerr result "Failed to add $component." + return geom end - return geom - end - end) + end, + ) + end + end + + for (variants, typeargs) in ( + ( + ((:multipoint, :point),), + ( + Vector{Tuple{Cdouble,Cdouble}}, + Vector{Tuple{Cdouble,Cdouble,Cdouble}}, + Vector{Vector{Cdouble}}, + ), + ), + ( + ( + (:polygon, :linearring), + (:multilinestring, :linestring), + (:multipolygon_noholes, :polygon), + ), + ( + Vector{Vector{Tuple{Cdouble,Cdouble}}}, + Vector{Vector{Tuple{Cdouble,Cdouble,Cdouble}}}, + Vector{Vector{Vector{Cdouble}}}, + ), + ), + ( + ((:multipolygon, :polygon),), + ( + Vector{Vector{Vector{Tuple{Cdouble,Cdouble}}}}, + Vector{Vector{Vector{Tuple{Cdouble,Cdouble,Cdouble}}}}, + Vector{Vector{Vector{Vector{Cdouble}}}}, + ), + ), + ) + for typearg in typeargs, (geom, component) in variants + eval( + quote + function $(Symbol("$f$geom"))(coords::$typearg)::$rt + geom = $(Symbol("$f$geom"))() + for coord in coords + subgeom = + $(Symbol("unsafe_create$component"))(coord) + result = GDAL.ogr_g_addgeometrydirectly( + geom.ptr, + subgeom.ptr, + ) + @ogrerr result "Failed to add $component." + end + return geom + end + end, + ) end end end diff --git a/src/ogr/styletable.jl b/src/ogr/styletable.jl index 664259af..3f94b9ff 100644 --- a/src/ogr/styletable.jl +++ b/src/ogr/styletable.jl @@ -1,5 +1,5 @@ """ - unsafe_createstylemanager(styletable = GDALStyleTable(C_NULL)) + unsafe_createstylemanager(styletable = C_NULL) OGRStyleMgr factory. @@ -9,8 +9,11 @@ OGRStyleMgr factory. ### Returns an handle to the new style manager object. """ -unsafe_createstylemanager(styletable = GDALStyleTable(C_NULL)) = - StyleManager(GDAL.ogr_sm_create(styletable)) +function unsafe_createstylemanager( + styletable::StyleTable = StyleTable(), +)::StyleManager + return StyleManager(GDAL.ogr_sm_create(styletable.ptr)) +end """ Destroy Style Manager. @@ -18,27 +21,12 @@ Destroy Style Manager. ### Parameters * `stylemanager`: handle to the style manager to destroy. """ -function destroy(sm::StyleManager) +function destroy(sm::StyleManager)::Nothing GDAL.ogr_sm_destroy(sm.ptr) sm.ptr = C_NULL + return nothing end - -""" - initialize!(stylemanager::StyleManager, feature::Feature) - -Initialize style manager from the style string of a feature. - -### Parameters -* `stylemanager`: handle to the style manager. -* `feature`: handle to the new feature from which to read the style. - -### Returns -the style string read from the feature, or NULL in case of error. -""" -initialize!(stylemanager::StyleManager, feature::Feature) = - GDAL.ogr_sm_initfromfeature(stylemanager.ptr, feature.ptr) - """ initialize!(stylemanager::StyleManager, stylestring = C_NULL) @@ -48,11 +36,14 @@ Initialize style manager from the style string. * `stylemanager`: handle to the style manager. * `stylestring`: the style string to use (can be NULL). ### Returns -`true` on success, `false` on errors. +`true` on success, `false` on error. """ -initialize!(stylemanager::StyleManager, stylestring = C_NULL) = +initialize!(stylemanager::StyleManager, stylestring::String)::Bool = Bool(GDAL.ogr_sm_initstylestring(stylemanager.ptr, stylestring)) +initialize!(stylemanager::StyleManager)::Bool = + Bool(GDAL.ogr_sm_initstylestring(stylemanager.ptr, C_NULL)) + """ npart(stylemanager::StyleManager) npart(stylemanager::StyleManager, stylestring::AbstractString) @@ -69,14 +60,15 @@ the number of parts (style tools) in the style. """ function npart end -npart(stylemanager::StyleManager) = +npart(stylemanager::StyleManager)::Integer = GDAL.ogr_sm_getpartcount(stylemanager.ptr, C_NULL) -npart(stylemanager::StyleManager, stylestring::AbstractString) = +npart(stylemanager::StyleManager, stylestring::AbstractString)::Integer = GDAL.ogr_sm_getpartcount(stylemanager.ptr, stylestring) """ - unsafe_getpart(stylemanager::StyleManager, id::Integer, stylestring = C_NULL) + unsafe_getpart(stylemanager::StyleManager, id::Integer, + stylestring = C_NULL) Fetch a part (style tool) from the current style. @@ -89,8 +81,13 @@ Fetch a part (style tool) from the current style. ### Returns OGRStyleToolH of the requested part (style tools) or NULL on error. """ -unsafe_getpart(stylemanager::StyleManager, id::Integer, stylestring = C_NULL) = - StyleTool(GDAL.ogr_sm_getpart(stylemanager.ptr, id, stylestring)) +function unsafe_getpart( + stylemanager::StyleManager, + id::Integer, + stylestring = C_NULL, +)::StyleTool + return StyleTool(GDAL.ogr_sm_getpart(stylemanager.ptr, id, stylestring)) +end """ addpart!(stylemanager::StyleManager, styletool::StyleTool) @@ -102,9 +99,9 @@ Add a part (style tool) to the current style. * `styletool`: the style tool defining the part to add. ### Returns -`true` on success, `false` on errors. +`true` on success, `false` on error. """ -addpart!(stylemanager::StyleManager, styletool::StyleTool) = +addpart!(stylemanager::StyleManager, styletool::StyleTool)::Bool = Bool(GDAL.ogr_sm_addpart(stylemanager.ptr, styletool.ptr)) """ @@ -119,17 +116,17 @@ Add a style to the current style table. the style stored in the manager. ### Returns -`true` on success, `false` on errors. +`true` on success, `false` on error. """ function addstyle!( - stylemanager::StyleManager, - stylename::AbstractString, - stylestring::AbstractString - ) + stylemanager::StyleManager, + stylename::AbstractString, + stylestring::AbstractString, +)::Bool return Bool(GDAL.ogr_sm_addstyle(stylemanager.ptr, stylename, stylestring)) end -addstyle!(stylemanager::StyleManager, stylename::AbstractString) = +addstyle!(stylemanager::StyleManager, stylename::AbstractString)::Bool = Bool(GDAL.ogr_sm_addstyle(stylemanager.ptr, stylename, C_NULL)) """ @@ -144,7 +141,7 @@ OGRStyleTool factory. ### Returns an handle to the new style tool object or NULL if the creation failed. """ -unsafe_createstyletool(classid::OGRSTClassId) = +unsafe_createstyletool(classid::OGRSTClassId)::StyleTool = StyleTool(GDAL.ogr_st_create(classid)) """ @@ -153,9 +150,10 @@ Destroy Style Tool. ### Parameters * `styletool`: handle to the style tool to destroy. """ -function destroy(styletool::StyleTool) +function destroy(styletool::StyleTool)::Nothing GDAL.ogr_st_destroy(styletool.ptr) styletool.ptr = C_NULL + return nothing end """ @@ -170,7 +168,7 @@ Determine type of Style Tool. the style tool type, one of OGRSTCPen (1), OGRSTCBrush (2), OGRSTCSymbol (3) or OGRSTCLabel (4). Returns OGRSTCNone (0) if the OGRStyleToolH is invalid. """ -gettype(styletool::StyleTool) = GDAL.ogr_st_gettype(styletool.ptr) +gettype(styletool::StyleTool)::OGRSTClassId = GDAL.ogr_st_gettype(styletool.ptr) """ getunit(styletool::StyleTool) @@ -183,7 +181,7 @@ Get Style Tool units. ### Returns the style tool units. """ -getunit(styletool::StyleTool) = GDAL.ogr_st_getunit(styletool.ptr) +getunit(styletool::StyleTool)::OGRSTUnitId = GDAL.ogr_st_getunit(styletool.ptr) """ setunit!(styletool::StyleTool, newunit::OGRSTUnitId, scale::Real) @@ -195,8 +193,14 @@ Set Style Tool units. * `newunit`: the new unit. * `scale`: ground to paper scale factor. """ -setunit!(styletool::StyleTool, newunit::OGRSTUnitId, scale::Real) = +function setunit!( + styletool::StyleTool, + newunit::OGRSTUnitId, + scale::Real, +)::StyleTool GDAL.ogr_st_setunit(styletool.ptr, newunit, scale) + return styletool +end """ asstring(styletool::StyleTool, id::Integer) @@ -215,10 +219,10 @@ Get Style Tool parameter value as a string. ### Returns the parameter value as a string and sets `nullflag`. """ -asstring(styletool::StyleTool, id::Integer, nullflag::Ref{Cint}) = +asstring(styletool::StyleTool, id::Integer, nullflag::Ref{Cint})::String = GDAL.ogr_st_getparamstr(styletool.ptr, id, nullflag) -asstring(styletool::StyleTool, id::Integer) = +asstring(styletool::StyleTool, id::Integer)::String = asstring(styletool, id, Ref{Cint}(0)) """ @@ -237,8 +241,13 @@ Get Style Tool parameter value as an integer. ### Returns the parameter value as an integer and sets `nullflag`. """ -asint(styletool::StyleTool, id::Integer, nullflag::Ref{Cint} = Ref{Cint}(0)) = - GDAL.ogr_st_getparamnum(styletool.ptr, id, nullflag) +function asint( + styletool::StyleTool, + id::Integer, + nullflag::Ref{Cint} = Ref{Cint}(0), +)::Int32 + return GDAL.ogr_st_getparamnum(styletool.ptr, id, nullflag) +end """ asdouble(styletool::StyleTool, id::Integer, nullflag = Ref{Cint}(0)) @@ -257,10 +266,10 @@ Get Style Tool parameter value as a double. the parameter value as a double and sets `nullflag`. """ function asdouble( - styletool::StyleTool, - id::Integer, - nullflag::Ref{Cint} = Ref{Cint}(0) - ) + styletool::StyleTool, + id::Integer, + nullflag::Ref{Cint} = Ref{Cint}(0), +)::Float64 return GDAL.ogr_st_getparamdbl(styletool.ptr, id, nullflag) end @@ -278,14 +287,24 @@ Set Style Tool parameter value. """ function setparam! end -setparam!(styletool::StyleTool, id::Integer, value::AbstractString) = +function setparam!( + styletool::StyleTool, + id::Integer, + value::AbstractString, +)::StyleTool GDAL.ogr_st_setparamstr(styletool.ptr, id, value) + return styletool +end -setparam!(styletool::StyleTool, id::Integer, value::Integer) = +function setparam!(styletool::StyleTool, id::Integer, value::Integer)::StyleTool GDAL.ogr_st_setparamnum(styletool.ptr, id, value) + return styletool +end -setparam!(styletool::StyleTool, id::Integer, value::Float64) = +function setparam!(styletool::StyleTool, id::Integer, value::Float64)::StyleTool GDAL.ogr_st_setparamdbl(styletool.ptr, id, value) + return styletool +end """ getstylestring(styletool::StyleTool) @@ -298,7 +317,8 @@ Get the style string for this Style Tool. ### Returns the style string for this style tool or "" if the styletool is invalid. """ -getstylestring(styletool::StyleTool) = GDAL.ogr_st_getstylestring(styletool.ptr) +getstylestring(styletool::StyleTool)::String = + GDAL.ogr_st_getstylestring(styletool.ptr) """ toRGBA(styletool::StyleTool, color::AbstractString) @@ -312,13 +332,24 @@ Return the r,g,b,a components of a color encoded in #RRGGBB[AA] format. ### Returns (R,G,B,A) tuple of Cints. """ -function toRGBA(styletool::StyleTool, color::AbstractString) - red = Ref{Cint}(0) - green = Ref{Cint}(0) - blue = Ref{Cint}(0) - alpha = Ref{Cint}(0) - result = Bool(GDAL.ogr_st_getrgbfromstring(styletool.ptr, color, red, green, - blue, alpha)) +function toRGBA( + styletool::StyleTool, + color::AbstractString, +)::Tuple{Int32,Int32,Int32,Int32} + red = Ref{Int32}(0) + green = Ref{Int32}(0) + blue = Ref{Int32}(0) + alpha = Ref{Int32}(0) + result = Bool( + GDAL.ogr_st_getrgbfromstring( + styletool.ptr, + color, + red, + green, + blue, + alpha, + ), + ) result || error("Error in getting RGBA from Styletool") return (red[], green[], blue[], alpha[]) end @@ -331,7 +362,7 @@ OGRStyleTable factory. ### Returns an handle to the new style table object. """ -unsafe_createstyletable() = StyleTable(GDAL.ogr_stbl_create()) +unsafe_createstyletable()::StyleTable = StyleTable(GDAL.ogr_stbl_create()) """ Destroy Style Table. @@ -339,9 +370,10 @@ Destroy Style Table. ### Parameters * `styletable`: handle to the style table to destroy. """ -function destroy(st::StyleTable) +function destroy(st::StyleTable)::Nothing GDAL.ogr_stbl_destroy(st.ptr) st.ptr = C_NULL + return nothing end """ @@ -358,10 +390,10 @@ Add a new style in the table. `true` on success, `false` on error """ function addstyle!( - styletable::StyleTable, - stylename::AbstractString, - stylestring::AbstractString - ) + styletable::StyleTable, + stylename::AbstractString, + stylestring::AbstractString, +)::Bool return Bool(GDAL.ogr_stbl_addstyle(styletable.ptr, stylename, stylestring)) end @@ -377,7 +409,7 @@ Save a style table to a file. ### Returns `true` on success, `false` on error """ -savestyletable(styletable::StyleTable, filename::AbstractString) = +savestyletable(styletable::StyleTable, filename::AbstractString)::Bool = Bool(GDAL.ogr_stbl_savestyletable(styletable.ptr, filename)) """ @@ -392,7 +424,7 @@ Load a style table from a file. ### Returns `true` on success, `false` on error """ -loadstyletable!(styletable::StyleTable, filename::AbstractString) = +loadstyletable!(styletable::StyleTable, filename::AbstractString)::Bool = Bool(GDAL.ogr_stbl_loadstyletable(styletable.ptr, filename)) """ @@ -407,7 +439,7 @@ Get a style string by name. ### Returns the style string matching the name or NULL if not found or error. """ -findstylestring(styletable::StyleTable, name::AbstractString) = +findstylestring(styletable::StyleTable, name::AbstractString)::String = GDAL.ogr_stbl_find(styletable.ptr, name) """ @@ -418,8 +450,10 @@ Reset the next style pointer to 0. ### Parameters * `styletable`: handle to the style table. """ -resetreading!(styletable::StyleTable) = +function resetreading!(styletable::StyleTable)::StyleTable GDAL.ogr_stbl_resetstylestringreading(styletable.ptr) + return styletable +end """ nextstyle(styletable::StyleTable) @@ -432,7 +466,8 @@ Get the next style string from the table. ### Returns the next style string or NULL on error. """ -nextstyle(styletable::StyleTable) = GDAL.ogr_stbl_getnextstyle(styletable.ptr) +nextstyle(styletable::StyleTable)::String = + GDAL.ogr_stbl_getnextstyle(styletable.ptr) """ laststyle(styletable::StyleTable) @@ -445,5 +480,5 @@ Get the style name of the last style string fetched with OGR_STBL_GetNextStyle. ### Returns the Name of the last style string or NULL on error. """ -laststyle(styletable::StyleTable) = +laststyle(styletable::StyleTable)::String = GDAL.ogr_stbl_getlaststylename(styletable.ptr) diff --git a/src/raster/array.jl b/src/raster/array.jl index ca4c8115..b237d401 100644 --- a/src/raster/array.jl +++ b/src/raster/array.jl @@ -2,47 +2,67 @@ import DiskArrays const AllowedXY = Union{Integer,Colon,AbstractRange} const AllowedBand = Union{Integer,Colon,AbstractArray} - """ RasterDataset(dataset::AbstractDataset) -This data structure is returned by the [`ArchGDAL.readraster`](@ref) function and -is a wrapper for a GDAL dataset. This wrapper is to signal the -user that the dataset should be treated as a 3D AbstractArray -where the first two dimensions correspond to longitude and latitude -and the third dimension corresponds to different raster bands. - -As it is a wrapper around a GDAL Dataset, it supports the usual -raster methods for a GDAL Dataset such as `getgeotransform`, -`nraster`, `getband`, `getproj`, `width`, and `height`. As it -is also a subtype of `AbstractDiskArray{T,3}`, it supports the -following additional methods: `readblock!`, `writeblock!`, -`eachchunk`, `haschunks`, etc. -This satisfies the DiskArray interface, allowing us to -be able to index into it like we would an array. - -Constructing a RasterDataset will error if the raster bands do not -have all the same size and a common element data type. +This data structure is returned by the [`ArchGDAL.readraster`](@ref) function +and is a wrapper for a GDAL dataset. This wrapper is to signal the user that the +dataset should be treated as a 3D AbstractArray where the first two dimensions +correspond to longitude and latitude and the third dimension corresponds to +different raster bands. + +As it is a wrapper around a GDAL Dataset, it supports the usual raster methods +for a GDAL Dataset such as `getgeotransform`, `nraster`, `getband`, `getproj`, +`width`, and `height`. As it is also a subtype of `AbstractDiskArray{T,3}`, it +supports the following additional methods: `readblock!`, `writeblock!`, +`eachchunk`, `haschunks`, etc. This satisfies the DiskArray interface, allowing +us to be able to index into it like we would an array. + +Constructing a RasterDataset will error if the raster bands do not have all the +same size and a common element data type. """ -struct RasterDataset{T,DS} <: AbstractDiskArray{T,3} +struct RasterDataset{T,DS<:AbstractDataset} <: AbstractDiskArray{T,3} ds::DS size::Tuple{Int,Int,Int} end -function RasterDataset(ds::AbstractDataset) - iszero(nraster(ds)) && throw(ArgumentError("The Dataset does not contain any raster bands")) +function RasterDataset( + ds::AbstractDataset, +)::RasterDataset{pixeltype(ds),typeof(ds)} + if iszero(nraster(ds)) + throw(ArgumentError("The Dataset does not contain any raster bands")) + end s = _common_size(ds) - RasterDataset{_dataset_type(ds), typeof(ds)}(ds, s) + return RasterDataset{pixeltype(ds),typeof(ds)}(ds, s) end # Forward a few functions # Here we try to include all functions that are relevant # for raster-like datasets. -for f in (:getgeotransform, :nraster, :getband, :getproj, - :width, :height, :destroy, :getdriver, :filelist, :listcapability, - :ngcp, :copy, :write, :testcapability, :setproj!, :buildoverviews!, - :metadata, :metadatadomainlist) - eval(:($(f)(x::RasterDataset, args...; kwargs...) = $(f)(x.ds, args...; kwargs...))) +for f in ( + :getgeotransform, + :nraster, + :getband, + :getproj, + :width, + :height, + :destroy, + :getdriver, + :filelist, + :listcapability, + :ngcp, + :copy, + :unsafe_copy, + :write, + :testcapability, + :setproj!, + :buildoverviews!, + :metadata, + :metadatadomainlist, +) + eval(:(function $(f)(x::RasterDataset, args...; kwargs...) + return $(f)(x.ds, args...; kwargs...) + end)) end # Here we need to special-case, to avoid a method ambiguity @@ -51,22 +71,31 @@ function metadataitem(obj::RasterDataset, name::AbstractString; kwargs...) end # Here we need to special-case, because source and dest might be rasters -copywholeraster(x::RasterDataset,y::AbstractDataset;kwargs...) = copywholeraster(x.ds,y;kwargs...) -copywholeraster(x::RasterDataset,y::RasterDataset;kwargs...) = copywholeraster(x.ds,y.ds;kwargs...) -copywholeraster(x::AbstractDataset,y::RasterDataset;kwargs...) = copywholeraster(x.ds,y.ds;kwargs...) +function copywholeraster!( + source::RasterDataset, + dest::D; + kwargs..., +)::D where {D<:AbstractDataset} + copywholeraster!(source.ds, dest; kwargs...) + return dest +end -""" - _dataset_type(ds::AbstractDataset) +function copywholeraster!( + source::RasterDataset, + dest::RasterDataset; + kwargs..., +)::RasterDataset + copywholeraster!(source, dest.ds; kwargs...) + return dest +end -Tries to determine a common dataset type for all the bands -in a raster dataset. -""" -function _dataset_type(ds::AbstractDataset) - alldatatypes = map(1:nraster(ds)) do i - b = getband(ds, i) - pixeltype(b) - end - reduce(promote_type, alldatatypes) +function copywholeraster!( + source::AbstractDataset, + dest::RasterDataset; + kwargs..., +)::RasterDataset + copywholeraster!(source, dest.ds; kwargs...) + return dest end """ @@ -79,76 +108,119 @@ function _common_size(ds::AbstractDataset) nr = nraster(ds) allsizes = map(1:nr) do i b = getband(ds, i) - size(b) + return size(b) end s = unique(allsizes) - length(s) == 1 || throw(DimensionMismatch("Can not coerce bands to single dataset, different sizes found")) - Int.((s[1]..., nr)) + if length(s) != 1 + throw( + DimensionMismatch( + "Can not coerce bands to single dataset, different sizes found", + ), + ) + end + return Int.((s[1]..., nr)) end -getband(ds::RasterDataset, i::Integer) = getband(ds.ds, i) -unsafe_readraster(args...; kwargs...) = RasterDataset(unsafe_read(args...; kwargs...)) +getband(ds::RasterDataset, i::Integer)::IRasterBand = getband(ds.ds, i) +unsafe_readraster(args...; kwargs...)::RasterDataset = + RasterDataset(unsafe_read(args...; kwargs...)) """ readraster(s::String; kwargs...) -Opens a GDAL raster dataset. The difference to `ArchGDAL.read` is that -this function returns a `RasterDataset`, which is a subtype of `AbstractDiskArray{T,3}`, -so that users can operate on the array using direct indexing. +Opens a GDAL raster dataset. The difference to `ArchGDAL.read` is that this +function returns a `RasterDataset`, which is a subtype of +`AbstractDiskArray{T,3}`, so that users can operate on the array using direct +indexing. """ -readraster(s::String; kwargs...) = RasterDataset(read(s; kwargs...)) +readraster(s::String; kwargs...)::RasterDataset = + RasterDataset(read(s; kwargs...)) -function DiskArrays.eachchunk(ds::RasterDataset) +function DiskArrays.eachchunk(ds::RasterDataset)::DiskArrays.GridChunks subchunks = DiskArrays.eachchunk(getband(ds, 1)) - DiskArrays.GridChunks(ds,(subchunks.chunksize..., 1)) + return DiskArrays.GridChunks(ds, (subchunks.chunksize..., 1)) end -DiskArrays.haschunks(::RasterDataset) = DiskArrays.Chunked() -DiskArrays.haschunks(::AbstractRasterBand) = DiskArrays.Chunked() +DiskArrays.haschunks(::RasterDataset)::DiskArrays.Chunked = DiskArrays.Chunked() +DiskArrays.haschunks(::AbstractRasterBand)::DiskArrays.Chunked = + DiskArrays.Chunked() -Base.size(band::AbstractRasterBand) = (width(band), height(band)) +function Base.size(band::AbstractRasterBand) + return (width(band), height(band)) +end -function DiskArrays.eachchunk(band::AbstractRasterBand) +function DiskArrays.eachchunk(band::AbstractRasterBand)::DiskArrays.GridChunks wI = windows(band) cs = (wI.blockiter.xbsize, wI.blockiter.ybsize) - DiskArrays.GridChunks(band, cs) + return DiskArrays.GridChunks(band, cs) end -function DiskArrays.readblock!(band::AbstractRasterBand, buffer, x::AbstractUnitRange, y::AbstractUnitRange) - xoffset, yoffset = first(x)-1, first(y)-1 +function DiskArrays.readblock!( + band::AbstractRasterBand, + buffer::T, + x::AbstractUnitRange, + y::AbstractUnitRange, +)::T where {T<:Any} + xoffset, yoffset = first(x) - 1, first(y) - 1 xsize, ysize = length(x), length(y) read!(band, buffer, xoffset, yoffset, xsize, ysize) + return buffer end -function DiskArrays.writeblock!(band::AbstractRasterBand, value, x::AbstractUnitRange, y::AbstractUnitRange) - xoffset, yoffset = first(x)-1, first(y)-1 +function DiskArrays.writeblock!( + band::AbstractRasterBand, + buffer::T, + x::AbstractUnitRange, + y::AbstractUnitRange, +)::T where {T<:Any} + xoffset, yoffset = first(x) - 1, first(y) - 1 xsize, ysize = length(x), length(y) - write!(band, value, xoffset, yoffset, xsize, ysize) + write!(band, buffer, xoffset, yoffset, xsize, ysize) + return buffer end - # AbstractDataset indexing -Base.size(dataset::RasterDataset) = dataset.size +Base.size(dataset::RasterDataset)::Tuple{Int,Int,Int} = dataset.size -function DiskArrays.readblock!(dataset::RasterDataset, buffer, x::AbstractUnitRange, y::AbstractUnitRange, z::AbstractUnitRange) +function DiskArrays.readblock!( + dataset::RasterDataset, + buffer::T, + x::AbstractUnitRange, + y::AbstractUnitRange, + z::AbstractUnitRange, +)::T where {T<:Any} buffer2 = Array(buffer) DiskArrays.readblock!(dataset::RasterDataset, buffer2, x, y, z) buffer .= buffer2 + return buffer end -function DiskArrays.readblock!(dataset::RasterDataset, buffer::Array, x::AbstractUnitRange, y::AbstractUnitRange, z::AbstractUnitRange) +function DiskArrays.readblock!( + dataset::RasterDataset, + buffer::T, + x::AbstractUnitRange, + y::AbstractUnitRange, + z::AbstractUnitRange, +)::T where {T<:Array} xoffset, yoffset = first.((x, y)) .- 1 - xsize, ysize= length.((x, y)) - indices = [Cint(i) for i in z] - read!(dataset.ds, buffer, indices, xoffset, yoffset, xsize, ysize) + xsize, ysize = length.((x, y)) + read!(dataset.ds, buffer, Cint.(z), xoffset, yoffset, xsize, ysize) + return buffer end -function DiskArrays.writeblock!(dataset::RasterDataset, value, x::AbstractUnitRange, y::AbstractUnitRange, bands::AbstractUnitRange) +function DiskArrays.writeblock!( + dataset::RasterDataset, + buffer::T, + x::AbstractUnitRange, + y::AbstractUnitRange, + bands::AbstractUnitRange, +)::T where {T<:Any} xoffset, yoffset = first.((x, y)) .- 1 - xsize, ysize= length.((x, y)) - indices = [Cint(i) for i in bands] - write!(dataset.ds, value, indices, xoffset, yoffset, xsize, ysize) + xsize, ysize = length.((x, y)) + indices = [Cint(i) for i in bands] + write!(dataset.ds, buffer, indices, xoffset, yoffset, xsize, ysize) + return buffer end -Base.Array(dataset::RasterDataset) = dataset[:,:,:] +Base.Array(dataset::RasterDataset) = dataset[:, :, :] diff --git a/src/raster/colortable.jl b/src/raster/colortable.jl index cd7f9af5..260ffd95 100644 --- a/src/raster/colortable.jl +++ b/src/raster/colortable.jl @@ -3,13 +3,14 @@ Construct a new color table. """ -unsafe_createcolortable(palette::GDALPaletteInterp) = +unsafe_createcolortable(palette::GDALPaletteInterp)::ColorTable = ColorTable(GDAL.gdalcreatecolortable(palette)) "Destroys a color table." -function destroy(ct::ColorTable) +function destroy(ct::ColorTable)::Nothing GDAL.gdaldestroycolortable(ct.ptr) ct.ptr = C_NULL + return nothing end """ @@ -17,7 +18,13 @@ end Make a copy of a color table. """ -unsafe_clone(ct::ColorTable) = ColorTable(GDAL.gdalclonecolortable(ct.ptr)) +function unsafe_clone(ct::ColorTable)::ColorTable + return if ct.ptr == C_NULL + ColorTable(C_NULL) + else + ColorTable(GDAL.gdalclonecolortable(ct.ptr)) + end +end """ paletteinterp(ct::ColorTable) @@ -27,22 +34,8 @@ Fetch palette interpretation. ### Returns palette interpretation enumeration value, usually `GPI_RGB`. """ -paletteinterp(ct::ColorTable) = GDAL.gdalgetpaletteinterpretation(ct.ptr) - -""" - ncolorentry(ct::ColorTable) - -Get number of color entries in table. -""" -ncolorentry(ct::ColorTable) = GDAL.gdalgetcolorentrycount(ct.ptr) - -""" - getcolorentry(ct::ColorTable, i::Integer) - -Fetch a color entry from table. -""" -getcolorentry(ct::ColorTable, i::Integer) = - unsafe_load(GDAL.gdalgetcolorentry(ct.ptr, i)) +paletteinterp(ct::ColorTable)::GDALPaletteInterp = + GDAL.gdalgetpaletteinterpretation(ct.ptr) """ getcolorentryasrgb(ct::ColorTable, i::Integer) @@ -59,7 +52,7 @@ tables. ### Returns `true` on success, or `false` if the conversion isn't supported. """ -function getcolorentryasrgb(ct::ColorTable, i::Integer) +function getcolorentryasrgb(ct::ColorTable, i::Integer)::GDAL.GDALColorEntry colorentry = Ref{GDAL.GDALColorEntry}(GDAL.GDALColorEntry(0, 0, 0, 0)) result = Bool(GDAL.gdalgetcolorentryasrgb(ct.ptr, i, colorentry)) result || @warn("The conversion to RGB isn't supported.") @@ -85,37 +78,3 @@ function setcolorentry!(ct::ColorTable, i::Integer, entry::GDAL.GDALColorEntry) GDAL.gdalsetcolorentry(ct.ptr, i, Ref{GDAL.GDALColorEntry}(entry)) return ct end - -""" - createcolorramp!(ct::ColorTable, startindex, startcolor::GDAL.GDALColorEntry, - endindex, endcolor::GDAL.GDALColorEntry) - -Create color ramp. - -Automatically creates a color ramp from one color entry to another. It can be -called several times to create multiples ramps in the same color table. - -### Parameters -* `startindex` index to start the ramp on the color table [0..255] -* `startcolor` a color entry value to start the ramp -* `endindex` index to end the ramp on the color table [0..255] -* `endcolor` a color entry value to end the ramp - -### Returns -total number of entries, -1 to report error -""" -function createcolorramp!( - ct::ColorTable, - startindex::Integer, - startcolor::GDAL.GDALColorEntry, - endindex::Integer, - endcolor::GDAL.GDALColorEntry - ) - return GDAL.gdalcreatecolorramp( - ct.ptr, - startindex, - Ref{GDAL.GDALColorEntry}(startcolor), - endindex, - Ref{GDAL.GDALColorEntry}(endcolor) - ) -end diff --git a/src/raster/images.jl b/src/raster/images.jl new file mode 100644 index 00000000..c8ca69de --- /dev/null +++ b/src/raster/images.jl @@ -0,0 +1,201 @@ +function imview(colortype::Type{<:ColorTypes.Colorant}, imgvalues...) + return PermutedDimsArray( + ImageCore.colorview( + colortype, + (ImageCore.normedview(img) for img in imgvalues)..., + ), + (2, 1), + ) +end + +function imview( + gci::GDALColorInterp, + imgvalues::AbstractMatrix; + colortable::ColorTable = ColorTable(C_NULL), +) + return if gci == GCI_GrayIndex + imview(ColorTypes.Gray, imgvalues) + elseif gci == GCI_Undefined + imview(ColorTypes.Gray, imgvalues) + elseif gci == GCI_RedBand + zerovalues = zeros(eltype(imgvalues), size(imgvalues)) + imview(GPI_RGB, imgvalues, zerovalues, zerovalues) + elseif gci == GCI_GreenBand + zerovalues = zeros(eltype(imgvalues), size(imgvalues)) + imview(GPI_RGB, zerovalues, imgvalues, zerovalues) + elseif gci == GCI_BlueBand + zerovalues = zeros(eltype(imgvalues), size(imgvalues)) + imview(GPI_RGB, zerovalues, zerovalues, imgvalues) + elseif gci == GCI_PaletteIndex + if colortable.ptr == C_NULL + error( + """ + `imview` is only supported for `GCI_PaletteIndex` with non-null + colortables. + """, + ) + end + gpi = paletteinterp(colortable) + if gpi == GPI_Gray + imview(GPI_Gray, imgvalues) + elseif gpi == GPI_RGB + colorentries = GDAL.GDALColorEntry[ + getcolorentryasrgb(colortable, i - 1) for + i in 1:GDAL.gdalgetcolorentrycount(colortable.ptr) + ] + c1 = Matrix{UInt8}(undef, size(imgvalues)...) + c2 = Matrix{UInt8}(undef, size(imgvalues)...) + c3 = Matrix{UInt8}(undef, size(imgvalues)...) + c4 = Matrix{UInt8}(undef, size(imgvalues)...) + for i in eachindex(imgvalues) + c1[i] = UInt8(colorentries[imgvalues[i]+1].c1) + c2[i] = UInt8(colorentries[imgvalues[i]+1].c2) + c3[i] = UInt8(colorentries[imgvalues[i]+1].c3) + c4[i] = UInt8(colorentries[imgvalues[i]+1].c4) + end + imview(GPI_RGB, c1, c2, c3, c4) + else + error(""" + Unsupported GPI: $(paletteinterp(colortable)). Please file an + issue at https://github.com/yeesian/ArchGDAL.jl/issues if it + should be supported. + """) + end + else + error(""" + Unknown GCI: $gci. Please file an issue at + https://github.com/yeesian/ArchGDAL.jl/issues if it should be + supported. + """) + end +end + +function imview(gpi::GDALPaletteInterp, imgvalues::AbstractMatrix) + return if gpi == GPI_Gray + imview(ColorTypes.Gray, imgvalues) + else + error(""" + Unsupported GPI: $gpi. Please file an issue at + https://github.com/yeesian/ArchGDAL.jl/issues if it should be + supported. + """) + end +end + +function imview( + gpi::GDALPaletteInterp, + c1::AbstractMatrix, + c2::AbstractMatrix, + c3::AbstractMatrix, +) + return if gpi == GPI_Gray + imview(ColorTypes.Gray, c1, c2, c3) + elseif gpi == GPI_RGB + imview(ColorTypes.RGB, c1, c2, c3) + else + error(""" + Unsupported GPI: $gpi. If it should be supported, please file an + issue at https://github.com/yeesian/ArchGDAL.jl/issues with the + desired output. + """) + end +end + +function imview( + gpi::GDALPaletteInterp, + c1::AbstractMatrix, + c2::AbstractMatrix, + c3::AbstractMatrix, + c4::AbstractMatrix, +) + return if gpi == GPI_Gray + imview(ColorTypes.Gray, c1, c2, c3, c4) + elseif gpi == GPI_RGB + imview(ColorTypes.RGBA, c1, c2, c3, c4) + else + error(""" + Unsupported GPI: $gpi. If it should be supported, please file an + issue at https://github.com/yeesian/ArchGDAL.jl/issues with the + desired output. + """) + end +end + +function imread( + colortype::Union{GDALPaletteInterp,GDALColorInterp}, + dataset::AbstractDataset, + i::Integer, + args..., +) + return imread(colortype, getband(dataset, i), args...) +end + +function imread(gpi::GDALPaletteInterp, rb::AbstractRasterBand, args...) + return imview(gpi, read(rb, args...)) +end + +function imread(gci::GDALColorInterp, rb::AbstractRasterBand, args...) + return getcolortable(rb) do colortable + return imview(gci, read(rb, args...), colortable = colortable) + end +end + +function imread(rb::AbstractRasterBand, args...) + return getcolortable(rb) do colortable + return imview( + getcolorinterp(rb), + read(rb, args...), + colortable = colortable, + ) + end +end + +function imread(dataset::AbstractDataset, i::Integer, args...) + return imread(getband(dataset, i), args...) +end + +function _colorindices(dataset::AbstractDataset, indices) + gci = unique( + GDALColorInterp[getcolorinterp(getband(dataset, i)) for i in indices], + ) + gciorder = sort(gci) + colortype = if gciorder == [GCI_GrayIndex] + GCI_GrayIndex + elseif gciorder == [GCI_PaletteIndex] + GCI_PaletteIndex + elseif gciorder == [GCI_RedBand, GCI_GreenBand, GCI_BlueBand] + GPI_RGB + elseif gciorder == [GCI_RedBand, GCI_GreenBand, GCI_BlueBand, GCI_AlphaBand] + GPI_RGB + else + error(""" + Unknown GCI: $gciorder. Please file an issue at + https://github.com/yeesian/ArchGDAL.jl/issues if it should be + supported. + """) + end + return colortype, Tuple(indices[sortperm(gci)]) +end + +function imread(dataset::AbstractDataset, indices, args...) + colortype, idxs = _colorindices(dataset, indices) + return if colortype == GCI_PaletteIndex + getcolortable(getband(dataset, 1)) do colortable + return imview( + colortype, + (read(getband(dataset, i), args...) for i in idxs)..., + colortable = colortable, + ) + end + else + imview(colortype, (read(getband(dataset, i), args...) for i in idxs)...) + end +end + +imread(dataset::AbstractDataset) = imread(dataset, 1:nraster(dataset)) + +function imread(filename::AbstractString) + return read(filename) do dataset + return imread(dataset) + end +end diff --git a/src/raster/rasterattributetable.jl b/src/raster/rasterattributetable.jl index 1469df42..dca5e62a 100644 --- a/src/raster/rasterattributetable.jl +++ b/src/raster/rasterattributetable.jl @@ -3,20 +3,22 @@ Construct empty table. """ -unsafe_createRAT() = RasterAttrTable(GDAL.gdalcreaterasterattributetable()) +unsafe_createRAT()::RasterAttrTable = + RasterAttrTable(GDAL.gdalcreaterasterattributetable()) """ unsafe_createRAT(ct::ColorTable) Construct table from an existing colortable. """ -unsafe_createRAT(ct::ColorTable) = +unsafe_createRAT(ct::ColorTable)::RasterAttrTable = initializeRAT!(unsafe_createRAT(), ct) "Destroys a RAT." -function destroy(rat::RasterAttrTable) +function destroy(rat::RasterAttrTable)::Nothing GDAL.gdaldestroyrasterattributetable(rat.ptr) rat.ptr = C_NULL + return nothing end """ @@ -24,7 +26,7 @@ end Fetch table column count. """ -ncolumn(rat::RasterAttrTable) = GDAL.gdalratgetcolumncount(rat.ptr) +ncolumn(rat::RasterAttrTable)::Integer = GDAL.gdalratgetcolumncount(rat.ptr) """ columnname(rat::RasterAttrTable, i::Integer) @@ -37,7 +39,7 @@ Fetch name of indicated column. ### Returns the column name or an empty string for invalid column numbers. """ -columnname(rat::RasterAttrTable, i::Integer) = +columnname(rat::RasterAttrTable, i::Integer)::String = GDAL.gdalratgetnameofcol(rat.ptr, i) """ @@ -45,7 +47,7 @@ columnname(rat::RasterAttrTable, i::Integer) = Fetch column usage value. """ -columnusage(rat::RasterAttrTable, i::Integer) = +columnusage(rat::RasterAttrTable, i::Integer)::GDALRATFieldUsage = GDAL.gdalratgetusageofcol(rat.ptr, i) """ @@ -59,7 +61,7 @@ Fetch column type. ### Returns column type or `GFT_Integer` if the column index is illegal. """ -columntype(rat::RasterAttrTable, i::Integer) = +columntype(rat::RasterAttrTable, i::Integer)::GDALRATFieldType = GDAL.gdalratgettypeofcol(rat.ptr, i) """ @@ -71,7 +73,7 @@ match is found. ### Parameters * `usage` usage type to search for. """ -findcolumnindex(rat::RasterAttrTable, usage::GDALRATFieldUsage) = +findcolumnindex(rat::RasterAttrTable, usage::GDALRATFieldUsage)::Integer = GDAL.gdalratgetcolofusage(rat.ptr, usage) """ @@ -79,7 +81,7 @@ findcolumnindex(rat::RasterAttrTable, usage::GDALRATFieldUsage) = Fetch row count. """ -nrow(rat::RasterAttrTable) = GDAL.gdalratgetrowcount(rat.ptr) +nrow(rat::RasterAttrTable)::Integer = GDAL.gdalratgetrowcount(rat.ptr) """ asstring(rat::RasterAttrTable, row::Integer, col::Integer) @@ -94,7 +96,7 @@ some precision may be lost. * `row` row to fetch (zero based). * `col` column to fetch (zero based). """ -asstring(rat::RasterAttrTable, row::Integer, col::Integer) = +asstring(rat::RasterAttrTable, row::Integer, col::Integer)::String = GDAL.gdalratgetvalueasstring(rat.ptr, row, col) """ @@ -109,7 +111,7 @@ Non-integer fields will be converted to int with the possibility of data loss. * `row` row to fetch (zero based). * `col` column to fetch (zero based). """ -asint(rat::RasterAttrTable, row::Integer, col::Integer) = +asint(rat::RasterAttrTable, row::Integer, col::Integer)::Integer = GDAL.gdalratgetvalueasint(rat.ptr, row, col) """ @@ -124,7 +126,7 @@ Non double fields will be converted to double with the possibility of data loss. * `row` row to fetch (zero based). * `col` column to fetch (zero based). """ -asdouble(rat::RasterAttrTable, row::Integer, col::Integer) = +asdouble(rat::RasterAttrTable, row::Integer, col::Integer)::Float64 = GDAL.gdalratgetvalueasdouble(rat.ptr, row, col) """ @@ -144,31 +146,31 @@ loss of precision. function setvalue! end function setvalue!( - rat::RasterAttrTable, - row::Integer, - col::Integer, - val::AbstractString - ) + rat::RasterAttrTable, + row::Integer, + col::Integer, + val::AbstractString, +)::RasterAttrTable GDAL.gdalratsetvalueasstring(rat.ptr, row, col, val) return rat end function setvalue!( - rat::RasterAttrTable, - row::Integer, - col::Integer, - val::Integer - ) + rat::RasterAttrTable, + row::Integer, + col::Integer, + val::Integer, +)::RasterAttrTable GDAL.gdalratsetvalueasint(rat.ptr, row, col, val) return rat end function setvalue!( - rat::RasterAttrTable, - row::Integer, - col::Integer, - val::Float64 - ) + rat::RasterAttrTable, + row::Integer, + col::Integer, + val::Float64, +)::RasterAttrTable GDAL.gdalratsetvalueasdouble(rat.ptr, row, col, val) return rat end @@ -178,15 +180,16 @@ end Determine whether changes made to this RAT are reflected directly in the dataset -If this returns `false` then GDALRasterBand.SetDefaultRAT() should be called. +If this returns `false` then RasterBand.SetDefaultRAT() should be called. Otherwise this is unnecessary since changes to this object are reflected in the dataset. """ -changesarewrittentofile(rat::RasterAttrTable) = +changesarewrittentofile(rat::RasterAttrTable)::Bool = Bool(GDAL.gdalratchangesarewrittentofile(rat.ptr)) """ - attributeio!(rat::RasterAttrTable, access::GDALRWFlag, col, startrow, nrows, data::Vector) + attributeio!(rat::RasterAttrTable, access::GDALRWFlag, col, startrow, nrows, + data::Vector) Read or Write a block of data to/from the Attribute Table. @@ -195,48 +198,67 @@ Read or Write a block of data to/from the Attribute Table. * `col` Column of the Attribute Table * `startrow` Row to start reading/writing (zero based) * `nrows` Number of rows to read or write -* `data` Vector of Float64, Int32 or AbstractString to read/write. Should be at least `nrows` long. +* `data` Vector of Float64, Int32 or AbstractString to read/write. Should + be at least `nrows` long. """ function attributeio! end function attributeio!( - rat::RasterAttrTable, - access::GDALRWFlag, - col::Integer, - startrow::Integer, - nrows::Integer, - data::Vector{Float64} + rat::RasterAttrTable, + access::GDALRWFlag, + col::Integer, + startrow::Integer, + nrows::Integer, + data::Vector{Float64}, +)::Vector{Float64} + result = GDAL.gdalratvaluesioasdouble( + rat.ptr, + access, + col, + startrow, + nrows, + data, ) - result = GDAL.gdalratvaluesioasdouble(rat.ptr, access, col, startrow, nrows, - data) @cplerr result "Failed to $access at column $col starting at $startrow" return data end function attributeio!( - rat::RasterAttrTable, - access::GDALRWFlag, - col::Integer, - startrow::Integer, - nrows::Integer, - data::Vector{Cint} + rat::RasterAttrTable, + access::GDALRWFlag, + col::Integer, + startrow::Integer, + nrows::Integer, + data::Vector{Cint}, +)::Vector{Cint} + result = GDAL.gdalratvaluesioasinteger( + rat.ptr, + access, + col, + startrow, + nrows, + data, ) - result = GDAL.gdalratvaluesioasinteger(rat.ptr, access, col, startrow, - nrows, data) @cplerr result "Failed to $access at column $col starting at $startrow" return data end function attributeio!( - rat::RasterAttrTable, - access::GDALRWFlag, - col::Integer, - startrow::Integer, - nrows::Integer, - data::Vector{T} - ) where T <: AbstractString - result = GDAL.gdalratvaluesioasstring(rat.ptr, access, col, startrow, nrows, - data) + rat::RasterAttrTable, + access::GDALRWFlag, + col::Integer, + startrow::Integer, + nrows::Integer, + data::Vector{T}, +)::Vector{T} where {T<:AbstractString} + result = GDAL.gdalratvaluesioasstring( + rat.ptr, + access, + col, + startrow, + nrows, + data, + ) @cplerr result "Failed to $access at column $col starting at $startrow" return data end @@ -250,13 +272,14 @@ Resizes the table to include the indicated number of rows. Newly created rows will be initialized to their default values - \"\" for strings, and zero for numeric fields. """ -function setrowcount!(rat::RasterAttrTable, n::Integer) +function setrowcount!(rat::RasterAttrTable, n::Integer)::RasterAttrTable GDAL.gdalratsetrowcount(rat.ptr, n) return rat end """ - createcolumn!(rat::RasterAttrTable, name, fieldtype::GDALRATFieldType, fieldusage::GDALRATFieldUsage) + createcolumn!(rat::RasterAttrTable, name, fieldtype::GDALRATFieldType, + fieldusage::GDALRATFieldUsage) Create new column. @@ -266,11 +289,11 @@ created as the last column, can will be column (field) \"GetColumnCount()-1\" after CreateColumn() has completed successfully. """ function createcolumn!( - rat::RasterAttrTable, - name::AbstractString, - fieldtype::GDALRATFieldType, - fieldusage::GDALRATFieldUsage - ) + rat::RasterAttrTable, + name::AbstractString, + fieldtype::GDALRATFieldType, + fieldusage::GDALRATFieldUsage, +)::RasterAttrTable result = GDAL.gdalratcreatecolumn(rat.ptr, name, fieldtype, fieldusage) @cplerr result "Failed to create column $name" return rat @@ -289,7 +312,11 @@ the table. * `row0min` the lower bound (pixel value) of the first category. * `binsize` the width of each category (in pixel value units). """ -function setlinearbinning!(rat::RasterAttrTable, row0min::Real, binsize::Real) +function setlinearbinning!( + rat::RasterAttrTable, + row0min::Real, + binsize::Real, +)::RasterAttrTable result = GDAL.gdalratsetlinearbinning(rat.ptr, row0min, binsize) @cplerr result "Fail to set linear binning: r0min=$row0min, width=$binsize" return rat @@ -304,7 +331,7 @@ Get linear binning information. * `row0min` the lower bound (pixel value) of the first category. * `binsize` the width of each category (in pixel value units). """ -function getlinearbinning(rat::RasterAttrTable) +function getlinearbinning(rat::RasterAttrTable)::Tuple{Cdouble,Cdouble} row0min = Ref{Cdouble}() binsize = Ref{Cdouble}() result = GDAL.gdalratgetlinearbinning(rat.ptr, row0min, binsize) @@ -327,7 +354,10 @@ The raster attribute table must be empty before calling `initializeRAT!()`. The Value fields are set based on the implicit assumption with color tables that entry 0 applies to pixel value 0, 1 to 1, etc. """ -function initializeRAT!(rat::RasterAttrTable, colortable::ColorTable) +function initializeRAT!( + rat::RasterAttrTable, + colortable::ColorTable, +)::RasterAttrTable result = GDAL.gdalratinitializefromcolortable(rat.ptr, colortable.ptr) @cplerr result "Failed to initialize RAT from color table" return rat @@ -344,7 +374,7 @@ Translate to a color table. ### Returns the generated color table or `NULL` on failure. """ -toColorTable(rat::RasterAttrTable, n::Integer = -1) = +toColorTable(rat::RasterAttrTable, n::Integer = -1)::ColorTable = ColorTable(GDAL.gdalrattranslatetocolortable(rat.ptr, n)) # """ @@ -367,14 +397,15 @@ the responsibility of the caller to destroy. May fail (return `NULL`) if the attribute table is too large to clone: `(nrow() * ncolumn() > RAT_MAX_ELEM_FOR_CLONE)` """ -unsafe_clone(rat::RasterAttrTable) = RasterAttrTable(GDAL.gdalratclone(rat.ptr)) +unsafe_clone(rat::RasterAttrTable)::RasterAttrTable = + RasterAttrTable(GDAL.gdalratclone(rat.ptr)) -""" - serializeJSON(rat::RasterAttrTable) - -Serialize Raster Attribute Table in Json format. -""" -serializeJSON(rat::RasterAttrTable) = GDAL.gdalratserializejson(rat.ptr) +# """ +# serializeJSON(rat::RasterAttrTable) +# +# Serialize Raster Attribute Table in Json format. +# """ +# serializeJSON(rat::RasterAttrTable) = GDAL.gdalratserializejson(rat.ptr) """ findrowindex(rat::RasterAttrTable, pxvalue::Real) @@ -390,5 +421,5 @@ which row in the table applies to the pixel value. The row index is returned. ### Returns The row index or -1 if no row is appropriate. """ -findrowindex(rat::RasterAttrTable, pxvalue::Real) = +findrowindex(rat::RasterAttrTable, pxvalue::Real)::Integer = GDAL.gdalratgetrowofvalue(rat.ptr, pxvalue) diff --git a/src/raster/rasterband.jl b/src/raster/rasterband.jl index 5e3888b4..d31978db 100644 --- a/src/raster/rasterband.jl +++ b/src/raster/rasterband.jl @@ -1,13 +1,13 @@ -function destroy(band::AbstractRasterBand) - band.ptr = GDALRasterBand(C_NULL) - return band +function destroy(band::AbstractRasterBand)::Nothing + band.ptr = C_NULL + return nothing end -function destroy(band::IRasterBand) - band.ptr = GDALRasterBand(C_NULL) +function destroy(band::IRasterBand)::Nothing + band.ptr = C_NULL band.ownedby = Dataset() - return band + return nothing end """ @@ -27,8 +27,10 @@ Note that the X and Y block sizes don't have to divide the image size evenly, meaning that right and bottom edge blocks may be incomplete. See `ReadBlock()` for an example of code dealing with these issues. """ -function blocksize(band::AbstractRasterBand) - xy = Array{Cint}(undef, 2); x = pointer(xy); y = x + sizeof(Cint) +function blocksize(band::AbstractRasterBand)::Vector{Cint} + xy = Vector{Cint}(undef, 2) + x = pointer(xy) + y = x + sizeof(Cint) GDAL.gdalgetblocksize(band.ptr, x, y) return xy end @@ -38,28 +40,32 @@ end Fetch the pixel data type for this band. """ -pixeltype(band::AbstractRasterBand{T}) where T = T +function pixeltype(band::AbstractRasterBand{T})::DataType where {T<:Any} + return T +end """ width(band::AbstractRasterBand) Fetch the width in pixels of this band. """ -width(band::AbstractRasterBand) = GDAL.gdalgetrasterbandxsize(band.ptr) +width(band::AbstractRasterBand)::Integer = GDAL.gdalgetrasterbandxsize(band.ptr) """ height(band::AbstractRasterBand) Fetch the height in pixels of this band. """ -height(band::AbstractRasterBand) = GDAL.gdalgetrasterbandysize(band.ptr) +height(band::AbstractRasterBand)::Integer = + GDAL.gdalgetrasterbandysize(band.ptr) """ accessflag(band::AbstractRasterBand) -Return the access flag (e.g. `OF_ReadOnly` or `OF_Update`) for this band. +Return the access flag (e.g. `OF_READONLY` or `OF_UPDATE`) for this band. """ -accessflag(band::AbstractRasterBand) = GDAL.gdalgetrasteraccess(band.ptr) +accessflag(band::AbstractRasterBand)::GDALAccess = + GDAL.gdalgetrasteraccess(band.ptr) """ indexof(band::AbstractRasterBand) @@ -67,9 +73,9 @@ accessflag(band::AbstractRasterBand) = GDAL.gdalgetrasteraccess(band.ptr) Fetch the band number (1+) within its dataset, or 0 if unknown. This method may return a value of 0 to indicate overviews, or free-standing -`GDALRasterBand` objects without a relationship to a dataset. +`RasterBand` objects without a relationship to a dataset. """ -indexof(band::AbstractRasterBand) = GDAL.gdalgetbandnumber(band.ptr) +indexof(band::AbstractRasterBand)::Integer = GDAL.gdalgetbandnumber(band.ptr) """ getdataset(band::AbstractRasterBand) @@ -79,7 +85,7 @@ Fetch the handle to its dataset handle, or `NULL` if this cannot be determined. Note that some `RasterBand`s are not considered to be a part of a dataset, such as overviews or other "freestanding" bands. """ -getdataset(band::AbstractRasterBand) = +getdataset(band::AbstractRasterBand)::Dataset = Dataset(GDAL.gdalgetbanddataset(band.ptr)) # ↑ GDAL wrapper checks null by default, but it is a valid result in this case @@ -89,7 +95,8 @@ getdataset(band::AbstractRasterBand) = Return a name for the units of this raster's values. For instance, it might be "m" for an elevation model in meters, or "ft" for feet. """ -getunittype(band::AbstractRasterBand) = GDAL.gdalgetrasterunittype(band.ptr) +getunittype(band::AbstractRasterBand)::String = + GDAL.gdalgetrasterunittype(band.ptr) """ setunittype!(band::AbstractRasterBand, unitstring::AbstractString) @@ -100,7 +107,10 @@ Values should be one of \"\" (the default indicating it is unknown), \"m\" indicating meters, or \"ft\" indicating feet, though other nonstandard values are allowed. """ -function setunittype!(band::AbstractRasterBand, unitstring::AbstractString) +function setunittype!( + band::T, + unitstring::AbstractString, +)::T where {T<:AbstractRasterBand} result = GDAL.gdalsetrasterunittype(band.ptr, unitstring) @cplerr result "Failed to set unit type" return band @@ -119,14 +129,15 @@ elevations in `GUInt16` bands with a precision of 0.1, starting from -100. For file formats that don't know this intrinsically, a value of 0 is returned. """ -getoffset(band::AbstractRasterBand) = GDAL.gdalgetrasteroffset(band.ptr, C_NULL) +getoffset(band::AbstractRasterBand)::Real = + GDAL.gdalgetrasteroffset(band.ptr, C_NULL) """ setoffset!(band::AbstractRasterBand, value::Real) Set scaling offset. """ -function setoffset!(band::AbstractRasterBand, value::Real) +function setoffset!(band::T, value::Real)::T where {T<:AbstractRasterBand} result = GDAL.gdalsetrasteroffset(band.ptr, value) @cplerr result "Failed to set scaling offset." return band @@ -146,14 +157,15 @@ and starting from -100. For file formats that don't know this intrinsically a value of one is returned. """ -getscale(band::AbstractRasterBand) = GDAL.gdalgetrasterscale(band.ptr, C_NULL) +getscale(band::AbstractRasterBand)::Real = + GDAL.gdalgetrasterscale(band.ptr, C_NULL) """ setscale!(band::AbstractRasterBand, ratio::Real) Set scaling ratio. """ -function setscale!(band::AbstractRasterBand, ratio::Real) +function setscale!(band::T, ratio::Real)::T where {T<:AbstractRasterBand} result = GDAL.gdalsetrasterscale(band.ptr, ratio) @cplerr result "Failed to set scaling ratio" return band @@ -164,24 +176,24 @@ end Fetch the no data value for this band. -If there is no out of data value, `nothing` will be -returned instead. The no data value for a band is generally a special marker value -used to mark pixels that are not valid data. Such pixels should generally -not be displayed, nor contribute to analysis operations. +If there is no out of data value, `nothing` will be returned instead. The no +data value for a band is generally a special marker value used to mark pixels +that are not valid data. Such pixels should generally not be displayed, nor +contribute to analysis operations. ### Returns the nodata value for this band or `nothing`. """ -function getnodatavalue(band::AbstractRasterBand) +function getnodatavalue(band::AbstractRasterBand)::Union{Float64,Nothing} # ### Parameters # * `pbSuccess` pointer to a boolean to use to indicate if a value is # actually associated with this layer. May be `NULL` (default). hasnodatavalue = Ref(Cint(0)) nodatavalue = GDAL.gdalgetrasternodatavalue(band.ptr, hasnodatavalue) - if Bool(hasnodatavalue[]) - return nodatavalue + return if Bool(hasnodatavalue[]) + nodatavalue else - return nothing + nothing end end @@ -190,49 +202,43 @@ end Set the no data value for this band. """ -function setnodatavalue!(band::AbstractRasterBand, value::Real) +function setnodatavalue!(band::T, value::Real)::T where {T<:AbstractRasterBand} result = GDAL.gdalsetrasternodatavalue(band.ptr, value) @cplerr result "Could not set nodatavalue" return band end -function deletenodatavalue!(band::AbstractRasterBand) +function deletenodatavalue!(band::T)::T where {T<:AbstractRasterBand} result = GDAL.gdaldeleterasternodatavalue(band.ptr) @cplerr result "Could not delete nodatavalue" return band end -""" - setcategorynames!(band::AbstractRasterBand, names) - -Set the category names for this band. -""" -function setcategorynames!(band::AbstractRasterBand, names) - result = GDAL.gdalsetrastercategorynames(band.ptr, names) - @cplerr result "Failed to set category names" - return band -end - """ minimum(band::AbstractRasterBand) Fetch the minimum value for this band. """ -minimum(band::AbstractRasterBand) = GDAL.gdalgetrasterminimum(band.ptr, C_NULL) +minimum(band::AbstractRasterBand)::Real = + GDAL.gdalgetrasterminimum(band.ptr, C_NULL) """ maximum(band::AbstractRasterBand) Fetch the maximum value for this band. """ -maximum(band::AbstractRasterBand) = GDAL.gdalgetrastermaximum(band.ptr, C_NULL) +maximum(band::AbstractRasterBand)::Real = + GDAL.gdalgetrastermaximum(band.ptr, C_NULL) """ getdefaultRAT(band::AbstractRasterBand) -Fetch default Raster Attribute Table. +A RAT will be returned if there is a default one associated with the band, +otherwise NULL is returned. The returned RAT is owned by the band and should not +be deleted by the application. """ -getdefaultRAT(band::AbstractRasterBand) = GDAL.gdalgetdefaultrat(band.ptr) +getdefaultRAT(band::AbstractRasterBand)::RasterAttrTable = + RasterAttrTable(GDAL.gdalgetdefaultrat(band.ptr)) """ setdefaultRAT!(band::AbstractRasterBand, rat::RasterAttrTable) @@ -243,7 +249,10 @@ Associates a default RAT with the band. If not implemented for the format a CPLE_NotSupported error will be issued. If successful a copy of the RAT is made, the original remains owned by the caller. """ -function setdefaultRAT!(band::AbstractRasterBand, rat::RasterAttrTable) +function setdefaultRAT!( + band::T, + rat::RasterAttrTable, +)::T where {T<:AbstractRasterBand} result = GDAL.gdalsetdefaultrat(band.ptr, rat.ptr) @cplerr result "Failed to set default raster attribute table" return band @@ -274,14 +283,19 @@ More options may be supported in the future. * `progressdata` callback data for progress function. """ function copywholeraster!( - source::AbstractRasterBand, - dest::AbstractRasterBand; - options = StringList(C_NULL), - progressdata = C_NULL, - progressfunc::Function = GDAL.gdaldummyprogress + source::T, + dest::AbstractRasterBand; + options = StringList(C_NULL), + progressdata = C_NULL, + progressfunc::Function = GDAL.gdaldummyprogress, +)::T where {T<:AbstractRasterBand} + result = GDAL.gdalrasterbandcopywholeraster( + source.ptr, + dest.ptr, + options, + @cplprogress(progressfunc), + progressdata, ) - result = GDAL.gdalrasterbandcopywholeraster(source.ptr, dest.ptr, options, - @cplprogress(progressfunc), progressdata) @cplerr result "Failed to copy whole raster" return source end @@ -291,17 +305,18 @@ end Return the number of overview layers available, zero if none. """ -noverview(band::AbstractRasterBand) = GDAL.gdalgetoverviewcount(band.ptr) +noverview(band::AbstractRasterBand)::Integer = + GDAL.gdalgetoverviewcount(band.ptr) """ getoverview(band::IRasterBand, i::Integer) Fetch overview raster band object. """ -getoverview(band::IRasterBand, i::Integer) = +getoverview(band::IRasterBand, i::Integer)::IRasterBand = IRasterBand(GDAL.gdalgetoverview(band.ptr, i), ownedby = band.ownedby) -unsafe_getoverview(band::AbstractRasterBand, i::Integer) = +unsafe_getoverview(band::AbstractRasterBand, i::Integer)::RasterBand = RasterBand(GDAL.gdalgetoverview(band.ptr, i)) """ @@ -315,14 +330,14 @@ number of desired samples to fetch the most reduced overview. The same band as was passed in will be returned if it has not overviews, or if none of the overviews have enough samples. """ -function sampleoverview(band::IRasterBand, nsamples::Integer) +function sampleoverview(band::IRasterBand, nsamples::Integer)::IRasterBand return IRasterBand( GDAL.gdalgetrastersampleoverviewex(band.ptr, UInt64(nsamples)), - ownedby = band.ownedby + ownedby = band.ownedby, ) end -unsafe_sampleoverview(band::AbstractRasterBand, nsamples::Integer) = +unsafe_sampleoverview(band::AbstractRasterBand, nsamples::Integer)::RasterBand = RasterBand(GDAL.gdalgetrastersampleoverviewex(band.ptr, UInt64(nsamples))) """ @@ -330,7 +345,7 @@ unsafe_sampleoverview(band::AbstractRasterBand, nsamples::Integer) = Color Interpretation value for band """ -getcolorinterp(band::AbstractRasterBand) = +getcolorinterp(band::AbstractRasterBand)::GDALColorInterp = GDAL.gdalgetrastercolorinterpretation(band.ptr) """ @@ -338,7 +353,10 @@ getcolorinterp(band::AbstractRasterBand) = Set color interpretation of a band. """ -function setcolorinterp!(band::AbstractRasterBand, color::GDALColorInterp) +function setcolorinterp!( + band::T, + color::GDALColorInterp, +)::T where {T<:AbstractRasterBand} result = GDAL.gdalsetrastercolorinterpretation(band.ptr, color) @cplerr result "Failed to set color interpretation" return band @@ -350,17 +368,11 @@ end Returns a clone of the color table associated with the band. (If there is no associated color table, the original result is `NULL`. The -original color table remains owned by the `GDALRasterBand`, and can't be +original color table remains owned by the `RasterBand`, and can't be depended on for long, nor should it ever be modified by the caller.) """ -function unsafe_getcolortable(band::AbstractRasterBand) - result = ColorTable(GDALColorTable(GDAL.gdalgetrastercolortable(band.ptr))) - if result.ptr == C_NULL - return result - else - return unsafe_clone(result) - end -end +unsafe_getcolortable(band::AbstractRasterBand)::ColorTable = + unsafe_clone(ColorTable(GDAL.gdalgetrastercolortable(band.ptr))) """ setcolortable!(band::AbstractRasterBand, colortable::ColorTable) @@ -373,21 +385,24 @@ owned by the caller after the call. ### Parameters * `colortable` color table to apply (where supported). """ -function setcolortable!(band::AbstractRasterBand, colortable::ColorTable) +function setcolortable!( + band::T, + colortable::ColorTable, +)::T where {T<:AbstractRasterBand} result = GDAL.gdalsetrastercolortable(band.ptr, colortable.ptr) @cplwarn result "CPLError $(result): action is unsupported by the driver" return band end -function clearcolortable!(band::AbstractRasterBand) - result = GDAL.gdalsetrastercolortable(band.ptr, GDALColorTable(C_NULL)) +function clearcolortable!(band::T)::T where {T<:AbstractRasterBand} + result = GDAL.gdalsetrastercolortable(band.ptr, C_NULL) @cplwarn result "CPLError $(result): action is unsupported by the driver" return band end """ - regenerateoverviews!(band::AbstractRasterBand, overviewbands::Vector{<:AbstractRasterBand}, - resampling = "NEAREST") + regenerateoverviews!(band::AbstractRasterBand, + overviewbands::Vector{<:AbstractRasterBand}, resampling = "NEAREST") Generate downsampled overviews. @@ -415,21 +430,21 @@ considered as the nodata value and not each value of the triplet independantly per band. """ function regenerateoverviews!( - band::AbstractRasterBand, - overviewbands::Vector{<:AbstractRasterBand}, - resampling::AbstractString = "NEAREST", - # progressfunc::Function = GDAL.gdaldummyprogress, - progressdata = C_NULL - ) - cfunc = @cfunction(GDAL.gdaldummyprogress, Cint, - (Cdouble, Cstring, Ptr{Cvoid})) + band::T, + overviewbands::Vector{<:AbstractRasterBand}, + resampling::AbstractString = "NEAREST", + # progressfunc::Function = GDAL.gdaldummyprogress, + progressdata = C_NULL, +)::T where {T<:AbstractRasterBand} + cfunc = + @cfunction(GDAL.gdaldummyprogress, Cint, (Cdouble, Cstring, Ptr{Cvoid})) result = GDAL.gdalregenerateoverviews( band.ptr, length(overviewbands), - GDALRasterBand[band.ptr for band in overviewbands], + [band.ptr for band in overviewbands], resampling, cfunc, - progressdata + progressdata, ) @cplerr result "Failed to regenerate overviews" return band @@ -458,7 +473,7 @@ NULL terminated array of strings. Raster values without associated names will have an empty string in the returned list. The first entry in the list is for raster values of zero, and so on. """ -getcategorynames(band::AbstractRasterBand) = +getcategorynames(band::AbstractRasterBand)::Vector{String} = GDAL.gdalgetrastercategorynames(band.ptr) """ @@ -466,7 +481,10 @@ getcategorynames(band::AbstractRasterBand) = Set the category names for this band. """ -function setcategorynames!(band::AbstractRasterBand, names::Vector{String}) +function setcategorynames!( + band::T, + names::Vector{String}, +)::T where {T<:AbstractRasterBand} result = GDAL.gdalsetrastercategorynames(band.ptr, names) @cplerr result "Failed to set category names for this band" return band @@ -500,10 +518,10 @@ allows the imaginary component of a complex constant value to be specified. * `imagvalue`: Imaginary component of fill value, defaults to zero """ function fillraster!( - band::AbstractRasterBand, - realvalue::Real, - imagvalue::Real = 0 - ) + band::T, + realvalue::Real, + imagvalue::Real = 0, +)::T where {T<:AbstractRasterBand} result = GDAL.gdalfillraster(band.ptr, realvalue, imagvalue) @cplerr result "Failed to fill raster band" return band @@ -514,7 +532,7 @@ end Return the mask band associated with the band. -The `GDALRasterBand` class includes a default implementation of `GetMaskBand()` +The `RasterBand` class includes a default implementation of `GetMaskBand()` that returns one of four default implementations: - If a corresponding .msk file exists it will be used for the mask band. @@ -532,7 +550,7 @@ apply to this band (specific rules yet to be determined) and that is of type `GDALAllValidRasterBand` class will be returned that has 255 values for all pixels. The null flags will return `GMF_ALL_VALID`. -Note that the `GetMaskBand()` should always return a `GDALRasterBand` mask, +Note that the `GetMaskBand()` should always return a `RasterBand` mask, even if it is only an all 255 mask with the flags indicating `GMF_ALL_VALID`. See also: http://trac.osgeo.org/gdal/wiki/rfc15_nodatabitmask @@ -540,10 +558,10 @@ See also: http://trac.osgeo.org/gdal/wiki/rfc15_nodatabitmask ### Returns a valid mask band. """ -getmaskband(band::IRasterBand) = +getmaskband(band::IRasterBand)::IRasterBand = IRasterBand(GDAL.gdalgetmaskband(band.ptr), ownedby = band.ownedby) -unsafe_getmaskband(band::AbstractRasterBand) = +unsafe_getmaskband(band::AbstractRasterBand)::RasterBand = RasterBand(GDAL.gdalgetmaskband(band.ptr)) """ @@ -563,7 +581,7 @@ have values other than 0 and 255. - `GMF_NODATA` (`0x08`): Indicates the mask is actually being generated from nodata values. (mutually exclusive of `GMF_ALPHA`) -The `GDALRasterBand` class includes a default implementation of `GetMaskBand()` +The `RasterBand` class includes a default implementation of `GetMaskBand()` that returns one of four default implementations: - If a corresponding .msk file exists it will be used for the mask band. @@ -586,14 +604,13 @@ See also: http://trac.osgeo.org/gdal/wiki/rfc15_nodatabitmask ### Returns a valid mask band. """ -maskflags(band::AbstractRasterBand) = GDAL.gdalgetmaskflags(band.ptr) - +maskflags(band::AbstractRasterBand)::Cint = GDAL.gdalgetmaskflags(band.ptr) """ maskflaginfo(band::AbstractRasterBand) -Returns the flags as in `maskflags`(@ref) but unpacks the bit values into a named -tuple with the following fields: +Returns the flags as in `maskflags`(@ref) but unpacks the bit values into a +named tuple with the following fields: * `all_valid` * `per_dataset` @@ -604,10 +621,12 @@ tuple with the following fields: A named tuple with unpacked mask flags """ -function maskflaginfo(band::AbstractRasterBand) +function maskflaginfo( + band::AbstractRasterBand, +)::NamedTuple{(:all_valid, :per_dataset, :alpha, :nodata),NTuple{4,Bool}} flags = maskflags(band) return ( - all_valid = !iszero(flags & 0x01), + all_valid = !iszero(flags & 0x01), per_dataset = !iszero(flags & 0x02), alpha = !iszero(flags & 0x04), nodata = !iszero(flags & 0x08), @@ -632,7 +651,10 @@ invalidated by `CreateMaskBand()`. So you have to call `GetMaskBand()` again. See also: http://trac.osgeo.org/gdal/wiki/rfc15_nodatabitmask """ -function createmaskband!(band::AbstractRasterBand, nflags::Integer) +function createmaskband!( + band::T, + nflags::Integer, +)::T where {T<:AbstractRasterBand} result = GDAL.gdalcreatemaskband(band.ptr, nflags) @cplerr result "Failed to create mask band" return band diff --git a/src/raster/rasterio.jl b/src/raster/rasterio.jl index 06af5131..41f45fd8 100644 --- a/src/raster/rasterio.jl +++ b/src/raster/rasterio.jl @@ -1,9 +1,13 @@ """ - rasterio!(dataset::AbstractDataset, buffer::Array{<:Real, 3}, bands; ) - rasterio!(dataset::AbstractDataset, buffer::Array{<:Real, 3}, bands, rows, cols; ) - rasterio!(rasterband::AbstractRasterBand, buffer::Matrix{<:Real}; ) - rasterio!(rasterband::AbstractRasterBand, buffer::Matrix{<:Real}, rows, cols; ) + rasterio!(dataset::AbstractDataset, buffer::Array{<:Any, 3}, + bands; ) + rasterio!(dataset::AbstractDataset, buffer::Array{<:Any, 3}, bands, rows, + cols; ) + rasterio!(rasterband::AbstractRasterBand, buffer::Matrix{<:Any}; + ) + rasterio!(rasterband::AbstractRasterBand, buffer::Matrix{<:Any}, rows, + cols; ) Read/write a region of image data from multiple bands. @@ -11,8 +15,8 @@ Read/write a region of image data from multiple bands. This method allows reading a region of one or more `RasterBand`s from this dataset into a buffer, or writing data from a buffer into a region of the `RasterBand`s. It automatically takes care of data type translation if the -element type (`<:Real`) of the buffer is different than that of the -`GDALRasterBand`. The method also takes care of image decimation / replication +element type (`<:Any`) of the buffer is different than that of the +`RasterBand`. The method also takes care of image decimation / replication if the buffer size (`xsz × ysz`) is different than the size of the region being accessed (`xsize × ysize`). @@ -24,12 +28,12 @@ boundaries\" as returned by `blocksize()`, or use the `readblock!()` and `writeblock!()` methods. ### Parameters -* `rows` A continuous range of rows expressed as a `UnitRange{<:Integer}`, - such as 2:9. -* `cols` A continuous range of columns expressed as a `UnitRange{<:Integer}`, - such as 2:9. -* `access` Either `GDAL.GF_Read` to read a region of data, or - `GDAL.GF_Write` to write a region of data. +* `rows` A continuous range of rows expressed as a + `UnitRange{<:Integer}`, such as 2:9. +* `cols` A continuous range of columns expressed as a + `UnitRange{<:Integer}`, such as 2:9. +* `access` Either `GF_Read` to read a region of data, or + `GF_Write` to write a region of data. * `xoffset` The pixel offset to the top left corner of the region to be accessed. It will be `0` (default) to start from the left. * `yoffset` The line offset to the top left corner of the region to be @@ -62,441 +66,480 @@ boundaries\" as returned by `blocksize()`, or use the `readblock!()` and function rasterio! end function rasterio!( - dataset::AbstractDataset, - buffer::Array{<:Real, 3}, + dataset::AbstractDataset, + buffer::T, + bands, + access::GDALRWFlag = GF_Read, + pxspace::Integer = 0, + linespace::Integer = 0, + bandspace::Integer = 0, +)::T where {T<:Array{<:Any,3}} + rasterio!( + dataset, + buffer, bands, - access::GDALRWFlag = GDAL.GF_Read, - pxspace::Integer = 0, - linespace::Integer = 0, - bandspace::Integer = 0 + 0, + 0, + size(buffer, 1), + size(buffer, 2), + access, + pxspace, + linespace, + bandspace, ) - rasterio!(dataset, buffer, bands, 0, 0, size(buffer, 1), size(buffer, 2), - access, pxspace, linespace, bandspace) return buffer end function rasterio!( - dataset::AbstractDataset, - buffer::Array{<:Real, 3}, + dataset::AbstractDataset, + buffer::Array{T,3}, + bands, + rows::UnitRange{<:Integer}, + cols::UnitRange{<:Integer}, + access::GDALRWFlag = GF_Read, + pxspace::Integer = 0, + linespace::Integer = 0, + bandspace::Integer = 0, +)::Array{T,3} where {T<:Any} + xsize = cols[end] - cols[1] + 1 + xsize < 0 && error("invalid window width") + ysize = rows[end] - rows[1] + 1 + ysize < 0 && error("invalid window height") + rasterio!( + dataset, + buffer, bands, - rows::UnitRange{<:Integer}, - cols::UnitRange{<:Integer}, - access::GDALRWFlag = GDAL.GF_Read, - pxspace::Integer = 0, - linespace::Integer = 0, - bandspace::Integer = 0 + cols[1] - 1, + rows[1] - 1, + xsize, + ysize, + access, + pxspace, + linespace, + bandspace, ) - xsize = cols[end] - cols[1] + 1; xsize < 0 && error("invalid window width") - ysize = rows[end] - rows[1] + 1; ysize < 0 && error("invalid window height") - rasterio!(dataset, buffer, bands, cols[1], rows[1], xsize, ysize, access, - pxspace, linespace, bandspace) return buffer end function rasterio!( - rasterband::AbstractRasterBand, - buffer::Matrix{<:Real}, - access::GDALRWFlag = GDAL.GF_Read, - pxspace::Integer = 0, - linespace::Integer = 0 + dataset::AbstractDataset, + buffer::Array{T,3}, + bands, + xoffset::Integer, + yoffset::Integer, + xsize::Integer, + ysize::Integer, + access::GDALRWFlag = GF_Read, + pxspace::Integer = 0, + linespace::Integer = 0, + bandspace::Integer = 0, + extraargs = Ptr{GDAL.GDALRasterIOExtraArg}(C_NULL), +)::Array{T,3} where {T<:Any} + # `psExtraArg` (new in GDAL 2.0) pointer to a GDALRasterIOExtraArg + # structure with additional arguments to specify resampling and + # progress callback, or `NULL` for default behaviour. The + # `GDAL_RASTERIO_RESAMPLING` configuration option can also be + # defined to override the default resampling to one of `BILINEAR`, + # `CUBIC`, `CUBICSPLINE`, `LANCZOS`, `AVERAGE` or `MODE`. + (dataset == C_NULL) && error("Can't read NULL dataset") + xbsize, ybsize, zbsize = size(buffer) + nband = length(bands) + bands = isa(bands, Vector{Cint}) ? bands : Cint.(collect(bands)) + @assert nband == zbsize + result = GDAL.gdaldatasetrasterioex( + dataset.ptr, + access, + xoffset, + yoffset, + xsize, + ysize, + pointer(buffer), + xbsize, + ybsize, + convert(GDALDataType, T), + nband, + pointer(bands), + pxspace, + linespace, + bandspace, + extraargs, ) - rasterio!(rasterband, buffer, 0, 0, width(rasterband), height(rasterband), - access, pxspace, linespace) + @cplerr result "Access in DatasetRasterIO failed." return buffer end function rasterio!( - rasterband::AbstractRasterBand, - buffer::Matrix{<:Real}, - rows::UnitRange{<:Integer}, - cols::UnitRange{<:Integer}, - access::GDALRWFlag = GDAL.GF_Read, - pxspace::Integer = 0, - linespace::Integer = 0 + rasterband::AbstractRasterBand, + buffer::T, + access::GDALRWFlag = GF_Read, + pxspace::Integer = 0, + linespace::Integer = 0, +)::T where {T<:Matrix{<:Any}} + rasterio!( + rasterband, + buffer, + 0, + 0, + width(rasterband), + height(rasterband), + access, + pxspace, + linespace, ) - xsize = length(cols); xsize < 1 && error("invalid window width") - ysize = length(rows); ysize < 1 && error("invalid window height") - rasterio!(rasterband, buffer, cols[1]-1, rows[1]-1, xsize, ysize, access, - pxspace, linespace) return buffer end -read!(rb::AbstractRasterBand, buffer::Matrix{<:Real}) = - rasterio!(rb, buffer, GDAL.GF_Read) +function rasterio!( + rasterband::AbstractRasterBand, + buffer::T, + rows::UnitRange{<:Integer}, + cols::UnitRange{<:Integer}, + access::GDALRWFlag = GF_Read, + pxspace::Integer = 0, + linespace::Integer = 0, +)::T where {T<:Matrix{<:Any}} + xsize = length(cols) + xsize < 1 && error("invalid window width") + ysize = length(rows) + ysize < 1 && error("invalid window height") + rasterio!( + rasterband, + buffer, + cols[1] - 1, + rows[1] - 1, + xsize, + ysize, + access, + pxspace, + linespace, + ) + return buffer +end -function read!( - rb::AbstractRasterBand, - buffer::Matrix{<:Real}, - xoffset::Integer, - yoffset::Integer, - xsize::Integer, - ysize::Integer +function rasterio!( + rasterband::AbstractRasterBand, + buffer::Matrix{T}, + xoffset::Integer, + yoffset::Integer, + xsize::Integer, + ysize::Integer, + access::GDALRWFlag = GF_Read, + pxspace::Integer = 0, + linespace::Integer = 0, + extraargs = Ptr{GDAL.GDALRasterIOExtraArg}(C_NULL), +)::Matrix{T} where {T<:Any} + # `psExtraArg` (new in GDAL 2.0) pointer to a GDALRasterIOExtraArg + # structure with additional arguments to specify resampling and + # progress callback, or `NULL` for default behaviour. The + # `GDAL_RASTERIO_RESAMPLING` configuration option can also be + # defined to override the default resampling to one of `BILINEAR`, + # `CUBIC`, `CUBICSPLINE`, `LANCZOS`, `AVERAGE` or `MODE`. + (rasterband == C_NULL) && error("Can't read NULL rasterband") + xbsize, ybsize = size(buffer) + result = GDAL.gdalrasterioex( + rasterband.ptr, + access, + xoffset, + yoffset, + xsize, + ysize, + pointer(buffer), + xbsize, + ybsize, + convert(GDALDataType, T), + pxspace, + linespace, + extraargs, ) + @cplerr result "Access in RasterIO failed." + return buffer +end + +function read!(rb::AbstractRasterBand, buffer::T)::T where {T<:Matrix{<:Any}} + rasterio!(rb, buffer, GF_Read) + return buffer +end + +function read!( + rb::AbstractRasterBand, + buffer::T, + xoffset::Integer, + yoffset::Integer, + xsize::Integer, + ysize::Integer, +)::T where {T<:Matrix{<:Any}} rasterio!(rb, buffer, xoffset, yoffset, xsize, ysize) return buffer end function read!( - rb::AbstractRasterBand, - buffer::Matrix{<:Real}, - rows::UnitRange{<:Integer}, - cols::UnitRange{<:Integer} - ) + rb::AbstractRasterBand, + buffer::T, + rows::UnitRange{<:Integer}, + cols::UnitRange{<:Integer}, +)::T where {T<:Matrix{<:Any}} rasterio!(rb, buffer, rows, cols) return buffer end -read(rb::AbstractRasterBand) = - rasterio!(rb, Array{pixeltype(rb)}(undef, width(rb), height(rb))) +function read(rb::AbstractRasterBand) + return rasterio!(rb, Array{pixeltype(rb)}(undef, width(rb), height(rb))) +end function read( - rb::AbstractRasterBand, - xoffset::Integer, - yoffset::Integer, - xsize::Integer, - ysize::Integer - ) - buffer = Array{pixeltype(rb)}(undef, xsize, ysize) + rb::AbstractRasterBand, + xoffset::Integer, + yoffset::Integer, + xsize::Integer, + ysize::Integer, +)::Matrix{pixeltype(rb)} + buffer = Matrix{pixeltype(rb)}(undef, xsize, ysize) rasterio!(rb, buffer, xoffset, yoffset, xsize, ysize) return buffer end function read( - rb::AbstractRasterBand, - rows::UnitRange{<:Integer}, - cols::UnitRange{<:Integer} - ) - buffer = Array{pixeltype(rb)}(undef, length(cols), length(rows)) + rb::AbstractRasterBand, + rows::UnitRange{<:Integer}, + cols::UnitRange{<:Integer}, +)::Matrix{pixeltype(rb)} + buffer = Matrix{pixeltype(rb)}(undef, length(cols), length(rows)) rasterio!(rb, buffer, rows, cols) return buffer end -function write!(rb::AbstractRasterBand, buffer::Matrix{<:Real}) - rasterio!(rb, buffer, GDAL.GF_Write) - return buffer -end - function write!( - rb::AbstractRasterBand, - buffer::Matrix{<:Real}, - xoffset::Integer, - yoffset::Integer, - xsize::Integer, - ysize::Integer - ) - rasterio!(rb, buffer, xoffset, yoffset, xsize, ysize, GDAL.GF_Write) + rb::AbstractRasterBand, + buffer::T, + xoffset::Integer, + yoffset::Integer, + xsize::Integer, + ysize::Integer, +)::T where {T<:Matrix{<:Any}} + rasterio!(rb, buffer, xoffset, yoffset, xsize, ysize, GF_Write) return buffer end function write!( - rb::AbstractRasterBand, - buffer::Matrix{<:Real}, - rows::UnitRange{<:Integer}, - cols::UnitRange{<:Integer} - ) - rasterio!(rb, buffer, rows, cols, GDAL.GF_Write) + rb::AbstractRasterBand, + buffer::T, + rows::UnitRange{<:Integer} = 1:height(rb), + cols::UnitRange{<:Integer} = 1:width(rb), +)::T where {T<:Matrix{<:Any}} + rasterio!(rb, buffer, rows, cols, GF_Write) return buffer end -function read!(dataset::AbstractDataset, buffer::Matrix{<:Real}, i::Integer) +function read!( + dataset::AbstractDataset, + buffer::T, + i::Integer, +)::T where {T<:Matrix{<:Any}} read!(getband(dataset, i), buffer) return buffer end function read!( - dataset::AbstractDataset, - buffer::Array{<:Real, 3}, - indices - ) - rasterio!(dataset, buffer, indices, GDAL.GF_Read) + dataset::AbstractDataset, + buffer::T, + indices, +)::T where {T<:Array{<:Any,3}} + rasterio!(dataset, buffer, indices, GF_Read) return buffer end -function read!(dataset::AbstractDataset, buffer::Array{<:Real, 3}) +function read!(dataset::AbstractDataset, buffer::T)::T where {T<:Array{<:Any,3}} nband = nraster(dataset) @assert size(buffer, 3) == nband - rasterio!(dataset, buffer, collect(Cint, 1:nband), GDAL.GF_Read) + rasterio!(dataset, buffer, collect(Cint, 1:nband), GF_Read) return buffer end function read!( - dataset::AbstractDataset, - buffer::Matrix{<:Real}, - i::Integer, - xoffset::Integer, - yoffset::Integer, - xsize::Integer, - ysize::Integer - ) + dataset::AbstractDataset, + buffer::T, + i::Integer, + xoffset::Integer, + yoffset::Integer, + xsize::Integer, + ysize::Integer, +)::T where {T<:Matrix{<:Any}} read!(getband(dataset, i), buffer, xoffset, yoffset, xsize, ysize) return buffer end function read!( - dataset::AbstractDataset, - buffer::Array{<:Real, 3}, - indices, - xoffset::Integer, - yoffset::Integer, - xsize::Integer, - ysize::Integer - ) + dataset::AbstractDataset, + buffer::T, + indices, + xoffset::Integer, + yoffset::Integer, + xsize::Integer, + ysize::Integer, +)::T where {T<:Array{<:Any,3}} rasterio!(dataset, buffer, indices, xoffset, yoffset, xsize, ysize) return buffer end function read!( - dataset::AbstractDataset, - buffer::Matrix{<:Real}, - i::Integer, - rows::UnitRange{<:Integer}, - cols::UnitRange{<:Integer} - ) + dataset::AbstractDataset, + buffer::T, + i::Integer, + rows::UnitRange{<:Integer}, + cols::UnitRange{<:Integer}, +)::T where {T<:Matrix{<:Any}} read!(getband(dataset, i), buffer, rows, cols) return buffer end function read!( - dataset::AbstractDataset, - buffer::Array{<:Real, 3}, - indices, - rows::UnitRange{<:Integer}, - cols::UnitRange{<:Integer} - ) + dataset::AbstractDataset, + buffer::T, + indices, + rows::UnitRange{<:Integer}, + cols::UnitRange{<:Integer}, +)::T where {T<:Array{<:Any,3}} rasterio!(dataset, buffer, indices, rows, cols) return buffer end read(dataset::AbstractDataset, i::Integer) = read(getband(dataset, i)) -function read( - dataset::AbstractDataset, - indices +function read(dataset::AbstractDataset, indices)::Array{pixeltype(dataset),3} + buffer = Array{pixeltype(dataset)}( + undef, + width(dataset), + height(dataset), + length(indices), ) - buffer = Array{pixeltype(getband(dataset, indices[1]))}(undef, - width(dataset), height(dataset), length(indices)) rasterio!(dataset, buffer, indices) return buffer end -function read(dataset::AbstractDataset) - buffer = Array{pixeltype(getband(dataset, 1))}(undef, width(dataset), - height(dataset), nraster(dataset)) +function read(dataset::AbstractDataset)::Array{pixeltype(dataset),3} + buffer = Array{pixeltype(dataset)}( + undef, + width(dataset), + height(dataset), + nraster(dataset), + ) read!(dataset, buffer) return buffer end function read( - dataset::AbstractDataset, - i::Integer, - xoffset::Integer, - yoffset::Integer, - xsize::Integer, - ysize::Integer - ) - buffer = read(getband(dataset, i), xoffset, yoffset, xsize, ysize) + dataset::AbstractDataset, + i::Integer, + xoffset::Integer, + yoffset::Integer, + xsize::Integer, + ysize::Integer, +)::Matrix{pixeltype(dataset)} + band = getband(dataset, i) + buffer = Matrix{pixeltype(band)}(undef, xsize, ysize) + read!(dataset, buffer, i, xoffset, yoffset, xsize, ysize) return buffer end function read( - dataset::AbstractDataset, - indices, - xoffset::Integer, - yoffset::Integer, - xsize::Integer, - ysize::Integer - ) - buffer = Array{pixeltype(getband(dataset, indices[1]))}(undef, - xsize, ysize, length(indices)) + dataset::AbstractDataset, + indices, + xoffset::Integer, + yoffset::Integer, + xsize::Integer, + ysize::Integer, +)::Array{pixeltype(dataset),3} + buffer = Array{pixeltype(dataset)}(undef, xsize, ysize, length(indices)) rasterio!(dataset, buffer, indices, xsize, ysize, xoffset, yoffset) return buffer end function read( - dataset::AbstractDataset, - i::Integer, - rows::UnitRange{<:Integer}, - cols::UnitRange{<:Integer} - ) + dataset::AbstractDataset, + i::Integer, + rows::UnitRange{<:Integer}, + cols::UnitRange{<:Integer}, +)::Matrix{pixeltype(dataset)} buffer = read(getband(dataset, i), rows, cols) return buffer end function read( - dataset::AbstractDataset, - indices, - rows::UnitRange{<:Integer}, - cols::UnitRange{<:Integer} + dataset::AbstractDataset, + indices, + rows::UnitRange{<:Integer}, + cols::UnitRange{<:Integer}, +)::Array{pixeltype(dataset),3} + buffer = Array{pixeltype(dataset),3}( + undef, + length(cols), + length(rows), + length(indices), ) - buffer = Array{pixeltype(getband(dataset, indices[1]))}(undef, - length(cols), length(rows), length(indices)) rasterio!(dataset, buffer, indices, rows, cols) return buffer end -function write!(dataset::AbstractDataset, buffer::Matrix{<:Real}, i::Integer) - write!(getband(dataset, i), buffer) - return dataset -end - function write!( - dataset::AbstractDataset, - buffer::Array{<:Real, 3}, - indices - ) - rasterio!(dataset, buffer, indices, GDAL.GF_Write) - return dataset -end - -function write!( - dataset::AbstractDataset, - buffer::Matrix{<:Real}, - i::Integer, - xoffset::Integer, - yoffset::Integer, - xsize::Integer, - ysize::Integer - ) + dataset::T, + buffer::Matrix{<:Any}, + i::Integer, + xoffset::Integer, + yoffset::Integer, + xsize::Integer, + ysize::Integer, +)::T where {T<:AbstractDataset} write!(getband(dataset, i), buffer, xoffset, yoffset, xsize, ysize) return dataset end function write!( - dataset::AbstractDataset, - buffer::Array{<:Real, 3}, + dataset::T, + buffer::Array{<:Any,3}, + indices, + xoffset::Integer, + yoffset::Integer, + xsize::Integer, + ysize::Integer, +)::T where {T<:AbstractDataset} + rasterio!( + dataset, + buffer, indices, - xoffset::Integer, - yoffset::Integer, - xsize::Integer, - ysize::Integer + xoffset, + yoffset, + xsize, + ysize, + GF_Write, ) - rasterio!(dataset, buffer, indices, xoffset, yoffset, xsize, ysize, - GDAL.GF_Write) return dataset end function write!( - dataset::AbstractDataset, - buffer::Matrix{<:Real}, - i::Integer, - rows::UnitRange{<:Integer}, - cols::UnitRange{<:Integer} - ) + dataset::T, + buffer::Matrix{<:Any}, + i::Integer, + rows::UnitRange{<:Integer} = 1:height(getband(dataset, i)), + cols::UnitRange{<:Integer} = 1:width(getband(dataset, i)), +)::T where {T<:AbstractDataset} write!(getband(dataset, i), buffer, rows, cols) return dataset end function write!( - dataset::AbstractDataset, - buffer::Array{<:Real, 3}, - indices, - rows::UnitRange{<:Integer}, - cols::UnitRange{<:Integer} - ) - rasterio!(dataset, buffer, indices, rows, cols, GDAL.GF_Write) + dataset::T, + buffer::Array{<:Any,3}, + indices, + rows::UnitRange{<:Integer} = 1:height(dataset), + cols::UnitRange{<:Integer} = 1:width(dataset), +)::T where {T<:AbstractDataset} + rasterio!(dataset, buffer, indices, rows, cols, GF_Write) return dataset end -for (T,GT) in _GDALTYPE - eval(quote - function rasterio!( - dataset::AbstractDataset, - buffer::Array{$T, 3}, - bands, - xoffset::Integer, - yoffset::Integer, - xsize::Integer, - ysize::Integer, - access::GDALRWFlag = GDAL.GF_Read, - pxspace::Integer = 0, - linespace::Integer = 0, - bandspace::Integer = 0, - extraargs = Ptr{GDAL.GDALRasterIOExtraArg}(C_NULL) - ) - # `psExtraArg` (new in GDAL 2.0) pointer to a GDALRasterIOExtraArg - # structure with additional arguments to specify resampling and - # progress callback, or `NULL` for default behaviour. The - # `GDAL_RASTERIO_RESAMPLING` configuration option can also be - # defined to override the default resampling to one of `BILINEAR`, - # `CUBIC`, `CUBICSPLINE`, `LANCZOS`, `AVERAGE` or `MODE`. - (dataset == C_NULL) && error("Can't read invalid rasterband") - xbsize, ybsize, zbsize = size(buffer) - nband = length(bands) - bands = isa(bands, Vector{Cint}) ? bands : Cint.(collect(bands)) - @assert nband == zbsize - result = ccall((:GDALDatasetRasterIOEx,GDAL.libgdal), - GDAL.CPLErr, # return type - (GDALDataset, - GDAL.GDALRWFlag, # access - Cint, # xoffset - Cint, # yoffset - Cint, # xsize - Cint, # ysize - Ptr{Cvoid}, # poiter to buffer - Cint, # xbsize - Cint, # ybsize - GDAL.GDALDataType, - Cint, # number of bands - Ptr{Cint}, # bands - GDAL.GSpacing, # pxspace - GDAL.GSpacing, # linespace - GDAL.GSpacing, # bandspace - Ptr{GDAL.GDALRasterIOExtraArg} # extra args - ), - dataset.ptr, - access, - xoffset, - yoffset, - xsize, - ysize, - pointer(buffer), - xbsize, - ybsize, - $GT, - nband, - pointer(bands), - pxspace, - linespace, - bandspace, - extraargs) - @cplerr result "Access in DatasetRasterIO failed." - return buffer - end - - function rasterio!( - rasterband::AbstractRasterBand, - buffer::Matrix{$T}, - xoffset::Integer, - yoffset::Integer, - xsize::Integer, - ysize::Integer, - access::GDALRWFlag = GDAL.GF_Read, - pxspace::Integer = 0, - linespace::Integer = 0, - extraargs = Ptr{GDAL.GDALRasterIOExtraArg}(C_NULL) - ) - # `psExtraArg` (new in GDAL 2.0) pointer to a GDALRasterIOExtraArg - # structure with additional arguments to specify resampling and - # progress callback, or `NULL` for default behaviour. The - # `GDAL_RASTERIO_RESAMPLING` configuration option can also be - # defined to override the default resampling to one of `BILINEAR`, - # `CUBIC`, `CUBICSPLINE`, `LANCZOS`, `AVERAGE` or `MODE`. - (rasterband == C_NULL) && error("Can't read invalid rasterband") - xbsize, ybsize = size(buffer) - result = ccall((:GDALRasterIOEx,GDAL.libgdal),GDAL.CPLErr, - (GDALRasterBand,GDAL.GDALRWFlag,Cint,Cint,Cint,Cint,Ptr{Cvoid}, - Cint,Cint,GDAL.GDALDataType,GDAL.GSpacing, GDAL.GSpacing, - Ptr{GDAL.GDALRasterIOExtraArg}),rasterband.ptr,access,xoffset, - yoffset,xsize,ysize,pointer(buffer),xbsize,ybsize,$GT,pxspace, - linespace,extraargs) - @cplerr result "Access in RasterIO failed." - return buffer - end - end) -end - """ - readblock!(rb::AbstractRasterBand, xoffset::Integer, yoffset::Integer, buffer) + readblock!(rb::AbstractRasterBand, xoffset::Integer, yoffset::Integer, + buffer) Read a block of image data efficiently. @@ -514,18 +557,19 @@ access use RasterIO(). GetRasterDataType(). """ function readblock!( - rb::AbstractRasterBand, - xoffset::Integer, - yoffset::Integer, - buffer - ) + rb::AbstractRasterBand, + xoffset::Integer, + yoffset::Integer, + buffer::T, +)::T where {T<:Any} result = GDAL.gdalreadblock(rb.ptr, xoffset, yoffset, buffer) @cplerr result "Failed to read block at ($xoffset,$yoffset)" return buffer end """ - writeblock!(rb::AbstractRasterBand, xoffset::Integer, yoffset::Integer, buffer) + writeblock!(rb::AbstractRasterBand, xoffset::Integer, yoffset::Integer, + buffer) Write a block of image data efficiently. @@ -543,12 +587,12 @@ access use RasterIO(). GetRasterDataType(). """ function writeblock!( - rb::AbstractRasterBand, - xoffset::Integer, - yoffset::Integer, - buffer - ) + rb::T, + xoffset::Integer, + yoffset::Integer, + buffer, +)::T where {T<:AbstractRasterBand} result = GDAL.gdalwriteblock(rb.ptr, xoffset, yoffset, buffer) - @cplerr result "Failed to write block at ($xoffset,$yoffset)" + @cplerr result "Failed to write block at ($xoffset, $yoffset)" return rb end diff --git a/src/spatialref.jl b/src/spatialref.jl index f000e117..d8ebbe2c 100644 --- a/src/spatialref.jl +++ b/src/spatialref.jl @@ -5,14 +5,14 @@ Import a coordinate reference system from a `GeoFormat` into GDAL, returning an `ArchGDAL.AbstractSpatialRef`. ## Keyword Arguments -- `order`: Sets the axis mapping strategy. `:trad` will use traditional lon/lat axis - ordering in any actions done with the crs. `:compliant`, the default will use axis - ordering compliant with the relevant CRS authority. +- `order`: Sets the axis mapping strategy. `:trad` will use traditional lon/lat + axis ordering in any actions done with the crs. `:compliant` (the default) + will use axis ordering compliant with the relevant CRS authority. """ -importCRS(x::GFT.GeoFormat; kwargs...) = +importCRS(x::GFT.GeoFormat; kwargs...)::ISpatialRef = importCRS!(newspatialref(; kwargs...), x) -unsafe_importCRS(x::GFT.GeoFormat; kwargs...) = +unsafe_importCRS(x::GFT.GeoFormat; kwargs...)::SpatialRef = importCRS!(unsafe_newspatialref(; kwargs...), x) """ @@ -22,75 +22,156 @@ Import a coordinate reference system from a `GeoFormat` into the spatial ref. """ function importCRS! end -importCRS!(spref::AbstractSpatialRef, x::GFT.EPSG) = +function importCRS!(spref::T, x::GFT.EPSG)::T where {T<:AbstractSpatialRef} importEPSG!(spref, GFT.val(x)) -importCRS!(spref::AbstractSpatialRef, x::GFT.AbstractWellKnownText) = + return spref +end + +function importCRS!( + spref::T, + x::GFT.AbstractWellKnownText, +)::T where {T<:AbstractSpatialRef} importWKT!(spref, GFT.val(x)) -importCRS!(spref::AbstractSpatialRef, x::GFT.ESRIWellKnownText) = + return spref +end + +function importCRS!( + spref::T, + x::GFT.ESRIWellKnownText, +)::T where {T<:AbstractSpatialRef} importESRI!(spref, GFT.val(x)) -importCRS!(spref::AbstractSpatialRef, x::GFT.ProjString) = + return spref +end + +function importCRS!( + spref::T, + x::GFT.ProjString, +)::T where {T<:AbstractSpatialRef} importPROJ4!(spref, GFT.val(x)) -importCRS!(spref::AbstractSpatialRef, x::GFT.GML) = + return spref +end + +function importCRS!(spref::T, x::GFT.GML)::T where {T<:AbstractSpatialRef} importXML!(spref, GFT.val(x)) -importCRS!(spref::AbstractSpatialRef, x::GFT.KML) = + return spref +end + +function importCRS!(spref::T, x::GFT.KML)::T where {T<:AbstractSpatialRef} importCRS!(spref, GFT.EPSG(4326)) + return spref +end """ - reproject(points, sourceproj::GeoFormat, destproj::GeoFormat; [order=:compliant]) + reproject(points, sourceproj::GeoFormat, destproj::GeoFormat; + [order=:compliant]) Reproject points to a different coordinate reference system and/or format. ## Arguments - `coord`: Vector of Geometry points - `sourcecrs`: The current coordinate reference system, as a `GeoFormat` -- `targetcrs`: The coordinate reference system to transform to, using any CRS capable `GeoFormat` +- `targetcrs`: The coordinate reference system to transform to, using any CRS + capable `GeoFormat` ## Keyword Arguments -- `order`: Sets the axis mapping strategy. `:trad` will use traditional lon/lat axis - ordering in any actions done with the crs. `:compliant`, the default will use axis - ordering compliant with the relevant CRS authority. +- `order`: Sets the axis mapping strategy. `:trad` will use traditional lon/lat + axis ordering in any actions done with the crs. `:compliant` (the default) + will use axis ordering compliant with the relevant CRS authority. ## Example ```julia-repl julia> using ArchGDAL, GeoFormatTypes -julia> ArchGDAL.reproject([[118, 34], [119, 35]], ProjString("+proj=longlat +datum=WGS84 +no_defs"), EPSG(2025)) +julia> ArchGDAL.reproject( + [[118, 34], [119, 35]], + ProjString("+proj=longlat +datum=WGS84 +no_defs"), + EPSG(2025) +) 2-element Array{Array{Float64,1},1}: [-2.60813482878655e6, 1.5770429674905164e7] [-2.663928675953517e6, 1.56208905951487e7] ``` """ -reproject(coord, sourcecrs::GFT.GeoFormat, targetcrs::Nothing; kwargs...) = coord +function reproject( + coord::T, + sourcecrs::GFT.GeoFormat, + targetcrs::Nothing; + kwargs..., +)::T where {T<:Any} + return coord +end -# These should be better integrated with geometry packages or follow some standard -const ReprojectCoord = Union{<:NTuple{2,<:Number},<:NTuple{3,<:Number},AbstractVector{<:Number}} +# These should be better integrated with geometry packages or follow a standard +const ReprojectCoord = + Union{<:NTuple{2,<:Number},<:NTuple{3,<:Number},AbstractVector{<:Number}} # Vector/Tuple coordinate(s) -reproject(coord::ReprojectCoord, sourcecrs::GFT.GeoFormat, targetcrs::GFT.GeoFormat; kwargs...) = - GeoInterface.coordinates(reproject(createpoint(coord...), sourcecrs, targetcrs; kwargs...)) -reproject(coords::AbstractArray{<:ReprojectCoord}, sourcecrs::GFT.GeoFormat, - targetcrs::GFT.GeoFormat; kwargs...) = - GeoInterface.coordinates.(reproject([createpoint(c...) for c in coords], sourcecrs, targetcrs; kwargs...)) +function reproject( + coord::ReprojectCoord, + sourcecrs::GFT.GeoFormat, + targetcrs::GFT.GeoFormat; + kwargs..., +) + return GeoInterface.coordinates( + reproject(createpoint(coord...), sourcecrs, targetcrs; kwargs...), + ) +end + +function reproject( + coords::AbstractArray{<:ReprojectCoord}, + sourcecrs::GFT.GeoFormat, + targetcrs::GFT.GeoFormat; + kwargs..., +) + return GeoInterface.coordinates.( + reproject( + [createpoint(c...) for c in coords], + sourcecrs, + targetcrs; + kwargs..., + ), + ) +end # GeoFormat -reproject(geom::GFT.GeoFormat, sourcecrs::GFT.GeoFormat, targetcrs::GFT.GeoFormat; kwargs...) = - convert(typeof(geom), reproject(convert(Geometry, geom), sourcecrs, targetcrs; kwargs...)) +function reproject( + geom::GFT.GeoFormat, + sourcecrs::GFT.GeoFormat, + targetcrs::GFT.GeoFormat; + kwargs..., +) + return convert( + typeof(geom), + reproject(convert(Geometry, geom), sourcecrs, targetcrs; kwargs...), + ) +end # Geometries -function reproject(geom::AbstractGeometry, sourcecrs::GFT.GeoFormat, targetcrs::GFT.GeoFormat; kwargs...) - crs2transform(sourcecrs, targetcrs; kwargs...) do transform - transform!(geom, transform) +function reproject( + geom::AbstractGeometry, + sourcecrs::GFT.GeoFormat, + targetcrs::GFT.GeoFormat; + kwargs..., +) + return crs2transform(sourcecrs, targetcrs; kwargs...) do transform + return transform!(geom, transform) end end -function reproject(geoms::AbstractArray{<:AbstractGeometry}, sourcecrs::GFT.GeoFormat, - targetcrs::GFT.GeoFormat; kwargs...) - crs2transform(sourcecrs, targetcrs; kwargs...) do transform - transform!.(geoms, Ref(transform)) + +function reproject( + geoms::AbstractArray{<:AbstractGeometry}, + sourcecrs::GFT.GeoFormat, + targetcrs::GFT.GeoFormat; + kwargs..., +) + return crs2transform(sourcecrs, targetcrs; kwargs...) do transform + return transform!.(geoms, Ref(transform)) end end """ - crs2transform(f::Function, sourcecrs::GeoFormat, targetcrs::GeoFormat; kwargs...) + crs2transform(f::Function, sourcecrs::GeoFormat, targetcrs::GeoFormat; + kwargs...) Run the function `f` on a coord transform generated from the source and target crs definitions. These can be any `GeoFormat` (from GeoFormatTypes) that holds @@ -98,11 +179,16 @@ a coordinate reference system. `kwargs` are passed through to `importCRS`. """ -function crs2transform(f::Function, sourcecrs::GFT.GeoFormat, targetcrs::GFT.GeoFormat; kwargs...) - importCRS(sourcecrs; kwargs...) do sourcecrs_ref +function crs2transform( + f::Function, + sourcecrs::GFT.GeoFormat, + targetcrs::GFT.GeoFormat; + kwargs..., +) + return importCRS(sourcecrs; kwargs...) do sourcecrs_ref importCRS(targetcrs; kwargs...) do targetcrs_ref createcoordtrans(sourcecrs_ref, targetcrs_ref) do transform - f(transform) + return f(transform) end end end @@ -114,28 +200,50 @@ end Construct a Spatial Reference System from its WKT. # Keyword Arguments -- `order`: Sets the axis mapping strategy. `:trad` will use traditional lon/lat axis - ordering in any actions done with the crs. `:compliant`, will use axis - ordering compliant with the relevant CRS authority. -""" -newspatialref(wkt::AbstractString = ""; order=:compliant) = - maybesetaxisorder!(ISpatialRef(GDAL.osrnewspatialreference(wkt)), order) +- `order`: Sets the axis mapping strategy. `:trad` will use traditional lon/lat + axis ordering in any actions done with the crs. `:compliant`, will use axis + ordering compliant with the relevant CRS authority. +""" +function newspatialref(wkt::AbstractString = ""; order::Symbol = :compliant) + return maybesetaxisorder!( + ISpatialRef(GDAL.osrnewspatialreference(wkt)), + order, + ) +end -unsafe_newspatialref(wkt::AbstractString = ""; order=:compliant) = - maybesetaxisorder!(SpatialRef(GDAL.osrnewspatialreference(wkt)), order) +function unsafe_newspatialref( + wkt::AbstractString = ""; + order::Symbol = :compliant, +) + return maybesetaxisorder!( + SpatialRef(GDAL.osrnewspatialreference(wkt)), + order, + ) +end -function maybesetaxisorder!(sr::AbstractSpatialRef, order) +function maybesetaxisorder!( + spref::T, + order::Symbol, +)::T where {T<:AbstractSpatialRef} if order == :trad - GDAL.osrsetaxismappingstrategy(sr.ptr, GDAL.OAMS_TRADITIONAL_GIS_ORDER) + GDAL.osrsetaxismappingstrategy( + spref.ptr, + GDAL.OAMS_TRADITIONAL_GIS_ORDER, + ) elseif order != :compliant - throw(ArgumentError("order $order is not supported. Use :trad or :compliant")) + throw( + ArgumentError( + "order $order is not supported. Use :trad or :compliant", + ), + ) end - sr + return spref end -function destroy(spref::AbstractSpatialRef) +function destroy(spref::AbstractSpatialRef)::Nothing GDAL.osrdestroyspatialreference(spref.ptr) spref.ptr = C_NULL + return nothing end """ @@ -143,19 +251,19 @@ end Makes a clone of the Spatial Reference System. May return NULL. """ -function clone(spref::AbstractSpatialRef) - if spref.ptr == C_NULL - return ISpatialRef() +function clone(spref::AbstractSpatialRef)::ISpatialRef + return if spref.ptr == C_NULL + ISpatialRef() else - return ISpatialRef(GDAL.osrclone(spref.ptr)) + ISpatialRef(GDAL.osrclone(spref.ptr)) end end -function unsafe_clone(spref::AbstractSpatialRef) - if spref.ptr == C_NULL - return SpatialRef() +function unsafe_clone(spref::AbstractSpatialRef)::SpatialRef + return if spref.ptr == C_NULL + SpatialRef() else - return SpatialRef(GDAL.osrclone(spref.ptr)) + SpatialRef(GDAL.osrclone(spref.ptr)) end end @@ -184,7 +292,7 @@ These support files are normally searched for in /usr/local/share/gdal or in the directory identified by the GDAL_DATA configuration option. See CPLFindFile() for details. """ -function importEPSG!(spref::AbstractSpatialRef, code::Integer) +function importEPSG!(spref::T, code::Integer)::T where {T<:AbstractSpatialRef} result = GDAL.osrimportfromepsg(spref.ptr, code) @ogrerr result "Failed to initialize SRS based on EPSG" return spref @@ -196,14 +304,14 @@ end Construct a Spatial Reference System from its EPSG GCS or PCS code. # Keyword Arguments -- `order`: Sets the axis mapping strategy. `:trad` will use traditional lon/lat axis - ordering in any actions done with the crs. `:compliant`, will use axis - ordering compliant with the relevant CRS authority. +- `order`: Sets the axis mapping strategy. `:trad` will use traditional lon/lat + axis ordering in any actions done with the crs. `:compliant`, will use axis + ordering compliant with the relevant CRS authority. """ -importEPSG(code::Integer; kwargs...) = +importEPSG(code::Integer; kwargs...)::ISpatialRef = importEPSG!(newspatialref(; kwargs...), code) -unsafe_importEPSG(code::Integer; kwargs...) = +unsafe_importEPSG(code::Integer; kwargs...)::SpatialRef = importEPSG!(unsafe_newspatialref(; kwargs...), code) """ @@ -218,7 +326,7 @@ are also a few projected coordinate systems that use northing/easting order contrary to typical GIS use). See `importFromEPSG()` for more details on operation of this method. """ -function importEPSGA!(spref::AbstractSpatialRef, code::Integer) +function importEPSGA!(spref::T, code::Integer)::T where {T<:AbstractSpatialRef} result = GDAL.osrimportfromepsga(spref.ptr, code) @ogrerr result "Failed to initializ SRS based on EPSGA" return spref @@ -237,14 +345,14 @@ contrary to typical GIS use). See `importFromEPSG()` for more details on operation of this method. # Keyword Arguments -- `order`: Sets the axis mapping strategy. `:trad` will use traditional lon/lat axis - ordering in any actions done with the crs. `:compliant`, will use axis - ordering compliant with the relevant CRS authority. +- `order`: Sets the axis mapping strategy. `:trad` will use traditional lon/lat + axis ordering in any actions done with the crs. `:compliant`, will use axis + ordering compliant with the relevant CRS authority. """ -importEPSGA(code::Integer; kwargs...) = +importEPSGA(code::Integer; kwargs...)::ISpatialRef = importEPSGA!(newspatialref(; kwargs...), code) -unsafe_importEPSGA(code::Integer; kwargs...) = +unsafe_importEPSGA(code::Integer; kwargs...)::SpatialRef = importEPSGA!(unsafe_newspatialref(; kwargs...), code) """ @@ -257,7 +365,10 @@ contents of the passed WKT string. Only as much of the input string as needed to construct this SRS is consumed from the input string, and the input string pointer is then updated to point to the remaining (unused) input. """ -function importWKT!(spref::AbstractSpatialRef, wktstr::AbstractString) +function importWKT!( + spref::T, + wktstr::AbstractString, +)::T where {T<:AbstractSpatialRef} result = GDAL.osrimportfromwkt(spref.ptr, [wktstr]) @ogrerr result "Failed to initialize SRS based on WKT string" return spref @@ -269,14 +380,14 @@ end Create SRS from its WKT string. # Keyword Arguments -- `order`: Sets the axis mapping strategy. `:trad` will use traditional lon/lat axis - ordering in any actions done with the crs. `:compliant`, will use axis - ordering compliant with the relevant CRS authority. +- `order`: Sets the axis mapping strategy. `:trad` will use traditional lon/lat + axis ordering in any actions done with the crs. `:compliant`, will use axis + ordering compliant with the relevant CRS authority. """ -importWKT(wktstr::AbstractString; kwargs...) = +importWKT(wktstr::AbstractString; kwargs...)::ISpatialRef = newspatialref(wktstr; kwargs...) -unsafe_importWKT(wktstr::AbstractString; kwargs...) = +unsafe_importWKT(wktstr::AbstractString; kwargs...)::SpatialRef = unsafe_newspatialref(wktstr; kwargs...) """ @@ -301,7 +412,10 @@ back to PROJ.4 format\". For example: `\"+proj=nzmg +lat_0=-41 +lon_0=173 +x_0=2510000 +y_0=6023150 +ellps=intl +units=m +nadgrids=nzgd2kgrid0005.gsb +wktext\"` """ -function importPROJ4!(spref::AbstractSpatialRef, projstr::AbstractString) +function importPROJ4!( + spref::T, + projstr::AbstractString, +)::T where {T<:AbstractSpatialRef} result = GDAL.osrimportfromproj4(spref.ptr, projstr) @ogrerr result "Failed to initialize SRS based on PROJ4 string" return spref @@ -313,14 +427,14 @@ end Create SRS from its PROJ.4 string. # Keyword Arguments -- `order`: Sets the axis mapping strategy. `:trad` will use traditional lon/lat axis - ordering in any actions done with the crs. `:compliant`, will use axis - ordering compliant with the relevant CRS authority. +- `order`: Sets the axis mapping strategy. `:trad` will use traditional lon/lat + axis ordering in any actions done with the crs. `:compliant`, will use axis + ordering compliant with the relevant CRS authority. """ -importPROJ4(projstr::AbstractString; kwargs...) = +importPROJ4(projstr::AbstractString; kwargs...)::ISpatialRef = importPROJ4!(newspatialref(; kwargs...), projstr) -unsafe_importPROJ4(projstr::AbstractString; kwargs...) = +unsafe_importPROJ4(projstr::AbstractString; kwargs...)::SpatialRef = importPROJ4!(unsafe_newspatialref(; kwargs...), projstr) """ @@ -345,7 +459,10 @@ At this time there is no equivalent `exportToESRI()` method. Writing old style and `exportToWkt()` methods can be used to generate output suitable to write to new style (Arc 8) .prj files. """ -function importESRI!(spref::AbstractSpatialRef, esristr::AbstractString) +function importESRI!( + spref::T, + esristr::AbstractString, +)::T where {T<:AbstractSpatialRef} result = GDAL.osrimportfromesri(spref.ptr, [esristr]) @ogrerr result "Failed to initialize SRS based on ESRI string" return spref @@ -359,10 +476,10 @@ Create SRS from its ESRI .prj format(s). Passing the keyword argument `order=:compliant` or `order=:trad` will set the mapping strategy to return compliant axis order or traditional lon/lat order. """ -importESRI(esristr::AbstractString; kwargs...) = +importESRI(esristr::AbstractString; kwargs...)::ISpatialRef = importESRI!(newspatialref(; kwargs...), esristr) -unsafe_importESRI(esristr::AbstractString; kwargs...) = +unsafe_importESRI(esristr::AbstractString; kwargs...)::SpatialRef = importESRI!(unsafe_newspatialref(; kwargs...), esristr) """ @@ -370,7 +487,10 @@ unsafe_importESRI(esristr::AbstractString; kwargs...) = Import SRS from XML format (GML only currently). """ -function importXML!(spref::AbstractSpatialRef, xmlstr::AbstractString) +function importXML!( + spref::T, + xmlstr::AbstractString, +)::T where {T<:AbstractSpatialRef} result = GDAL.osrimportfromxml(spref.ptr, xmlstr) @ogrerr result "Failed to initialize SRS based on XML string" return spref @@ -385,14 +505,14 @@ Passing the keyword argument `order=:compliant` or `order=:trad` will set the mapping strategy to return compliant axis order or traditional lon/lat order. # Keyword Arguments -- `order`: Sets the axis mapping strategy. `:trad` will use traditional lon/lat axis - ordering in any actions done with the crs. `:compliant`, will use axis - ordering compliant with the relevant CRS authority. +- `order`: Sets the axis mapping strategy. `:trad` will use traditional lon/lat + axis ordering in any actions done with the crs. `:compliant`, will use axis + ordering compliant with the relevant CRS authority. """ -importXML(xmlstr::AbstractString; kwargs...) = +importXML(xmlstr::AbstractString; kwargs...)::ISpatialRef = importXML!(newspatialref(; kwargs...), xmlstr) -unsafe_importXML(xmlstr::AbstractString; kwargs...) = +unsafe_importXML(xmlstr::AbstractString; kwargs...)::SpatialRef = importXML!(unsafe_newspatialref(; kwargs...), xmlstr) """ @@ -403,7 +523,10 @@ Set spatial reference from a URL. This method will download the spatial reference at a given URL and feed it into SetFromUserInput for you. """ -function importURL!(spref::AbstractSpatialRef, url::AbstractString) +function importURL!( + spref::T, + url::AbstractString, +)::T where {T<:AbstractSpatialRef} result = GDAL.osrimportfromurl(spref.ptr, url) @ogrerr result "Failed to initialize SRS from URL" return spref @@ -418,14 +541,14 @@ This method will download the spatial reference at a given URL and feed it into SetFromUserInput for you. # Keyword Arguments -- `order`: Sets the axis mapping strategy. `:trad` will use traditional lon/lat axis - ordering in any actions done with the crs. `:compliant`, will use axis - ordering compliant with the relevant CRS authority. +- `order`: Sets the axis mapping strategy. `:trad` will use traditional lon/lat + axis ordering in any actions done with the crs. `:compliant`, will use axis + ordering compliant with the relevant CRS authority. """ -importURL(url::AbstractString; kwargs...) = +importURL(url::AbstractString; kwargs...)::ISpatialRef = importURL!(newspatialref(; kwargs...), url) -unsafe_importURL(url::AbstractString; kwargs...) = +unsafe_importURL(url::AbstractString; kwargs...)::SpatialRef = importURL!(unsafe_newspatialref(; kwargs...), url) """ @@ -433,7 +556,7 @@ unsafe_importURL(url::AbstractString; kwargs...) = Convert this SRS into WKT format. """ -function toWKT(spref::AbstractSpatialRef) +function toWKT(spref::AbstractSpatialRef)::String wktptr = Ref{Cstring}() result = GDAL.osrexporttowkt(spref.ptr, wktptr) @ogrerr result "Failed to convert this SRS into WKT format" @@ -447,10 +570,10 @@ Convert this SRS into a nicely formatted WKT string for display to a person. ### Parameters * `spref`: the SRS to be converted -* `simplify`: `true` if the `AXIS`, `AUTHORITY` and `EXTENSION` nodes should be - stripped off. +* `simplify`: `true` if the `AXIS`, `AUTHORITY` and `EXTENSION` nodes should + be stripped off. """ -function toWKT(spref::AbstractSpatialRef, simplify::Bool) +function toWKT(spref::AbstractSpatialRef, simplify::Bool)::String wktptr = Ref{Cstring}() result = GDAL.osrexporttoprettywkt(spref.ptr, wktptr, simplify) @ogrerr result "Failed to convert this SRS into pretty WKT" @@ -462,7 +585,7 @@ end Export coordinate system in PROJ.4 format. """ -function toPROJ4(spref::AbstractSpatialRef) +function toPROJ4(spref::AbstractSpatialRef)::String projptr = Ref{Cstring}() result = GDAL.osrexporttoproj4(spref.ptr, projptr) @ogrerr result "Failed to export this SRS to PROJ.4 format" @@ -478,7 +601,7 @@ Converts the loaded coordinate reference system into XML format to the extent possible. LOCAL_CS coordinate systems are not translatable. An empty string will be returned along with OGRERR_NONE. """ -function toXML(spref::AbstractSpatialRef) +function toXML(spref::AbstractSpatialRef)::String xmlptr = Ref{Cstring}() result = GDAL.osrexporttoxml(spref.ptr, xmlptr, C_NULL) @ogrerr result "Failed to convert this SRS into XML" @@ -490,7 +613,7 @@ end Export coordinate system in Mapinfo style CoordSys format. """ -function toMICoordSys(spref::AbstractSpatialRef) +function toMICoordSys(spref::AbstractSpatialRef)::String ptr = Ref{Cstring}() result = GDAL.osrexporttomicoordsys(spref.ptr, ptr) @ogrerr result "Failed to convert this SRS into XML" @@ -507,7 +630,7 @@ closely map onto the ESRI concept of WKT format. This includes renaming a variety of projections and arguments, and stripping out nodes note recognised by ESRI (like AUTHORITY and AXIS). """ -function morphtoESRI!(spref::AbstractSpatialRef) +function morphtoESRI!(spref::T)::T where {T<:AbstractSpatialRef} result = GDAL.osrmorphtoesri(spref.ptr) @ogrerr result "Failed to convert in place to ESRI WKT format" return spref @@ -541,14 +664,15 @@ of the following (`TOWGS84` recommended for proper datum shift calculations) `importFromEPSG(n)`, using `EPSG` code `n` corresponding to the existing `GEOGCS`. Does not impact `PROJCS` values. """ -function morphfromESRI!(spref::AbstractSpatialRef) +function morphfromESRI!(spref::T)::T where {T<:AbstractSpatialRef} result = GDAL.osrmorphfromesri(spref.ptr) @ogrerr result "Failed to convert in place from ESRI WKT format" return spref end """ - setattrvalue!(spref::AbstractSpatialRef, path::AbstractString, value::AbstractString) + setattrvalue!(spref::AbstractSpatialRef, path::AbstractString, + value::AbstractString) Set attribute value in spatial reference. @@ -562,16 +686,19 @@ the value otherwise the zeroth child will be assigned the value. out if you just want to force creation of the intermediate path. """ function setattrvalue!( - spref::AbstractSpatialRef, - path::AbstractString, - value::AbstractString - ) + spref::T, + path::AbstractString, + value::AbstractString, +)::T where {T<:AbstractSpatialRef} result = GDAL.osrsetattrvalue(spref.ptr, path, value) @ogrerr result "Failed to set attribute path to value" return spref end -function setattrvalue!(spref::AbstractSpatialRef, path::AbstractString) +function setattrvalue!( + spref::T, + path::AbstractString, +)::T where {T<:AbstractSpatialRef} result = GDAL.osrsetattrvalue(spref.ptr, path, C_NULL) @ogrerr result "Failed to set attribute path" return spref @@ -592,13 +719,19 @@ Parameters `i` the child of the node to fetch (zero based). Returns -the requested value, or NULL if it fails for any reason. -""" -getattrvalue(spref::AbstractSpatialRef, name::AbstractString, i::Integer) = - GDAL.osrgetattrvalue(spref.ptr, name, i) +the requested value, or `nothing` if it fails for any reason. +""" +function getattrvalue( + spref::AbstractSpatialRef, + name::AbstractString, + i::Integer, +)::Union{String,Nothing} + return GDAL.osrgetattrvalue(spref.ptr, name, i) +end """ - unsafe_createcoordtrans(source::AbstractSpatialRef, target::AbstractSpatialRef) + unsafe_createcoordtrans(source::AbstractSpatialRef, + target::AbstractSpatialRef) Create transformation object. @@ -610,17 +743,19 @@ Create transformation object. NULL on failure or a ready to use transformation object. """ function unsafe_createcoordtrans( - source::AbstractSpatialRef, - target::AbstractSpatialRef + source::AbstractSpatialRef, + target::AbstractSpatialRef, +)::CoordTransform + return CoordTransform( + GDAL.octnewcoordinatetransformation(source.ptr, target.ptr), ) - return CoordTransform(GDAL.octnewcoordinatetransformation(source.ptr, - target.ptr)) end "OGRCoordinateTransformation destructor." -function destroy(obj::CoordTransform) +function destroy(obj::CoordTransform)::Nothing GDAL.octdestroycoordinatetransformation(obj.ptr) obj.ptr = C_NULL + return nothing end """ @@ -637,16 +772,23 @@ Transform points from source to destination space. `true` on success, or `false` if some or all points fail to transform. """ function transform!( - xvertices::Vector{Cdouble}, - yvertices::Vector{Cdouble}, - zvertices::Vector{Cdouble}, - obj::CoordTransform - ) - # The method TransformEx() allows extended success information to be captured - # indicating which points failed to transform. + xvertices::Vector{Cdouble}, + yvertices::Vector{Cdouble}, + zvertices::Vector{Cdouble}, + obj::CoordTransform, +)::Bool + # The method TransformEx() allows extended success information to be + # captured indicating which points failed to transform. n = length(xvertices) @assert length(yvertices) == n @assert length(zvertices) == n - return Bool(GDAL.octtransform(obj.ptr, n, pointer(xvertices), - pointer(yvertices), pointer(zvertices))) + return Bool( + GDAL.octtransform( + obj.ptr, + n, + pointer(xvertices), + pointer(yvertices), + pointer(zvertices), + ), + ) end diff --git a/src/tables.jl b/src/tables.jl index ed35a5f9..13e15290 100644 --- a/src/tables.jl +++ b/src/tables.jl @@ -1,73 +1,59 @@ -""" -Constructs `Table` out of `FeatureLayer`, where every row is a `Feature` consisting of Geometry and attributes. -``` -ArchGDAL.Table(T::Union{IFeatureLayer, FeatureLayer}) -``` -""" -struct Table{T<:Union{IFeatureLayer, FeatureLayer}} - layer::T -end - -getlayer(t::Table) = Base.getfield(t, :layer) - -function Tables.schema(layer::AbstractFeatureLayer) - field_names, geom_names, featuredefn, fielddefns = schema_names(layer) +function Tables.schema(layer::AbstractFeatureLayer)::Tables.Schema + geom_names, field_names, featuredefn, fielddefns = + schema_names(layerdefn(layer)) ngeom = ArchGDAL.ngeom(featuredefn) - geomtypes = (IGeometry{ArchGDAL.gettype(ArchGDAL.getgeomdefn(featuredefn, i))} for i in 0:ngeom-1) - field_types = (_FIELDTYPE[gettype(fielddefn)] for fielddefn in fielddefns) - Tables.Schema((geom_names..., field_names...), (geomtypes..., field_types...)) + geom_types = + (IGeometry{gettype(getgeomdefn(featuredefn, i))} for i in 0:ngeom-1) + field_types = + (convert(DataType, gettype(fielddefn)) for fielddefn in fielddefns) + return Tables.Schema( + (geom_names..., field_names...), + (geom_types..., field_types...), + ) end -Tables.istable(::Type{<:Table}) = true -Tables.rowaccess(::Type{<:Table}) = true -Tables.rows(t::Table) = t +Tables.istable(::Type{<:AbstractFeatureLayer})::Bool = true +Tables.rowaccess(::Type{<:AbstractFeatureLayer})::Bool = true -function Base.iterate(t::Table, st = 0) - layer = getlayer(t) - st >= nfeature(layer) && return nothing - if iszero(st) - resetreading!(layer) - end - return nextnamedtuple(layer), st + 1 +function Tables.rows(layer::T)::T where {T<:AbstractFeatureLayer} + return layer end -function Base.getindex(t::Table, idx::Integer) - layer = getlayer(t) - setnextbyindex!(layer, idx-1) - return nextnamedtuple(layer) +function Tables.getcolumn(row::Feature, i::Int) + if i > nfield(row) + return getgeom(row, i - nfield(row) - 1) + elseif i > 0 + return getfield(row, i - 1) + else + return missing + end end -Base.IteratorSize(::Type{<:Table}) = Base.HasLength() -Base.size(t::Table) = nfeature(getlayer(t)) -Base.length(t::Table) = size(t) -Base.IteratorEltype(::Type{<:Table}) = Base.HasEltype() -Base.propertynames(t::Table) = Tables.schema(getlayer(t)).names -Base.getproperty(t::Table, s::Symbol) = [getproperty(row, s) for row in t] - -""" -Returns the feature row of a layer as a `NamedTuple` - -Calling it iteratively will work similar to `nextfeature` i.e. give the consecutive feature as `NamedTuple` -""" -function nextnamedtuple(layer::IFeatureLayer) - field_names, geom_names = schema_names(layer) - return nextfeature(layer) do feature - prop = (getfield(feature, name) for name in field_names) - geom = (getgeom(feature, idx-1) for idx in 1:length(geom_names)) - NamedTuple{(geom_names..., field_names...)}((geom..., prop...)) +function Tables.getcolumn(row::Feature, name::Symbol) + field = getfield(row, name) + if !ismissing(field) + return field + end + geom = getgeom(row, name) + if geom.ptr != C_NULL + return geom end + return missing end -function schema_names(layer::AbstractFeatureLayer) - featuredefn = layerdefn(layer) - fielddefns = (getfielddefn(featuredefn, i) for i in 0:nfield(layer)-1) - field_names = (Symbol(getname(fielddefn)) for fielddefn in fielddefns) - geom_names = collect(Symbol(getname(getgeomdefn(featuredefn, i-1))) for i in 1:ngeom(layer)) - replace!(geom_names, Symbol("")=>Symbol("geometry"), count=1) - return (field_names, geom_names, featuredefn, fielddefns) +function Tables.columnnames( + row::Feature, +)::NTuple{Int64(nfield(row) + ngeom(row)),Symbol} + geom_names, field_names = schema_names(getfeaturedefn(row)) + return (geom_names..., field_names...) end -function Base.show(io::IO, t::Table) - println(io, "Table with $(nfeature(getlayer(t))) features") +function schema_names(featuredefn::IFeatureDefnView) + fielddefns = (getfielddefn(featuredefn, i) for i in 0:nfield(featuredefn)-1) + field_names = (Symbol(getname(fielddefn)) for fielddefn in fielddefns) + geom_names = collect( + Symbol(getname(getgeomdefn(featuredefn, i - 1))) for + i in 1:ngeom(featuredefn) + ) + return (geom_names, field_names, featuredefn, fielddefns) end -Base.show(io::IO, ::MIME"text/plain", t::Table) = show(io, t) diff --git a/src/types.jl b/src/types.jl index 6d50bb60..affc7d17 100644 --- a/src/types.jl +++ b/src/types.jl @@ -1,63 +1,44 @@ import DiskArrays: AbstractDiskArray -const GDALColorTable = GDAL.GDALColorTableH -const GDALCoordTransform = GDAL.OGRCoordinateTransformationH -const GDALDataset = GDAL.GDALDatasetH -const GDALDriver = GDAL.GDALDriverH -const GDALFeature = GDAL.OGRFeatureH -const GDALFeatureDefn = GDAL.OGRFeatureDefnH -const GDALFeatureLayer = GDAL.OGRLayerH -const GDALField = GDAL.OGRField -const GDALFieldDefn = GDAL.OGRFieldDefnH -const GDALGeometry = GDAL.OGRGeometryH -const GDALGeomFieldDefn = GDAL.OGRGeomFieldDefnH -const GDALProgressFunc = GDAL.GDALProgressFunc -const GDALRasterAttrTable = GDAL.GDALRasterAttributeTableH -const GDALRasterBand = GDAL.GDALRasterBandH -const GDALSpatialRef = GDAL.OGRSpatialReferenceH -const GDALStyleManager = GDAL.OGRStyleMgrH -const GDALStyleTable = GDAL.OGRStyleTableH -const GDALStyleTool = GDAL.OGRStyleToolH - -const StringList = Ptr{Cstring} +import Base.convert abstract type AbstractGeometry <: GeoInterface.AbstractGeometry end - # needs to have a `ptr::GDALGeometry` attribute +# needs to have a `ptr::GDAL.OGRGeometryH` attribute abstract type AbstractSpatialRef end - # needs to have a `ptr::GDALSpatialRef` attribute +# needs to have a `ptr::GDAL.OGRSpatialReferenceH` attribute abstract type AbstractDataset end - # needs to have a `ptr::GDALDataset` attribute +# needs to have a `ptr::GDAL.GDALDatasetH` attribute abstract type AbstractFeatureDefn end - # needs to have a `ptr::GDALFeatureDefn` attribute +# needs to have a `ptr::GDAL.OGRFeatureDefnH` attribute abstract type AbstractFeatureLayer end - # needs to have a `ptr::GDALDataset` attribute +# needs to have a `ptr::GDAL.OGRLayerH` attribute abstract type AbstractFieldDefn end - # needs to have a `ptr::GDALFieldDefn` attribute +# needs to have a `ptr::GDAL.OGRFieldDefnH` attribute abstract type AbstractGeomFieldDefn end - # needs to have a `ptr::GDALGeomFieldDefn` attribute +# needs to have a `ptr::GDAL.OGRGeomFieldDefnH` attribute abstract type AbstractRasterBand{T} <: AbstractDiskArray{T,2} end - # needs to have a `ptr::GDALDataset` attribute +# needs to have a `ptr::GDAL.GDALRasterBandH` attribute mutable struct CoordTransform - ptr::GDALCoordTransform + ptr::GDAL.OGRCoordinateTransformationH end mutable struct Dataset <: AbstractDataset - ptr::GDALDataset + ptr::GDAL.GDALDatasetH - Dataset(ptr::GDALDataset = C_NULL) = new(ptr) + Dataset(ptr::GDAL.GDALDatasetH = C_NULL) = new(ptr) end mutable struct IDataset <: AbstractDataset - ptr::GDALDataset + ptr::GDAL.GDALDatasetH - function IDataset(ptr::GDALDataset = C_NULL) + function IDataset(ptr::GDAL.GDALDatasetH = C_NULL) dataset = new(ptr) finalizer(destroy, dataset) return dataset @@ -65,21 +46,21 @@ mutable struct IDataset <: AbstractDataset end mutable struct Driver - ptr::GDALDriver + ptr::GDAL.GDALDriverH end mutable struct Field - ptr::GDALField + ptr::GDAL.OGRField end mutable struct FieldDefn <: AbstractFieldDefn - ptr::GDALFieldDefn + ptr::GDAL.OGRFieldDefnH end mutable struct IFieldDefnView <: AbstractFieldDefn - ptr::GDALFieldDefn + ptr::GDAL.OGRFieldDefnH - function IFieldDefnView(ptr::GDALFieldDefn = C_NULL) + function IFieldDefnView(ptr::GDAL.OGRFieldDefnH = C_NULL) fielddefn = new(ptr) finalizer(destroy, fielddefn) return fielddefn @@ -87,21 +68,21 @@ mutable struct IFieldDefnView <: AbstractFieldDefn end mutable struct GeomFieldDefn <: AbstractGeomFieldDefn - ptr::GDALGeomFieldDefn + ptr::GDAL.OGRGeomFieldDefnH spatialref::AbstractSpatialRef function GeomFieldDefn( - ptr::GDALGeomFieldDefn = C_NULL; - spatialref::AbstractSpatialRef = SpatialRef() - ) + ptr::GDAL.OGRGeomFieldDefnH = C_NULL; + spatialref::AbstractSpatialRef = SpatialRef(), + ) return new(ptr, spatialref) end end mutable struct IGeomFieldDefnView <: AbstractGeomFieldDefn - ptr::GDALGeomFieldDefn + ptr::GDAL.OGRGeomFieldDefnH - function IGeomFieldDefnView(ptr::GDALGeomFieldDefn = C_NULL) + function IGeomFieldDefnView(ptr::GDAL.OGRGeomFieldDefnH = C_NULL) geomdefn = new(ptr) finalizer(destroy, geomdefn) return geomdefn @@ -109,35 +90,39 @@ mutable struct IGeomFieldDefnView <: AbstractGeomFieldDefn end mutable struct RasterAttrTable - ptr::GDALRasterAttrTable + ptr::GDAL.GDALRasterAttributeTableH end mutable struct StyleManager - ptr::GDALStyleManager + ptr::GDAL.OGRStyleMgrH + + StyleManager(ptr::GDAL.OGRStyleMgrH = C_NULL) = new(ptr) end mutable struct StyleTable - ptr::GDALStyleTable + ptr::GDAL.OGRStyleTableH + + StyleTable(ptr::GDAL.OGRStyleTableH = C_NULL) = new(ptr) end mutable struct StyleTool - ptr::GDALStyleTool + ptr::GDAL.OGRStyleToolH end mutable struct FeatureLayer <: AbstractFeatureLayer - ptr::GDALFeatureLayer + ptr::GDAL.OGRLayerH end mutable struct IFeatureLayer <: AbstractFeatureLayer - ptr::GDALFeatureLayer + ptr::GDAL.OGRLayerH ownedby::AbstractDataset spatialref::AbstractSpatialRef function IFeatureLayer( - ptr::GDALFeatureLayer = C_NULL; - ownedby::AbstractDataset = Dataset(), - spatialref::AbstractSpatialRef = SpatialRef() - ) + ptr::GDAL.OGRLayerH = C_NULL; + ownedby::AbstractDataset = Dataset(), + spatialref::AbstractSpatialRef = SpatialRef(), + ) layer = new(ptr, ownedby, spatialref) finalizer(destroy, layer) return layer @@ -145,266 +130,527 @@ mutable struct IFeatureLayer <: AbstractFeatureLayer end mutable struct Feature - ptr::GDALFeature + ptr::GDAL.OGRFeatureH end mutable struct FeatureDefn <: AbstractFeatureDefn - ptr::GDALFeatureDefn + ptr::GDAL.OGRFeatureDefnH end mutable struct IFeatureDefnView <: AbstractFeatureDefn - ptr::GDALFeatureDefn + ptr::GDAL.OGRFeatureDefnH - function IFeatureDefnView(ptr::GDALFeatureDefn = C_NULL) + function IFeatureDefnView(ptr::GDAL.OGRFeatureDefnH = C_NULL) featuredefn = new(ptr) finalizer(destroy, featuredefn) return featuredefn end end +"Fetch the pixel data type for this band." +pixeltype(ptr::GDAL.GDALRasterBandH)::DataType = + convert(GDALDataType, GDAL.gdalgetrasterdatatype(ptr)) + mutable struct RasterBand{T} <: AbstractRasterBand{T} - ptr::GDALRasterBand -end -function RasterBand(ptr::GDALRasterBand) - t = _JLTYPE[GDAL.gdalgetrasterdatatype(ptr)] - RasterBand{t}(ptr) + ptr::GDAL.GDALRasterBandH end +RasterBand(ptr::GDAL.GDALRasterBandH)::RasterBand{pixeltype(ptr)} = + RasterBand{pixeltype(ptr)}(ptr) + mutable struct IRasterBand{T} <: AbstractRasterBand{T} - ptr::GDALRasterBand + ptr::GDAL.GDALRasterBandH ownedby::AbstractDataset function IRasterBand{T}( - ptr::GDALRasterBand = C_NULL; - ownedby::AbstractDataset = Dataset() - ) where T + ptr::GDAL.GDALRasterBandH = C_NULL; + ownedby::AbstractDataset = Dataset(), + )::IRasterBand{T} where {T} rasterband = new(ptr, ownedby) finalizer(destroy, rasterband) return rasterband end end -function IRasterBand(ptr::GDALRasterBand; ownedby = Dataset()) - t = _JLTYPE[GDAL.gdalgetrasterdatatype(ptr)] - IRasterBand{t}(ptr, ownedby=ownedby) +function IRasterBand( + ptr::GDAL.GDALRasterBandH; + ownedby = Dataset(), +)::IRasterBand{pixeltype(ptr)} + return IRasterBand{pixeltype(ptr)}(ptr, ownedby = ownedby) end mutable struct SpatialRef <: AbstractSpatialRef - ptr::GDALSpatialRef + ptr::GDAL.OGRSpatialReferenceH - SpatialRef(ptr::GDALSpatialRef = C_NULL) = new(ptr) + SpatialRef(ptr::GDAL.OGRSpatialReferenceH = C_NULL) = new(ptr) end mutable struct ISpatialRef <: AbstractSpatialRef - ptr::GDALSpatialRef + ptr::GDAL.OGRSpatialReferenceH - function ISpatialRef(ptr::GDALSpatialRef = C_NULL) + function ISpatialRef(ptr::GDAL.OGRSpatialReferenceH = C_NULL) spref = new(ptr) finalizer(destroy, spref) return spref end end +function _infergeomtype(ptr::GDAL.OGRGeometryH = C_NULL)::OGRwkbGeometryType + return if ptr != C_NULL + convert(OGRwkbGeometryType, GDAL.ogr_g_getgeometrytype(ptr)) + else + wkbUnknown + end +end + mutable struct Geometry{OGRwkbGeometryType} <: AbstractGeometry - ptr::GDALGeometry + ptr::GDAL.OGRGeometryH - Geometry(ptr::GDALGeometry = C_NULL) = new{ptr != C_NULL ? GDAL.ogr_g_getgeometrytype(ptr) : GDAL.wkbUnknown}(ptr) + Geometry(ptr::GDAL.OGRGeometryH = C_NULL) = new{_infergeomtype(ptr)}(ptr) end -_geomtype(::Geometry{T}) where T = T +_geomtype(::Geometry{T}) where {T} = T mutable struct IGeometry{OGRwkbGeometryType} <: AbstractGeometry - ptr::GDALGeometry + ptr::GDAL.OGRGeometryH - function IGeometry(ptr::GDALGeometry = C_NULL) - geom = new{ptr != C_NULL ? GDAL.ogr_g_getgeometrytype(ptr) : GDAL.wkbUnknown}(ptr) + function IGeometry(ptr::GDAL.OGRGeometryH = C_NULL) + geom = new{_infergeomtype(ptr)}(ptr) finalizer(destroy, geom) return geom end end -_geomtype(::IGeometry{T}) where T = T +_geomtype(::IGeometry{T}) where {T} = T mutable struct ColorTable - ptr::GDALColorTable + ptr::GDAL.GDALColorTableH end -CPLErr = GDAL.CPLErr -CPLXMLNodeType = GDAL.CPLXMLNodeType -GDALDataType = GDAL.GDALDataType -GDALAsyncStatusType = GDAL.GDALAsyncStatusType -GDALAccess = GDAL.GDALAccess -GDALRWFlag = GDAL.GDALRWFlag -GDALRIOResampleAlg = GDAL.GDALRIOResampleAlg -GDALColorInterp = GDAL.GDALColorInterp -GDALPaletteInterp = GDAL.GDALPaletteInterp -GDALRATFieldType = GDAL.GDALRATFieldType -GDALRATFieldUsage = GDAL.GDALRATFieldUsage -GDALTileOrganization = GDAL.GDALTileOrganization -GDALGridAlgorithm = GDAL.GDALGridAlgorithm -OGRwkbGeometryType = GDAL.OGRwkbGeometryType -OGRwkbVariant = GDAL.OGRwkbVariant -OGRwkbByteOrder = GDAL.OGRwkbByteOrder -OGRFieldType = GDAL.OGRFieldType -OGRFieldSubType = GDAL.OGRFieldSubType -OGRJustification = GDAL.OGRJustification -OGRSTClassId = GDAL.OGRSTClassId -OGRSTUnitId = GDAL.OGRSTUnitId -OGRSTPenParam = GDAL.OGRSTPenParam -OGRSTBrushParam = GDAL.OGRSTBrushParam -OGRSTSymbolParam = GDAL.OGRSTSymbolParam -OGRSTLabelParam = GDAL.OGRSTLabelParam -GDALResampleAlg = GDAL.GDALResampleAlg -GWKAverageOrModeAlg = GDAL.GWKAverageOrModeAlg -OGRAxisOrientation = GDAL.OGRAxisOrientation - -"return the corresponding `DataType` in julia" -const _JLTYPE = Dict{GDAL.GDALDataType, DataType}( - GDAL.GDT_Unknown => Any, - GDAL.GDT_Byte => UInt8, - GDAL.GDT_UInt16 => UInt16, - GDAL.GDT_Int16 => Int16, - GDAL.GDT_UInt32 => UInt32, - GDAL.GDT_Int32 => Int32, - GDAL.GDT_Float32 => Float32, - GDAL.GDT_Float64 => Float64) - -const _GDALTYPE = Dict{DataType,GDAL.GDALDataType}( - Any => GDAL.GDT_Unknown, - UInt8 => GDAL.GDT_Byte, - UInt16 => GDAL.GDT_UInt16, - Int16 => GDAL.GDT_Int16, - UInt32 => GDAL.GDT_UInt32, - Int32 => GDAL.GDT_Int32, - Float32 => GDAL.GDT_Float32, - Float64 => GDAL.GDT_Float64) - -"return the corresponding `DataType` in julia" -const _FIELDTYPE = Dict{OGRFieldType, DataType}( - GDAL.OFTInteger => Int32, - GDAL.OFTIntegerList => Nothing, - GDAL.OFTReal => Float64, - GDAL.OFTRealList => Nothing, - GDAL.OFTString => String, - GDAL.OFTStringList => Nothing, - GDAL.OFTWideString => Nothing, # deprecated - GDAL.OFTWideStringList => Nothing, # deprecated - GDAL.OFTBinary => Nothing, - GDAL.OFTDate => Date, - GDAL.OFTTime => Nothing, - GDAL.OFTDateTime => DateTime, - GDAL.OFTInteger64 => Int64, - GDAL.OFTInteger64List => Nothing) - -@enum(GDALOpenFlag, - OF_ReadOnly = GDAL.GDAL_OF_READONLY, # 0x00 - OF_Update = GDAL.GDAL_OF_UPDATE, # 0x01 - # OF_All = GDAL.GDAL_OF_ALL, # 0x00 - OF_Raster = GDAL.GDAL_OF_RASTER, # 0x02 - OF_Vector = GDAL.GDAL_OF_VECTOR, # 0x04 - OF_GNM = GDAL.GDAL_OF_GNM, # 0x08 - OF_Kind_Mask = GDAL.GDAL_OF_KIND_MASK, # 0x1e - OF_Shared = GDAL.GDAL_OF_SHARED, # 0x20 - OF_Verbose_Error = GDAL.GDAL_OF_VERBOSE_ERROR, # 0x40 - OF_Internal = GDAL.GDAL_OF_INTERNAL, # 0x80 - # OF_DEFAULT_BLOCK_ACCESS = GDAL.GDAL_OF_DEFAULT_BLOCK_ACCESS, # 0 - OF_Array_Block_Access = GDAL.GDAL_OF_ARRAY_BLOCK_ACCESS, # 0x0100 - OF_Hashset_Block_Access = GDAL.GDAL_OF_HASHSET_BLOCK_ACCESS, # 0x0200 - # OF_RESERVED_1 = GDAL.GDAL_OF_RESERVED_1, # 0x0300 - OF_Block_Access_Mask = GDAL.GDAL_OF_BLOCK_ACCESS_MASK) # 0x0300 +eval( + @convert( + GDALDataType::GDAL.GDALDataType, + GDT_Unknown::GDAL.GDT_Unknown, + GDT_Byte::GDAL.GDT_Byte, + GDT_UInt16::GDAL.GDT_UInt16, + GDT_Int16::GDAL.GDT_Int16, + GDT_UInt32::GDAL.GDT_UInt32, + GDT_Int32::GDAL.GDT_Int32, + GDT_Float32::GDAL.GDT_Float32, + GDT_Float64::GDAL.GDT_Float64, + GDT_CInt16::GDAL.GDT_CInt16, + GDT_CInt32::GDAL.GDT_CInt32, + GDT_CFloat32::GDAL.GDT_CFloat32, + GDT_CFloat64::GDAL.GDT_CFloat64, + GDT_TypeCount::GDAL.GDT_TypeCount, + ) +) + +eval( + @convert( + GDALDataType::ImageCore.Normed, + GDT_Byte::ImageCore.N0f8, + GDT_UInt16::ImageCore.N0f16, + GDT_UInt32::ImageCore.N0f32, + ) +) + +eval( + @convert( + GDALDataType::DataType, + GDT_Unknown::Any, + GDT_Byte::UInt8, + GDT_UInt16::UInt16, + GDT_Int16::Int16, + GDT_UInt32::UInt32, + GDT_Int32::Int32, + GDT_Float32::Float32, + GDT_Float64::Float64, + ) +) + +eval( + @convert( + OGRFieldType::GDAL.OGRFieldType, + OFTInteger::GDAL.OFTInteger, + OFTIntegerList::GDAL.OFTIntegerList, + OFTReal::GDAL.OFTReal, + OFTRealList::GDAL.OFTRealList, + OFTString::GDAL.OFTString, + OFTStringList::GDAL.OFTStringList, + OFTWideString::GDAL.OFTWideString, + OFTWideStringList::GDAL.OFTWideStringList, + OFTBinary::GDAL.OFTBinary, + OFTDate::GDAL.OFTDate, + OFTTime::GDAL.OFTTime, + OFTDateTime::GDAL.OFTDateTime, + OFTInteger64::GDAL.OFTInteger64, + OFTInteger64List::GDAL.OFTInteger64List, + ) +) + +eval( + @convert( + OGRFieldType::DataType, + OFTInteger::Int32, + OFTIntegerList::Vector{Int32}, + OFTReal::Float64, + OFTRealList::Vector{Float64}, + OFTString::String, + OFTStringList::Vector{String}, + OFTWideString::Nothing, + OFTWideStringList::Nothing, + OFTBinary::Vector{UInt8}, + OFTDate::Dates.Date, + OFTTime::Dates.Time, + OFTDateTime::Dates.DateTime, + OFTInteger64::Int64, + OFTInteger64List::Vector{Int64}, + ) +) + +eval( + @convert( + OGRFieldSubType::GDAL.OGRFieldSubType, + OFSTNone::GDAL.OFSTNone, + OFSTBoolean::GDAL.OFSTBoolean, + OFSTInt16::GDAL.OFSTInt16, + OFSTFloat32::GDAL.OFSTFloat32, + OFSTJSON::GDAL.OFSTJSON, + ) +) + +eval( + @convert( + OGRFieldSubType::DataType, + OFSTNone::Nothing, + OFSTBoolean::Bool, + OFSTInt16::Int16, + OFSTFloat32::Float32, + OFSTJSON::String, + ) +) + +eval( + @convert( + OGRJustification::GDAL.OGRJustification, + OJUndefined::GDAL.OJUndefined, + OJLeft::GDAL.OJLeft, + OJRight::GDAL.OJRight, + ) +) + +eval( + @convert( + GDALRATFieldType::GDAL.GDALRATFieldType, + GFT_Integer::GDAL.GFT_Integer, + GFT_Real::GDAL.GFT_Real, + GFT_String::GDAL.GFT_String, + ) +) + +eval( + @convert( + GDALRATFieldUsage::GDAL.GDALRATFieldUsage, + GFU_Generic::GDAL.GFU_Generic, + GFU_PixelCount::GDAL.GFU_PixelCount, + GFU_Name::GDAL.GFU_Name, + GFU_Min::GDAL.GFU_Min, + GFU_Max::GDAL.GFU_Max, + GFU_MinMax::GDAL.GFU_MinMax, + GFU_Red::GDAL.GFU_Red, + GFU_Green::GDAL.GFU_Green, + GFU_Blue::GDAL.GFU_Blue, + GFU_Alpha::GDAL.GFU_Alpha, + GFU_RedMin::GDAL.GFU_RedMin, + GFU_GreenMin::GDAL.GFU_GreenMin, + GFU_BlueMin::GDAL.GFU_BlueMin, + GFU_AlphaMin::GDAL.GFU_AlphaMin, + GFU_RedMax::GDAL.GFU_RedMax, + GFU_GreenMax::GDAL.GFU_GreenMax, + GFU_BlueMax::GDAL.GFU_BlueMax, + GFU_AlphaMax::GDAL.GFU_AlphaMax, + GFU_MaxCount::GDAL.GFU_MaxCount, + ) +) + +eval( + @convert( + GDALAccess::GDAL.GDALAccess, + GA_ReadOnly::GDAL.GA_ReadOnly, + GA_Update::GDAL.GA_Update, + ) +) + +eval( + @convert( + GDALRWFlag::GDAL.GDALRWFlag, + GF_Read::GDAL.GF_Read, + GF_Write::GDAL.GF_Write, + ) +) + +eval( + @convert( + GDALPaletteInterp::GDAL.GDALPaletteInterp, + GPI_Gray::GDAL.GPI_Gray, + GPI_RGB::GDAL.GPI_RGB, + GPI_CMYK::GDAL.GPI_CMYK, + GPI_HLS::GDAL.GPI_HLS, + ) +) + +eval( + @convert( + GDALColorInterp::GDAL.GDALColorInterp, + GCI_Undefined::GDAL.GCI_Undefined, + GCI_GrayIndex::GDAL.GCI_GrayIndex, + GCI_PaletteIndex::GDAL.GCI_PaletteIndex, + GCI_RedBand::GDAL.GCI_RedBand, + GCI_GreenBand::GDAL.GCI_GreenBand, + GCI_BlueBand::GDAL.GCI_BlueBand, + GCI_AlphaBand::GDAL.GCI_AlphaBand, + GCI_HueBand::GDAL.GCI_HueBand, + GCI_SaturationBand::GDAL.GCI_SaturationBand, + GCI_LightnessBand::GDAL.GCI_LightnessBand, + GCI_CyanBand::GDAL.GCI_CyanBand, + GCI_MagentaBand::GDAL.GCI_MagentaBand, + GCI_YellowBand::GDAL.GCI_YellowBand, + GCI_BlackBand::GDAL.GCI_BlackBand, + GCI_YCbCr_YBand::GDAL.GCI_YCbCr_YBand, + GCI_YCbCr_CbBand::GDAL.GCI_YCbCr_CbBand, + GCI_YCbCr_CrBand::GDAL.GCI_YCbCr_CrBand, + ) +) + +eval( + @convert( + GDALAsyncStatusType::GDAL.GDALAsyncStatusType, + GARIO_PENDING::GDAL.GARIO_PENDING, + GARIO_UPDATE::GDAL.GARIO_UPDATE, + GARIO_ERROR::GDAL.GARIO_ERROR, + GARIO_COMPLETE::GDAL.GARIO_COMPLETE, + GARIO_TypeCount::GDAL.GARIO_TypeCount, + ) +) + +eval( + @convert( + OGRSTClassId::GDAL.OGRSTClassId, + OGRSTCNone::GDAL.OGRSTCNone, + OGRSTCPen::GDAL.OGRSTCPen, + OGRSTCBrush::GDAL.OGRSTCBrush, + OGRSTCSymbol::GDAL.OGRSTCSymbol, + OGRSTCLabel::GDAL.OGRSTCLabel, + OGRSTCVector::GDAL.OGRSTCVector, + ) +) + +eval( + @convert( + OGRSTUnitId::GDAL.OGRSTUnitId, + OGRSTUGround::GDAL.OGRSTUGround, + OGRSTUPixel::GDAL.OGRSTUPixel, + OGRSTUPoints::GDAL.OGRSTUPoints, + OGRSTUMM::GDAL.OGRSTUMM, + OGRSTUCM::GDAL.OGRSTUCM, + OGRSTUInches::GDAL.OGRSTUInches, + ) +) + +eval( + @convert( + OGRwkbGeometryType::GDAL.OGRwkbGeometryType, + wkbUnknown::GDAL.wkbUnknown, + wkbPoint::GDAL.wkbPoint, + wkbLineString::GDAL.wkbLineString, + wkbPolygon::GDAL.wkbPolygon, + wkbMultiPoint::GDAL.wkbMultiPoint, + wkbMultiLineString::GDAL.wkbMultiLineString, + wkbMultiPolygon::GDAL.wkbMultiPolygon, + wkbGeometryCollection::GDAL.wkbGeometryCollection, + wkbCircularString::GDAL.wkbCircularString, + wkbCompoundCurve::GDAL.wkbCompoundCurve, + wkbCurvePolygon::GDAL.wkbCurvePolygon, + wkbMultiCurve::GDAL.wkbMultiCurve, + wkbMultiSurface::GDAL.wkbMultiSurface, + wkbCurve::GDAL.wkbCurve, + wkbSurface::GDAL.wkbSurface, + wkbPolyhedralSurface::GDAL.wkbPolyhedralSurface, + wkbTIN::GDAL.wkbTIN, + wkbTriangle::GDAL.wkbTriangle, + wkbNone::GDAL.wkbNone, + wkbLinearRing::GDAL.wkbLinearRing, + wkbCircularStringZ::GDAL.wkbCircularStringZ, + wkbCompoundCurveZ::GDAL.wkbCompoundCurveZ, + wkbCurvePolygonZ::GDAL.wkbCurvePolygonZ, + wkbMultiCurveZ::GDAL.wkbMultiCurveZ, + wkbMultiSurfaceZ::GDAL.wkbMultiSurfaceZ, + wkbCurveZ::GDAL.wkbCurveZ, + wkbSurfaceZ::GDAL.wkbSurfaceZ, + wkbPolyhedralSurfaceZ::GDAL.wkbPolyhedralSurfaceZ, + wkbTINZ::GDAL.wkbTINZ, + wkbTriangleZ::GDAL.wkbTriangleZ, + wkbPointM::GDAL.wkbPointM, + wkbLineStringM::GDAL.wkbLineStringM, + wkbPolygonM::GDAL.wkbPolygonM, + wkbMultiPointM::GDAL.wkbMultiPointM, + wkbMultiLineStringM::GDAL.wkbMultiLineStringM, + wkbMultiPolygonM::GDAL.wkbMultiPolygonM, + wkbGeometryCollectionM::GDAL.wkbGeometryCollectionM, + wkbCircularStringM::GDAL.wkbCircularStringM, + wkbCompoundCurveM::GDAL.wkbCompoundCurveM, + wkbCurvePolygonM::GDAL.wkbCurvePolygonM, + wkbMultiCurveM::GDAL.wkbMultiCurveM, + wkbMultiSurfaceM::GDAL.wkbMultiSurfaceM, + wkbCurveM::GDAL.wkbCurveM, + wkbSurfaceM::GDAL.wkbSurfaceM, + wkbPolyhedralSurfaceM::GDAL.wkbPolyhedralSurfaceM, + wkbTINM::GDAL.wkbTINM, + wkbTriangleM::GDAL.wkbTriangleM, + wkbPointZM::GDAL.wkbPointZM, + wkbLineStringZM::GDAL.wkbLineStringZM, + wkbPolygonZM::GDAL.wkbPolygonZM, + wkbMultiPointZM::GDAL.wkbMultiPointZM, + wkbMultiLineStringZM::GDAL.wkbMultiLineStringZM, + wkbMultiPolygonZM::GDAL.wkbMultiPolygonZM, + wkbGeometryCollectionZM::GDAL.wkbGeometryCollectionZM, + wkbCircularStringZM::GDAL.wkbCircularStringZM, + wkbCompoundCurveZM::GDAL.wkbCompoundCurveZM, + wkbCurvePolygonZM::GDAL.wkbCurvePolygonZM, + wkbMultiCurveZM::GDAL.wkbMultiCurveZM, + wkbMultiSurfaceZM::GDAL.wkbMultiSurfaceZM, + wkbCurveZM::GDAL.wkbCurveZM, + wkbSurfaceZM::GDAL.wkbSurfaceZM, + wkbPolyhedralSurfaceZM::GDAL.wkbPolyhedralSurfaceZM, + wkbTINZM::GDAL.wkbTINZM, + wkbTriangleZM::GDAL.wkbTriangleZM, + wkbPoint25D::GDAL.wkbPoint25D, + wkbLineString25D::GDAL.wkbLineString25D, + wkbPolygon25D::GDAL.wkbPolygon25D, + wkbMultiPoint25D::GDAL.wkbMultiPoint25D, + wkbMultiLineString25D::GDAL.wkbMultiLineString25D, + wkbMultiPolygon25D::GDAL.wkbMultiPolygon25D, + wkbGeometryCollection25D::GDAL.wkbGeometryCollection25D, + ) +) + +function basetype(gt::OGRwkbGeometryType)::OGRwkbGeometryType + wkbGeomType = convert(GDAL.OGRwkbGeometryType, gt) + wkbGeomType &= (~0x80000000) # Remove 2.5D flag. + wkbGeomType %= 1000 # Normalize Z, M, and ZM types. + return GDAL.OGRwkbGeometryType(wkbGeomType) +end + +eval( + @convert( + OGRwkbByteOrder::GDAL.OGRwkbByteOrder, + wkbXDR::GDAL.wkbXDR, + wkbNDR::GDAL.wkbNDR, + ) +) import Base.| -|(x::GDALOpenFlag,y::UInt8) = UInt8(x) | y -|(x::UInt8,y::GDALOpenFlag) = x | UInt8(y) -|(x::GDALOpenFlag,y::GDALOpenFlag) = UInt8(x) | UInt8(y) +for T in (GDALOpenFlag, FieldValidation) + eval(quote + |(x::$T, y::UInt8)::UInt8 = UInt8(x) | y + |(x::UInt8, y::$T)::UInt8 = x | UInt8(y) + |(x::$T, y::$T)::UInt8 = UInt8(x) | UInt8(y) + end) +end """ typesize(dt::GDALDataType) -Get data type size in bits. +Get the number of bits or zero if it is not recognised. """ -typesize(dt::GDALDataType) = GDAL.gdalgetdatatypesize(dt) +typesize(dt::GDALDataType)::Integer = GDAL.gdalgetdatatypesize(dt) """ typename(dt::GDALDataType) name (string) corresponding to GDAL data type. """ -typename(dt::GDALDataType) = GDAL.gdalgetdatatypename(dt) +typename(dt::GDALDataType)::String = GDAL.gdalgetdatatypename(dt) """ gettype(name::AbstractString) Returns GDAL data type by symbolic name. """ -gettype(name::AbstractString) = GDAL.gdalgetdatatypebyname(name) +gettype(name::AbstractString)::GDALDataType = GDAL.gdalgetdatatypebyname(name) """ typeunion(dt1::GDALDataType, dt2::GDALDataType) Return the smallest data type that can fully express both input data types. """ -typeunion(dt1::GDALDataType, dt2::GDALDataType) = GDAL.gdaldatatypeunion(dt1, dt2) +typeunion(dt1::GDALDataType, dt2::GDALDataType)::GDALDataType = + GDAL.gdaldatatypeunion(dt1, dt2) """ iscomplex(dtype::GDALDataType) `true` if `dtype` is one of `GDT_{CInt16|CInt32|CFloat32|CFloat64}.` """ -iscomplex(dtype::GDALDataType) = Bool(GDAL.gdaldatatypeiscomplex(dtype)) +iscomplex(dtype::GDALDataType)::Bool = Bool(GDAL.gdaldatatypeiscomplex(dtype)) """ getname(dtype::GDALAsyncStatusType) Get name of AsyncStatus data type. """ -getname(dtype::GDALAsyncStatusType) = GDAL.gdalgetasyncstatustypename(dtype) +getname(dtype::GDALAsyncStatusType)::String = + GDAL.gdalgetasyncstatustypename(dtype) """ asyncstatustype(name::AbstractString) Get AsyncStatusType by symbolic name. """ -asyncstatustype(name::AbstractString) = GDAL.gdalgetasyncstatustypebyname(name) +asyncstatustype(name::AbstractString)::GDALAsyncStatusType = + GDAL.gdalgetasyncstatustypebyname(name) """ getname(obj::GDALColorInterp) Return name (string) corresponding to color interpretation. """ -getname(obj::GDALColorInterp) = GDAL.gdalgetcolorinterpretationname(obj) +getname(obj::GDALColorInterp)::String = GDAL.gdalgetcolorinterpretationname(obj) """ colorinterp(name::AbstractString) Get color interpretation corresponding to the given symbolic name. """ -colorinterp(name::AbstractString) = GDAL.gdalgetcolorinterpretationbyname(name) +colorinterp(name::AbstractString)::GDALColorInterp = + GDAL.gdalgetcolorinterpretationbyname(name) """ getname(obj::GDALPaletteInterp) Get name of palette interpretation. """ -getname(obj::GDALPaletteInterp) = GDAL.gdalgetpaletteinterpretationname(obj) +getname(obj::GDALPaletteInterp)::String = + GDAL.gdalgetpaletteinterpretationname(obj) """ getname(obj::OGRFieldType) Fetch human readable name for a field type. """ -getname(obj::OGRFieldType) = GDAL.ogr_getfieldtypename(obj) +getname(obj::OGRFieldType)::String = GDAL.ogr_getfieldtypename(obj) """ getname(obj::OGRFieldSubType) Fetch human readable name for a field subtype. """ -getname(obj::OGRFieldSubType) = GDAL.ogr_getfieldsubtypename(obj) +getname(obj::OGRFieldSubType)::String = GDAL.ogr_getfieldsubtypename(obj) """ arecompatible(dtype::OGRFieldType, subtype::OGRFieldSubType) Return if type and subtype are compatible. """ -arecompatible(dtype::OGRFieldType, subtype::OGRFieldSubType) = +arecompatible(dtype::OGRFieldType, subtype::OGRFieldSubType)::Bool = Bool(GDAL.ogr_aretypesubtypecompatible(dtype, subtype)) diff --git a/src/utilities.jl b/src/utilities.jl index 01240b6e..db4611bd 100644 --- a/src/utilities.jl +++ b/src/utilities.jl @@ -1,5 +1,5 @@ """ - gdalinfo(dataset::Dataset, options = String[]) + gdalinfo(dataset::AbstractDataset, options = String[]) List various information about a GDAL supported raster dataset. @@ -11,7 +11,7 @@ List various information about a GDAL supported raster dataset. ### Returns String corresponding to the information about the raster dataset. """ -function gdalinfo(dataset::Dataset, options = String[]) +function gdalinfo(dataset::AbstractDataset, options = String[])::String options = GDAL.gdalinfooptionsnew(options, C_NULL) result = GDAL.gdalinfo(dataset.ptr, options) GDAL.gdalinfooptionsfree(options) @@ -19,7 +19,10 @@ function gdalinfo(dataset::Dataset, options = String[]) end """ - unsafe_gdaltranslate(dataset::Dataset, options = String[]; dest = "/vsimem/tmp") + unsafe_gdaltranslate( + dataset::AbstractDataset, + options = String[]; + dest = "/vsimem/tmp") Convert raster data between different formats. @@ -32,10 +35,10 @@ Convert raster data between different formats. The output dataset. """ function unsafe_gdaltranslate( - dataset::Dataset, - options = String[]; - dest = "/vsimem/tmp" - ) + dataset::AbstractDataset, + options = String[]; + dest = "/vsimem/tmp", +)::Dataset options = GDAL.gdaltranslateoptionsnew(options, C_NULL) usage_error = Ref{Cint}() result = GDAL.gdaltranslate(dest, dataset.ptr, options, usage_error) @@ -44,7 +47,10 @@ function unsafe_gdaltranslate( end """ - unsafe_gdalwarp(datasets::Vector{Dataset}, options = String[]; dest = "/vsimem/tmp") + unsafe_gdalwarp( + datasets::Vector{<:AbstractDataset}, + options = String[]; + dest = "/vsimem/tmp") Image reprojection and warping function. @@ -57,20 +63,29 @@ Image reprojection and warping function. The output dataset. """ function unsafe_gdalwarp( - datasets::Vector{Dataset}, - options = String[]; - dest = "/vsimem/tmp" - ) + datasets::Vector{<:AbstractDataset}, + options = String[]; + dest = "/vsimem/tmp", +)::Dataset options = GDAL.gdalwarpappoptionsnew(options, C_NULL) usage_error = Ref{Cint}() - result = GDAL.gdalwarp(dest, C_NULL, - length(datasets), [ds.ptr for ds in datasets], options, usage_error) + result = GDAL.gdalwarp( + dest, + C_NULL, + length(datasets), + [ds.ptr for ds in datasets], + options, + usage_error, + ) GDAL.gdalwarpappoptionsfree(options) return Dataset(result) end """ - unsafe_gdalvectortranslate(datasets::Vector{Dataset}, options = String[]; dest = "/vsimem/tmp") + unsafe_gdalvectortranslate( + datasets::Vector{<:AbstractDataset}, + options = String[]; + dest = "/vsimem/tmp") Convert vector data between file formats. @@ -83,20 +98,31 @@ Convert vector data between file formats. The output dataset. """ function unsafe_gdalvectortranslate( - datasets::Vector{Dataset}, - options = String[]; - dest = "/vsimem/tmp" - ) + datasets::Vector{<:AbstractDataset}, + options = String[]; + dest = "/vsimem/tmp", +)::Dataset options = GDAL.gdalvectortranslateoptionsnew(options, C_NULL) usage_error = Ref{Cint}() - result = GDAL.gdalvectortranslate(dest, C_NULL, length(datasets), - [ds.ptr for ds in datasets], options, usage_error) + result = GDAL.gdalvectortranslate( + dest, + C_NULL, + length(datasets), + [ds.ptr for ds in datasets], + options, + usage_error, + ) GDAL.gdalvectortranslateoptionsfree(options) return Dataset(result) end """ - unsafe_gdaldem(dataset::Dataset, processing::String, options = String[]; dest = "/vsimem/tmp", colorfile) + unsafe_gdaldem( + dataset::AbstractDataset, + processing::String, + options = String[]; + dest = "/vsimem/tmp", + colorfile) Tools to analyze and visualize DEMs. @@ -115,25 +141,34 @@ Tools to analyze and visualize DEMs. The output dataset. """ function unsafe_gdaldem( - dataset::Dataset, - processing::String, - options = String[]; - dest = "/vsimem/tmp", - colorfile = C_NULL - ) + dataset::AbstractDataset, + processing::String, + options = String[]; + dest = "/vsimem/tmp", + colorfile = C_NULL, +)::Dataset if processing == "color-relief" @assert colorfile != C_NULL end options = GDAL.gdaldemprocessingoptionsnew(options, C_NULL) usage_error = Ref{Cint}() - result = GDAL.gdaldemprocessing(dest, dataset.ptr, processing, colorfile, - options, usage_error) + result = GDAL.gdaldemprocessing( + dest, + dataset.ptr, + processing, + colorfile, + options, + usage_error, + ) GDAL.gdaldemprocessingoptionsfree(options) return Dataset(result) end """ - unsafe_gdalnearblack(dataset::Dataset, options = String[]; dest = "/vsimem/tmp") + unsafe_gdalnearblack( + dataset::AbstractDataset, + options = String[]; + dest = "/vsimem/tmp") Convert nearly black/white borders to exact value. @@ -146,10 +181,10 @@ Convert nearly black/white borders to exact value. The output dataset. """ function unsafe_gdalnearblack( - dataset::Dataset, - options = String[]; - dest = "/vsimem/tmp" - ) + dataset::AbstractDataset, + options = String[]; + dest = "/vsimem/tmp", +)::Dataset options = GDAL.gdalnearblackoptionsnew(options, C_NULL) usage_error = Ref{Cint}() result = GDAL.gdalnearblack(dest, C_NULL, dataset.ptr, options, usage_error) @@ -158,7 +193,10 @@ function unsafe_gdalnearblack( end """ - unsafe_gdalgrid(dataset::Dataset, options = String[]; dest = "/vsimem/tmp") + unsafe_gdalgrid( + dataset::AbstractDataset, + options = String[]; + dest = "/vsimem/tmp") Create a raster from the scattered data. @@ -171,10 +209,10 @@ Create a raster from the scattered data. The output dataset. """ function unsafe_gdalgrid( - dataset::Dataset, - options = String[]; - dest = "/vsimem/tmp" - ) + dataset::AbstractDataset, + options = String[]; + dest = "/vsimem/tmp", +)::Dataset options = GDAL.gdalgridoptionsnew(options, C_NULL) usage_error = Ref{Cint}() result = GDAL.gdalgrid(dest, dataset.ptr, options, usage_error) @@ -183,7 +221,10 @@ function unsafe_gdalgrid( end """ - unsafe_gdalrasterize(dataset::Dataset, options = String[]; dest = "/vsimem/tmp") + unsafe_gdalrasterize( + dataset::AbstractDataset, + options = String[]; + dest = "/vsimem/tmp") Burn vector geometries into a raster. @@ -196,10 +237,10 @@ Burn vector geometries into a raster. The output dataset. """ function unsafe_gdalrasterize( - dataset::Dataset, - options = String[]; - dest = "/vsimem/tmp" - ) + dataset::AbstractDataset, + options = String[]; + dest = "/vsimem/tmp", +)::Dataset options = GDAL.gdalrasterizeoptionsnew(options, C_NULL) usage_error = Ref{Cint}() result = GDAL.gdalrasterize(dest, C_NULL, dataset.ptr, options, usage_error) @@ -208,7 +249,10 @@ function unsafe_gdalrasterize( end """ - unsafe_gdalbuildvrt(datasets::Vector{Dataset}, options = String[]; dest = "/vsimem/tmp") + unsafe_gdalbuildvrt( + datasets::Vector{<:AbstractDataset}, + options = String[]; + dest = "/vsimem/tmp") Build a VRT from a list of datasets. @@ -221,14 +265,20 @@ Build a VRT from a list of datasets. The output dataset. """ function unsafe_gdalbuildvrt( - datasets::Vector{Dataset}, - options = String[]; - dest = "/vsimem/tmp" - ) + datasets::Vector{<:AbstractDataset}, + options = String[]; + dest = "/vsimem/tmp", +)::Dataset options = GDAL.gdalbuildvrtoptionsnew(options, C_NULL) usage_error = Ref{Cint}() - result = GDAL.gdalbuildvrt(dest, length(datasets), - [ds.ptr for ds in datasets], C_NULL, options, usage_error) + result = GDAL.gdalbuildvrt( + dest, + length(datasets), + [ds.ptr for ds in datasets], + C_NULL, + options, + usage_error, + ) GDAL.gdalbuildvrtoptionsfree(options) return Dataset(result) end diff --git a/src/utils.jl b/src/utils.jl index e4236760..9a3822f9 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -1,3 +1,50 @@ +""" + + eval(@convert(GDALRWFlag::GDAL.GDALRWFlag, + GF_Read::GDAL.GF_Read, + GF_Write::GDAL.GF_Write, + )) + +does the equivalent of + + Base.convert(::Type{GDAL.GDALRWFlag}, ft::GDALRWFlag) = + Dict{GDALRWFlag, GDAL.GDALRWFlag}( + GF_Read => GDAL.GF_Read, + GF_Write => GDAL.GF_Write + )[ft] + + Base.convert(::Type{GDALRWFlag}, ft::GDAL.GDALRWFlag) = + Dict{GDAL.GDALRWFlag, GDALRWFlag}( + GDAL.GF_Read => GF_Read, + GDAL.GF_Write => GF_Write + )[ft] + +""" +macro convert(args...) + @assert length(args) > 0 + @assert args[1].head == :(::) + type1 = esc(args[1].args[1]) + type2 = esc(args[1].args[2]) + forward_map = Expr[Expr(:tuple, esc.(a.args)...) for a in args[2:end]] + reverse_map = + Expr[Expr(:tuple, esc.(reverse(a.args))...) for a in args[2:end]] + quote + function Base.convert(::Type{$type2}, ft::$type1) + fwd = Dict{$type1,$type2}(Tuple{$type1,$type2}[$(forward_map...)]) + return get(fwd, ft) do + return error("Unknown type: $ft") + end + end + + function Base.convert(::Type{$type1}, ft::$type2) + rev = Dict{$type2,$type1}(Tuple{$type2,$type1}[$(reverse_map...)]) + return get(rev, ft) do + return error("Unknown type: $ft") + end + end + end +end + macro gdal(args...) @assert length(args) > 0 @assert args[1].head == :(::) @@ -5,7 +52,9 @@ macro gdal(args...) returntype = args[1].args[2] argtypes = Expr(:tuple, [esc(a.args[2]) for a in args[2:end]]...) args = [esc(a.args[1]) for a in args[2:end]] - return quote ccall($fhead, $returntype, $argtypes, $(args...)) end + return quote + ccall($fhead, $returntype, $argtypes, $(args...)) + end end macro ogrerr(code, message) @@ -33,7 +82,7 @@ macro cplwarn(code, message) end macro cplprogress(progressfunc) - @cfunction($(esc(progressfunc)),Cint,(Cdouble,Cstring,Ptr{Cvoid})) + @cfunction($(esc(progressfunc)), Cint, (Cdouble, Cstring, Ptr{Cvoid})) end # """ @@ -60,18 +109,19 @@ end Fetch list of (non-empty) metadata domains. """ -metadatadomainlist(obj) = GDAL.gdalgetmetadatadomainlist(obj.ptr) +metadatadomainlist(obj)::Vector{String} = + GDAL.gdalgetmetadatadomainlist(obj.ptr) """ metadata(obj; domain::AbstractString = "") Fetch metadata. Note that relatively few formats return any metadata. """ -metadata(obj; domain::AbstractString = "") = +metadata(obj; domain::AbstractString = "")::Vector{String} = GDAL.gdalgetmetadata(obj.ptr, domain) """ - metadataitem(obj, name::AbstractString, domain::AbstractString) + metadataitem(obj, name::AbstractString, domain::AbstractString) Fetch single metadata item. @@ -82,8 +132,14 @@ Fetch single metadata item. ### Returns The metadata item on success, or an empty string on failure. """ -function metadataitem(obj, name::AbstractString; domain::AbstractString = "") +function metadataitem( + obj, + name::AbstractString; + domain::AbstractString = "", +)::String item = GDAL.gdalgetmetadataitem(obj.ptr, name, domain) + # Use `=== nothing` instead of isnothing() for performance. + # See https://github.com/JuliaLang/julia/pull/36444 for context. return item === nothing ? "" : item end @@ -106,8 +162,10 @@ defined in the environment. If `setconfigoption()` is called several times with the same key, the value provided during the last call will be used. """ -setconfigoption(option::AbstractString, value) = +function setconfigoption(option::AbstractString, value)::Nothing GDAL.cplsetconfigoption(option, value) + return nothing +end """ clearconfigoption(option::AbstractString) @@ -117,7 +175,10 @@ This function can be used to clear a setting. Note: it will not unset an existing environment variable; it will just unset a value previously set by `setconfigoption()`. """ -clearconfigoption(option::AbstractString) = setconfigoption(option, C_NULL) +function clearconfigoption(option::AbstractString)::Nothing + setconfigoption(option, C_NULL) + return nothing +end """ getconfigoption(option::AbstractString, default = C_NULL) @@ -135,11 +196,9 @@ it in environment variables. ### Returns the value associated to the key, or the default value if not found. """ -function getconfigoption(option::AbstractString, default = C_NULL) - result = @gdal(CPLGetConfigOption::Cstring, - option::Cstring, - default::Cstring - ) +function getconfigoption(option::AbstractString, default = C_NULL)::String + result = + @gdal(CPLGetConfigOption::Cstring, option::Cstring, default::Cstring) return (result == C_NULL) ? "" : unsafe_string(result) end @@ -159,8 +218,10 @@ This function sets the configuration option that only applies in the current thread, as opposed to `setconfigoption()` which sets an option that applies on all threads. """ -setthreadconfigoption(option::AbstractString, value) = +function setthreadconfigoption(option::AbstractString, value)::Nothing GDAL.cplsetthreadlocalconfigoption(option, value) + return nothing +end """ clearthreadconfigoption(option::AbstractString) @@ -170,16 +231,19 @@ This function can be used to clear a setting. Note: it will not unset an existing environment variable; it will just unset a value previously set by `setthreadconfigoption()`. """ -clearthreadconfigoption(option::AbstractString) = +function clearthreadconfigoption(option::AbstractString)::Nothing setthreadconfigoption(option, C_NULL) + return nothing +end """ getthreadconfigoption(option::AbstractString, default = C_NULL) Same as `getconfigoption()` but with settings from `setthreadconfigoption()`. """ -function getthreadconfigoption(option::AbstractString, default = C_NULL) - result = @gdal(CPLGetThreadLocalConfigOption::Cstring, +function getthreadconfigoption(option::AbstractString, default = C_NULL)::String + result = @gdal( + CPLGetThreadLocalConfigOption::Cstring, option::Cstring, default::Cstring ) diff --git a/test/Project.toml b/test/Project.toml new file mode 100644 index 00000000..134631c2 --- /dev/null +++ b/test/Project.toml @@ -0,0 +1,15 @@ +[deps] +ColorTypes = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" +Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" +DiskArrays = "3c3547ce-8d99-4f5e-a174-61eb10b00ae3" +Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" +Downloads = "f43a241f-c20a-4ad4-852c-f6b1247861c6" +GDAL = "add2ef01-049f-52c4-9ee2-e494f65e021a" +GeoFormatTypes = "68eda718-8dee-11e9-39e7-89f7f65f511f" +GeoInterface = "cf35fbd7-0cd7-5166-be24-54bfbe79505f" +ImageCore = "a09fc81d-aa75-5fe9-8630-4744c3626534" +Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" +SHA = "ea8e919c-243c-51af-8825-aaa63cd721ce" +Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" +Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/test/README.md b/test/README.md new file mode 100644 index 00000000..c1e0c52b --- /dev/null +++ b/test/README.md @@ -0,0 +1,24 @@ +# ArchGDAL.jl unit tests + +This package uses the [standard library for unit testing](https://docs.julialang.org/en/v1/stdlib/Test/). To run the suite of tests in this directory, + +```julia +pkg> test ArchGDAL +``` + +## Working with Data + +In general, we prefer for unit tests to be written independently of the need for data to be fetched from remote files. + +If you are introducing data to be used for unit testing, please be mindful for it to be released under an appropriate license, and for it to be pared down into a sufficiently small file that still exercises the corresponding logic to be tested. + +The data used for testing in this package are fetched from https://github.com/yeesian/ArchGDALDatasets in [remotefiles.jl](https://github.com/yeesian/ArchGDAL.jl/blob/master/test/remotefiles.jl). + +To add a file, please upload it to that repository with the corresponding license, and follow the below steps to generate the SHA: + +```julia +julia> using SHA +julia> open(filepath/filename) do f + bytes2hex(sha256(f)) + end +``` diff --git a/test/remotefiles.jl b/test/remotefiles.jl index 9193637e..a8fe07de 100644 --- a/test/remotefiles.jl +++ b/test/remotefiles.jl @@ -1,10 +1,8 @@ +using Downloads +using SHA + # this file downloads files which are used during testing the package # if they are already present and their checksum matches, they are not downloaded again - -using Pkg.PlatformEngines - -probe_platform_engines!() # for download - const testdatadir = @__DIR__ REPO_URL = "https://github.com/yeesian/ArchGDALDatasets/blob/master/" @@ -20,31 +18,164 @@ julia> open(filepath/filename) do f ``` """ remotefiles = [ - ("data/multi_geom.csv", "00520017658b66ff21e40cbf553672fa8e280cddae6e7a5d1f8bd36bcd521770"), - ("data/missing_testcase.csv", "d49ba446aae9ef334350b64c876b4de652f28595fdecf78bea4e16af4033f7c6"), - ("data/point.geojson", "8744593479054a67c784322e0c198bfa880c9388b39a2ddd4c56726944711bd9"), - ("data/utmsmall.tif", "f40dae6e8b5e18f3648e9f095e22a0d7027014bb463418d32f732c3756d8c54f"), - ("gdalworkshop/world.tif", "b376dc8af62f9894b5050a6a9273ac0763ae2990b556910d35d4a8f4753278bb"), - ("ospy/data1/sites.dbf", "7df95edea06c46418287ae3430887f44f9116b29715783f7d1a11b2b931d6e7d"), - ("ospy/data1/sites.prj", "81fb1a246728609a446b25b0df9ede41c3e7b6a133ce78f10edbd2647fc38ce1"), - ("ospy/data1/sites.sbn", "198d9d695f3e7a0a0ac0ebfd6afbe044b78db3e685fffd241a32396e8b341ed3"), - ("ospy/data1/sites.sbx", "49bbe1942b899d52cf1d1b01ea10bd481ec40bdc4c94ff866aece5e81f2261f6"), - ("ospy/data1/sites.shp", "69af5a6184053f0b71f266dc54c944f1ec02013fb66dbb33412d8b1976d5ea2b"), - ("ospy/data1/sites.shx", "1f3da459ccb151958743171e41e6a01810b2a007305d55666e01d680da7bbf08"), - ("ospy/data2/ut_counties.txt", "06585b736091f5bbc62eb040918b1693b2716f550ab306026732e1dfa6cd49a7"), - ("ospy/data3/cache_towns.dbf", "2344b5195e1a7cbc141f38d6f3214f04c0d43058309b162e877fca755cd1d9fa"), - ("ospy/data3/cache_towns.sbn", "217e938eb0bec1cdccf26d87e5127d395d68b5d660bc1ecc1d7ec7b3f052f4e3"), - ("ospy/data3/cache_towns.sbx", "e027b3f67bbb60fc9cf67ab6f430b286fd8a1eaa6c344edaa7da4327485ee9f2"), - ("ospy/data3/cache_towns.shp", "635998f789d349d80368cb105e7e0d61f95cc6eecd36b34bf005d8c7e966fedb"), - ("ospy/data3/cache_towns.shx", "0cafc504b829a3da2c0363074f775266f9e1f6aaaf1e066b8a613d5862f313b7"), - ("ospy/data4/aster.img", "2423205bdf820b1c2a3f03862664d84ea4b5b899c57ed33afd8962664e80a298"), - ("ospy/data4/aster.rrd", "18e038aabe8fd92b0d12cd4f324bb2e0368343e20cc41e5411a6d038108a25cf"), - ("ospy/data5/doq1.img", "70b8e641c52367107654962e81977be65402aa3c46736a07cb512ce960203bb7"), - ("ospy/data5/doq1.rrd", "f9f2fe57d789977090ec0c31e465052161886e79a4c4e10805b5e7ab28c06177"), - ("ospy/data5/doq2.img", "1e1d744f17e6a3b97dd9b7d8705133c72ff162613bae43ad94417c54e6aced5d"), - ("ospy/data5/doq2.rrd", "8274dad00b27e008e5ada62afb1025b0e6e2ef2d2ff2642487ecaee64befd914"), + ( + "data/multi_geom.csv", + "00520017658b66ff21e40cbf553672fa8e280cddae6e7a5d1f8bd36bcd521770", + ), + ( + "data/missing_testcase.csv", + "d49ba446aae9ef334350b64c876b4de652f28595fdecf78bea4e16af4033f7c6", + ), + ( + "data/point.geojson", + "8744593479054a67c784322e0c198bfa880c9388b39a2ddd4c56726944711bd9", + ), + ( + "data/color_relief.txt", + "f44feef9b8529b3ff9cb64de4d01a3107f99aad7822a0a2d91055e44a915c267", + ), + ( + "data/utmsmall.tif", + "f40dae6e8b5e18f3648e9f095e22a0d7027014bb463418d32f732c3756d8c54f", + ), + ( + "gdalworkshop/world.tif", + "b376dc8af62f9894b5050a6a9273ac0763ae2990b556910d35d4a8f4753278bb", + ), + ( + "ospy/data1/sites.dbf", + "7df95edea06c46418287ae3430887f44f9116b29715783f7d1a11b2b931d6e7d", + ), + ( + "ospy/data1/sites.prj", + "81fb1a246728609a446b25b0df9ede41c3e7b6a133ce78f10edbd2647fc38ce1", + ), + ( + "ospy/data1/sites.sbn", + "198d9d695f3e7a0a0ac0ebfd6afbe044b78db3e685fffd241a32396e8b341ed3", + ), + ( + "ospy/data1/sites.sbx", + "49bbe1942b899d52cf1d1b01ea10bd481ec40bdc4c94ff866aece5e81f2261f6", + ), + ( + "ospy/data1/sites.shp", + "69af5a6184053f0b71f266dc54c944f1ec02013fb66dbb33412d8b1976d5ea2b", + ), + ( + "ospy/data1/sites.shx", + "1f3da459ccb151958743171e41e6a01810b2a007305d55666e01d680da7bbf08", + ), + ( + "ospy/data2/ut_counties.txt", + "06585b736091f5bbc62eb040918b1693b2716f550ab306026732e1dfa6cd49a7", + ), + ( + "ospy/data3/cache_towns.dbf", + "2344b5195e1a7cbc141f38d6f3214f04c0d43058309b162e877fca755cd1d9fa", + ), + ( + "ospy/data3/cache_towns.sbn", + "217e938eb0bec1cdccf26d87e5127d395d68b5d660bc1ecc1d7ec7b3f052f4e3", + ), + ( + "ospy/data3/cache_towns.sbx", + "e027b3f67bbb60fc9cf67ab6f430b286fd8a1eaa6c344edaa7da4327485ee9f2", + ), + ( + "ospy/data3/cache_towns.shp", + "635998f789d349d80368cb105e7e0d61f95cc6eecd36b34bf005d8c7e966fedb", + ), + ( + "ospy/data3/cache_towns.shx", + "0cafc504b829a3da2c0363074f775266f9e1f6aaaf1e066b8a613d5862f313b7", + ), + ( + "ospy/data4/aster.img", + "2423205bdf820b1c2a3f03862664d84ea4b5b899c57ed33afd8962664e80a298", + ), + ( + "ospy/data4/aster.rrd", + "18e038aabe8fd92b0d12cd4f324bb2e0368343e20cc41e5411a6d038108a25cf", + ), + ( + "ospy/data5/doq1.img", + "70b8e641c52367107654962e81977be65402aa3c46736a07cb512ce960203bb7", + ), + ( + "ospy/data5/doq1.rrd", + "f9f2fe57d789977090ec0c31e465052161886e79a4c4e10805b5e7ab28c06177", + ), + ( + "ospy/data5/doq2.img", + "1e1d744f17e6a3b97dd9b7d8705133c72ff162613bae43ad94417c54e6aced5d", + ), + ( + "ospy/data5/doq2.rrd", + "8274dad00b27e008e5ada62afb1025b0e6e2ef2d2ff2642487ecaee64befd914", + ), ] +function verify(path::AbstractString, hash::AbstractString) + @assert occursin(r"^[0-9a-f]{64}$", hash) + hash = lowercase(hash) + if isfile(path) + calc_hash = open(path) do file + return bytes2hex(sha256(file)) + end + @assert occursin(r"^[0-9a-f]{64}$", calc_hash) + if calc_hash != hash + @error "Hash Mismatch! Expected: $hash, Calculated: $calc_hash\n" + return false + else + return true + end + else + error("File read error: $path") + end +end + +function download_verify( + url::AbstractString, + hash::Union{AbstractString,Nothing}, + dest::AbstractString, +) + file_existed = false + # verify if file exists + if isfile(dest) + file_existed = true + if hash !== nothing && verify(dest, hash) + # hash verified + return true + else + # either hash is nothing or couldn't pass the SHA test + @error( + "Failed to verify file: $dest with hash: $hash. Re-downloading file..." + ) + end + end + # if the file exists but some problem exists, we delete it to start from scratch + file_existed && Base.rm(dest; force = true) + # Make sure the containing folder exists + mkpath(dirname(dest)) + # downloads the file at dest + Downloads.download(url, dest) + # hash exists and verification fails + if hash !== nothing && !verify(dest, hash) + if file_existed + # the file might be corrupted so we start from scracth + Base.rm(dest; force = true) + Downloads.download(url, dest) + if hash !== nothing && !verify(dest, hash) + error("Verification failed") + end + else + error("Verification failed. File not created after download.") + end + end + return !file_existed +end + for (f, sha) in remotefiles # create the directories if they don't exist currdir = dirname(f) @@ -52,5 +183,5 @@ for (f, sha) in remotefiles # download the file if it is not there or if it has a different checksum currfile = normpath(joinpath(testdatadir, f)) url = REPO_URL * f * "?raw=true" - PlatformEngines.download_verify(url, sha, currfile; force=true) + download_verify(url, sha, currfile) end diff --git a/test/runtests.jl b/test/runtests.jl index 55b2e28f..b47a6f30 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,6 +1,5 @@ using Test using Dates -using Pkg.PlatformEngines # ensure all testing files are present include("remotefiles.jl") @@ -8,6 +7,7 @@ include("remotefiles.jl") @testset "ArchGDAL" begin cd(dirname(@__FILE__)) do isdir("tmp") || mkpath("tmp") + include("test_doctest.jl") include("test_convert.jl") include("test_tables.jl") include("test_gdal_tutorials.jl") @@ -31,6 +31,9 @@ include("remotefiles.jl") include("test_geos_operations.jl") include("test_cookbook_geometry.jl") include("test_cookbook_projection.jl") + include("test_geotransform.jl") + include("test_images.jl") include("test_utils.jl") + return nothing end end diff --git a/test/test_array.jl b/test/test_array.jl index 384216b8..2a74d578 100644 --- a/test/test_array.jl +++ b/test/test_array.jl @@ -1,143 +1,178 @@ using Test -import GDAL using DiskArrays: eachchunk, haschunks, Chunked, GridChunks, readblock! -import ArchGDAL; const AG = ArchGDAL +import ArchGDAL; +const AG = ArchGDAL; -@testset "RasterDataset Type" begin - AG.readraster("ospy/data4/aster.img") do ds - @testset "Test forwarded methods" begin - @test ds isa AG.RasterDataset{UInt8} - @test AG.getgeotransform(ds) == [419976.5, 15.0, 0.0, 4.6624225e6, 0.0, -15.0] - @test AG.nraster(ds) == 3 - @test AG.getband(ds,1) isa AG.AbstractRasterBand - @test startswith(AG.getproj(ds),"PROJCS") - @test AG.width(ds)==5665 - @test AG.height(ds)==5033 - @test AG.getdriver(ds) isa AG.Driver - @test splitpath(AG.filelist(ds)[1]) == ["ospy", "data4", "aster.img"] - @test splitpath(AG.filelist(ds)[2]) == ["ospy", "data4", "aster.rrd"] - @test AG.listcapability(ds) isa Dict - @test AG.ngcp(ds)==0 - @test AG.write(ds,tempname()) == C_NULL - @test AG.testcapability(ds,"ODsCCreateLayer") == false - end - @testset "DiskArray chunk interface" begin - b = AG.getband(ds,1) - @test eachchunk(ds) == GridChunks(size(ds),(64,64,1)) - @test eachchunk(b) == GridChunks(size(b),(64,64)) - @test haschunks(ds) == Chunked() - @test haschunks(b) == Chunked() - end - @testset "Reading into non-arrays" begin - data1 = view(zeros(3,3,3), 1:3, 1:3, 1:3) - readblock!(ds, data1, 1:3, 1:3, 1:3) - @test data1 == ds[1:3,1:3,1:3] - end - end -end - - - - - -@testset "Test Array getindex" begin - AG.readraster("ospy/data4/aster.img") do ds - @testset "Dataset indexing" begin - @testset "dims dropped correctly" begin - @test typeof(ds[:, :, :]) <: Array{UInt8,3} - @test typeof(ds[:, :, 1]) <: Array{UInt8,2} - @test typeof(ds[:, 1, 1]) <: Array{UInt8,1} - @test typeof(ds[1, 1, 1]) <: UInt8 +@testset "test_array.jl" begin + @testset "RasterDataset Type" begin + AG.readraster("ospy/data4/aster.img") do ds + @testset "Test forwarded methods" begin + @test ds isa AG.RasterDataset{UInt8} + @test AG.getgeotransform(ds) == + [419976.5, 15.0, 0.0, 4.6624225e6, 0.0, -15.0] + @test AG.nraster(ds) == 3 + @test AG.getband(ds, 1) isa AG.AbstractRasterBand + @test startswith(AG.getproj(ds), "PROJCS") + @test AG.width(ds) == 5665 + @test AG.height(ds) == 5033 + @test AG.getdriver(ds) isa AG.Driver + @test splitpath(AG.filelist(ds)[1]) == + ["ospy", "data4", "aster.img"] + @test splitpath(AG.filelist(ds)[2]) == + ["ospy", "data4", "aster.rrd"] + @test AG.listcapability(ds) isa Dict + @test AG.ngcp(ds) == 0 + @test AG.write(ds, tempname()) == nothing + @test AG.testcapability(ds, "ODsCCreateLayer") == false end - @testset "range indexing" begin - buffer = ds[1:AG.width(ds), 1:AG.height(ds), 1:1] - @test typeof(buffer) <: Array{UInt8,3} - total = sum(buffer) - count = sum(buffer .> 0) - @test total / count ≈ 76.33891347095299 - @test total / (AG.height(ds) * AG.width(ds)) ≈ 47.55674749653172 + @testset "DiskArray chunk interface" begin + b = AG.getband(ds, 1) + @test eachchunk(ds) == GridChunks(size(ds), (64, 64, 1)) + @test eachchunk(b) == GridChunks(size(b), (64, 64)) + @test haschunks(ds) == Chunked() + @test haschunks(b) == Chunked() end - @testset "colon indexing" begin - buffer = ds[:, :, 1] - total = sum(buffer) - count = sum(buffer .> 0) - @test total / count ≈ 76.33891347095299 - @test total / (AG.height(ds) * AG.width(ds)) ≈ 47.55674749653172 - end - @testset "int indexing" begin - @test ds[755, 2107, 1] == 0xff + @testset "Reading into non-arrays" begin + data1 = view(zeros(3, 3, 3), 1:3, 1:3, 1:3) + readblock!(ds, data1, 1:3, 1:3, 1:3) + @test data1 == ds[1:3, 1:3, 1:3] end end - @testset "RasterBand indexing" begin - band = AG.getband(ds, 1) - @testset "dims dropped correctly" begin - @test typeof(band[:, :]) <: Array{UInt8,2} - @test typeof(band[:, 1]) <: Array{UInt8,1} - @test typeof(band[1, 1]) <: UInt8 + end + + @testset "Test Array getindex" begin + AG.readraster("ospy/data4/aster.img") do ds + @testset "Dataset indexing" begin + @testset "dims dropped correctly" begin + @test typeof(ds[:, :, :]) <: Array{UInt8,3} + @test typeof(ds[:, :, 1]) <: Array{UInt8,2} + @test typeof(ds[:, 1, 1]) <: Array{UInt8,1} + @test typeof(ds[1, 1, 1]) <: UInt8 + end + @testset "range indexing" begin + buffer = ds[1:AG.width(ds), 1:AG.height(ds), 1:1] + @test typeof(buffer) <: Array{UInt8,3} + total = sum(buffer) + count = sum(buffer .> 0) + @test total / count ≈ 76.33891347095299 + @test total / (AG.height(ds) * AG.width(ds)) ≈ + 47.55674749653172 + end + @testset "colon indexing" begin + buffer = ds[:, :, 1] + total = sum(buffer) + count = sum(buffer .> 0) + @test total / count ≈ 76.33891347095299 + @test total / (AG.height(ds) * AG.width(ds)) ≈ + 47.55674749653172 + end + @testset "int indexing" begin + @test ds[755, 2107, 1] == 0xff + end end - @testset "range indexing" begin - buffer = band[1:AG.width(band), 1:AG.height(band)] - @test typeof(buffer) <: Array{UInt8,2} - total = sum(buffer) - count = sum(buffer .> 0) - @test total / count ≈ 76.33891347095299 - @test total / (AG.height(band) * AG.width(band)) ≈ 47.55674749653172 + @testset "RasterBand indexing" begin + band = AG.getband(ds, 1) + @testset "dims dropped correctly" begin + @test typeof(band[:, :]) <: Array{UInt8,2} + @test typeof(band[:, 1]) <: Array{UInt8,1} + @test typeof(band[1, 1]) <: UInt8 + end + @testset "range indexing" begin + buffer = band[1:AG.width(band), 1:AG.height(band)] + @test typeof(buffer) <: Array{UInt8,2} + total = sum(buffer) + count = sum(buffer .> 0) + @test total / count ≈ 76.33891347095299 + @test total / (AG.height(band) * AG.width(band)) ≈ + 47.55674749653172 + end + @testset "colon indexing" begin + buffer = band[:, :] + total = sum(buffer) + count = sum(buffer .> 0) + @test total / count ≈ 76.33891347095299 + @test total / (AG.height(band) * AG.width(band)) ≈ + 47.55674749653172 + end + @testset "int indexing" begin + @test band[755, 2107] == 0xff + end end - @testset "colon indexing" begin - buffer = band[:, :] - total = sum(buffer) - count = sum(buffer .> 0) - @test total / count ≈ 76.33891347095299 - @test total / (AG.height(band) * AG.width(band)) ≈ 47.55674749653172 + end + end + + cp("ospy/data4/aster.img", "ospy/data4/aster_write.img"; force = true) + + @testset "Test Array setindex" begin + AG.readraster("ospy/data4/aster_write.img"; flags = AG.OF_UPDATE) do ds + @testset "Dataset setindex" begin + @test ds[755, 2107, 1] == 0xff + ds[755, 2107, 1] = 0x00 + @test ds[755, 2107, 1] == 0x00 + ds[755:755, 2107:2107, 1:1] = reshape([0x01], 1, 1, 1) + @test ds[755, 2107, 1] == 0x01 + ds[755:755, 2107:2107, 1] = reshape([0x02], 1, 1) + @test ds[755, 2107, 1] == 0x02 + ds[755:755, 2107, 1] = [0x03] + @test ds[755, 2107, 1] == 0x03 end - @testset "int indexing" begin + @testset "RasterBand setindex" begin + band = AG.getband(ds, 1) + band[755:755, 2107] = [0x00] + @test band[755, 2107] == 0x00 + band[755, 2107] = 0x01 + @test band[755, 2107] == 0x01 + band[755:755, 2107:2107] = reshape([0xff], 1, 1) @test band[755, 2107] == 0xff + buffer = band[:, :] + band[:, :] = buffer .* 0x00 + @test sum(band[:, :]) == 0x00 + band[:, 1:500] = buffer[:, 1:500] + band[:, 501:end] = buffer[:, 501:end] + @test sum(buffer) / sum(buffer .> 0) ≈ 76.33891347095299 + @test_throws DimensionMismatch band[:, 501:end] = [1, 2, 3] end end end -end -cp("ospy/data4/aster.img", "ospy/data4/aster_write.img"; force=true) + @testset "Test Array constructor" begin + AG.readraster("ospy/data4/aster_write.img"; flags = AG.OF_UPDATE) do ds + @test sprint(print, ds) == """ + GDAL Dataset (Driver: HFA/Erdas Imagine Images (.img)) + File(s): + ospy/data4/aster_write.img -@testset "Test Array setindex" begin - AG.readraster("ospy/data4/aster_write.img"; flags=AG.OF_Update) do ds - @testset "Dataset setindex" begin - @test ds[755, 2107, 1] == 0xff - ds[755, 2107, 1] = 0x00 - @test ds[755, 2107, 1] == 0x00 - ds[755:755, 2107:2107, 1:1] = reshape([0x01], 1, 1, 1) - @test ds[755, 2107, 1] == 0x01 - ds[755:755, 2107:2107, 1] = reshape([0x02], 1, 1) - @test ds[755, 2107, 1] == 0x02 - ds[755:755, 2107, 1] = [0x03] - @test ds[755, 2107, 1] == 0x03 - end - @testset "RasterBand setindex" begin - band = AG.getband(ds, 1) - band[755:755, 2107] = [0x00] - @test band[755, 2107] == 0x00 - band[755, 2107] = 0x01 - @test band[755, 2107] == 0x01 - band[755:755, 2107:2107] = reshape([0xff], 1, 1) - @test band[755, 2107] == 0xff - buffer = band[:, :] - band[:, :] = buffer .* 0x00 - @test sum(band[:, :]) == 0x00 - band[:, 1:500] = buffer[:, 1:500] - band[:, 501:end] = buffer[:, 501:end] - @test sum(buffer) / sum(buffer .> 0) ≈ 76.33891347095299 - @test_throws DimensionMismatch band[:, 501:end] = [1, 2, 3] + Dataset (width x height): 5665 x 5033 (pixels) + Number of raster bands: 3 + [GA_Update] Band 1 (Undefined): 5665 x 5033 (UInt8) + [GA_Update] Band 2 (Undefined): 5665 x 5033 (UInt8) + [GA_Update] Band 3 (Undefined): 5665 x 5033 (UInt8) + """ + buffer = Array(ds) + typeof(buffer) <: Array{UInt8,3} + total = sum(buffer[:, :, 1:1]) + count = sum(buffer[:, :, 1:1] .> 0) + @test total / count ≈ 76.33891347095299 + @test total / (AG.height(ds) * AG.width(ds)) ≈ 47.55674749653172 + + AG.copy(ds) do copy1 + @test typeof(AG.copywholeraster!(ds, copy1)) == typeof(copy1) + @test typeof( + AG.copywholeraster!(AG.RasterDataset(copy1), ds), + ) == typeof(ds) + @test typeof(AG.copywholeraster!(copy1, ds)) == typeof(ds) + end end - end -end -@testset "Test Array constructor" begin - AG.readraster("ospy/data4/aster_write.img"; flags=AG.OF_Update) do ds - buffer = Array(ds) - typeof(buffer) <: Array{UInt8,3} - total = sum(buffer[:, :, 1:1]) - count = sum(buffer[:, :, 1:1] .> 0) - @test total / count ≈ 76.33891347095299 - @test total / (AG.height(ds) * AG.width(ds)) ≈ 47.55674749653172 + AG.create( + AG.getdriver("MEM"), + width = 2, + height = 2, + nbands = 0, + dtype = UInt8, + ) do dataset + @test_throws ArgumentError AG.RasterDataset(dataset) + @test_throws DimensionMismatch AG._common_size(dataset) + end end end diff --git a/test/test_convert.jl b/test/test_convert.jl index de576cc7..9b9e8013 100644 --- a/test/test_convert.jl +++ b/test/test_convert.jl @@ -1,30 +1,54 @@ using Test -import ArchGDAL; const AG = ArchGDAL -import GeoFormatTypes; const GFT = GeoFormatTypes +import ArchGDAL; +const AG = ArchGDAL; +import GeoFormatTypes; +const GFT = GeoFormatTypes; -# Tests high level convert methods +@testset "test_convert.jl" begin -@testset "convert point format" begin - point = AG.createpoint(100, 70) - json = convert(GFT.GeoJSON, point) - kml = convert(GFT.KML, point) - gml = convert(GFT.GML, point) - wkb = convert(GFT.WellKnownBinary, point) - wkt = convert(GFT.WellKnownText, point) - @test json.val == AG.toJSON(point) - @test kml.val == AG.toKML(point) - @test gml.val == AG.toGML(point) - @test wkb.val == AG.toWKB(point) - @test wkt.val == AG.toWKT(point) - @test convert(GFT.GeoJSON, json) == convert(GFT.GeoJSON, wkb) == - convert(GFT.GeoJSON, wkt) == convert(GFT.GeoJSON, gml) == json - @test convert(GFT.KML, gml) == convert(GFT.KML, wkt) -end + # Tests high level convert methods + @testset "convert point format" begin + point = AG.createpoint(100, 70) + json = convert(GFT.GeoJSON, point) + @test sprint(print, convert(AG.IGeometry, json)) == + "Geometry: POINT (100 70)" + kml = convert(GFT.KML, point) + gml = convert(GFT.GML, point) + wkb = convert(GFT.WellKnownBinary, point) + wkt = convert(GFT.WellKnownText, point) + @test json.val == AG.toJSON(point) + @test kml.val == AG.toKML(point) + @test gml.val == AG.toGML(point) + @test wkb.val == AG.toWKB(point) + @test wkt.val == AG.toWKT(point) + @test convert(GFT.GeoJSON, json) == + convert(GFT.GeoJSON, wkb) == + convert(GFT.GeoJSON, wkt) == + convert(GFT.GeoJSON, gml) == + json + @test convert(GFT.KML, gml) == convert(GFT.KML, wkt) + end + + @testset "convert crs format" begin + proj4326 = GFT.ProjString("+proj=longlat +datum=WGS84 +no_defs") + @test convert( + GFT.ProjString, + GFT.CRS(), + convert( + GFT.WellKnownText, + GFT.CRS(), + convert(GFT.ESRIWellKnownText, GFT.CRS(), GFT.EPSG(4326)), + ), + ) == proj4326 + @test convert(GFT.CoordSys, GFT.CRS(), proj4326) isa GFT.CoordSys + @test convert(GFT.GML, GFT.CRS(), proj4326) isa GeoFormatTypes.GML + end -@testset "convert crs format" begin - proj4326 = GFT.ProjString("+proj=longlat +datum=WGS84 +no_defs") - @test convert(GFT.ProjString, GFT.CRS(), - convert(GFT.WellKnownText, GFT.CRS(), - convert(GFT.ESRIWellKnownText, GFT.CRS(), - GFT.EPSG(4326)))) == proj4326 + @testset "geometry conversions" begin + geom1 = AG.createpoint(1, 2) + @test typeof(geom1) == AG.IGeometry{AG.wkbPoint} + geom2 = convert(AG.IGeometry{AG.wkbUnknown}, geom1) + @test typeof(geom2) == AG.IGeometry{AG.wkbUnknown} + @test AG.toWKT(geom1) == AG.toWKT(geom2) + end end diff --git a/test/test_cookbook_geometry.jl b/test/test_cookbook_geometry.jl index 10bda492..1f62c9ee 100644 --- a/test/test_cookbook_geometry.jl +++ b/test/test_cookbook_geometry.jl @@ -1,146 +1,157 @@ # adapted from http://pcjericks.github.io/py-gdalogr-cookbook/geometry.html using Test import GeoInterface -import GDAL, ArchGDAL; const AG = ArchGDAL +import ArchGDAL; +const AG = ArchGDAL; -@testset "Create a Point" begin +@testset "test_cookbook_geometry.jl" begin + @testset "Create a Point" begin + x, y = 1198054.34, 648493.09 + wktpoint = "POINT (1198054.34 648493.09)" - x, y = 1198054.34, 648493.09 - wktpoint = "POINT (1198054.34 648493.09)" + # Method 1 + AG.createpoint(x, y) do point + @test AG.toWKT(point) == wktpoint + end + AG.createpoint((x, y)) do point + @test AG.toWKT(point) == wktpoint + end - # Method 1 - AG.createpoint(x, y) do point - @test AG.toWKT(point) == wktpoint - end - AG.createpoint((x, y)) do point - @test AG.toWKT(point) == wktpoint - end + # Method 2 + AG.createpoint() do point + AG.addpoint!(point, x, y) + @test AG.toWKT(point) == wktpoint + end - # Method 2 - AG.createpoint() do point - AG.addpoint!(point, x, y) - @test AG.toWKT(point) == wktpoint - end + # Method 3 + AG.creategeom(AG.wkbPoint) do point + AG.addpoint!(point, x, y) + @test AG.toWKT(point) == wktpoint + end - # Method 3 - AG.creategeom(GDAL.wkbPoint) do point + # Method 4 + point = AG.creategeom(AG.wkbPoint) AG.addpoint!(point, x, y) @test AG.toWKT(point) == wktpoint - end - - # Method 4 - point = AG.creategeom(GDAL.wkbPoint) - AG.addpoint!(point, x, y) - @test AG.toWKT(point) == wktpoint - # Method 5 - @test AG.toWKT(AG.createpoint(x, y)) == wktpoint -end + # Method 5 + @test AG.toWKT(AG.createpoint(x, y)) == wktpoint + end + + @testset "Create a LineString" begin + # Method 1 + wktline = + "LINESTRING (1116651.43937912 637392.696988746," * + "1188804.01084985 652655.740953707,1226730.36252036 634155.081602239,1281307.30760719 636467.664021172)" + AG.createlinestring([ + (1116651.439379124, 637392.6969887456), + (1188804.0108498496, 652655.7409537067), + (1226730.3625203592, 634155.0816022386), + (1281307.30760719, 636467.6640211721), + ]) do line + @test AG.toWKT(line) == wktline + end -@testset "Create a LineString" begin - # Method 1 - wktline = "LINESTRING (1116651.43937912 637392.696988746," * - "1188804.01084985 652655.740953707,1226730.36252036 634155.081602239,1281307.30760719 636467.664021172)" - AG.createlinestring([(1116651.439379124, 637392.6969887456), - (1188804.0108498496, 652655.7409537067), - (1226730.3625203592, 634155.0816022386), - (1281307.30760719, 636467.6640211721)]) do line - @test AG.toWKT(line) == wktline - end + # Method 2 + AG.createlinestring() do line + AG.addpoint!(line, 1116651.439379124, 637392.6969887456) + AG.addpoint!(line, 1188804.0108498496, 652655.7409537067) + AG.addpoint!(line, 1226730.3625203592, 634155.0816022386) + AG.addpoint!(line, 1281307.30760719, 636467.6640211721) + @test AG.toWKT(line) == wktline + end - # Method 2 - AG.createlinestring() do line - AG.addpoint!(line, 1116651.439379124, 637392.6969887456) - AG.addpoint!(line, 1188804.0108498496, 652655.7409537067) - AG.addpoint!(line, 1226730.3625203592, 634155.0816022386) - AG.addpoint!(line, 1281307.30760719, 636467.6640211721) - @test AG.toWKT(line) == wktline - end + # Method 3 + AG.creategeom(AG.wkbLineString) do line + AG.addpoint!(line, 1116651.439379124, 637392.6969887456) + AG.addpoint!(line, 1188804.0108498496, 652655.7409537067) + AG.addpoint!(line, 1226730.3625203592, 634155.0816022386) + AG.addpoint!(line, 1281307.30760719, 636467.6640211721) + @test AG.toWKT(line) == wktline + end - # Method 3 - AG.creategeom(GDAL.wkbLineString) do line - AG.addpoint!(line, 1116651.439379124, 637392.6969887456) + # Method 4 + line = AG.creategeom(AG.wkbLineString) + AG.addpoint!(line, 1116651.439379124, 637392.6969887456) AG.addpoint!(line, 1188804.0108498496, 652655.7409537067) AG.addpoint!(line, 1226730.3625203592, 634155.0816022386) - AG.addpoint!(line, 1281307.30760719, 636467.6640211721) + AG.addpoint!(line, 1281307.30760719, 636467.6640211721) @test AG.toWKT(line) == wktline - end - - # Method 4 - line = AG.creategeom(GDAL.wkbLineString) - AG.addpoint!(line, 1116651.439379124, 637392.6969887456) - AG.addpoint!(line, 1188804.0108498496, 652655.7409537067) - AG.addpoint!(line, 1226730.3625203592, 634155.0816022386) - AG.addpoint!(line, 1281307.30760719, 636467.6640211721) - @test AG.toWKT(line) == wktline - - # Method 5 - @test AG.toWKT(AG.createlinestring([ - (1116651.439379124, 637392.6969887456), - (1188804.0108498496, 652655.7409537067), - (1226730.3625203592, 634155.0816022386), - (1281307.30760719, 636467.6640211721) - ])) == wktline -end - -@testset "Create a Polygon" begin - wktpoly = "POLYGON ((1179091.16469033 712782.883845978,1161053.02182265 667456.268434881," * - "1214704.9339419 641092.828859039,1228580.42845551 682719.312399842," * - "1218405.0658122 721108.180554139,1179091.16469033 712782.883845978))" - - # Method 1 - AG.createpolygon([(1179091.1646903288, 712782.8838459781), - (1161053.0218226474, 667456.2684348812), - (1214704.933941905, 641092.8288590391), - (1228580.428455506, 682719.3123998424), - (1218405.0658121984, 721108.1805541387), - (1179091.1646903288, 712782.8838459781)]) do poly - @test AG.toWKT(poly) == wktpoly - end - - # Method 2 - AG.createpolygon() do poly - ring = AG.createlinearring( - [(1179091.1646903288, 712782.8838459781), - (1161053.0218226474, 667456.2684348812), - (1214704.933941905, 641092.8288590391), - (1228580.428455506, 682719.3123998424), - (1218405.0658121984, 721108.1805541387), - (1179091.1646903288, 712782.8838459781)]) - AG.addgeom!(poly, ring) - end + # Method 5 + @test AG.toWKT( + AG.createlinestring([ + (1116651.439379124, 637392.6969887456), + (1188804.0108498496, 652655.7409537067), + (1226730.3625203592, 634155.0816022386), + (1281307.30760719, 636467.6640211721), + ]), + ) == wktline + end + + @testset "Create a Polygon" begin + wktpoly = + "POLYGON ((1179091.16469033 712782.883845978,1161053.02182265 667456.268434881," * + "1214704.9339419 641092.828859039,1228580.42845551 682719.312399842," * + "1218405.0658122 721108.180554139,1179091.16469033 712782.883845978))" + + # Method 1 + AG.createpolygon([ + (1179091.1646903288, 712782.8838459781), + (1161053.0218226474, 667456.2684348812), + (1214704.933941905, 641092.8288590391), + (1228580.428455506, 682719.3123998424), + (1218405.0658121984, 721108.1805541387), + (1179091.1646903288, 712782.8838459781), + ]) do poly + @test AG.toWKT(poly) == wktpoly + end - AG.createlinearring( - [(1179091.1646903288, 712782.8838459781), - (1161053.0218226474, 667456.2684348812), - (1214704.933941905, 641092.8288590391), - (1228580.428455506, 682719.3123998424), - (1218405.0658121984, 721108.1805541387), - (1179091.1646903288, 712782.8838459781)]) do ring + # Method 2 AG.createpolygon() do poly + ring = AG.createlinearring([ + (1179091.1646903288, 712782.8838459781), + (1161053.0218226474, 667456.2684348812), + (1214704.933941905, 641092.8288590391), + (1228580.428455506, 682719.3123998424), + (1218405.0658121984, 721108.1805541387), + (1179091.1646903288, 712782.8838459781), + ]) AG.addgeom!(poly, ring) - @test AG.toWKT(poly) == wktpoly + return nothing end - end - # Method 3 - AG.creategeom(GDAL.wkbLinearRing) do ring - AG.addpoint!(ring, 1179091.1646903288, 712782.8838459781) - AG.addpoint!(ring, 1161053.0218226474, 667456.2684348812) - AG.addpoint!(ring, 1214704.933941905, 641092.8288590391) - AG.addpoint!(ring, 1228580.428455506, 682719.3123998424) - AG.addpoint!(ring, 1218405.0658121984, 721108.1805541387) - AG.addpoint!(ring, 1179091.1646903288, 712782.8838459781) + AG.createlinearring([ + (1179091.1646903288, 712782.8838459781), + (1161053.0218226474, 667456.2684348812), + (1214704.933941905, 641092.8288590391), + (1228580.428455506, 682719.3123998424), + (1218405.0658121984, 721108.1805541387), + (1179091.1646903288, 712782.8838459781), + ]) do ring + AG.createpolygon() do poly + AG.addgeom!(poly, ring) + @test AG.toWKT(poly) == wktpoly + end + end - AG.creategeom(GDAL.wkbPolygon) do poly - AG.addgeom!(poly, ring) - @test AG.toWKT(poly) == wktpoly + # Method 3 + AG.creategeom(AG.wkbLinearRing) do ring + AG.addpoint!(ring, 1179091.1646903288, 712782.8838459781) + AG.addpoint!(ring, 1161053.0218226474, 667456.2684348812) + AG.addpoint!(ring, 1214704.933941905, 641092.8288590391) + AG.addpoint!(ring, 1228580.428455506, 682719.3123998424) + AG.addpoint!(ring, 1218405.0658121984, 721108.1805541387) + AG.addpoint!(ring, 1179091.1646903288, 712782.8838459781) + + AG.creategeom(AG.wkbPolygon) do poly + AG.addgeom!(poly, ring) + @test AG.toWKT(poly) == wktpoly + end end - end - # Method 4 - ring = AG.creategeom(GDAL.wkbLinearRing) + # Method 4 + ring = AG.creategeom(AG.wkbLinearRing) AG.addpoint!(ring, 1179091.1646903288, 712782.8838459781) AG.addpoint!(ring, 1161053.0218226474, 667456.2684348812) AG.addpoint!(ring, 1214704.933941905, 641092.8288590391) @@ -148,149 +159,174 @@ end AG.addpoint!(ring, 1218405.0658121984, 721108.1805541387) AG.addpoint!(ring, 1179091.1646903288, 712782.8838459781) - poly = AG.creategeom(GDAL.wkbPolygon) - AG.addgeom!(poly, ring) - @test AG.toWKT(poly) == wktpoly - - @test AG.toWKT(AG.createpolygon([ - (1179091.1646903288, 712782.8838459781), - (1161053.0218226474, 667456.2684348812), - (1214704.933941905, 641092.8288590391), - (1228580.428455506, 682719.3123998424), - (1218405.0658121984, 721108.1805541387), - (1179091.1646903288, 712782.8838459781) - ])) == wktpoly -end - -@testset "Create a Polygon with holes" begin - - wktpoly = "POLYGON ((1154115.27456585 686419.444270136,1154115.27456585 653118.257437493," * - "1165678.18666051 653118.257437493,1165678.18666051 686419.444270136," * - "1154115.27456585 686419.444270136)," * - "(1149490.10972798 691044.609108003,1149490.10972798 648030.57611584," * - "1191579.10975257 648030.57611584,1191579.10975257 691044.609108003," * - "1149490.10972798 691044.609108003))" - - # Method 1 - AG.createpolygon([# outring - [(1154115.274565847, 686419.4442701361), - (1154115.274565847, 653118.2574374934), - (1165678.1866605144, 653118.2574374934), - (1165678.1866605144, 686419.4442701361), - (1154115.274565847, 686419.4442701361)], - # innerring(s) - [(1149490.1097279799, 691044.6091080031), - (1149490.1097279799, 648030.5761158396), - (1191579.1097525698, 648030.5761158396), - (1191579.1097525698, 691044.6091080031), - (1149490.1097279799, 691044.6091080031)] - ]) do poly + poly = AG.creategeom(AG.wkbPolygon) + AG.addgeom!(poly, ring) @test AG.toWKT(poly) == wktpoly - end - # Method 2 - AG.createpolygon() do poly - outring = AG.creategeom(GDAL.wkbLinearRing) + @test AG.toWKT( + AG.createpolygon([ + (1179091.1646903288, 712782.8838459781), + (1161053.0218226474, 667456.2684348812), + (1214704.933941905, 641092.8288590391), + (1228580.428455506, 682719.3123998424), + (1218405.0658121984, 721108.1805541387), + (1179091.1646903288, 712782.8838459781), + ]), + ) == wktpoly + end + + @testset "Create a Polygon with holes" begin + wktpoly = + "POLYGON ((1154115.27456585 686419.444270136,1154115.27456585 653118.257437493," * + "1165678.18666051 653118.257437493,1165678.18666051 686419.444270136," * + "1154115.27456585 686419.444270136)," * + "(1149490.10972798 691044.609108003,1149490.10972798 648030.57611584," * + "1191579.10975257 648030.57611584,1191579.10975257 691044.609108003," * + "1149490.10972798 691044.609108003))" + + # Method 1 + AG.createpolygon([# outring + [ + (1154115.274565847, 686419.4442701361), + (1154115.274565847, 653118.2574374934), + (1165678.1866605144, 653118.2574374934), + (1165678.1866605144, 686419.4442701361), + (1154115.274565847, 686419.4442701361), + ], + # innerring(s) + [ + (1149490.1097279799, 691044.6091080031), + (1149490.1097279799, 648030.5761158396), + (1191579.1097525698, 648030.5761158396), + (1191579.1097525698, 691044.6091080031), + (1149490.1097279799, 691044.6091080031), + ], + ]) do poly + @test AG.toWKT(poly) == wktpoly + end + + # Method 2 + AG.createpolygon() do poly + outring = AG.creategeom(AG.wkbLinearRing) AG.addpoint!(outring, 1154115.274565847, 686419.4442701361) AG.addpoint!(outring, 1154115.274565847, 653118.2574374934) AG.addpoint!(outring, 1165678.1866605144, 653118.2574374934) AG.addpoint!(outring, 1165678.1866605144, 686419.4442701361) AG.addpoint!(outring, 1154115.274565847, 686419.4442701361) - innerring = AG.creategeom(GDAL.wkbLinearRing) + innerring = AG.creategeom(AG.wkbLinearRing) AG.addpoint!(innerring, 1149490.1097279799, 691044.6091080031) AG.addpoint!(innerring, 1149490.1097279799, 648030.5761158396) AG.addpoint!(innerring, 1191579.1097525698, 648030.5761158396) AG.addpoint!(innerring, 1191579.1097525698, 691044.6091080031) AG.addpoint!(innerring, 1149490.1097279799, 691044.6091080031) - AG.addgeom!(poly, outring) - AG.addgeom!(poly, innerring) - @test AG.toWKT(poly) == wktpoly - end + AG.addgeom!(poly, outring) + AG.addgeom!(poly, innerring) + @test AG.toWKT(poly) == wktpoly + end - # Method 3 - AG.creategeom(GDAL.wkbPolygon) do poly - outring = AG.creategeom(GDAL.wkbLinearRing) + # Method 3 + AG.creategeom(AG.wkbPolygon) do poly + outring = AG.creategeom(AG.wkbLinearRing) AG.addpoint!(outring, 1154115.274565847, 686419.4442701361) AG.addpoint!(outring, 1154115.274565847, 653118.2574374934) AG.addpoint!(outring, 1165678.1866605144, 653118.2574374934) AG.addpoint!(outring, 1165678.1866605144, 686419.4442701361) AG.addpoint!(outring, 1154115.274565847, 686419.4442701361) - innerring = AG.creategeom(GDAL.wkbLinearRing) + innerring = AG.creategeom(AG.wkbLinearRing) AG.addpoint!(innerring, 1149490.1097279799, 691044.6091080031) AG.addpoint!(innerring, 1149490.1097279799, 648030.5761158396) AG.addpoint!(innerring, 1191579.1097525698, 648030.5761158396) AG.addpoint!(innerring, 1191579.1097525698, 691044.6091080031) AG.addpoint!(innerring, 1149490.1097279799, 691044.6091080031) - AG.addgeom!(poly, outring) - AG.addgeom!(poly, innerring) - @test AG.toWKT(poly) == wktpoly - end + AG.addgeom!(poly, outring) + AG.addgeom!(poly, innerring) + @test AG.toWKT(poly) == wktpoly + end - # Method 4 - poly = AG.creategeom(GDAL.wkbPolygon) - outring = AG.creategeom(GDAL.wkbLinearRing) - AG.addpoint!(outring, 1154115.274565847, 686419.4442701361) - AG.addpoint!(outring, 1154115.274565847, 653118.2574374934) - AG.addpoint!(outring, 1165678.1866605144, 653118.2574374934) - AG.addpoint!(outring, 1165678.1866605144, 686419.4442701361) - AG.addpoint!(outring, 1154115.274565847, 686419.4442701361) + # Method 4 + poly = AG.creategeom(AG.wkbPolygon) + outring = AG.creategeom(AG.wkbLinearRing) + AG.addpoint!(outring, 1154115.274565847, 686419.4442701361) + AG.addpoint!(outring, 1154115.274565847, 653118.2574374934) + AG.addpoint!(outring, 1165678.1866605144, 653118.2574374934) + AG.addpoint!(outring, 1165678.1866605144, 686419.4442701361) + AG.addpoint!(outring, 1154115.274565847, 686419.4442701361) AG.addgeom!(poly, outring) - innerring = AG.creategeom(GDAL.wkbLinearRing) - AG.addpoint!(innerring, 1149490.1097279799, 691044.6091080031) - AG.addpoint!(innerring, 1149490.1097279799, 648030.5761158396) - AG.addpoint!(innerring, 1191579.1097525698, 648030.5761158396) - AG.addpoint!(innerring, 1191579.1097525698, 691044.6091080031) - AG.addpoint!(innerring, 1149490.1097279799, 691044.6091080031) + innerring = AG.creategeom(AG.wkbLinearRing) + AG.addpoint!(innerring, 1149490.1097279799, 691044.6091080031) + AG.addpoint!(innerring, 1149490.1097279799, 648030.5761158396) + AG.addpoint!(innerring, 1191579.1097525698, 648030.5761158396) + AG.addpoint!(innerring, 1191579.1097525698, 691044.6091080031) + AG.addpoint!(innerring, 1149490.1097279799, 691044.6091080031) AG.addgeom!(poly, innerring) - @test AG.toWKT(poly) == wktpoly - - # Method 5 - @test AG.toWKT(AG.createpolygon([ - # outerring - [(1154115.274565847, 686419.4442701361), - (1154115.274565847, 653118.2574374934), - (1165678.1866605144, 653118.2574374934), - (1165678.1866605144, 686419.4442701361), - (1154115.274565847, 686419.4442701361)], - # innerring(s) - [(1149490.1097279799, 691044.6091080031), - (1149490.1097279799, 648030.5761158396), - (1191579.1097525698, 648030.5761158396), - (1191579.1097525698, 691044.6091080031), - (1149490.1097279799, 691044.6091080031)] - ])) == wktpoly -end - -@testset "Create a MultiPoint" begin + @test AG.toWKT(poly) == wktpoly - wktpoints = "MULTIPOINT (1251243.73616105 598078.795866876," * - "1240605.85703396 601778.927737169,1250318.70319348 606404.092575036)" + # Method 5 + @test AG.toWKT( + AG.createpolygon([ + # outerring + [ + (1154115.274565847, 686419.4442701361), + (1154115.274565847, 653118.2574374934), + (1165678.1866605144, 653118.2574374934), + (1165678.1866605144, 686419.4442701361), + (1154115.274565847, 686419.4442701361), + ], + # innerring(s) + [ + (1149490.1097279799, 691044.6091080031), + (1149490.1097279799, 648030.5761158396), + (1191579.1097525698, 648030.5761158396), + (1191579.1097525698, 691044.6091080031), + (1149490.1097279799, 691044.6091080031), + ], + ]), + ) == wktpoly + end + + @testset "Create a MultiPoint" begin + wktpoints = + "MULTIPOINT (1251243.73616105 598078.795866876," * + "1240605.85703396 601778.927737169,1250318.70319348 606404.092575036)" + + # Method 1 + AG.createmultipoint([ + (1251243.7361610543, 598078.7958668759), + (1240605.8570339603, 601778.9277371694), + (1250318.7031934808, 606404.0925750365), + ]) do multipoint + @test AG.toWKT(multipoint) == wktpoints + end - # Method 1 - AG.createmultipoint([(1251243.7361610543, 598078.7958668759), - (1240605.8570339603, 601778.9277371694), - (1250318.7031934808, 606404.0925750365)]) do multipoint - @test AG.toWKT(multipoint) == wktpoints - end + # Method 2 + AG.createmultipoint() do multipoint + point1 = AG.createpoint(1251243.7361610543, 598078.7958668759) + point2 = AG.createpoint(1240605.8570339603, 601778.9277371694) + point3 = AG.createpoint(1250318.7031934808, 606404.0925750365) + AG.addgeom!(multipoint, point1) + AG.addgeom!(multipoint, point2) + AG.addgeom!(multipoint, point3) + @test AG.toWKT(multipoint) == wktpoints + end - # Method 2 - AG.createmultipoint() do multipoint - point1 = AG.createpoint(1251243.7361610543, 598078.7958668759) - point2 = AG.createpoint(1240605.8570339603, 601778.9277371694) - point3 = AG.createpoint(1250318.7031934808, 606404.0925750365) - AG.addgeom!(multipoint, point1) - AG.addgeom!(multipoint, point2) - AG.addgeom!(multipoint, point3) - @test AG.toWKT(multipoint) == wktpoints - end + # Method 3 + AG.creategeom(AG.wkbMultiPoint) do multipoint + point1 = AG.createpoint(1251243.7361610543, 598078.7958668759) + point2 = AG.createpoint(1240605.8570339603, 601778.9277371694) + point3 = AG.createpoint(1250318.7031934808, 606404.0925750365) + AG.addgeom!(multipoint, point1) + AG.addgeom!(multipoint, point2) + AG.addgeom!(multipoint, point3) + @test AG.toWKT(multipoint) == wktpoints + end - # Method 3 - AG.creategeom(GDAL.wkbMultiPoint) do multipoint + # Method 4 + multipoint = AG.creategeom(AG.wkbMultiPoint) point1 = AG.createpoint(1251243.7361610543, 598078.7958668759) point2 = AG.createpoint(1240605.8570339603, 601778.9277371694) point3 = AG.createpoint(1250318.7031934808, 606404.0925750365) @@ -298,473 +334,628 @@ end AG.addgeom!(multipoint, point2) AG.addgeom!(multipoint, point3) @test AG.toWKT(multipoint) == wktpoints - end - # Method 4 - multipoint = AG.creategeom(GDAL.wkbMultiPoint) - point1 = AG.createpoint(1251243.7361610543, 598078.7958668759) - point2 = AG.createpoint(1240605.8570339603, 601778.9277371694) - point3 = AG.createpoint(1250318.7031934808, 606404.0925750365) - AG.addgeom!(multipoint, point1) - AG.addgeom!(multipoint, point2) - AG.addgeom!(multipoint, point3) - @test AG.toWKT(multipoint) == wktpoints - - # Method 5 - @test AG.toWKT(AG.createmultipoint([ - (1251243.7361610543, 598078.7958668759), - (1240605.8570339603, 601778.9277371694), - (1250318.7031934808, 606404.0925750365) - ])) == wktpoints -end - -@testset "Create a MultiLineString" begin - - wktline = "MULTILINESTRING ((1214242.41745812 617041.971702131,1234593.14274473 629529.916764372)," * - "(1184641.36249577 626754.817861651,1219792.61526356 606866.609058823))" - - # Method 1 - AG.createmultilinestring(Vector{Tuple{Float64,Float64}}[ - [(1214242.4174581182, 617041.9717021306), - (1234593.142744733, 629529.9167643716)], - [(1184641.3624957693, 626754.8178616514), - (1219792.6152635587, 606866.6090588232)] - ]) do multiline - @test GeoInterface.geotype(multiline) == :MultiLineString - @test AG.toWKT(multiline) == wktline - end + # Method 5 + @test AG.toWKT( + AG.createmultipoint([ + (1251243.7361610543, 598078.7958668759), + (1240605.8570339603, 601778.9277371694), + (1250318.7031934808, 606404.0925750365), + ]), + ) == wktpoints + end + + @testset "Create a MultiLineString" begin + wktline = + "MULTILINESTRING ((1214242.41745812 617041.971702131,1234593.14274473 629529.916764372)," * + "(1184641.36249577 626754.817861651,1219792.61526356 606866.609058823))" + + # Method 1 + AG.createmultilinestring( + Vector{Tuple{Float64,Float64}}[ + [ + (1214242.4174581182, 617041.9717021306), + (1234593.142744733, 629529.9167643716), + ], + [ + (1184641.3624957693, 626754.8178616514), + (1219792.6152635587, 606866.6090588232), + ], + ], + ) do multiline + @test GeoInterface.geotype(multiline) == :MultiLineString + @test AG.toWKT(multiline) == wktline + end - # Method 2 - AG.createmultilinestring() do multiline - AG.addgeom!(multiline, AG.createlinestring( - [(1214242.4174581182, 617041.9717021306), - (1234593.142744733, 629529.9167643716)])) - AG.addgeom!(multiline, AG.createlinestring( - [(1184641.3624957693, 626754.8178616514), - (1219792.6152635587, 606866.6090588232)])) - @test AG.toWKT(multiline) == wktline - end + # Method 2 + AG.createmultilinestring() do multiline + AG.addgeom!( + multiline, + AG.createlinestring([ + (1214242.4174581182, 617041.9717021306), + (1234593.142744733, 629529.9167643716), + ]), + ) + AG.addgeom!( + multiline, + AG.createlinestring([ + (1184641.3624957693, 626754.8178616514), + (1219792.6152635587, 606866.6090588232), + ]), + ) + @test AG.toWKT(multiline) == wktline + end - # Method 3 - AG.creategeom(GDAL.wkbMultiLineString) do multiline - line = AG.creategeom(GDAL.wkbLineString) + # Method 3 + AG.creategeom(AG.wkbMultiLineString) do multiline + line = AG.creategeom(AG.wkbLineString) AG.addpoint!(line, 1214242.4174581182, 617041.9717021306) AG.addpoint!(line, 1234593.142744733, 629529.9167643716) - AG.addgeom!(multiline, line) + AG.addgeom!(multiline, line) - line = AG.creategeom(GDAL.wkbLineString) + line = AG.creategeom(AG.wkbLineString) AG.addpoint!(line, 1184641.3624957693, 626754.8178616514) AG.addpoint!(line, 1219792.6152635587, 606866.6090588232) - AG.addgeom!(multiline, line) + AG.addgeom!(multiline, line) - @test AG.toWKT(multiline) == wktline - end + @test AG.toWKT(multiline) == wktline + end - # Method 4 - multiline = AG.creategeom(GDAL.wkbMultiLineString) - line = AG.creategeom(GDAL.wkbLineString) - AG.addpoint!(line, 1214242.4174581182, 617041.9717021306) - AG.addpoint!(line, 1234593.142744733, 629529.9167643716) + # Method 4 + multiline = AG.creategeom(AG.wkbMultiLineString) + line = AG.creategeom(AG.wkbLineString) + AG.addpoint!(line, 1214242.4174581182, 617041.9717021306) + AG.addpoint!(line, 1234593.142744733, 629529.9167643716) AG.addgeom!(multiline, line) - line = AG.creategeom(GDAL.wkbLineString) - AG.addpoint!(line, 1184641.3624957693, 626754.8178616514) - AG.addpoint!(line, 1219792.6152635587, 606866.6090588232) + line = AG.creategeom(AG.wkbLineString) + AG.addpoint!(line, 1184641.3624957693, 626754.8178616514) + AG.addpoint!(line, 1219792.6152635587, 606866.6090588232) AG.addgeom!(multiline, line) - - @test AG.toWKT(multiline) == wktline - - # Method 5 - @test AG.toWKT(AG.createmultilinestring(Vector{Tuple{Float64,Float64}}[ - [(1214242.4174581182, 617041.9717021306), - (1234593.142744733, 629529.9167643716)], - [(1184641.3624957693, 626754.8178616514), - (1219792.6152635587, 606866.6090588232)] - ])) == wktline -end - -@testset "Create a MultiPolygon" begin - - wktmultipolygon = "MULTIPOLYGON " * - "(((1204067.05481481 634617.598086025,1204067.05481481 620742.103572424," * - "1215167.45042569 620742.103572424,1215167.45042569 634617.598086025," * - "1204067.05481481 634617.598086025))," * - "((1179553.68117412 647105.543148266,1179553.68117412 626292.301377865," * - "1194354.20865529 626292.301377865,1194354.20865529 647105.543148266," * - "1179553.68117412 647105.543148266)))" - - # Method 1 - AG.createmultipolygon_noholes(Vector{Tuple{Float64,Float64}}[ - [(1204067.0548148106, 634617.5980860253), - (1204067.0548148106, 620742.1035724243), - (1215167.4504256917, 620742.1035724243), - (1215167.4504256917, 634617.5980860253), - (1204067.0548148106, 634617.5980860253)], - [(1179553.6811741155, 647105.5431482664), - (1179553.6811741155, 626292.3013778647), - (1194354.20865529, 626292.3013778647), - (1194354.20865529, 647105.5431482664), - (1179553.6811741155, 647105.5431482664)] - ]) do multipolygon - @test AG.toWKT(multipolygon) == wktmultipolygon - end - - # Method 2 - AG.createmultipolygon() do multipolygon - poly = AG.createpolygon( - [(1204067.0548148106, 634617.5980860253), - (1204067.0548148106, 620742.1035724243), - (1215167.4504256917, 620742.1035724243), - (1215167.4504256917, 634617.5980860253), - (1204067.0548148106, 634617.5980860253)]) - AG.addgeom!(multipolygon,poly) - - poly = AG.createpolygon( - [(1179553.6811741155, 647105.5431482664), - (1179553.6811741155, 626292.3013778647), - (1194354.20865529, 626292.3013778647), - (1194354.20865529, 647105.5431482664), - (1179553.6811741155, 647105.5431482664)]) - AG.addgeom!(multipolygon,poly) - @test AG.toWKT(multipolygon) == wktmultipolygon - end + @test AG.toWKT(multiline) == wktline - # Method 3 - AG.creategeom(GDAL.wkbMultiPolygon) do multipolygon - poly = AG.creategeom(GDAL.wkbPolygon) - ring = AG.creategeom(GDAL.wkbLinearRing) - AG.addpoint!(ring, 1204067.0548148106, 634617.5980860253) - AG.addpoint!(ring, 1204067.0548148106, 620742.1035724243) - AG.addpoint!(ring, 1215167.4504256917, 620742.1035724243) - AG.addpoint!(ring, 1215167.4504256917, 634617.5980860253) - AG.addpoint!(ring, 1204067.0548148106, 634617.5980860253) - AG.addgeom!(poly, ring) - AG.addgeom!(multipolygon,poly) - - poly = AG.creategeom(GDAL.wkbPolygon) - ring = AG.creategeom(GDAL.wkbLinearRing) - AG.addpoint!(ring, 1179553.6811741155, 647105.5431482664) - AG.addpoint!(ring, 1179553.6811741155, 626292.3013778647) - AG.addpoint!(ring, 1194354.20865529, 626292.3013778647) - AG.addpoint!(ring, 1194354.20865529, 647105.5431482664) - AG.addpoint!(ring, 1179553.6811741155, 647105.5431482664) - AG.addgeom!(poly, ring) - AG.addgeom!(multipolygon,poly) + # Method 5 + @test AG.toWKT( + AG.createmultilinestring( + Vector{Tuple{Float64,Float64}}[ + [ + (1214242.4174581182, 617041.9717021306), + (1234593.142744733, 629529.9167643716), + ], + [ + (1184641.3624957693, 626754.8178616514), + (1219792.6152635587, 606866.6090588232), + ], + ], + ), + ) == wktline + end + + @testset "Create a MultiPolygon" begin + wktmultipolygon = + "MULTIPOLYGON " * + "(((1204067.05481481 634617.598086025,1204067.05481481 620742.103572424," * + "1215167.45042569 620742.103572424,1215167.45042569 634617.598086025," * + "1204067.05481481 634617.598086025))," * + "((1179553.68117412 647105.543148266,1179553.68117412 626292.301377865," * + "1194354.20865529 626292.301377865,1194354.20865529 647105.543148266," * + "1179553.68117412 647105.543148266)))" + + # Method 1 + AG.createmultipolygon_noholes( + Vector{Tuple{Float64,Float64}}[ + [ + (1204067.0548148106, 634617.5980860253), + (1204067.0548148106, 620742.1035724243), + (1215167.4504256917, 620742.1035724243), + (1215167.4504256917, 634617.5980860253), + (1204067.0548148106, 634617.5980860253), + ], + [ + (1179553.6811741155, 647105.5431482664), + (1179553.6811741155, 626292.3013778647), + (1194354.20865529, 626292.3013778647), + (1194354.20865529, 647105.5431482664), + (1179553.6811741155, 647105.5431482664), + ], + ], + ) do multipolygon + @test AG.toWKT(multipolygon) == wktmultipolygon + end - @test AG.toWKT(multipolygon) == wktmultipolygon - end + # Method 2 + AG.createmultipolygon() do multipolygon + poly = AG.createpolygon([ + (1204067.0548148106, 634617.5980860253), + (1204067.0548148106, 620742.1035724243), + (1215167.4504256917, 620742.1035724243), + (1215167.4504256917, 634617.5980860253), + (1204067.0548148106, 634617.5980860253), + ]) + AG.addgeom!(multipolygon, poly) + + poly = AG.createpolygon([ + (1179553.6811741155, 647105.5431482664), + (1179553.6811741155, 626292.3013778647), + (1194354.20865529, 626292.3013778647), + (1194354.20865529, 647105.5431482664), + (1179553.6811741155, 647105.5431482664), + ]) + AG.addgeom!(multipolygon, poly) + + @test AG.toWKT(multipolygon) == wktmultipolygon + end - # Method 4 - multipolygon = AG.creategeom(GDAL.wkbMultiPolygon) - poly = AG.creategeom(GDAL.wkbPolygon) - ring = AG.creategeom(GDAL.wkbLinearRing) - AG.addpoint!(ring, 1204067.0548148106, 634617.5980860253) - AG.addpoint!(ring, 1204067.0548148106, 620742.1035724243) - AG.addpoint!(ring, 1215167.4504256917, 620742.1035724243) - AG.addpoint!(ring, 1215167.4504256917, 634617.5980860253) - AG.addpoint!(ring, 1204067.0548148106, 634617.5980860253) + # Method 3 + AG.creategeom(AG.wkbMultiPolygon) do multipolygon + poly = AG.creategeom(AG.wkbPolygon) + ring = AG.creategeom(AG.wkbLinearRing) + AG.addpoint!(ring, 1204067.0548148106, 634617.5980860253) + AG.addpoint!(ring, 1204067.0548148106, 620742.1035724243) + AG.addpoint!(ring, 1215167.4504256917, 620742.1035724243) + AG.addpoint!(ring, 1215167.4504256917, 634617.5980860253) + AG.addpoint!(ring, 1204067.0548148106, 634617.5980860253) AG.addgeom!(poly, ring) - AG.addgeom!(multipolygon,poly) - - poly = AG.creategeom(GDAL.wkbPolygon) - ring = AG.creategeom(GDAL.wkbLinearRing) - AG.addpoint!(ring, 1179553.6811741155, 647105.5431482664) - AG.addpoint!(ring, 1179553.6811741155, 626292.3013778647) - AG.addpoint!(ring, 1194354.20865529, 626292.3013778647) - AG.addpoint!(ring, 1194354.20865529, 647105.5431482664) - AG.addpoint!(ring, 1179553.6811741155, 647105.5431482664) + AG.addgeom!(multipolygon, poly) + + poly = AG.creategeom(AG.wkbPolygon) + ring = AG.creategeom(AG.wkbLinearRing) + AG.addpoint!(ring, 1179553.6811741155, 647105.5431482664) + AG.addpoint!(ring, 1179553.6811741155, 626292.3013778647) + AG.addpoint!(ring, 1194354.20865529, 626292.3013778647) + AG.addpoint!(ring, 1194354.20865529, 647105.5431482664) + AG.addpoint!(ring, 1179553.6811741155, 647105.5431482664) AG.addgeom!(poly, ring) - AG.addgeom!(multipolygon,poly) - - @test AG.toWKT(multipolygon) == wktmultipolygon - - # Method 5 - @test AG.toWKT(AG.createmultipolygon_noholes(Vector{Tuple{Float64,Float64}}[ - [(1204067.0548148106, 634617.5980860253), - (1204067.0548148106, 620742.1035724243), - (1215167.4504256917, 620742.1035724243), - (1215167.4504256917, 634617.5980860253), - (1204067.0548148106, 634617.5980860253)], - [(1179553.6811741155, 647105.5431482664), - (1179553.6811741155, 626292.3013778647), - (1194354.20865529, 626292.3013778647), - (1194354.20865529, 647105.5431482664), - (1179553.6811741155, 647105.5431482664)] - ])) == wktmultipolygon -end + AG.addgeom!(multipolygon, poly) -@testset "Create a GeometryCollection" begin + @test AG.toWKT(multipolygon) == wktmultipolygon + end - wktcollection = "GEOMETRYCOLLECTION " * - "(POINT (-122.23 47.09),LINESTRING (-122.6 47.14,-122.48 47.23))" + # Method 4 + multipolygon = AG.creategeom(AG.wkbMultiPolygon) + poly = AG.creategeom(AG.wkbPolygon) + ring = AG.creategeom(AG.wkbLinearRing) + AG.addpoint!(ring, 1204067.0548148106, 634617.5980860253) + AG.addpoint!(ring, 1204067.0548148106, 620742.1035724243) + AG.addpoint!(ring, 1215167.4504256917, 620742.1035724243) + AG.addpoint!(ring, 1215167.4504256917, 634617.5980860253) + AG.addpoint!(ring, 1204067.0548148106, 634617.5980860253) + AG.addgeom!(poly, ring) + AG.addgeom!(multipolygon, poly) + + poly = AG.creategeom(AG.wkbPolygon) + ring = AG.creategeom(AG.wkbLinearRing) + AG.addpoint!(ring, 1179553.6811741155, 647105.5431482664) + AG.addpoint!(ring, 1179553.6811741155, 626292.3013778647) + AG.addpoint!(ring, 1194354.20865529, 626292.3013778647) + AG.addpoint!(ring, 1194354.20865529, 647105.5431482664) + AG.addpoint!(ring, 1179553.6811741155, 647105.5431482664) + AG.addgeom!(poly, ring) + AG.addgeom!(multipolygon, poly) - # Method not applicable here + @test AG.toWKT(multipolygon) == wktmultipolygon - # Method 2 - AG.creategeomcollection() do geomcol - for g in [AG.createpoint(-122.23, 47.09), - AG.createlinestring([(-122.60, 47.14), (-122.48, 47.23)])] - AG.addgeom!(geomcol, g) + # Method 5 + @test AG.toWKT( + AG.createmultipolygon_noholes( + Vector{Tuple{Float64,Float64}}[ + [ + (1204067.0548148106, 634617.5980860253), + (1204067.0548148106, 620742.1035724243), + (1215167.4504256917, 620742.1035724243), + (1215167.4504256917, 634617.5980860253), + (1204067.0548148106, 634617.5980860253), + ], + [ + (1179553.6811741155, 647105.5431482664), + (1179553.6811741155, 626292.3013778647), + (1194354.20865529, 626292.3013778647), + (1194354.20865529, 647105.5431482664), + (1179553.6811741155, 647105.5431482664), + ], + ], + ), + ) == wktmultipolygon + end + + @testset "Create a GeometryCollection" begin + wktcollection = + "GEOMETRYCOLLECTION " * + "(POINT (-122.23 47.09),LINESTRING (-122.6 47.14,-122.48 47.23))" + + # Method not applicable here + + # Method 2 + AG.creategeomcollection() do geomcol + for g in [ + AG.createpoint(-122.23, 47.09), + AG.createlinestring([(-122.60, 47.14), (-122.48, 47.23)]), + ] + AG.addgeom!(geomcol, g) + end + @test AG.toWKT(geomcol) == wktcollection end - @test AG.toWKT(geomcol) == wktcollection - end - # Method 3 - AG.creategeom(GDAL.wkbGeometryCollection) do geomcol - point = AG.creategeom(GDAL.wkbPoint) + # Method 3 + AG.creategeom(AG.wkbGeometryCollection) do geomcol + point = AG.creategeom(AG.wkbPoint) AG.addpoint!(point, -122.23, 47.09) - AG.addgeom!(geomcol, point) + AG.addgeom!(geomcol, point) - line = AG.creategeom(GDAL.wkbLineString) + line = AG.creategeom(AG.wkbLineString) AG.addpoint!(line, -122.60, 47.14) AG.addpoint!(line, -122.48, 47.23) - AG.addgeom!(geomcol, line) + AG.addgeom!(geomcol, line) - @test AG.toWKT(geomcol) == wktcollection - end + @test AG.toWKT(geomcol) == wktcollection + end - # Method 4 - geomcol = AG.creategeom(GDAL.wkbGeometryCollection) - point = AG.creategeom(GDAL.wkbPoint) - AG.addpoint!(point, -122.23, 47.09) + # Method 4 + geomcol = AG.creategeom(AG.wkbGeometryCollection) + point = AG.creategeom(AG.wkbPoint) + AG.addpoint!(point, -122.23, 47.09) AG.addgeom!(geomcol, point) - line = AG.creategeom(GDAL.wkbLineString) - AG.addpoint!(line, -122.60, 47.14) - AG.addpoint!(line, -122.48, 47.23) + line = AG.creategeom(AG.wkbLineString) + AG.addpoint!(line, -122.60, 47.14) + AG.addpoint!(line, -122.48, 47.23) AG.addgeom!(geomcol, line) - @test AG.toWKT(geomcol) == wktcollection -end + @test AG.toWKT(geomcol) == wktcollection + end + + @testset "Create Geometry from WKT" begin + wkt = "POINT (1120351.5712494177 741921.4223245403)" + x, y = 1120351.5712494177, 741921.4223245403 -@testset "Create Geometry from WKT" begin - wkt = "POINT (1120351.5712494177 741921.4223245403)" - x, y = 1120351.5712494177, 741921.4223245403 + # Method 1 + AG.fromWKT(wkt) do point + @test AG.getx(point, 0) ≈ x + @test AG.gety(point, 0) ≈ y + end - # Method 1 - AG.fromWKT(wkt) do point + # Method 2 + point = AG.fromWKT(wkt) @test AG.getx(point, 0) ≈ x @test AG.gety(point, 0) ≈ y end - # Method 2 - point = AG.fromWKT(wkt) - @test AG.getx(point, 0) ≈ x - @test AG.gety(point, 0) ≈ y -end + @testset "Create Geometry from GeoJSON" begin + geojson = """{"type":"Point","coordinates":[108420.33,753808.59]}""" + x, y = 108420.33, 753808.59 -@testset "Create Geometry from GeoJSON" begin - geojson = """{"type":"Point","coordinates":[108420.33,753808.59]}""" - x, y = 108420.33, 753808.59 + # Method 1 + AG.fromJSON(geojson) do point + @test AG.getx(point, 0) ≈ x + @test AG.gety(point, 0) ≈ y + end - # Method 1 - AG.fromJSON(geojson) do point + # Method 2 + point = AG.fromJSON(geojson) @test AG.getx(point, 0) ≈ x @test AG.gety(point, 0) ≈ y end - # Method 2 - point = AG.fromJSON(geojson) - @test AG.getx(point, 0) ≈ x - @test AG.gety(point, 0) ≈ y -end + @testset "Create Geometry from GML" begin + gml = """ + 108420.33,753808.59 + """ + x, y = 108420.33, 753808.59 -@testset "Create Geometry from GML" begin - gml = """ - 108420.33,753808.59 - """ - x, y = 108420.33, 753808.59 + # Method 1 + AG.fromGML(gml) do point + @test AG.getx(point, 0) ≈ x + @test AG.gety(point, 0) ≈ y + end - # Method 1 - AG.fromGML(gml) do point + # Method 2 + point = AG.fromGML(gml) @test AG.getx(point, 0) ≈ x @test AG.gety(point, 0) ≈ y end - # Method 2 - point = AG.fromGML(gml) - @test AG.getx(point, 0) ≈ x - @test AG.gety(point, 0) ≈ y -end + @testset "Create Geometry from WKB" begin + #! format: off + wkb = [0x01, 0x01, 0x00, 0x00, 0x00, 0x7b, 0x14, 0xae, 0x47, 0x45, 0x78, 0xfa, 0x40, 0xe1, 0x7a, 0x14, 0x2e, 0x21, 0x01, 0x27, 0x41] + #! format: on + x, y = 108420.33, 753808.59 -@testset "Create Geometry from WKB" begin - wkb = [0x01,0x01,0x00,0x00,0x00,0x7b,0x14,0xae,0x47,0x45,0x78,0xfa,0x40, - 0xe1,0x7a,0x14,0x2e,0x21,0x01,0x27,0x41] - x, y = 108420.33, 753808.59 + # Method 1 + AG.fromWKB(wkb) do point + @test AG.getx(point, 0) ≈ x + @test AG.gety(point, 0) ≈ y + end - # Method 1 - AG.fromWKB(wkb) do point + # Method 2 + point = AG.fromWKB(wkb) @test AG.getx(point, 0) ≈ x @test AG.gety(point, 0) ≈ y end - # Method 2 - point = AG.fromWKB(wkb) - @test AG.getx(point, 0) ≈ x - @test AG.gety(point, 0) ≈ y -end + @testset "Count Points in a LineString" begin + wkt = "LINESTRING (1181866.263593049 615654.4222507705, 1205917.1207499576 623979.7189589312, 1227192.8790041457 643405.4112779726, 1224880.2965852122 665143.6860159477)" -@testset "Count Points in a LineString" begin - wkt = "LINESTRING (1181866.263593049 615654.4222507705, 1205917.1207499576 623979.7189589312, 1227192.8790041457 643405.4112779726, 1224880.2965852122 665143.6860159477)" + # Method 1 + AG.fromWKT(wkt) do geom + @test AG.ngeom(geom) == 4 + end - # Method 1 - AG.fromWKT(wkt) do geom - @test AG.ngeom(geom) == 4 + # Method 2 + @test AG.ngeom(AG.fromWKT(wkt)) == 4 end - # Method 2 - @test AG.ngeom(AG.fromWKT(wkt)) == 4 -end + @testset "Count Points in a MultiPoint" begin + wkt = "MULTIPOINT (1181866.263593049 615654.4222507705, 1205917.1207499576 623979.7189589312, 1227192.8790041457 643405.4112779726, 1224880.2965852122 665143.6860159477)" -@testset "Count Points in a MultiPoint" begin - wkt = "MULTIPOINT (1181866.263593049 615654.4222507705, 1205917.1207499576 623979.7189589312, 1227192.8790041457 643405.4112779726, 1224880.2965852122 665143.6860159477)" + # Method 1 + AG.fromWKT(wkt) do geom + @test AG.ngeom(geom) == 4 + end - # Method 1 - AG.fromWKT(wkt) do geom - @test AG.ngeom(geom) == 4 + # Method 2 + @test AG.ngeom(AG.fromWKT(wkt)) == 4 end - # Method 2 - @test AG.ngeom(AG.fromWKT(wkt)) == 4 -end - -@testset "Iterate over Geometries in a Geometry" begin - wkt = "MULTIPOINT (1181866.263593049 615654.4222507705, 1205917.1207499576 623979.7189589312, 1227192.8790041457 643405.4112779726, 1224880.2965852122 665143.6860159477)" - AG.fromWKT(wkt) do geom - # TODO Should getgeom use Julian counting from 1? - @test AG.toWKT(AG.getgeom(geom, 3)) == "POINT (1224880.29658521 665143.686015948)" + @testset "Iterate over Geometries in a Geometry" begin + wkt = "MULTIPOINT (1181866.263593049 615654.4222507705, 1205917.1207499576 623979.7189589312, 1227192.8790041457 643405.4112779726, 1224880.2965852122 665143.6860159477)" + AG.fromWKT(wkt) do geom + # TODO Should getgeom use Julian counting from 1? + @test AG.toWKT(AG.getgeom(geom, 3)) == + "POINT (1224880.29658521 665143.686015948)" + end end -end -@testset "Iterate over Points in a Geometry" begin - wkt = "LINESTRING (1181866.263593049 615654.4222507705, 1205917.1207499576 623979.7189589312, 1227192.8790041457 643405.4112779726, 1224880.2965852122 665143.6860159477)" - AG.fromWKT(wkt) do geom - @test AG.getpoint(geom, 3) == (1.2248802965852122e6, 665143.6860159477, 0.0) + @testset "Iterate over Points in a Geometry" begin + wkt = "LINESTRING (1181866.263593049 615654.4222507705, 1205917.1207499576 623979.7189589312, 1227192.8790041457 643405.4112779726, 1224880.2965852122 665143.6860159477)" + AG.fromWKT(wkt) do geom + @test AG.getpoint(geom, 3) == + (1.2248802965852122e6, 665143.6860159477, 0.0) + end end -end -@testset "Buffer a Geometry" begin - wkt = "POINT (1198054.34 648493.09)" + @testset "Buffer a Geometry" begin + wkt = "POINT (1198054.34 648493.09)" - # Method 1 - AG.fromWKT(wkt) do pt - bufferdist = 500 - AG.buffer(pt, bufferdist) do poly - @test AG.getgeomtype(poly) == GDAL.wkbPolygon + # Method 1 + AG.fromWKT(wkt) do pt + bufferdist = 500 + AG.buffer(pt, bufferdist) do poly + @test AG.getgeomtype(poly) == AG.wkbPolygon + end end - end - # Method 2 - @test AG.getgeomtype(AG.buffer(AG.fromWKT(wkt), 500)) == GDAL.wkbPolygon -end - -@testset "Calculate Envelope of a Geometry" begin - wkt = "LINESTRING (1181866.263593049 615654.4222507705, 1205917.1207499576 623979.7189589312, 1227192.8790041457 643405.4112779726, 1224880.2965852122 665143.6860159477)" - AG.fromWKT(wkt) do line - env = AG.envelope(line) - @test (env.MaxX - env.MinX) * (env.MaxY - env.MinY) ≈ 2.2431808256625123e9 + # Method 2 + @test AG.getgeomtype(AG.buffer(AG.fromWKT(wkt), 500)) == AG.wkbPolygon end -end -@testset "Calculate Bounding Box of a Geometry" begin - wkt = "POLYGON ((1162440.5712740074 672081.4332727483, 1162440.5712740074 647105.5431482664, 1195279.2416228633 647105.5431482664, 1195279.2416228633 672081.4332727483, 1162440.5712740074 672081.4332727483))" - expected = AG.createpolygon([[672081.4332727483, 1162440.5712740074], [672081.4332727483, 1195279.2416228633], [647105.5431482664, 1195279.2416228633], [647105.5431482664, 1162440.5712740074], [672081.4332727483, 1162440.5712740074]]) - AG.fromWKT(wkt) do polygon - calculated = AG.boundingbox(polygon) - @test AG.equals(expected, calculated) + @testset "Calculate Envelope of a Geometry" begin + wkt = "LINESTRING (1181866.263593049 615654.4222507705, 1205917.1207499576 623979.7189589312, 1227192.8790041457 643405.4112779726, 1224880.2965852122 665143.6860159477)" + AG.fromWKT(wkt) do line + env = AG.envelope(line) + @test (env.MaxX - env.MinX) * (env.MaxY - env.MinY) ≈ + 2.2431808256625123e9 + end end -end -@testset "Calculate the Area of a Geometry" begin - wkt = "POLYGON ((1162440.5712740074 672081.4332727483, 1162440.5712740074 647105.5431482664, 1195279.2416228633 647105.5431482664, 1195279.2416228633 672081.4332727483, 1162440.5712740074 672081.4332727483))" - AG.fromWKT(wkt) do poly - @test AG.geomarea(poly) ≈ 8.201750224671059e8 + @testset "Calculate Bounding Box of a Geometry" begin + wkt = "POLYGON ((1162440.5712740074 672081.4332727483, 1162440.5712740074 647105.5431482664, 1195279.2416228633 647105.5431482664, 1195279.2416228633 672081.4332727483, 1162440.5712740074 672081.4332727483))" + expected = AG.createpolygon([ + [672081.4332727483, 1162440.5712740074], + [672081.4332727483, 1195279.2416228633], + [647105.5431482664, 1195279.2416228633], + [647105.5431482664, 1162440.5712740074], + [672081.4332727483, 1162440.5712740074], + ]) + AG.fromWKT(wkt) do polygon + calculated = AG.boundingbox(polygon) + @test AG.equals(expected, calculated) + end end -end -@testset "Calculate the Length of a Geometry" begin - wkt = "LINESTRING (1181866.263593049 615654.4222507705, 1205917.1207499576 623979.7189589312, 1227192.8790041457 643405.4112779726, 1224880.2965852122 665143.6860159477)" - AG.fromWKT(wkt) do line - @test AG.geomlength(line) ≈ 76121.94397805972 + @testset "Calculate the Area of a Geometry" begin + wkt = "POLYGON ((1162440.5712740074 672081.4332727483, 1162440.5712740074 647105.5431482664, 1195279.2416228633 647105.5431482664, 1195279.2416228633 672081.4332727483, 1162440.5712740074 672081.4332727483))" + AG.fromWKT(wkt) do poly + @test AG.geomarea(poly) ≈ 8.201750224671059e8 + end end -end -@testset "Get the geometry type (as a string) from a Geometry" begin - types = ["POINT", "LINESTRING", "POLYGON"] - wkts = [ - "POINT (1198054.34 648493.09)", - "LINESTRING (1181866.263593049 615654.4222507705, 1205917.1207499576 623979.7189589312, 1227192.8790041457 643405.4112779726, 1224880.2965852122 665143.6860159477)", - "POLYGON ((1162440.5712740074 672081.4332727483, 1162440.5712740074 647105.5431482664, 1195279.2416228633 647105.5431482664, 1195279.2416228633 672081.4332727483, 1162440.5712740074 672081.4332727483))" - ] - for (i, wkt) in enumerate(wkts) - AG.fromWKT(wkt) do geom - @test AG.geomname(geom) == types[i] + @testset "Calculate the Length of a Geometry" begin + wkt = "LINESTRING (1181866.263593049 615654.4222507705, 1205917.1207499576 623979.7189589312, 1227192.8790041457 643405.4112779726, 1224880.2965852122 665143.6860159477)" + AG.fromWKT(wkt) do line + @test AG.geomlength(line) ≈ 76121.94397805972 end end -end - -@testset "Calculate intersection between two Geometries" begin - wkt1 = "POLYGON ((1208064.271243039 624154.6783778917, 1208064.271243039 601260.9785661874, 1231345.9998651114 601260.9785661874, 1231345.9998651114 624154.6783778917, 1208064.271243039 624154.6783778917))" - wkt2 = "POLYGON ((1199915.6662253144 633079.3410163528, 1199915.6662253144 614453.958118695, 1219317.1067437078 614453.958118695, 1219317.1067437078 633079.3410163528, 1199915.6662253144 633079.3410163528)))" - wkt3 = "POLYGON ((1208064.27124304 624154.678377892,1219317.10674371 624154.678377892,1219317.10674371 614453.958118695,1208064.27124304 614453.958118695,1208064.27124304 624154.678377892))" - - # Method 1 - AG.fromWKT(wkt1) do poly1 - AG.fromWKT(wkt2) do poly2 - AG.intersection(poly1, poly2) do poly3 - @test AG.toWKT(poly3) == wkt3 + @testset "Get the geometry type (as a string) from a Geometry" begin + types = ["POINT", "LINESTRING", "POLYGON"] + wkts = [ + "POINT (1198054.34 648493.09)", + "LINESTRING (1181866.263593049 615654.4222507705, 1205917.1207499576 623979.7189589312, 1227192.8790041457 643405.4112779726, 1224880.2965852122 665143.6860159477)", + "POLYGON ((1162440.5712740074 672081.4332727483, 1162440.5712740074 647105.5431482664, 1195279.2416228633 647105.5431482664, 1195279.2416228633 672081.4332727483, 1162440.5712740074 672081.4332727483))", + ] + for (i, wkt) in enumerate(wkts) + AG.fromWKT(wkt) do geom + @test AG.geomname(geom) == types[i] + end end end - end - - # Method 2 - @test AG.toWKT(AG.intersection(AG.fromWKT(wkt1), AG.fromWKT(wkt2))) == wkt3 -end -@testset "Calculate union between two Geometries" begin - wkt1 = "POLYGON ((1208064.271243039 624154.6783778917, 1208064.271243039 601260.9785661874, 1231345.9998651114 601260.9785661874, 1231345.9998651114 624154.6783778917, 1208064.271243039 624154.6783778917))" - wkt2 = "POLYGON ((1199915.6662253144 633079.3410163528, 1199915.6662253144 614453.958118695, 1219317.1067437078 614453.958118695, 1219317.1067437078 633079.3410163528, 1199915.6662253144 633079.3410163528)))" - wkt3 = "POLYGON ((1199915.66622531 614453.958118695,1199915.66622531 633079.341016353,1219317.10674371 633079.341016353,1219317.10674371 624154.678377892,1231345.99986511 624154.678377892,1231345.99986511 601260.978566187,1208064.27124304 601260.978566187,1208064.27124304 614453.958118695,1199915.66622531 614453.958118695))" + @testset "Calculate intersection between two Geometries" begin + wkt1 = "POLYGON ((1208064.271243039 624154.6783778917, 1208064.271243039 601260.9785661874, 1231345.9998651114 601260.9785661874, 1231345.9998651114 624154.6783778917, 1208064.271243039 624154.6783778917))" + wkt2 = "POLYGON ((1199915.6662253144 633079.3410163528, 1199915.6662253144 614453.958118695, 1219317.1067437078 614453.958118695, 1219317.1067437078 633079.3410163528, 1199915.6662253144 633079.3410163528)))" + wkt3 = "POLYGON ((1208064.27124304 624154.678377892,1219317.10674371 624154.678377892,1219317.10674371 614453.958118695,1208064.27124304 614453.958118695,1208064.27124304 624154.678377892))" - # Method 1 - AG.fromWKT(wkt1) do poly1 - AG.fromWKT(wkt2) do poly2 - AG.union(poly1, poly2) do poly3 - @test AG.toWKT(poly3) == wkt3 + # Method 1 + AG.fromWKT(wkt1) do poly1 + AG.fromWKT(wkt2) do poly2 + AG.intersection(poly1, poly2) do poly3 + @test AG.toWKT(poly3) == wkt3 + end + end end - end + + # Method 2 + @test AG.toWKT(AG.intersection(AG.fromWKT(wkt1), AG.fromWKT(wkt2))) == + wkt3 end - # Method 2 - @test AG.toWKT(AG.union(AG.fromWKT(wkt1), AG.fromWKT(wkt2))) == wkt3 -end + @testset "Calculate union between two Geometries" begin + wkt1 = "POLYGON ((1208064.271243039 624154.6783778917, 1208064.271243039 601260.9785661874, 1231345.9998651114 601260.9785661874, 1231345.9998651114 624154.6783778917, 1208064.271243039 624154.6783778917))" + wkt2 = "POLYGON ((1199915.6662253144 633079.3410163528, 1199915.6662253144 614453.958118695, 1219317.1067437078 614453.958118695, 1219317.1067437078 633079.3410163528, 1199915.6662253144 633079.3410163528)))" + wkt3 = "POLYGON ((1199915.66622531 614453.958118695,1199915.66622531 633079.341016353,1219317.10674371 633079.341016353,1219317.10674371 624154.678377892,1231345.99986511 624154.678377892,1231345.99986511 601260.978566187,1208064.27124304 601260.978566187,1208064.27124304 614453.958118695,1199915.66622531 614453.958118695))" -@testset "Write Geometry to GeoJSON|GML|WKT|WKB" begin - AG.createpolygon([(1179091.1646903288, 712782.8838459781), - (1161053.0218226474, 667456.2684348812), - (1214704.933941905, 641092.8288590391), - (1228580.428455506, 682719.3123998424), - (1218405.0658121984, 721108.1805541387), - (1179091.1646903288, 712782.8838459781)]) do poly - @test AG.toJSON(poly)[1:19] == "{ \"type\": \"Polygon\"" - @test AG.toGML(poly)[1:13] == "" - @test AG.toWKT(poly)[1:7] == "POLYGON" - @test AG.toWKB(poly) == UInt8[0x01, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x38, 0x25, 0x29, 0x2a, 0xd3, 0xfd, 0x31, 0x41, 0xc5, 0x75, 0x87, 0xc4, 0x9d, 0xc0, 0x25, 0x41, 0x45, 0x2b, 0x96, 0x05, 0x5d, 0xb7, 0x31, 0x41, 0xf8, 0x4b, 0x70, 0x89, 0x80, 0x5e, 0x24, 0x41, 0x12, 0xd1, 0x16, 0xef, 0xf0, 0x88, 0x32, 0x41, 0x44, 0x36, 0x60, 0xa8, 0x89, 0x90, 0x23, 0x41, 0x92, 0x42, 0xaf, 0x6d, 0x24, 0xbf, 0x32, 0x41, 0x45, 0xdf, 0xf2, 0x9f, 0xbe, 0xd5, 0x24, 0x41, 0x78, 0x11, 0xd9, 0x10, 0x65, 0x97, 0x32, 0x41, 0x92, 0x97, 0x71, 0x5c, 0xa8, 0x01, 0x26, 0x41, 0x38, 0x25, 0x29, 0x2a, 0xd3, 0xfd, 0x31, 0x41, 0xc5, 0x75, 0x87, 0xc4, 0x9d, 0xc0, 0x25, 0x41] + # Method 1 + AG.fromWKT(wkt1) do poly1 + AG.fromWKT(wkt2) do poly2 + AG.union(poly1, poly2) do poly3 + @test AG.toWKT(poly3) == wkt3 + end + end + end + + # Method 2 + @test AG.toWKT(AG.union(AG.fromWKT(wkt1), AG.fromWKT(wkt2))) == wkt3 + end + + @testset "Write Geometry to GeoJSON|GML|WKT|WKB" begin + AG.createpolygon([ + (1179091.1646903288, 712782.8838459781), + (1161053.0218226474, 667456.2684348812), + (1214704.933941905, 641092.8288590391), + (1228580.428455506, 682719.3123998424), + (1218405.0658121984, 721108.1805541387), + (1179091.1646903288, 712782.8838459781), + ]) do poly + @test AG.toJSON(poly)[1:19] == "{ \"type\": \"Polygon\"" + @test AG.toGML(poly)[1:13] == "" + @test AG.toWKT(poly)[1:7] == "POLYGON" + @test AG.toWKB(poly) == UInt8[ + 0x01, + 0x03, + 0x00, + 0x00, + 0x00, + 0x01, + 0x00, + 0x00, + 0x00, + 0x06, + 0x00, + 0x00, + 0x00, + 0x38, + 0x25, + 0x29, + 0x2a, + 0xd3, + 0xfd, + 0x31, + 0x41, + 0xc5, + 0x75, + 0x87, + 0xc4, + 0x9d, + 0xc0, + 0x25, + 0x41, + 0x45, + 0x2b, + 0x96, + 0x05, + 0x5d, + 0xb7, + 0x31, + 0x41, + 0xf8, + 0x4b, + 0x70, + 0x89, + 0x80, + 0x5e, + 0x24, + 0x41, + 0x12, + 0xd1, + 0x16, + 0xef, + 0xf0, + 0x88, + 0x32, + 0x41, + 0x44, + 0x36, + 0x60, + 0xa8, + 0x89, + 0x90, + 0x23, + 0x41, + 0x92, + 0x42, + 0xaf, + 0x6d, + 0x24, + 0xbf, + 0x32, + 0x41, + 0x45, + 0xdf, + 0xf2, + 0x9f, + 0xbe, + 0xd5, + 0x24, + 0x41, + 0x78, + 0x11, + 0xd9, + 0x10, + 0x65, + 0x97, + 0x32, + 0x41, + 0x92, + 0x97, + 0x71, + 0x5c, + 0xa8, + 0x01, + 0x26, + 0x41, + 0x38, + 0x25, + 0x29, + 0x2a, + 0xd3, + 0xfd, + 0x31, + 0x41, + 0xc5, + 0x75, + 0x87, + 0xc4, + 0x9d, + 0xc0, + 0x25, + 0x41, + ] + end end -end -@testset "Force polygon to multipolygon" begin - wkt = "POLYGON ((1179091.164690328761935 712782.883845978067257,1161053.021822647424415 667456.268434881232679,1214704.933941904921085 641092.828859039116651,1228580.428455505985767 682719.312399842427112,1218405.065812198445201 721108.180554138729349,1179091.164690328761935 712782.883845978067257))" + @testset "Force polygon to multipolygon" begin + wkt = "POLYGON ((1179091.164690328761935 712782.883845978067257,1161053.021822647424415 667456.268434881232679,1214704.933941904921085 641092.828859039116651,1228580.428455505985767 682719.312399842427112,1218405.065812198445201 721108.180554138729349,1179091.164690328761935 712782.883845978067257))" - # Method 1 - AG.fromWKT(wkt) do poly - @test AG.getgeomtype(poly) == GDAL.wkbPolygon - AG.forceto(poly, GDAL.wkbMultiPolygon) do mpoly - @test AG.getgeomtype(mpoly) == GDAL.wkbMultiPolygon + # Method 1 + AG.fromWKT(wkt) do poly + @test AG.getgeomtype(poly) == AG.wkbPolygon + AG.forceto(poly, AG.wkbMultiPolygon) do mpoly + @test AG.getgeomtype(mpoly) == AG.wkbMultiPolygon + end end - end - # Method 2 - @test AG.getgeomtype(AG.forceto(AG.fromWKT(wkt), GDAL.wkbMultiPolygon)) == GDAL.wkbMultiPolygon + # Method 2 + @test AG.getgeomtype(AG.forceto(AG.fromWKT(wkt), AG.wkbMultiPolygon)) == + AG.wkbMultiPolygon + end end diff --git a/test/test_cookbook_projection.jl b/test/test_cookbook_projection.jl index c2d860fc..ea8f1415 100644 --- a/test/test_cookbook_projection.jl +++ b/test/test_cookbook_projection.jl @@ -1,97 +1,148 @@ using Test -import GeoInterface, GeoFormatTypes, ArchGDAL +import GeoInterface, GeoFormatTypes, ArchGDAL const AG = ArchGDAL const GFT = GeoFormatTypes -@testset "Reproject a Geometry" begin - @testset "Method 1" begin - AG.importEPSG(2927) do source; AG.importEPSG(4326) do target - AG.createcoordtrans(source, target) do transform - AG.fromWKT("POINT (1120351.57 741921.42)") do point - @test AG.toWKT(point) == "POINT (1120351.57 741921.42)" - AG.transform!(point, transform) - @test GeoInterface.coordinates(point) ≈ [47.3488070138318, -122.5981499431438] - end end end end - end - - @testset "Method 2" begin - AG.importEPSG(2927) do source; AG.importEPSG(4326) do target - AG.createcoordtrans(source, target) do transform - xs = [47.348801, 47.348801] - ys = [-122.598135,-122.598135] - zs = [0.0, 0.0] - @test AG.transform!(xs, ys, zs, transform) == true - @test xs ≈ [45.151458, 45.151458] - @test ys ≈ [-126.863475, -126.863475] - @test zs ≈ [0.0, 0.0] - end end end - end +@testset "test_cookbook_projection.jl" begin + @testset "Reproject a Geometry" begin + @testset "Method 1" begin + AG.importEPSG(2927) do source + AG.importEPSG(4326) do target + AG.createcoordtrans(source, target) do transform + AG.fromWKT("POINT (1120351.57 741921.42)") do point + @test AG.toWKT(point) == + "POINT (1120351.57 741921.42)" + AG.transform!(point, transform) + @test GeoInterface.coordinates(point) ≈ + [47.3488070138318, -122.5981499431438] + end + end + end + end + end - @testset "Use reproject" begin - @testset "reciprocal reprojection of wkt" begin - wktpoint = GFT.WellKnownText(GFT.Geom(), "POINT (1120351.57 741921.42)") - result = GFT.WellKnownText(GFT.Geom(), "POINT (47.3488070138318 -122.598149943144)") - @test AG.reproject(wktpoint, GFT.EPSG(2927), GFT.EPSG(4326)) == result - @test convert(AG.Geometry, AG.reproject(result, GFT.EPSG(4326), GFT.EPSG(2927))) |> - GeoInterface.coordinates ≈ [1.12035156999967e6, 741921.420000271] + @testset "Method 2" begin + AG.importEPSG(2927) do source + AG.importEPSG(4326) do target + AG.createcoordtrans(source, target) do transform + xs = [47.348801, 47.348801] + ys = [-122.598135, -122.598135] + zs = [0.0, 0.0] + @test AG.transform!(xs, ys, zs, transform) == true + @test xs ≈ [45.151458, 45.151458] + @test ys ≈ [-126.863475, -126.863475] + @test zs ≈ [0.0, 0.0] + end + end + end end - @testset "reproject vector, vector of vector, or tuple" begin - coord = [1120351.57, 741921.42] - @test AG.reproject(coord, GFT.EPSG(2927), GFT.EPSG(4326)) ≈ - [47.3488070138318, -122.5981499431438] - @test AG.reproject([coord], GFT.EPSG(2927), GFT.EPSG(4326)) ≈ - [[47.3488070138318, -122.5981499431438]] - coord = (1120351.57, 741921.42) - @test AG.reproject(coord, GFT.EPSG(2927), GFT.EPSG(4326); order=:compliant) ≈ - [47.3488070138318, -122.5981499431438] - @test AG.reproject(coord, GFT.EPSG(2927), GFT.EPSG(4326); order=:trad) ≈ - [-122.5981499431438, 47.3488070138318] - @test AG.reproject([coord], GFT.EPSG(2927), convert(GFT.WellKnownText, GFT.EPSG(4326)); order=:compliant) ≈ - [[47.3488070138318, -122.5981499431438]] - @test AG.reproject([coord], GFT.EPSG(2927), convert(GFT.WellKnownText, GFT.EPSG(4326)); order=:trad) ≈ - [[-122.5981499431438, 47.3488070138318]] - # :compliant doesn't work on PROJ axis order, it loses authority information - @test AG.reproject([coord], GFT.EPSG(2927), convert(GFT.ProjString, GFT.EPSG(4326)); order=:compliant) ≈ - [[-122.5981499431438, 47.3488070138318]] - @test AG.reproject([coord], GFT.EPSG(2927), convert(GFT.ProjString, GFT.EPSG(4326)); order=:trad) ≈ - [[-122.5981499431438, 47.3488070138318]] + + @testset "Use reproject" begin + @testset "reciprocal reprojection of wkt" begin + wktpoint = GFT.WellKnownText( + GFT.Geom(), + "POINT (1120351.57 741921.42)", + ) + result = GFT.WellKnownText( + GFT.Geom(), + "POINT (47.3488070138318 -122.598149943144)", + ) + @test AG.reproject(wktpoint, GFT.EPSG(2927), GFT.EPSG(4326)) == + result + @test convert( + AG.Geometry, + AG.reproject(result, GFT.EPSG(4326), GFT.EPSG(2927)), + ) |> GeoInterface.coordinates ≈ + [1.12035156999967e6, 741921.420000271] + end + @testset "reproject vector, vector of vector, or tuple" begin + coord = [1120351.57, 741921.42] + @test AG.reproject(coord, GFT.EPSG(2927), nothing) ≈ + [1120351.57, 741921.42] + @test AG.reproject(coord, GFT.EPSG(2927), GFT.EPSG(4326)) ≈ + [47.3488070138318, -122.5981499431438] + @test AG.reproject([coord], GFT.EPSG(2927), GFT.EPSG(4326)) ≈ + [[47.3488070138318, -122.5981499431438]] + coord = (1120351.57, 741921.42) + @test AG.reproject( + coord, + GFT.EPSG(2927), + GFT.EPSG(4326); + order = :compliant, + ) ≈ [47.3488070138318, -122.5981499431438] + @test AG.reproject( + coord, + GFT.EPSG(2927), + GFT.EPSG(4326); + order = :trad, + ) ≈ [-122.5981499431438, 47.3488070138318] + @test AG.reproject( + [coord], + GFT.EPSG(2927), + convert(GFT.WellKnownText, GFT.EPSG(4326)); + order = :compliant, + ) ≈ [[47.3488070138318, -122.5981499431438]] + @test AG.reproject( + [coord], + GFT.EPSG(2927), + convert(GFT.WellKnownText, GFT.EPSG(4326)); + order = :trad, + ) ≈ [[-122.5981499431438, 47.3488070138318]] + # :compliant doesn't work on PROJ axis order, it loses authority information + @test AG.reproject( + [coord], + GFT.EPSG(2927), + convert(GFT.ProjString, GFT.EPSG(4326)); + order = :compliant, + ) ≈ [[-122.5981499431438, 47.3488070138318]] + @test AG.reproject( + [coord], + GFT.EPSG(2927), + convert(GFT.ProjString, GFT.EPSG(4326)); + order = :trad, + ) ≈ [[-122.5981499431438, 47.3488070138318]] + end end end -end - -@testset "Get Projection" begin - AG.read("ospy/data1/sites.shp") do dataset - layer = AG.getlayer(dataset, 0) - spatialref = AG.getspatialref(layer) - @test AG.toWKT(spatialref)[1:6] == "PROJCS" - @test AG.toWKT(spatialref, false)[1:6] == "PROJCS" - @test AG.toPROJ4(spatialref) == "+proj=utm +zone=12 +datum=WGS84 +units=m +no_defs" - @test AG.toXML(spatialref)[1:17] == " true) == - "(geometry = Geometry: wkbPoint, FID = 2.0, pointname = \"point-a\")" - @test sprint(print, AG.layerdefn(layer)) == """ - Geometry (index 0): (wkbPoint) - Field (index 0): FID (OFTReal) - Field (index 1): pointname (OFTString) - """ - @test sprint(print, AG.getspatialref(layer)) == - "Spatial Reference System: +proj=longlat +datum=WGS84 +no_defs" + Number of feature layers: 1 + Layer 0: point (wkbPoint) + """ - AG.getfeature(layer, 2) do feature - @test sprint(print, feature) == """ - Feature - (index 0) geom => POINT - (index 0) FID => 0.0 - (index 1) pointname => a + layer = AG.getlayer(dataset, 0) + @test sprint(print, layer) == """ + Layer: point + Geometry 0 (): [wkbPoint], POINT (100 0), POINT (100.2785 0.0893), ... + Field 0 (FID): [OFTReal], 2.0, 3.0, 0.0, 3.0 + Field 1 (pointname): [OFTString], point-a, point-b, a, b + """ + @test sprint(print, AG.layerdefn(layer)) == """ + Geometry (index 0): (wkbPoint) + Field (index 0): FID (OFTReal) + Field (index 1): pointname (OFTString) """ - @test sprint(print, AG.getfielddefn(feature, 1)) == - "pointname (OFTString)" - @test sprint(print, AG.getgeomdefn(feature, 0)) == - " (wkbPoint)" + @test sprint(print, AG.getspatialref(layer)) == + "Spatial Reference System: +proj=longlat +datum=WGS84 +no_defs" + + AG.getfeature(layer, 2) do feature + @test sprint(print, feature) == """ + Feature + (index 0) geom => POINT + (index 0) FID => 0.0 + (index 1) pointname => a + """ + @test sprint(print, AG.getfielddefn(feature, 1)) == + "pointname (OFTString)" + @test sprint(print, AG.getgeomdefn(feature, 0)) == " (wkbPoint)" + end end - end - AG.read("gdalworkshop/world.tif") do dataset - @test sprint(print, dataset) == """ - GDAL Dataset (Driver: GTiff/GeoTIFF) - File(s): - gdalworkshop/world.tif + AG.read("gdalworkshop/world.tif") do dataset + @test sprint(print, dataset) == """ + GDAL Dataset (Driver: GTiff/GeoTIFF) + File(s): + gdalworkshop/world.tif - Dataset (width x height): 2048 x 1024 (pixels) - Number of raster bands: 3 - [GA_ReadOnly] Band 1 (Red): 2048 x 1024 (UInt8) - [GA_ReadOnly] Band 2 (Green): 2048 x 1024 (UInt8) - [GA_ReadOnly] Band 3 (Blue): 2048 x 1024 (UInt8) - """ + Dataset (width x height): 2048 x 1024 (pixels) + Number of raster bands: 3 + [GA_ReadOnly] Band 1 (Red): 2048 x 1024 (UInt8) + [GA_ReadOnly] Band 2 (Green): 2048 x 1024 (UInt8) + [GA_ReadOnly] Band 3 (Blue): 2048 x 1024 (UInt8) + """ - @test sprint(print, AG.getband(dataset, 1)) == """ - [GA_ReadOnly] Band 1 (Red): 2048 x 1024 (UInt8) - blocksize: 256×256, nodata: nothing, units: 1.0px + 0.0 - overviews: (0) 1024x512 (1) 512x256 (2) 256x128 - (3) 128x64 (4) 64x32 (5) 32x16 - (6) 16x8 """ + @test sprint(print, AG.getband(dataset, 1)) == """ + [GA_ReadOnly] Band 1 (Red): 2048 x 1024 (UInt8) + blocksize: 256×256, nodata: nothing, units: 1.0px + 0.0 + overviews: (0) 1024x512 (1) 512x256 (2) 256x128 + (3) 128x64 (4) 64x32 (5) 32x16 + (6) 16x8 """ + end end -end -# untested -# Geometry with length(toWKT(geom)) > 60 # should be able to see ... -# Dataset with nlayer(dataset) > 5 + # untested + # Geometry with length(toWKT(geom)) > 60 # should be able to see ... + # Dataset with nlayer(dataset) > 5 + +end diff --git a/test/test_doctest.jl b/test/test_doctest.jl new file mode 100644 index 00000000..56f95aea --- /dev/null +++ b/test/test_doctest.jl @@ -0,0 +1,8 @@ +using Test, Documenter, ArchGDAL +DocMeta.setdocmeta!( + ArchGDAL, + :DocTestSetup, + :(using ArchGDAL, GDAL); + recursive = true, +) +doctest(ArchGDAL) diff --git a/test/test_drivers.jl b/test/test_drivers.jl index 87fdb1ac..a46b0388 100644 --- a/test/test_drivers.jl +++ b/test/test_drivers.jl @@ -1,90 +1,108 @@ using Test -import ArchGDAL; const AG = ArchGDAL +import ArchGDAL; +const AG = ArchGDAL; -@testset "Testing ConfigOptions" begin - @test AG.getconfigoption("GDAL_CACHEMAX") == "" - AG.setconfigoption("GDAL_CACHEMAX", "64") - @test AG.getconfigoption("GDAL_CACHEMAX") == "64" - AG.clearconfigoption("GDAL_CACHEMAX") - @test AG.getconfigoption("GDAL_CACHEMAX", "128") == "128" - - @test AG.getthreadconfigoption("GDAL_CACHEMAX") == "" - AG.setthreadconfigoption("GDAL_CACHEMAX","32") - @test AG.getthreadconfigoption("GDAL_CACHEMAX") == "32" - AG.clearthreadconfigoption("GDAL_CACHEMAX") - @test AG.getthreadconfigoption("GDAL_CACHEMAX", "128") == "128" - - @test AG.getconfigoption("GDAL_CACHEMAX") == "" - @test AG.getconfigoption("CPL_LOG_ERRORS") == "" - @test AG.getthreadconfigoption("GDAL_CACHEMAX") == "" - @test AG.getthreadconfigoption("CPL_LOG_ERRORS") == "" +@testset "test_drivers.jl" begin + @testset "Testing ConfigOptions" begin + @test AG.getconfigoption("GDAL_CACHEMAX") == "" + AG.setconfigoption("GDAL_CACHEMAX", "64") + @test AG.getconfigoption("GDAL_CACHEMAX") == "64" + AG.clearconfigoption("GDAL_CACHEMAX") + @test AG.getconfigoption("GDAL_CACHEMAX", "128") == "128" - AG.environment(globalconfig=[("GDAL_CACHEMAX","64"), - ("CPL_LOG_ERRORS","ON")], - threadconfig=[("GDAL_CACHEMAX","32"), - ("CPL_LOG_ERRORS","OFF")]) do - # it seems that thread settings overwrites global settings? - @test AG.getconfigoption("GDAL_CACHEMAX") == "32" - @test AG.getconfigoption("CPL_LOG_ERRORS") == "OFF" + @test AG.getthreadconfigoption("GDAL_CACHEMAX") == "" + AG.setthreadconfigoption("GDAL_CACHEMAX", "32") @test AG.getthreadconfigoption("GDAL_CACHEMAX") == "32" - @test AG.getthreadconfigoption("CPL_LOG_ERRORS") == "OFF" - end + AG.clearthreadconfigoption("GDAL_CACHEMAX") + @test AG.getthreadconfigoption("GDAL_CACHEMAX", "128") == "128" - AG.environment(globalconfig=[("GDAL_CACHEMAX","64"), - ("CPL_LOG_ERRORS","ON")]) do - # everything normal here - @test AG.getconfigoption("GDAL_CACHEMAX") == "64" - @test AG.getconfigoption("CPL_LOG_ERRORS") == "ON" + @test AG.getconfigoption("GDAL_CACHEMAX") == "" + @test AG.getconfigoption("CPL_LOG_ERRORS") == "" @test AG.getthreadconfigoption("GDAL_CACHEMAX") == "" @test AG.getthreadconfigoption("CPL_LOG_ERRORS") == "" + + AG.environment( + globalconfig = [("GDAL_CACHEMAX", "64"), ("CPL_LOG_ERRORS", "ON")], + threadconfig = [("GDAL_CACHEMAX", "32"), ("CPL_LOG_ERRORS", "OFF")], + ) do + # it seems that thread settings overwrites global settings? + @test AG.getconfigoption("GDAL_CACHEMAX") == "32" + @test AG.getconfigoption("CPL_LOG_ERRORS") == "OFF" + @test AG.getthreadconfigoption("GDAL_CACHEMAX") == "32" + @test AG.getthreadconfigoption("CPL_LOG_ERRORS") == "OFF" + end + + AG.environment( + globalconfig = [("GDAL_CACHEMAX", "64"), ("CPL_LOG_ERRORS", "ON")], + ) do + # everything normal here + @test AG.getconfigoption("GDAL_CACHEMAX") == "64" + @test AG.getconfigoption("CPL_LOG_ERRORS") == "ON" + @test AG.getthreadconfigoption("GDAL_CACHEMAX") == "" + @test AG.getthreadconfigoption("CPL_LOG_ERRORS") == "" + end end -end -@testset "Test Driver Capabilities" begin - drivers = AG.listdrivers() - @test drivers["GTiff"] == "GeoTIFF" - @test length(AG.driveroptions("GTiff")) > 100 - @test sprint(print, AG.identifydriver("data/point.geojson")) == "Driver: GeoJSON/GeoJSON" - @test sprint(print, AG.identifydriver("data/utmsmall.tif")) == "Driver: GTiff/GeoTIFF" + @testset "Test Driver Capabilities" begin + drivers = AG.listdrivers() + @test drivers["GTiff"] == "GeoTIFF" + @test length(AG.driveroptions("GTiff")) > 100 + @test sprint(print, AG.identifydriver("data/point.geojson")) == + "Driver: GeoJSON/GeoJSON" + @test sprint(print, AG.identifydriver("data/utmsmall.tif")) == + "Driver: GTiff/GeoTIFF" + + driver = AG.getdriver("GTiff") + @test isnothing(AG.deregister(driver)) + @test isnothing(AG.register(driver)) + @test AG.validate(driver, ["COMPRESS=LZW", "INTERLEAVE=PIXEL"]) == true + @test AG.validate(driver, ["COMPRESS=LZW"]) == true + @test AG.validate(driver, ["INTERLEAVE=PIXEL"]) == true - @test AG.validate(AG.getdriver("GTiff"), ["COMPRESS=LZW", "INTERLEAVE=PIXEL"]) == true - @test AG.validate(AG.getdriver("GTiff"), ["COMPRESS=LZW"]) == true - @test AG.validate(AG.getdriver("GTiff"), ["INTERLEAVE=PIXEL"]) == true - AG.read("data/point.geojson") do dataset - @test AG.listcapability(dataset) == Dict( - "CreateLayer"=>false, - "DeleteLayer"=>false, - "CreateGeomFieldAfterCreateLayer"=>false, - "CurveGeometries"=>false, - "Transactions"=>false, - "EmulatedTransactions"=>false - ) - @test AG.listcapability(AG.getlayer(dataset,0)) == Dict( - "SequentialWrite"=>false, "DeleteField"=>false, - "IgnoreFields"=>false, "FastSpatialFilter"=>false, - "DeleteFeature"=>false, "FastFeatureCount"=>true, - "StringsAsUTF8"=>true, "CreateGeomField"=>false, - "ReorderFields"=>false, "MeasuredGeometries"=>true, - "FastSetNextByIndex"=>true, "CreateField"=>false, - "RandomWrite"=>false, "RandomRead"=>true, - "CurveGeometries"=>false, "FastGetExtent"=>false, - "Transactions"=>false, "AlterFieldDefn"=>false - ) + AG.read("data/point.geojson") do dataset + @test AG.listcapability(dataset) == Dict( + "CreateLayer" => false, + "DeleteLayer" => false, + "CreateGeomFieldAfterCreateLayer" => false, + "CurveGeometries" => false, + "Transactions" => false, + "EmulatedTransactions" => false, + ) + @test AG.listcapability(AG.getlayer(dataset, 0)) == Dict( + "SequentialWrite" => false, + "DeleteField" => false, + "IgnoreFields" => false, + "FastSpatialFilter" => false, + "DeleteFeature" => false, + "FastFeatureCount" => true, + "StringsAsUTF8" => true, + "CreateGeomField" => false, + "ReorderFields" => false, + "MeasuredGeometries" => true, + "FastSetNextByIndex" => true, + "CreateField" => false, + "RandomWrite" => false, + "RandomRead" => true, + "CurveGeometries" => false, + "FastGetExtent" => false, + "Transactions" => false, + "AlterFieldDefn" => false, + ) + end end -end -@testset "Test extensions list" begin - exts = AG.extensions() - @test exts[".tif"] == "GTiff" - @test exts[".grb"] == "GRIB" - @test exts[".geojson"] == "GeoJSON" -end + @testset "Test extensions list" begin + exts = AG.extensions() + @test exts[".tif"] == "GTiff" + @test exts[".grb"] == "GRIB" + @test exts[".geojson"] == "GeoJSON" + end -@testset "Test getting extensiondriver" begin - @test AG.extensiondriver("filename.tif") == "GTiff" - @test AG.extensiondriver(".tif") == "GTiff" - @test AG.extensiondriver("filename.asc") == "AAIGrid" - @test AG.extensiondriver(".asc") == "AAIGrid" - @test_throws ArgumentError AG.extensiondriver(".not_an_extension") + @testset "Test getting extensiondriver" begin + @test AG.extensiondriver("filename.tif") == "GTiff" + @test AG.extensiondriver(".tif") == "GTiff" + @test AG.extensiondriver("filename.asc") == "AAIGrid" + @test AG.extensiondriver(".asc") == "AAIGrid" + @test_throws ArgumentError AG.extensiondriver(".not_an_extension") + end end - diff --git a/test/test_feature.jl b/test/test_feature.jl index c0d31133..501ec7c9 100644 --- a/test/test_feature.jl +++ b/test/test_feature.jl @@ -1,170 +1,196 @@ using Test -import GDAL -import ArchGDAL; const AG = ArchGDAL - -AG.read("data/point.geojson") do dataset - layer = AG.getlayer(dataset, 0) - AG.getfeature(layer, 0) do f1 - AG.getfeature(layer, 2) do f2 - @test sprint(print, f1) == """ - Feature - (index 0) geom => POINT - (index 0) FID => 2.0 - (index 1) pointname => point-a - """ - @test sprint(print, AG.getgeom(f1)) == "Geometry: POINT (100 0)" - fid1 = AG.getfid(f1) - @test fid1 == 0 - - @test sprint(print, f2) == """ - Feature - (index 0) geom => POINT - (index 0) FID => 0.0 - (index 1) pointname => a - """ - @test sprint(print, AG.getgeom(f2)) == "Geometry: POINT (100 0)" - fid2 = AG.getfid(f2) - @test fid2 == 2 - - AG.clone(f1) do f3 - @test AG.equals(AG.getgeom(f1), AG.getgeom(f3)) == true +import ArchGDAL; +const AG = ArchGDAL; + +@testset "test_feature.jl" begin + AG.read("data/point.geojson") do dataset + layer = AG.getlayer(dataset, 0) + AG.getfeature(layer, 0) do f1 + AG.getfeature(layer, 2) do f2 + @test sprint(print, f1) == """ + Feature + (index 0) geom => POINT + (index 0) FID => 2.0 + (index 1) pointname => point-a + """ + AG.getgeom(f1) do g1 + @test sprint(print, g1) == "Geometry: POINT (100 0)" + end + fid1 = AG.getfid(f1) + @test fid1 == 0 + + AG.getgeom(f2, "fake name") do g + @test sprint(print, g) == "NULL Geometry" + end + + @test sprint(print, f2) == """ + Feature + (index 0) geom => POINT + (index 0) FID => 0.0 + (index 1) pointname => a + """ + AG.getgeom(f2, "") do g2 + @test sprint(print, g2) == "Geometry: POINT (100 0)" + end + fid2 = AG.getfid(f2) + @test fid2 == 2 + + AG.clone(f1) do f3 + @test AG.equals(AG.getgeom(f1), AG.getgeom(f3)) == true + end + AG.setfid!(f1, fid2) + AG.setfid!(f2, fid1) + @test AG.getfid(f1) == 2 + @test AG.getfid(f2) == 0 + + @test AG.findgeomindex(f1, "geom") == -1 + @test AG.findgeomindex(f1, "") == 0 + @test AG.findgeomindex(f2, "geom") == -1 + @test AG.findgeomindex(f2, "") == 0 + @test AG.gettype(AG.getgeomdefn(f1, 0)) == AG.wkbPoint + @test AG.gettype(AG.getgeomdefn(f2, 0)) == AG.wkbPoint end - AG.setfid!(f1, fid2); AG.setfid!(f2, fid1) - @test AG.getfid(f1) == 2 - @test AG.getfid(f2) == 0 - - @test AG.findgeomindex(f1, "geom") == -1 - @test AG.findgeomindex(f1, "") == 0 - @test AG.findgeomindex(f2, "geom") == -1 - @test AG.findgeomindex(f2, "") == 0 - @test AG.gettype(AG.getgeomdefn(f1, 0)) == GDAL.wkbPoint - @test AG.gettype(AG.getgeomdefn(f2, 0)) == GDAL.wkbPoint - end - end - - AG.getfeature(layer, 0) do f - @test AG.toWKT(AG.getgeom(f,0)) == "POINT (100 0)" - AG.setgeom!(f, 0, AG.createpoint(0,100)) - @test AG.toWKT(AG.getgeom(f,0)) == "POINT (0 100)" - AG.createpolygon([(0.,100.),(100.,0.)]) do poly - AG.setgeom!(f, 0, poly) end - @test AG.toWKT(AG.getgeom(f,0)) == "POLYGON ((0 100,100 0))" - AG.setstylestring!(f, "@Name") - @test AG.getstylestring(f) == "@Name" - AG.setstylestring!(f, "NewName") - @test AG.getstylestring(f) == "NewName" + AG.getfeature(layer, 0) do f + @test AG.toWKT(AG.getgeom(f, 0)) == "POINT (100 0)" + AG.setgeom!(f, 0, AG.createpoint(0, 100)) + @test AG.toWKT(AG.getgeom(f, 0)) == "POINT (0 100)" + AG.createpolygon([(0.0, 100.0), (100.0, 0.0)]) do poly + return AG.setgeom!(f, 0, poly) + end + @test AG.toWKT(AG.getgeom(f, 0)) == "POLYGON ((0 100,100 0))" - AG.createstyletable() do st - AG.setstyletable!(f, st) - end + AG.setstylestring!(f, "@Name") + @test AG.getstylestring(f) == "@Name" + AG.setstylestring!(f, "NewName") + @test AG.getstylestring(f) == "NewName" - AG.setnativedata!(f, "nativedata1") - @test AG.getnativedata(f) == "nativedata1" - AG.setnativedata!(f, "nativedata2") - @test AG.getnativedata(f) == "nativedata2" - - AG.setmediatype!(f, "mediatype1") - @test AG.getmediatype(f) == "mediatype1" - AG.setmediatype!(f, "mediatype2") - @test AG.getmediatype(f) == "mediatype2" - - @test AG.validate(f, GDAL.OGR_F_VAL_NULL, false) == true - @test AG.validate(f, GDAL.OGR_F_VAL_GEOM_TYPE, false) == false - @test AG.validate(f, GDAL.OGR_F_VAL_WIDTH, false) == true - @test AG.validate(f, GDAL.OGR_F_VAL_ALLOW_NULL_WHEN_DEFAULT, false) == true - @test AG.validate(f, GDAL.OGR_F_VAL_ALLOW_DIFFERENT_GEOM_DIM, false) == true - - @test AG.getfield(f, 1) == "point-a" - @test AG.getdefault(f, 1) == "" - AG.setdefault!(AG.getfielddefn(f, 1), "default value") - @test AG.getdefault(f, 1) == "default value" - @test AG.getfield(f, 1) == "point-a" - AG.unsetfield!(f, 1) - @test AG.getfield(f, 1) == "default value" - AG.fillunsetwithdefault!(f, notnull = false) - @test AG.getfield(f, 1) == AG.getdefault(f, 1) - end -end + AG.createstyletable() do st + AG.addstyle!(st, "name", "style") + AG.setstyletable!(f, st) + @test AG.findstylestring(AG.getstyletable(f), "name") == "style" + end -@testset "In-Memory Driver" begin - AG.create(AG.getdriver("MEMORY")) do output - layer = AG.createlayer(dataset = output, geom=GDAL.wkbPolygon) - AG.createfielddefn("int64field", GDAL.OFTInteger64) do fielddefn - AG.addfielddefn!(layer, fielddefn) - end - AG.createfielddefn("doublefield", GDAL.OFTReal) do fielddefn - AG.addfielddefn!(layer, fielddefn) - end - AG.createfielddefn("intlistfield", GDAL.OFTIntegerList) do fielddefn - AG.addfielddefn!(layer, fielddefn) - end - AG.createfielddefn("int64listfield", GDAL.OFTInteger64List) do fielddefn - AG.addfielddefn!(layer, fielddefn) - end - AG.createfielddefn("doublelistfield", GDAL.OFTRealList) do fielddefn - AG.addfielddefn!(layer, fielddefn) - end - AG.createfielddefn("stringlistfield", GDAL.OFTStringList) do fielddefn - AG.addfielddefn!(layer, fielddefn) - end - AG.createfielddefn("binaryfield", GDAL.OFTBinary) do fielddefn - AG.addfielddefn!(layer, fielddefn) - end - AG.createfielddefn("datetimefield", GDAL.OFTDateTime) do fielddefn - AG.addfielddefn!(layer, fielddefn) + AG.setnativedata!(f, "nativedata1") + @test AG.getnativedata(f) == "nativedata1" + AG.setnativedata!(f, "nativedata2") + @test AG.getnativedata(f) == "nativedata2" + + AG.setmediatype!(f, "mediatype1") + @test AG.getmediatype(f) == "mediatype1" + AG.setmediatype!(f, "mediatype2") + @test AG.getmediatype(f) == "mediatype2" + + @test AG.validate(f, AG.F_VAL_NULL, false) == true + @test AG.validate(f, AG.F_VAL_GEOM_TYPE, false) == false + @test AG.validate(f, AG.F_VAL_WIDTH, false) == true + @test AG.validate(f, AG.F_VAL_ALLOW_NULL_WHEN_DEFAULT, false) == + true + @test AG.validate(f, AG.F_VAL_ALLOW_DIFFERENT_GEOM_DIM, false) == + true + + @test AG.getfield(f, 1) == "point-a" + @test AG.getdefault(f, 1) == "" + AG.setdefault!(AG.getfielddefn(f, 1), "default value") + @test AG.getdefault(f, 1) == "default value" + @test AG.getfield(f, 1) == "point-a" + AG.unsetfield!(f, 1) + @test AG.getfield(f, 1) == "default value" + AG.fillunsetwithdefault!(f, notnull = false) + @test AG.getfield(f, 1) == AG.getdefault(f, 1) end - AG.createfeature(layer) do feature - AG.setfield!(feature, 0, 1) - AG.setfield!(feature, 1, 1.0) - AG.setfield!(feature, 2, Cint[1, 2]) - AG.setfield!(feature, 3, Int64[1, 2]) - AG.setfield!(feature, 4, Float64[1.0, 2.0]) - AG.setfield!(feature, 5, ["1", "2.0"]) - AG.setfield!(feature, 6, GDAL.GByte[1,2,3,4]) - AG.setfield!(feature, 7, Dates.DateTime(2016,9,25,21,17,0)) - - AG.addfeature(layer) do newfeature - AG.setfrom!(newfeature, feature) - @test AG.getfield(newfeature, 0) == 1 - @test AG.getfield(newfeature, 1) ≈ 1.0 - @test AG.getfield(newfeature, 2) == Int32[1, 2] - @test AG.getfield(newfeature, 3) == Int64[1, 2] - @test AG.getfield(newfeature, 4) ≈ [1.0, 2.0] - @test AG.getfield(newfeature, 5) == ["1", "2.0"] - @test AG.getfield(newfeature, 6) == GDAL.GByte[1,2,3,4] - @test AG.getfield(newfeature, 7) == Dates.DateTime(2016,9,25,21,17,0) - - AG.createfeature(layer) do lastfeature - AG.setfrom!(lastfeature, feature) - AG.setfield!(lastfeature, 0, 45) - AG.setfield!(lastfeature, 1, 18.2) - AG.setfield!(lastfeature, 5, ["foo", "bar"]) - @test AG.getfield(lastfeature, 0) == 45 - @test AG.getfield(lastfeature, 1) ≈ 18.2 - @test AG.getfield(lastfeature, 2) == Int32[1, 2] - @test AG.getfield(lastfeature, 3) == Int64[1, 2] - @test AG.getfield(lastfeature, 4) ≈ [1.0, 2.0] - @test AG.getfield(lastfeature, 5) == ["foo", "bar"] - @test AG.getfield(lastfeature, 6) == GDAL.GByte[1,2,3,4] - @test AG.getfield(lastfeature, 7) == Dates.DateTime(2016,9,25,21,17,0) + end + + @testset "In-Memory Driver" begin + AG.create(AG.getdriver("MEMORY")) do output + layer = AG.createlayer(dataset = output, geom = AG.wkbPolygon) + AG.createfielddefn("int64field", AG.OFTInteger64) do fielddefn + return AG.addfielddefn!(layer, fielddefn) + end + AG.createfielddefn("doublefield", AG.OFTReal) do fielddefn + return AG.addfielddefn!(layer, fielddefn) + end + AG.createfielddefn("intlistfield", AG.OFTIntegerList) do fielddefn + return AG.addfielddefn!(layer, fielddefn) + end + AG.createfielddefn( + "int64listfield", + AG.OFTInteger64List, + ) do fielddefn + return AG.addfielddefn!(layer, fielddefn) + end + AG.createfielddefn("doublelistfield", AG.OFTRealList) do fielddefn + return AG.addfielddefn!(layer, fielddefn) + end + AG.createfielddefn("stringlistfield", AG.OFTStringList) do fielddefn + return AG.addfielddefn!(layer, fielddefn) + end + AG.createfielddefn("binaryfield", AG.OFTBinary) do fielddefn + return AG.addfielddefn!(layer, fielddefn) + end + AG.createfielddefn("datetimefield", AG.OFTDateTime) do fielddefn + return AG.addfielddefn!(layer, fielddefn) + end + AG.createfeature(layer) do feature + AG.setfield!(feature, 0, 1) + AG.setfield!(feature, 1, 1.0) + AG.setfield!(feature, 2, Int32[1, 2]) + AG.setfield!(feature, 3, Int64[1, 2]) + AG.setfield!(feature, 4, Float64[1.0, 2.0]) + AG.setfield!(feature, 5, ["1", "2.0"]) + AG.setfield!(feature, 6, UInt8[1, 2, 3, 4]) + AG.setfield!(feature, 7, Dates.DateTime(2016, 9, 25, 21, 17, 0)) + @test sprint(print, AG.getgeom(feature)) == "NULL Geometry" + AG.getgeom(feature) do geom + @test sprint(print, geom) == "NULL Geometry" + end + @test sprint(print, AG.getgeom(feature, 0)) == "NULL Geometry" + AG.getgeom(feature, 0) do geom + @test sprint(print, geom) == "NULL Geometry" + end + AG.addfeature(layer) do newfeature + AG.setfrom!(newfeature, feature) @test AG.getfield(newfeature, 0) == 1 @test AG.getfield(newfeature, 1) ≈ 1.0 - @test AG.getfield(newfeature, 5) == ["1", "2.0"] - AG.setfrom!(newfeature, lastfeature, collect(Cint, 0:7)) - @test AG.getfield(newfeature, 0) == 45 - @test AG.getfield(newfeature, 1) ≈ 18.2 - @test AG.getfield(newfeature, 5) == ["foo", "bar"] + @test AG.getfield(newfeature, 2) == Int32[1, 2] + @test AG.getfield(newfeature, 3) == Int64[1, 2] + @test AG.getfield(newfeature, 4) ≈ Float64[1.0, 2.0] + @test AG.getfield(newfeature, 5) == String["1", "2.0"] + @test AG.getfield(newfeature, 6) == UInt8[1, 2, 3, 4] + @test AG.getfield(newfeature, 7) == + Dates.DateTime(2016, 9, 25, 21, 17, 0) + + AG.createfeature(layer) do lastfeature + AG.setfrom!(lastfeature, feature) + AG.setfield!(lastfeature, 0, 45) + AG.setfield!(lastfeature, 1, 18.2) + AG.setfield!(lastfeature, 5, ["foo", "bar"]) + @test AG.getfield(lastfeature, 0) == 45 + @test AG.getfield(lastfeature, 1) ≈ 18.2 + @test AG.getfield(lastfeature, 2) == Int32[1, 2] + @test AG.getfield(lastfeature, 3) == Int64[1, 2] + @test AG.getfield(lastfeature, 4) ≈ Float64[1.0, 2.0] + @test AG.getfield(lastfeature, 5) == + String["foo", "bar"] + @test AG.getfield(lastfeature, 6) == UInt8[1, 2, 3, 4] + @test AG.getfield(lastfeature, 7) == + Dates.DateTime(2016, 9, 25, 21, 17, 0) + + @test AG.getfield(newfeature, 0) == 1 + @test AG.getfield(newfeature, 1) ≈ 1.0 + @test AG.getfield(newfeature, 5) == String["1", "2.0"] + AG.setfrom!(newfeature, lastfeature, collect(Cint, 0:7)) + @test AG.getfield(newfeature, 0) == 45 + @test AG.getfield(newfeature, 1) ≈ 18.2 + @test AG.getfield(newfeature, 5) == String["foo", "bar"] + end + @test AG.nfeature(layer) == 1 end - @test AG.nfeature(layer) == 1 + @test AG.nfeature(layer) == 2 end - @test AG.nfeature(layer) == 2 + @test AG.nfeature(layer) == 3 end - @test AG.nfeature(layer) == 3 end end - -# untested -# getstyletable diff --git a/test/test_featurelayer.jl b/test/test_featurelayer.jl index fde501a9..15a4ec76 100644 --- a/test/test_featurelayer.jl +++ b/test/test_featurelayer.jl @@ -1,43 +1,51 @@ using Test -import GDAL -import ArchGDAL; const AG = ArchGDAL +import ArchGDAL; +const AG = ArchGDAL; -@testset "Testing FeatureLayer Methods" begin - AG.read("data/point.geojson") do dataset - AG.copy(dataset) do tmpcopy - @test AG.nlayer(tmpcopy) == 1 - tmplayer = AG.getlayer(tmpcopy,0) - @test sprint(print, AG.getspatialref(tmplayer)) == "NULL Spatial Reference System" - AG.getspatialref(tmplayer) do spref - @test sprint(print, spref) == "NULL Spatial Reference System" +@testset "test_featurelayer.jl" begin + @testset "Testing FeatureLayer Methods" begin + AG.read("data/point.geojson") do dataset + AG.copy(dataset) do tmpcopy + @test AG.nlayer(tmpcopy) == 1 + tmplayer = AG.getlayer(tmpcopy, 0) + @test sprint(print, AG.getspatialref(tmplayer)) == + "NULL Spatial Reference System" + AG.getspatialref(tmplayer) do spref + @test sprint(print, spref) == + "NULL Spatial Reference System" + end + @test AG.isignored(AG.getgeomdefn(AG.layerdefn(tmplayer), 0)) == + false + AG.setignoredfields!(tmplayer, ["OGR_GEOMETRY"]) + @test AG.isignored(AG.getgeomdefn(AG.layerdefn(tmplayer), 0)) == + true end - @test AG.isignored(AG.getgeomdefn(AG.layerdefn(tmplayer),0)) == false - AG.setignoredfields!(tmplayer, ["OGR_GEOMETRY"]) - @test AG.isignored(AG.getgeomdefn(AG.layerdefn(tmplayer),0)) == true - end - AG.copy(dataset, driver = AG.getdriver("Memory")) do tmpcopy - tmplayer = AG.getlayer(dataset, 0) - @test sprint(print, AG.getspatialref(tmplayer)) == "Spatial Reference System: +proj=longlat +datum=WGS84 +no_defs" - AG.getspatialref(tmplayer) do spref - @test sprint(print, spref) == "Spatial Reference System: +proj=longlat +datum=WGS84 +no_defs" - end - newlayer = AG.createlayer( - name = "new layer", - dataset = tmpcopy, - spatialref = AG.getspatialref(tmplayer), - geom = GDAL.wkbPoint - ) + AG.copy(dataset, driver = AG.getdriver("Memory")) do tmpcopy + tmplayer = AG.getlayer(dataset, 0) + AG.copy(tmplayer) do copylayer + @test AG.nfeature(tmplayer) == AG.nfeature(copylayer) + end + @test sprint(print, AG.getspatialref(tmplayer)) == + "Spatial Reference System: +proj=longlat +datum=WGS84 +no_defs" + AG.getspatialref(tmplayer) do spref + @test sprint(print, spref) == + "Spatial Reference System: +proj=longlat +datum=WGS84 +no_defs" + end + newlayer = AG.createlayer( + name = "new layer", + dataset = tmpcopy, + spatialref = AG.getspatialref(tmplayer), + geom = AG.wkbPoint, + ) @test AG.ngeom(AG.layerdefn(newlayer)) == 1 @test sprint(print, newlayer) == """ Layer: new layer Geometry 0 (): [wkbPoint] """ - AG.writegeomdefn(newlayer, - "new geom", - GDAL.wkbLineString) do gfd + AG.writegeomdefn(newlayer, "new geom", AG.wkbLineString) do gfd @test AG.getname(gfd) == "new geom" - @test AG.gettype(gfd) == GDAL.wkbLineString + @test AG.gettype(gfd) == AG.wkbLineString end @test sprint(print, newlayer) == """ Layer: new layer @@ -49,6 +57,7 @@ import ArchGDAL; const AG = ArchGDAL AG.createfeature(newlayer) do newfeature AG.setgeom!(newfeature, 0, AG.createpoint()) AG.setgeom!(newfeature, 1, AG.createlinestring()) + return nothing end @test AG.nfeature(newlayer) == 1 @test sprint(print, newlayer) == """ @@ -63,7 +72,7 @@ import ArchGDAL; const AG = ArchGDAL Geometry 0 (): [wkbPoint] Geometry 1 (new geom): [wkbLineString] """ - AG.writegeomdefn!(newlayer, "new poly", GDAL.wkbPolygon) + AG.writegeomdefn!(newlayer, "new poly", AG.wkbPolygon) @test AG.ngeom(AG.layerdefn(newlayer)) == 3 @test sprint(print, newlayer) == """ Layer: new layer @@ -75,12 +84,24 @@ import ArchGDAL; const AG = ArchGDAL AG.addfeature(newlayer) do newfeature AG.setgeom!(newfeature, 0, AG.createpoint()) AG.setgeom!(newfeature, 1, AG.createlinestring()) - AG.setgeom!(newfeature, 2, AG.createpolygon([[[0.,0.], [1.,1.], [0.,1.]]])) + AG.setgeom!( + newfeature, + 2, + AG.createpolygon([[ + [0.0, 0.0], + [1.0, 1.0], + [0.0, 1.0], + ]]), + ) - @test sprint(print, AG.getgeom(newfeature)) == "Geometry: POINT EMPTY" - @test sprint(print, AG.getgeom(newfeature, 0)) == "Geometry: POINT EMPTY" - @test sprint(print, AG.getgeom(newfeature, 1)) == "Geometry: LINESTRING EMPTY" - @test sprint(print, AG.getgeom(newfeature, 2)) == "Geometry: POLYGON ((0 0,1 1,0 1))" + @test sprint(print, AG.getgeom(newfeature)) == + "Geometry: POINT EMPTY" + @test sprint(print, AG.getgeom(newfeature, 0)) == + "Geometry: POINT EMPTY" + @test sprint(print, AG.getgeom(newfeature, 1)) == + "Geometry: LINESTRING EMPTY" + @test sprint(print, AG.getgeom(newfeature, 2)) == + "Geometry: POLYGON ((0 0,1 1,0 1))" AG.getgeom(newfeature) do g @test sprint(print, g) == "Geometry: POINT EMPTY" end @@ -88,93 +109,130 @@ import ArchGDAL; const AG = ArchGDAL @test sprint(print, g) == "Geometry: LINESTRING EMPTY" end AG.getgeom(newfeature, 2) do g - @test sprint(print, g) == "Geometry: POLYGON ((0 0,1 1,0 1))" + @test sprint(print, g) == + "Geometry: POLYGON ((0 0,1 1,0 1))" end end - end + end - layer = AG.getlayer(dataset, 0) - @test sprint(print, AG.getspatialref(layer)) == "Spatial Reference System: +proj=longlat +datum=WGS84 +no_defs" - @test AG.fidcolumnname(layer) == "" - @test AG.geomcolumnname(layer) == "" - @test AG.nreference(layer) == 0 - AG.reference(layer) - @test AG.nreference(layer) == 1 - AG.dereference(layer) - @test AG.nreference(layer) == 0 + layer = AG.getlayer(dataset, 0) + @test sprint(print, AG.getspatialref(layer)) == + "Spatial Reference System: +proj=longlat +datum=WGS84 +no_defs" + @test AG.fidcolumnname(layer) == "" + @test AG.geomcolumnname(layer) == "" + @test AG.nreference(layer) == 0 + AG.reference(layer) + @test AG.nreference(layer) == 1 + AG.dereference(layer) + @test AG.nreference(layer) == 0 - @test AG.nfeature(layer) == 4 - @test AG.getfield.(layer, 1) == ["point-a", "point-b", "a", "b"] - AG.setspatialfilter!(layer,100,-1,100.1,1) - @test AG.toWKT(AG.getspatialfilter(layer)) == "POLYGON ((100 -1,100 1,100.1 1.0,100.1 -1,100 -1))" - @test AG.nfeature(layer) == -1 - @test AG.getfield.(layer, 1) == ["point-a", "a"] - AG.clearspatialfilter!(layer) + @test AG.nfeature(layer) == 4 + @test AG.getfield.(layer, 1) == ["point-a", "point-b", "a", "b"] + AG.setspatialfilter!(layer, 100, -1, 100.1, 1) + @test AG.toWKT(AG.getspatialfilter(layer)) == + "POLYGON ((100 -1,100 1,100.1 1.0,100.1 -1,100 -1))" + @test AG.nfeature(layer) == -1 + @test AG.getfield.(layer, 1) == ["point-a", "a"] + AG.clearspatialfilter!(layer) - @test AG.nfeature(layer) == 4 - @test AG.getfield.(layer, 1) == ["point-a", "point-b", "a", "b"] - @test AG.findgeomindex(AG.layerdefn(layer)) == 0 - AG.setspatialfilter!(layer,0,100,-1,100.1,1) - @test AG.toWKT(AG.getspatialfilter(layer)) == "POLYGON ((100 -1,100 1,100.1 1.0,100.1 -1,100 -1))" - @test AG.nfeature(layer) == -1 - @test AG.getfield.(layer, 1) == ["point-a", "a"] + @test AG.nfeature(layer) == 4 + @test AG.getfield.(layer, 1) == ["point-a", "point-b", "a", "b"] + @test AG.findgeomindex(AG.layerdefn(layer)) == 0 + AG.setspatialfilter!(layer, 0, 100, -1, 100.1, 1) + @test AG.toWKT(AG.getspatialfilter(layer)) == + "POLYGON ((100 -1,100 1,100.1 1.0,100.1 -1,100 -1))" + @test AG.nfeature(layer) == -1 + @test AG.getfield.(layer, 1) == ["point-a", "a"] - AG.clone(AG.getspatialfilter(layer)) do poly - n = 0; for feature in layer; n += 1 end; @test n == 2 - AG.clearspatialfilter!(layer) - @test sprint(print, AG.getspatialfilter(layer)) == "NULL Geometry" - n = 0; for feature in layer; n += 1 end; @test n == 4 - - @testset "Test with setting to index of geomfield" begin - AG.setspatialfilter!(layer, 0, poly) - n = 0; for feature in layer; n += 1 end; @test n == 2 - AG.clearspatialfilter!(layer, 0) - n = 0; for feature in layer; n += 1 end; @test n == 4 - - AG.setattributefilter!(layer, "FID = 2") - n = 0; for feature in layer; n += 1 end; @test n == 1 - AG.setattributefilter!(layer, "FID = 3") - n = 0; for feature in layer; n += 1 end; @test n == 2 - AG.clearattributefilter!(layer) - n = 0; for feature in layer; n += 1 end; @test n == 4 - AG.nextfeature(layer) do feature - @test AG.getfield(feature, 1) == "point-a" + AG.clone(AG.getspatialfilter(layer)) do poly + n = 0 + for feature in layer + n += 1 end - AG.setnextbyindex!(layer, 2) - AG.nextfeature(layer) do feature - @test AG.getfield(feature, 1) == "a" + @test n == 2 + AG.clearspatialfilter!(layer) + @test sprint(print, AG.getspatialfilter(layer)) == + "NULL Geometry" + n = 0 + for feature in layer + n += 1 + end + @test n == 4 + + @testset "Test with setting to index of geomfield" begin + AG.setspatialfilter!(layer, 0, poly) + n = 0 + for feature in layer + n += 1 + end + @test n == 2 + AG.clearspatialfilter!(layer, 0) + n = 0 + for feature in layer + n += 1 + end + @test n == 4 + + AG.setattributefilter!(layer, "FID = 2") + n = 0 + for feature in layer + n += 1 + end + @test n == 1 + AG.setattributefilter!(layer, "FID = 3") + n = 0 + for feature in layer + n += 1 + end + @test n == 2 + AG.clearattributefilter!(layer) + n = 0 + for feature in layer + n += 1 + end + @test n == 4 + AG.nextfeature(layer) do feature + @test AG.getfield(feature, 1) == "point-a" + end + AG.setnextbyindex!(layer, 2) + AG.nextfeature(layer) do feature + @test AG.getfield(feature, 1) == "a" + end + @test AG.testcapability(layer, "OLCRandomWrite") == false end - @test AG.testcapability(layer,"OLCRandomWrite") == false end + @test AG.findfieldindex(layer, "FID", true) == 0 + @test AG.findfieldindex(layer, :FID, false) == 0 + @test AG.findfieldindex(layer, "pointname", true) == 1 + @test AG.findfieldindex(layer, "pointname", false) == 1 + @test AG.findfieldindex(layer, "geom", true) == -1 + @test AG.findfieldindex(layer, "geom", true) == -1 + @test AG.findfieldindex(layer, "rubbish", true) == -1 + @test AG.findfieldindex(layer, "rubbish", false) == -1 + @test sprint(print, AG.envelope(layer, 0, true)) == + "GDAL.OGREnvelope(100.0, 100.2785, 0.0, 0.0893)" + @test sprint(print, AG.envelope(layer, true)) == + "GDAL.OGREnvelope(100.0, 100.2785, 0.0, 0.0893)" end - @test AG.findfieldindex(layer, "FID", true) == 0 - @test AG.findfieldindex(layer, :FID, false) == 0 - @test AG.findfieldindex(layer, "pointname", true) == 1 - @test AG.findfieldindex(layer, "pointname", false) == 1 - @test AG.findfieldindex(layer, "geom", true) == -1 - @test AG.findfieldindex(layer, "geom", true) == -1 - @test AG.findfieldindex(layer, "rubbish", true) == -1 - @test AG.findfieldindex(layer, "rubbish", false) == -1 - @test sprint(print, AG.envelope(layer, 0, true)) == "GDAL.OGREnvelope(100.0, 100.2785, 0.0, 0.0893)" - @test sprint(print, AG.envelope(layer, true)) == "GDAL.OGREnvelope(100.0, 100.2785, 0.0, 0.0893)" end -end -# Not implemented yet: + # Not implemented yet: -# intersection(input::FeatureLayer, method::FeatureLayer, result::FeatureLayer; options = StringList(C_NULL), progressfunc::Function = GDAL.gdaldummyprogress, progressdata = C_NULL) -# union(input::FeatureLayer, method::FeatureLayer, result::FeatureLayer; options = StringList(C_NULL), progressdata = C_NULL, progressfunc::Function = GDAL.gdaldummyprogress) -# symdifference(input::FeatureLayer, method::FeatureLayer, result::FeatureLayer; options = StringList(C_NULL), progressfunc::Function = GDAL.gdaldummyprogress, progressdata = C_NULL) -# identity(input::FeatureLayer, method::FeatureLayer, result::FeatureLayer; options = StringList(C_NULL), progressfunc::Function = GDAL.gdaldummyprogress, progressdata = C_NULL) -# update(input::FeatureLayer, method::FeatureLayer, result::FeatureLayer; options = StringList(C_NULL), progressfunc::Function = GDAL.gdaldummyprogress, progressdata = C_NULL) -# clip(input::FeatureLayer, method::FeatureLayer, result::FeatureLayer; options = StringList(C_NULL), progressfunc::Function = GDAL.gdaldummyprogress, progressdata = C_NULL) -# erase(input::FeatureLayer, method::FeatureLayer, result::FeatureLayer; options = StringList(C_NULL), progressfunc::Function = GDAL.gdaldummyprogress, progressdata = C_NULL) + # intersection(input::FeatureLayer, method::FeatureLayer, result::FeatureLayer; options = StringList(C_NULL), progressfunc::Function = GDAL.gdaldummyprogress, progressdata = C_NULL) + # union(input::FeatureLayer, method::FeatureLayer, result::FeatureLayer; options = StringList(C_NULL), progressdata = C_NULL, progressfunc::Function = GDAL.gdaldummyprogress) + # symdifference(input::FeatureLayer, method::FeatureLayer, result::FeatureLayer; options = StringList(C_NULL), progressfunc::Function = GDAL.gdaldummyprogress, progressdata = C_NULL) + # identity(input::FeatureLayer, method::FeatureLayer, result::FeatureLayer; options = StringList(C_NULL), progressfunc::Function = GDAL.gdaldummyprogress, progressdata = C_NULL) + # update(input::FeatureLayer, method::FeatureLayer, result::FeatureLayer; options = StringList(C_NULL), progressfunc::Function = GDAL.gdaldummyprogress, progressdata = C_NULL) + # clip(input::FeatureLayer, method::FeatureLayer, result::FeatureLayer; options = StringList(C_NULL), progressfunc::Function = GDAL.gdaldummyprogress, progressdata = C_NULL) + # erase(input::FeatureLayer, method::FeatureLayer, result::FeatureLayer; options = StringList(C_NULL), progressfunc::Function = GDAL.gdaldummyprogress, progressdata = C_NULL) -# deletefielddefn!(layer::FeatureLayer, i::Integer) -# reorderfielddefn!(layer::FeatureLayer, indices::Vector{Cint}) -# reorderfielddefn!(layer::FeatureLayer, oldpos::Integer, newpos::Integer) -# updatefielddefn!(layer::FeatureLayer, i::Integer, newfielddefn::FieldDefn, flags::UInt8) + # deletefielddefn!(layer::FeatureLayer, i::Integer) + # reorderfielddefn!(layer::FeatureLayer, indices::Vector{Cint}) + # reorderfielddefn!(layer::FeatureLayer, oldpos::Integer, newpos::Integer) + # updatefielddefn!(layer::FeatureLayer, i::Integer, newfielddefn::FieldDefn, flags::UInt8) -# starttransaction(layer) -# committransaction(layer) -# rollbacktransaction(layer) + # starttransaction(layer) + # committransaction(layer) + # rollbacktransaction(layer) + +end diff --git a/test/test_fielddefn.jl b/test/test_fielddefn.jl index 75c6f1a5..c0687040 100644 --- a/test/test_fielddefn.jl +++ b/test/test_fielddefn.jl @@ -1,172 +1,185 @@ using Test -import GDAL -import ArchGDAL; const AG = ArchGDAL +import ArchGDAL; +const AG = ArchGDAL; -@testset "Tests for field defn" begin - AG.createfielddefn("fieldname",GDAL.OFTInteger) do fd - @test sprint(print, fd) == "fieldname (OFTInteger)" - @test AG.getname(fd) == "fieldname" - AG.setname!(fd, "newname") - @test AG.getname(fd) == "newname" - @test AG.gettype(fd) == GDAL.OFTInteger - AG.settype!(fd, GDAL.OFTDate) - @test AG.gettype(fd) == GDAL.OFTDate - AG.settype!(fd, GDAL.OFTInteger) - @test AG.getsubtype(fd) == GDAL.OFSTNone - AG.setsubtype!(fd, GDAL.OFSTInt16) - @test AG.getsubtype(fd) == GDAL.OFSTInt16 - AG.setsubtype!(fd, GDAL.OFSTBoolean) - @test AG.getsubtype(fd) == GDAL.OFSTBoolean - AG.setsubtype!(fd, GDAL.OFSTNone) - @test AG.getjustify(fd) == GDAL.OJUndefined - AG.setjustify!(fd, GDAL.OJLeft) - @test AG.getjustify(fd) == GDAL.OJLeft - @test AG.getwidth(fd) == 0 - AG.setwidth!(fd, 10) - @test AG.getwidth(fd) == 10 - @test AG.getprecision(fd) == 0 - AG.setprecision!(fd, 20) - @test AG.getprecision(fd) == 20 - AG.setparams!(fd, "finalname", GDAL.OFTDate, nwidth=5, nprecision=2, - justify=GDAL.OJRight) - @test AG.gettype(fd) == GDAL.OFTDate - @test AG.getname(fd) == "finalname" - @test AG.getsubtype(fd) == GDAL.OFSTNone - @test AG.getjustify(fd) == GDAL.OJRight - @test AG.getwidth(fd) == 5 - @test AG.getprecision(fd) == 2 - @test AG.isignored(fd) == false - AG.setignored!(fd, true) - @test AG.isignored(fd) == true - AG.setignored!(fd, false) - @test AG.isignored(fd) == false +@testset "test_fielddefn.jl" begin + @testset "Tests for field defn" begin + AG.createfielddefn("fieldname", AG.OFTInteger) do fd + @test sprint(print, fd) == "fieldname (OFTInteger)" + @test AG.getname(fd) == "fieldname" + AG.setname!(fd, "newname") + @test AG.getname(fd) == "newname" + @test AG.gettype(fd) == AG.OFTInteger + AG.settype!(fd, AG.OFTDate) + @test AG.gettype(fd) == AG.OFTDate + AG.settype!(fd, AG.OFTInteger) + @test AG.getsubtype(fd) == AG.OFSTNone + AG.setsubtype!(fd, AG.OFSTInt16) + @test AG.getsubtype(fd) == AG.OFSTInt16 + AG.setsubtype!(fd, AG.OFSTBoolean) + @test AG.getsubtype(fd) == AG.OFSTBoolean + AG.setsubtype!(fd, AG.OFSTNone) + @test AG.getjustify(fd) == AG.OJUndefined + AG.setjustify!(fd, AG.OJLeft) + @test AG.getjustify(fd) == AG.OJLeft + @test AG.getwidth(fd) == 0 + AG.setwidth!(fd, 10) + @test AG.getwidth(fd) == 10 + @test AG.getprecision(fd) == 0 + AG.setprecision!(fd, 20) + @test AG.getprecision(fd) == 20 + AG.setparams!( + fd, + "finalname", + AG.OFTDate, + nwidth = 5, + nprecision = 2, + justify = AG.OJRight, + ) + @test AG.gettype(fd) == AG.OFTDate + @test AG.getname(fd) == "finalname" + @test AG.getsubtype(fd) == AG.OFSTNone + @test AG.getjustify(fd) == AG.OJRight + @test AG.getwidth(fd) == 5 + @test AG.getprecision(fd) == 2 + @test AG.isignored(fd) == false + AG.setignored!(fd, true) + @test AG.isignored(fd) == true + AG.setignored!(fd, false) + @test AG.isignored(fd) == false - @test AG.isnullable(fd) == true - AG.setnullable!(fd, false) - @test AG.isnullable(fd) == false - AG.setnullable!(fd, true) - @test AG.isnullable(fd) == true + @test AG.isnullable(fd) == true + AG.setnullable!(fd, false) + @test AG.isnullable(fd) == false + AG.setnullable!(fd, true) + @test AG.isnullable(fd) == true - @test AG.getdefault(fd) == "" - AG.setdefault!(fd, "0001/01/01 00:00:00") - @test AG.getdefault(fd) == "0001/01/01 00:00:00" - @test AG.isdefaultdriverspecific(fd) == true + @test AG.getdefault(fd) == "" + AG.setdefault!(fd, "0001/01/01 00:00:00") + @test AG.getdefault(fd) == "0001/01/01 00:00:00" + @test AG.isdefaultdriverspecific(fd) == true + end end -end -@testset "Tests for Geom Field Defn" begin - AG.creategeomdefn("geomname", GDAL.wkbPolygon) do gfd - @test AG.getname(gfd) == "geomname" - AG.setname!(gfd, "my name!") - @test AG.getname(gfd) == "my name!" + @testset "Tests for Geom Field Defn" begin + AG.creategeomdefn("geomname", AG.wkbPolygon) do gfd + @test AG.getname(gfd) == "geomname" + AG.setname!(gfd, "my name!") + @test AG.getname(gfd) == "my name!" - @test AG.gettype(gfd) == GDAL.wkbPolygon - AG.settype!(gfd, GDAL.wkbPolyhedralSurface) - @test AG.gettype(gfd) == GDAL.wkbPolyhedralSurface + @test AG.gettype(gfd) == AG.wkbPolygon + AG.settype!(gfd, AG.wkbPolyhedralSurface) + @test AG.gettype(gfd) == AG.wkbPolyhedralSurface - @test sprint(print, AG.getspatialref(gfd)) == "NULL Spatial Reference System" - AG.getspatialref(gfd) do spref - @test sprint(print, spref) == "NULL Spatial Reference System" - end - AG.setspatialref!(gfd, AG.importEPSG(4326)) - @test sprint(print, AG.getspatialref(gfd)) == "Spatial Reference System: +proj=longlat +datum=WGS84 +no_defs" - AG.getspatialref(gfd) do spref - @test sprint(print, spref) == "Spatial Reference System: +proj=longlat +datum=WGS84 +no_defs" - end + @test sprint(print, AG.getspatialref(gfd)) == + "NULL Spatial Reference System" + AG.getspatialref(gfd) do spref + @test sprint(print, spref) == "NULL Spatial Reference System" + end + AG.setspatialref!(gfd, AG.importEPSG(4326)) + @test sprint(print, AG.getspatialref(gfd)) == + "Spatial Reference System: +proj=longlat +datum=WGS84 +no_defs" + AG.getspatialref(gfd) do spref + @test sprint(print, spref) == + "Spatial Reference System: +proj=longlat +datum=WGS84 +no_defs" + end - @test AG.isignored(gfd) == false - AG.setignored!(gfd, true) - @test AG.isignored(gfd) == true - AG.setignored!(gfd, false) - @test AG.isignored(gfd) == false + @test AG.isignored(gfd) == false + AG.setignored!(gfd, true) + @test AG.isignored(gfd) == true + AG.setignored!(gfd, false) + @test AG.isignored(gfd) == false - @test AG.isnullable(gfd) == true - AG.setnullable!(gfd, false) - @test AG.isnullable(gfd) == false - AG.setnullable!(gfd, true) - @test AG.isnullable(gfd) == true + @test AG.isnullable(gfd) == true + AG.setnullable!(gfd, false) + @test AG.isnullable(gfd) == false + AG.setnullable!(gfd, true) + @test AG.isnullable(gfd) == true + end end -end -@testset "Tests for Feature Defn" begin - AG.createfeaturedefn("new_feature") do fd - @test AG.nreference(fd) == 0 - AG.reference(fd) - @test AG.nreference(fd) == 1 - AG.reference(fd) - @test AG.nreference(fd) == 2 - AG.release(fd) - @test AG.nreference(fd) == 1 - AG.dereference(fd) - @test AG.nreference(fd) == 0 - AG.createfielddefn("fieldname",GDAL.OFTInteger) do fielddef - @test AG.nfield(fd) == 0 - AG.addfielddefn!(fd, fielddef) - @test AG.nfield(fd) == 1 - AG.addfielddefn!(fd, fielddef) - @test AG.nfield(fd) == 2 - AG.addfielddefn!(fd, fielddef) - @test AG.nfield(fd) == 3 - AG.createfielddefn("newfield",GDAL.OFTInteger) do fielddef2 - AG.addfielddefn!(fd, fielddef2) - @test AG.nfield(fd) == 4 - AG.getname(AG.getfielddefn(fd,0)) == "fieldname" - AG.getname(AG.getfielddefn(fd,1)) == "fieldname" - AG.getname(AG.getfielddefn(fd,2)) == "fieldname" - AG.getname(AG.getfielddefn(fd,3)) == "newfield" + @testset "Tests for Feature Defn" begin + AG.createfeaturedefn("new_feature") do fd + @test AG.nreference(fd) == 0 + AG.reference(fd) + @test AG.nreference(fd) == 1 + AG.reference(fd) + @test AG.nreference(fd) == 2 + AG.release(fd) + @test AG.nreference(fd) == 1 + AG.dereference(fd) + @test AG.nreference(fd) == 0 + AG.createfielddefn("fieldname", AG.OFTInteger) do fielddef + @test AG.nfield(fd) == 0 + AG.addfielddefn!(fd, fielddef) + @test AG.nfield(fd) == 1 + AG.addfielddefn!(fd, fielddef) + @test AG.nfield(fd) == 2 + AG.addfielddefn!(fd, fielddef) + @test AG.nfield(fd) == 3 + AG.createfielddefn("newfield", AG.OFTInteger) do fielddef2 + AG.addfielddefn!(fd, fielddef2) + @test AG.nfield(fd) == 4 + @test AG.getname(AG.getfielddefn(fd, 0)) == "fieldname" + @test AG.getname(AG.getfielddefn(fd, 1)) == "fieldname" + @test AG.getname(AG.getfielddefn(fd, 2)) == "fieldname" + @test AG.getname(AG.getfielddefn(fd, 3)) == "newfield" + return nothing + end end - end - AG.deletefielddefn!(fd, 0) - @test AG.nfield(fd) == 3 - AG.getname(AG.getfielddefn(fd,0)) == "fieldname" - AG.getname(AG.getfielddefn(fd,1)) == "fieldname" - AG.getname(AG.getfielddefn(fd,2)) == "newfield" - - AG.reorderfielddefns!(fd, Cint[2,1,0]) - @test AG.nfield(fd) == 3 - AG.getname(AG.getfielddefn(fd,0)) == "newfield" - AG.getname(AG.getfielddefn(fd,1)) == "fieldname" - AG.getname(AG.getfielddefn(fd,2)) == "fieldname" + AG.deletefielddefn!(fd, 0) + @test AG.nfield(fd) == 3 + @test AG.getname(AG.getfielddefn(fd, 0)) == "fieldname" + @test AG.getname(AG.getfielddefn(fd, 1)) == "fieldname" + @test AG.getname(AG.getfielddefn(fd, 2)) == "newfield" - @test AG.ngeom(fd) == 1 - @test AG.getgeomtype(fd) == GDAL.wkbUnknown - AG.setgeomtype!(fd, GDAL.wkbPolygon) - @test AG.getgeomtype(fd) == GDAL.wkbPolygon - @test AG.ngeom(fd) == 1 + AG.reorderfielddefns!(fd, Cint[2, 1, 0]) + @test AG.nfield(fd) == 3 + @test AG.getname(AG.getfielddefn(fd, 0)) == "newfield" + @test AG.getname(AG.getfielddefn(fd, 1)) == "fieldname" + @test AG.getname(AG.getfielddefn(fd, 2)) == "fieldname" + + @test AG.ngeom(fd) == 1 + @test AG.getgeomtype(fd) == AG.wkbUnknown + AG.setgeomtype!(fd, AG.wkbPolygon) + @test AG.getgeomtype(fd) == AG.wkbPolygon + @test AG.ngeom(fd) == 1 - @test AG.isgeomignored(fd) == false - AG.setgeomignored!(fd, true) - @test AG.isgeomignored(fd) == true - AG.setgeomignored!(fd, false) - @test AG.isgeomignored(fd) == false + @test AG.isgeomignored(fd) == false + AG.setgeomignored!(fd, true) + @test AG.isgeomignored(fd) == true + AG.setgeomignored!(fd, false) + @test AG.isgeomignored(fd) == false - @test AG.isstyleignored(fd) == false - AG.setstyleignored!(fd, true) - @test AG.isstyleignored(fd) == true - AG.setstyleignored!(fd, false) - @test AG.isstyleignored(fd) == false + @test AG.isstyleignored(fd) == false + AG.setstyleignored!(fd, true) + @test AG.isstyleignored(fd) == true + AG.setstyleignored!(fd, false) + @test AG.isstyleignored(fd) == false - @test AG.findgeomindex(fd) == 0 - gfd0 = AG.getgeomdefn(fd, 0) - @test AG.ngeom(fd) == 1 - AG.addgeomdefn!(fd, gfd0) - @test AG.ngeom(fd) == 2 - gfd1 = AG.getgeomdefn(fd, 1) - AG.setname!(gfd0, "name0") - AG.setname!(gfd1, "name1") - @test AG.findgeomindex(fd, "") == -1 - @test AG.findgeomindex(fd, "name0") == 0 - @test AG.findgeomindex(fd, "name1") == 1 - AG.deletegeomdefn!(fd, 0) - @test AG.ngeom(fd) == 1 - @test AG.findgeomindex(fd, "") == -1 - @test AG.findgeomindex(fd, "name0") == -1 - @test AG.findgeomindex(fd, "name1") == 0 - @test AG.nreference(fd) == 0 - AG.createfeature(fd) do f - @test AG.nreference(fd) == 2 # artificially inflated - @test AG.issame(AG.getfeaturedefn(f), fd) == true + @test AG.findgeomindex(fd) == 0 + gfd0 = AG.getgeomdefn(fd, 0) + @test AG.ngeom(fd) == 1 + AG.addgeomdefn!(fd, gfd0) + @test AG.ngeom(fd) == 2 + gfd1 = AG.getgeomdefn(fd, 1) + AG.setname!(gfd0, "name0") + AG.setname!(gfd1, "name1") + @test AG.findgeomindex(fd, "") == -1 + @test AG.findgeomindex(fd, "name0") == 0 + @test AG.findgeomindex(fd, "name1") == 1 + AG.deletegeomdefn!(fd, 0) + @test AG.ngeom(fd) == 1 + @test AG.findgeomindex(fd, "") == -1 + @test AG.findgeomindex(fd, "name0") == -1 + @test AG.findgeomindex(fd, "name1") == 0 + @test AG.nreference(fd) == 0 + AG.createfeature(fd) do f + @test AG.nreference(fd) == 2 # artificially inflated + @test AG.issame(AG.getfeaturedefn(f), fd) == true + return nothing + end + @test AG.nreference(fd) == 0 end - @test AG.nreference(fd) == 0 end end diff --git a/test/test_gdal_tutorials.jl b/test/test_gdal_tutorials.jl index 242aaf05..77be3fc4 100644 --- a/test/test_gdal_tutorials.jl +++ b/test/test_gdal_tutorials.jl @@ -1,130 +1,128 @@ using Test -import GDAL -import ArchGDAL; const AG = ArchGDAL - -@testset "Raster Tutorial" begin - driver = AG.getdriver("GTiff") - - AG.read("data/utmsmall.tif") do dataset - @test AG.shortname(driver) == "GTiff" - @test AG.longname(driver) == "GeoTIFF" - @test AG.width(dataset) == 100 - @test AG.height(dataset) == 100 - @test AG.nraster(dataset) == 1 - - nad27_prefix = "PROJCS[\"NAD27 / UTM zone 11N\",GEOGCS[\"NAD27\",DATUM[\"North_American_Datum_1927\"," - @test startswith(AG.getproj(dataset), nad27_prefix) == true - @test AG.getgeotransform(dataset) ≈ - [440720.0,60.0,0.0,3.75132e6,0.0,-60.0] - - band = AG.getband(dataset, 1) - @test AG.blocksize(band) ≈ [100, 81] - @test AG.pixeltype(band) == UInt8 - @test AG.getname(AG.getcolorinterp(band)) == "Gray" - - @test AG.minimum(band) ≈ 0.0 - @test AG.maximum(band) ≈ 255.0 - - @test AG.noverview(band) == 0 - - # Reading Raster Data - @test AG.width(band) == 100 - data = map(Float32, AG.read(dataset, 1)) - @test data[:,1] ≈ Float32[107.0f0,123.0f0,132.0f0,115.0f0, - 132.0f0,132.0f0,140.0f0,132.0f0,132.0f0,132.0f0,107.0f0, - 132.0f0,107.0f0,132.0f0,132.0f0,107.0f0,123.0f0,115.0f0, - 156.0f0,148.0f0,107.0f0,132.0f0,107.0f0,115.0f0,99.0f0, - 123.0f0,99.0f0,74.0f0,115.0f0,82.0f0,115.0f0,115.0f0, - 107.0f0,123.0f0,123.0f0,99.0f0,123.0f0,123.0f0,115.0f0, - 115.0f0,107.0f0,90.0f0,99.0f0,107.0f0,107.0f0,99.0f0, - 123.0f0,107.0f0,140.0f0,123.0f0,123.0f0,115.0f0,99.0f0, - 132.0f0,123.0f0,115.0f0,115.0f0,123.0f0,132.0f0,115.0f0, - 123.0f0,132.0f0,214.0f0,156.0f0,165.0f0,148.0f0,115.0f0, - 148.0f0,156.0f0,148.0f0,140.0f0,165.0f0,156.0f0,197.0f0, - 156.0f0,197.0f0,140.0f0,173.0f0,156.0f0,165.0f0,148.0f0, - 156.0f0,206.0f0,214.0f0,181.0f0,206.0f0,173.0f0,222.0f0, - 206.0f0,255.0f0,214.0f0,173.0f0,214.0f0,255.0f0,214.0f0, - 247.0f0,255.0f0,230.0f0,206.0f0,197.0f0] - - @test AG.metadatadomainlist(dataset) == ["IMAGE_STRUCTURE", "", "DERIVED_SUBDATASETS"] - @test AG.metadata(dataset) == ["AREA_OR_POINT=Area"] - @test AG.metadataitem(dataset, "AREA_OR_POINT") == "Area" - @test AG.metadata(dataset, domain = "IMAGE_STRUCTURE") == ["INTERLEAVE=BAND"] - @test AG.metadata(dataset, domain = "") == ["AREA_OR_POINT=Area"] - @test AG.metadata(dataset, domain = "DERIVED_SUBDATASETS") == [ - "DERIVED_SUBDATASET_1_NAME=DERIVED_SUBDATASET:LOGAMPLITUDE:data/utmsmall.tif", - "DERIVED_SUBDATASET_1_DESC=log10 of amplitude of input bands from data/utmsmall.tif" - ] - end +import ArchGDAL; +const AG = ArchGDAL; + +@testset "test_gdal_tutorials.jl" begin + @testset "Raster Tutorial" begin + driver = AG.getdriver("GTiff") + + AG.read("data/utmsmall.tif") do dataset + @test AG.shortname(driver) == "GTiff" + @test AG.longname(driver) == "GeoTIFF" + @test AG.width(dataset) == 100 + @test AG.height(dataset) == 100 + @test AG.nraster(dataset) == 1 + + nad27_prefix = "PROJCS[\"NAD27 / UTM zone 11N\",GEOGCS[\"NAD27\",DATUM[\"North_American_Datum_1927\"," + @test startswith(AG.getproj(dataset), nad27_prefix) == true + @test AG.getgeotransform(dataset) ≈ + [440720.0, 60.0, 0.0, 3.75132e6, 0.0, -60.0] + + band = AG.getband(dataset, 1) + @test AG.blocksize(band) ≈ [100, 81] + @test AG.pixeltype(band) == UInt8 + @test AG.getname(AG.getcolorinterp(band)) == "Gray" + + @test AG.minimum(band) ≈ 0.0 + @test AG.maximum(band) ≈ 255.0 + + @test AG.noverview(band) == 0 + + # Reading Raster Data + @test AG.width(band) == 100 + data = map(Float32, AG.read(dataset, 1)) + #! format: off + @test data[:, 1] ≈ Float32[107.0f0, 123.0f0, 132.0f0, 115.0f0, 132.0f0, 132.0f0, 140.0f0, 132.0f0, 132.0f0, 132.0f0, 107.0f0, 132.0f0, 107.0f0, 132.0f0, 132.0f0, 107.0f0, 123.0f0, 115.0f0, 156.0f0, 148.0f0, 107.0f0, 132.0f0, 107.0f0, 115.0f0, 99.0f0, 123.0f0, 99.0f0, 74.0f0, 115.0f0, 82.0f0, 115.0f0, 115.0f0, 107.0f0, 123.0f0, 123.0f0, 99.0f0, 123.0f0, 123.0f0, 115.0f0, 115.0f0, 107.0f0, 90.0f0, 99.0f0, 107.0f0, 107.0f0, 99.0f0, 123.0f0, 107.0f0, 140.0f0, 123.0f0, 123.0f0, 115.0f0, 99.0f0, 132.0f0, 123.0f0, 115.0f0, 115.0f0, 123.0f0, 132.0f0, 115.0f0, 123.0f0, 132.0f0, 214.0f0, 156.0f0, 165.0f0, 148.0f0, 115.0f0, 148.0f0, 156.0f0, 148.0f0, 140.0f0, 165.0f0, 156.0f0, 197.0f0, 156.0f0, 197.0f0, 140.0f0, 173.0f0, 156.0f0, 165.0f0, 148.0f0, 156.0f0, 206.0f0, 214.0f0, 181.0f0, 206.0f0, 173.0f0, 222.0f0, 206.0f0, 255.0f0, 214.0f0, 173.0f0, 214.0f0, 255.0f0, 214.0f0, 247.0f0, 255.0f0, 230.0f0, 206.0f0, 197.0f0] + #! format: on + + @test AG.metadatadomainlist(dataset) == + ["IMAGE_STRUCTURE", "", "DERIVED_SUBDATASETS"] + @test AG.metadata(dataset) == ["AREA_OR_POINT=Area"] + @test AG.metadataitem(dataset, "AREA_OR_POINT") == "Area" + @test AG.metadata(dataset, domain = "IMAGE_STRUCTURE") == + ["INTERLEAVE=BAND"] + @test AG.metadata(dataset, domain = "") == ["AREA_OR_POINT=Area"] + @test AG.metadata(dataset, domain = "DERIVED_SUBDATASETS") == [ + "DERIVED_SUBDATASET_1_NAME=DERIVED_SUBDATASET:LOGAMPLITUDE:data/utmsmall.tif", + "DERIVED_SUBDATASET_1_DESC=log10 of amplitude of input bands from data/utmsmall.tif", + ] + end - # Get metadata from a RasterDataset - AG.readraster("data/utmsmall.tif") do dataset - # interestingly the list order below is different from the order above - @test AG.metadatadomainlist(dataset) == ["IMAGE_STRUCTURE", "DERIVED_SUBDATASETS", ""] - @test AG.metadata(dataset) == ["AREA_OR_POINT=Area"] - @test AG.metadataitem(dataset, "AREA_OR_POINT") == "Area" - end + # Get metadata from a RasterDataset + AG.readraster("data/utmsmall.tif") do dataset + # interestingly the list order below is different from the order above + @test AG.metadatadomainlist(dataset) == + ["IMAGE_STRUCTURE", "DERIVED_SUBDATASETS", ""] + @test AG.metadata(dataset) == ["AREA_OR_POINT=Area"] + @test AG.metadataitem(dataset, "AREA_OR_POINT") == "Area" + end - # Techniques for Creating Files - @test GDAL.gdalgetmetadataitem(driver.ptr, "DCAP_CREATE", "") == "YES" - @test GDAL.gdalgetmetadataitem(driver.ptr, "DCAP_CREATECOPY", "") == "YES" + # Techniques for Creating Files + @test AG.metadataitem(driver, "DCAP_CREATE", domain = "") == "YES" + @test AG.metadataitem(driver, "DCAP_CREATECOPY", domain = "") == "YES" - AG.read("data/utmsmall.tif") do ds_src - AG.write(ds_src, "/vsimem/utmsmall.tif") - AG.read("/vsimem/utmsmall.tif") do ds_copy - @test AG.read(ds_src) == AG.read(ds_copy) + AG.read("data/utmsmall.tif") do ds_src + AG.write(ds_src, "/vsimem/utmsmall.tif") + AG.read("/vsimem/utmsmall.tif") do ds_copy + @test AG.read(ds_src) == AG.read(ds_copy) + end end end -end -@testset "Vector Tutorial" begin - AG.read("data/point.geojson") do dataset - @test AG.nlayer(dataset) == 1 - layer = AG.getlayer(dataset, 0) - @test (AG.getname(layer) in ["point", "OGRGeoJSON"]) == true - layerbyname = AG.getlayer(dataset, "point") - @test layerbyname.ptr == layer.ptr - AG.resetreading!(layer) - - featuredefn = AG.layerdefn(layer) - @test AG.nfield(featuredefn) == 2 - fielddefn = AG.getfielddefn(featuredefn, 0) - @test AG.gettype(fielddefn) == GDAL.OFTReal - fielddefn = AG.getfielddefn(featuredefn, 1) - @test AG.gettype(fielddefn) == GDAL.OFTString - - AG.nextfeature(layer) do feature - @test AG.asdouble(feature, 0) ≈ 2.0 - @test AG.asstring(feature, 1) == "point-a" - end - AG.nextfeature(layer) do feature # second feature - @test AG.asdouble(feature, 0) ≈ 3.0 - @test AG.asstring(feature, 1) == "point-b" + @testset "Vector Tutorial" begin + AG.read("data/point.geojson") do dataset + @test AG.nlayer(dataset) == 1 + layer = AG.getlayer(dataset, 0) + @test (AG.getname(layer) in ["point", "OGRGeoJSON"]) == true + layerbyname = AG.getlayer(dataset, "point") + @test layerbyname.ptr == layer.ptr + AG.resetreading!(layer) - geometry = AG.getgeom(feature) - @test AG.geomname(geometry) == "POINT" - @test AG.getgeomtype(geometry) == GDAL.wkbPoint + featuredefn = AG.layerdefn(layer) @test AG.nfield(featuredefn) == 2 - @test AG.getx(geometry, 0) ≈ 100.2785 - @test AG.gety(geometry, 0) ≈ 0.0893 - @test AG.getpoint(geometry, 0) == (100.2785,0.0893,0.0) + fielddefn = AG.getfielddefn(featuredefn, 0) + @test AG.gettype(fielddefn) == AG.OFTReal + fielddefn = AG.getfielddefn(featuredefn, 1) + @test AG.gettype(fielddefn) == AG.OFTString + + AG.nextfeature(layer) do feature + @test AG.asdouble(feature, 0) ≈ 2.0 + @test AG.asstring(feature, 1) == "point-a" + end + AG.nextfeature(layer) do feature # second feature + @test AG.asdouble(feature, 0) ≈ 3.0 + @test AG.asstring(feature, 1) == "point-b" + + geometry = AG.getgeom(feature) + @test AG.geomname(geometry) == "POINT" + @test AG.getgeomtype(geometry) == AG.wkbPoint + @test AG.nfield(featuredefn) == 2 + @test AG.getx(geometry, 0) ≈ 100.2785 + @test AG.gety(geometry, 0) ≈ 0.0893 + @test AG.getpoint(geometry, 0) == (100.2785, 0.0893, 0.0) + end end - end - AG.create(AG.getdriver("MEMORY")) do dataset - layer = AG.createlayer( - name = "point_out", - dataset = dataset, - geom = GDAL.wkbPoint - ) - AG.addfielddefn!(layer, "Name", GDAL.OFTString, nwidth = 32) - featuredefn = AG.layerdefn(layer) - @test AG.getname(featuredefn) == "point_out" - @test AG.nfeature(layer) == 0 - AG.createfeature(layer) do feature - AG.setfield!(feature, AG.findfieldindex(feature, "Name"), "myname") - AG.setgeom!(feature, AG.createpoint(100.123, 0.123)) + AG.create(AG.getdriver("MEMORY")) do dataset + layer = AG.createlayer( + name = "point_out", + dataset = dataset, + geom = AG.wkbPoint, + ) + AG.addfielddefn!(layer, "Name", AG.OFTString, nwidth = 32) + featuredefn = AG.layerdefn(layer) + @test AG.getname(featuredefn) == "point_out" + @test AG.nfeature(layer) == 0 + AG.createfeature(layer) do feature + AG.setfield!( + feature, + AG.findfieldindex(feature, "Name"), + "myname", + ) + AG.setgeom!(feature, AG.createpoint(100.123, 0.123)) + return nothing + end + @test AG.nfeature(layer) == 1 end - @test AG.nfeature(layer) == 1 end end diff --git a/test/test_gdalutilities.jl b/test/test_gdalutilities.jl index 5055241b..bc3e0d01 100644 --- a/test/test_gdalutilities.jl +++ b/test/test_gdalutilities.jl @@ -1,14 +1,212 @@ -using ArchGDAL, GDAL; AG = ArchGDAL +import ArchGDAL, GDAL; +const AG = ArchGDAL using Test -AG.read("data/utmsmall.tif") do ds_small +@testset "test_gdalutilities.jl" begin + AG.read("data/utmsmall.tif") do ds_small + @testset "GDAL Error" begin + @test_throws GDAL.GDALError AG.gdalinfo( + ds_small, + ["-novalidoption"], + ) + @test_throws GDAL.GDALError AG.unsafe_gdaltranslate( + ds_small, + ["-novalidoption"], + ) + @test_throws GDAL.GDALError AG.unsafe_gdalbuildvrt( + [ds_small], + ["-novalidoption"], + ) + @test_throws GDAL.GDALError AG.unsafe_gdaldem( + ds_small, + "hillshade", + ["-novalidoption"], + ) + @test_throws GDAL.GDALError AG.unsafe_gdalnearblack( + ds_small, + ["-novalidoption"], + ) + @test_throws GDAL.GDALError AG.unsafe_gdalwarp( + [ds_small], + ["-novalidoption"], + ) + end + + @testset "GDAL Info" begin + infostr = AG.gdalinfo(ds_small, ["-checksum"]) + @test occursin("Checksum=50054", infostr) + info_default = AG.gdalinfo(ds_small) + @test occursin("Driver: GTiff/GeoTIFF", info_default) + end + + AG.gdaltranslate( + ds_small, # resample to a 5×5 ascii grid + ["-of", "AAIGrid", "-r", "cubic", "-tr", "1200", "1200"], + ) do ds_tiny + @testset "GDAL Translate" begin + @test AG.read(ds_tiny, 1) == [ + 128 171 127 93 83 + 126 164 148 114 101 + 161 175 177 164 140 + 185 206 205 172 128 + 193 205 209 181 122 + ] + end + + @testset "GDAL Build VRT" begin + AG.gdalbuildvrt([ds_tiny]) do ds_vrt + @test AG.read(ds_vrt, 1) == [ + 128 171 127 93 83 + 126 164 148 114 101 + 161 175 177 164 140 + 185 206 205 172 128 + 193 205 209 181 122 + ] + end + end + + @testset "GDAL DEM Processing" begin + AG.gdaldem(ds_tiny, "hillshade", ["-of", "AAIGrid"]) do ds_dempr + @test AG.read(ds_dempr, 1) == [ + 0 0 0 0 0 + 0 183 180 181 0 + 0 184 182 181 0 + 0 183 181 177 0 + 0 0 0 0 0 + ] + end + AG.gdaldem( + ds_tiny, + "color-relief", + colorfile = "data/color_relief.txt", + ) do ds_dempr + @test AG.read(ds_dempr, 1) == [ + 0x80 0x87 0x80 0x7b 0x7a + 0x80 0x86 0x83 0x7e 0x7d + 0x85 0x87 0x88 0x86 0x82 + 0x89 0x8c 0x8c 0x87 0x80 + 0x8a 0x8c 0x8c 0x88 0x80 + ] + end + end + + @testset "GDAL Near Black" begin + AG.gdalnearblack( + ds_tiny, + ["-of", "GTiff", "-color", "0"], + ) do ds_nearblack + @test AG.read(ds_nearblack, 1) == [ + 0 0 0 0 0 + 0 0 0 0 0 + 0 0 177 0 0 + 0 0 0 0 0 + 0 0 0 0 0 + ] + end + end + end + + @testset "GDAL Warp" begin + AG.gdalwarp( + [ds_small], + ["-of", "MEM", "-t_srs", "EPSG:4326"], + ) do ds_warped + @test AG.width(ds_small) == 100 + @test AG.height(ds_small) == 100 + @test AG.width(ds_warped) == 109 + @test AG.height(ds_warped) == 91 + end + end + @testset "GDAL Warp" begin + AG.gdalwarp([ds_small], ["-of", "MEM"]) do ds_warped + @test AG.width(ds_small) == 100 + @test AG.height(ds_small) == 100 + @test AG.shortname(AG.getdriver(ds_small)) == "GTiff" + @test AG.width(ds_warped) == 100 + @test AG.height(ds_warped) == 100 + @test AG.shortname(AG.getdriver(ds_warped)) == "MEM" + end + end + end + + AG.read("data/point.geojson") do ds_point + @testset "GDAL Grid" begin + AG.gdalgrid( + ds_point, + [ + "-of", + "MEM", + "-outsize", + "3", + "10", + "-txe", + "100", + "100.3", + "-tye", + "0", + "0.1", + ], + ) do ds_grid + @test AG.getgeotransform(ds_grid) ≈ + [100.0, 0.1, 0.0, 0.0, 0.0, 0.01] + end + end + + @testset "GDAL Rasterize" begin + AG.gdalrasterize( + ds_point, + ["-of", "MEM", "-tr", "0.05", "0.05"], + ) do ds_rasterize + @test AG.getgeotransform(ds_rasterize) ≈ + [99.975, 0.05, 0.0, 0.1143, 0.0, -0.05] + end + end + + @testset "GDAL Vector Translate" begin + AG.destroy( + AG.unsafe_gdalvectortranslate( + [ds_point], + ["-f", "CSV", "-lco", "GEOMETRY=AS_XY"], + dest = "data/point.csv", + ), + ) + @test replace(read("data/point.csv", String), "\r" => "") == """ + X,Y,FID,pointname + 100,0,2,point-a + 100.2785,0.0893,3,point-b + 100,0,0,a + 100.2785,0.0893,3,b + """ + rm("data/point.csv") + end + end +end + +@testset "Interactive data/utmsmall.tif" begin + ds_small = AG.read("data/utmsmall.tif") @testset "GDAL Error" begin @test_throws GDAL.GDALError AG.gdalinfo(ds_small, ["-novalidoption"]) - @test_throws GDAL.GDALError AG.unsafe_gdaltranslate(ds_small, ["-novalidoption"]) - @test_throws GDAL.GDALError AG.unsafe_gdalbuildvrt([ds_small], ["-novalidoption"]) - @test_throws GDAL.GDALError AG.unsafe_gdaldem(ds_small, "hillshade", ["-novalidoption"]) - @test_throws GDAL.GDALError AG.unsafe_gdalnearblack(ds_small, ["-novalidoption"]) - @test_throws GDAL.GDALError AG.unsafe_gdalwarp([ds_small], ["-novalidoption"]) + @test_throws GDAL.GDALError AG.unsafe_gdaltranslate( + ds_small, + ["-novalidoption"], + ) + @test_throws GDAL.GDALError AG.unsafe_gdalbuildvrt( + [ds_small], + ["-novalidoption"], + ) + @test_throws GDAL.GDALError AG.unsafe_gdaldem( + ds_small, + "hillshade", + ["-novalidoption"], + ) + @test_throws GDAL.GDALError AG.unsafe_gdalnearblack( + ds_small, + ["-novalidoption"], + ) + @test_throws GDAL.GDALError AG.unsafe_gdalwarp( + [ds_small], + ["-novalidoption"], + ) end @testset "GDAL Info" begin @@ -18,93 +216,74 @@ AG.read("data/utmsmall.tif") do ds_small @test occursin("Driver: GTiff/GeoTIFF", info_default) end - AG.gdaltranslate(ds_small, # resample to a 5×5 ascii grid - ["-of","AAIGrid","-r","cubic","-tr","1200","1200"] - ) do ds_tiny - @testset "GDAL Translate" begin - @test AG.read(ds_tiny, 1) == [128 171 127 93 83; - 126 164 148 114 101; - 161 175 177 164 140; - 185 206 205 172 128; - 193 205 209 181 122] - end + ds_tiny = AG.unsafe_gdaltranslate( + ds_small, # resample to a 5×5 ascii grid + ["-of", "AAIGrid", "-r", "cubic", "-tr", "1200", "1200"], + ) + @test typeof(ds_tiny) == AG.Dataset + @testset "GDAL Translate" begin + @test AG.read(ds_tiny, 1) == [ + 128 171 127 93 83 + 126 164 148 114 101 + 161 175 177 164 140 + 185 206 205 172 128 + 193 205 209 181 122 + ] + end - @testset "GDAL Build VRT" begin - AG.gdalbuildvrt([ds_tiny]) do ds_vrt - @test AG.read(ds_vrt, 1) == [128 171 127 93 83; - 126 164 148 114 101; - 161 175 177 164 140; - 185 206 205 172 128; - 193 205 209 181 122] - end - end + @testset "GDAL Build VRT" begin + ds_vrt = AG.unsafe_gdalbuildvrt([ds_tiny]) + @test AG.read(ds_vrt, 1) == [ + 128 171 127 93 83 + 126 164 148 114 101 + 161 175 177 164 140 + 185 206 205 172 128 + 193 205 209 181 122 + ] + end - @testset "GDAL DEM Processing" begin - AG.gdaldem(ds_tiny, "hillshade", ["-of","AAIGrid"]) do ds_dempr - @test AG.read(ds_dempr, 1) == [ 0 0 0 0 0; - 0 183 180 181 0; - 0 184 182 181 0; - 0 183 181 177 0; - 0 0 0 0 0] - end - end + @testset "GDAL DEM Processing" begin + ds_dempr = AG.unsafe_gdaldem(ds_tiny, "hillshade", ["-of", "AAIGrid"]) + @test AG.read(ds_dempr, 1) == [ + 0 0 0 0 0 + 0 183 180 181 0 + 0 184 182 181 0 + 0 183 181 177 0 + 0 0 0 0 0 + ] + end - @testset "GDAL Near Black" begin - AG.gdalnearblack(ds_tiny, ["-of","GTiff","-color","0"]) do ds_nearblack - @test AG.read(ds_nearblack, 1) == [ 0 0 0 0 0; - 0 0 0 0 0; - 0 0 177 0 0; - 0 0 0 0 0; - 0 0 0 0 0] - end - end + @testset "GDAL Near Black" begin + ds_nearblack = + AG.unsafe_gdalnearblack(ds_tiny, ["-of", "GTiff", "-color", "0"]) + @test AG.read(ds_nearblack, 1) == [ + 0 0 0 0 0 + 0 0 0 0 0 + 0 0 177 0 0 + 0 0 0 0 0 + 0 0 0 0 0 + ] end @testset "GDAL Warp" begin - AG.gdalwarp([ds_small], ["-of","MEM","-t_srs","EPSG:4326"]) do ds_warped + AG.gdalwarp( + [ds_small], + ["-of", "MEM", "-t_srs", "EPSG:4326"], + ) do ds_warped @test AG.width(ds_small) == 100 @test AG.height(ds_small) == 100 @test AG.width(ds_warped) == 109 @test AG.height(ds_warped) == 91 end end - @testset "GDAL Warp" begin - AG.gdalwarp([ds_small], ["-of","MEM"]) do ds_warped - @test AG.width(ds_small) == 100 - @test AG.height(ds_small) == 100 - @test AG.shortname(AG.getdriver(ds_small)) == "GTiff" - @test AG.width(ds_warped) == 100 - @test AG.height(ds_warped) == 100 - @test AG.shortname(AG.getdriver(ds_warped)) == "MEM" - end - end -end -AG.read("data/point.geojson") do ds_point - @testset "GDAL Grid" begin - AG.gdalgrid(ds_point, ["-of","MEM","-outsize","3", - "10","-txe","100","100.3","-tye","0","0.1"]) do ds_grid - @test AG.getgeotransform(ds_grid) ≈ [100.0,0.1,0.0,0.0,0.0,0.01] - end - end - - @testset "GDAL Rasterize" begin - AG.gdalrasterize(ds_point, ["-of","MEM","-tr","0.05","0.05"]) do ds_rasterize - @test AG.getgeotransform(ds_rasterize) ≈ [99.975,0.05,0.0,0.1143,0.0,-0.05] - end - end - - @testset "GDAL Vector Translate" begin - AG.gdalvectortranslate([ds_point], ["-f","CSV","-lco", - "GEOMETRY=AS_XY"], dest = "data/point.csv") do ds_csv - end - @test replace(read("data/point.csv", String), "\r" => "") == """ - X,Y,FID,pointname - 100,0,2,point-a - 100.2785,0.0893,3,point-b - 100,0,0,a - 100.2785,0.0893,3,b - """ - rm("data/point.csv") + @testset "GDAL Warp #2" begin + ds_warped = AG.unsafe_gdalwarp([ds_small], ["-of", "MEM"]) + @test AG.width(ds_small) == 100 + @test AG.height(ds_small) == 100 + @test AG.shortname(AG.getdriver(ds_small)) == "GTiff" + @test AG.width(ds_warped) == 100 + @test AG.height(ds_warped) == 100 + @test AG.shortname(AG.getdriver(ds_warped)) == "MEM" end end diff --git a/test/test_geometry.jl b/test/test_geometry.jl index 78f7a7d8..4beeb4be 100644 --- a/test/test_geometry.jl +++ b/test/test_geometry.jl @@ -1,23 +1,166 @@ using Test -import GeoInterface, GeoFormatTypes, GDAL, ArchGDAL; +import GeoInterface, GeoFormatTypes, ArchGDAL; const AG = ArchGDAL const GFT = GeoFormatTypes -@testset "Incomplete GeoInterface geometries" begin - @test_logs (:warn, "unknown geometry type") GeoInterface.geotype(AG.creategeom(GDAL.wkbCircularString)) - @test_logs (:warn, "unknown geometry type") GeoInterface.geotype(AG.creategeom(GDAL.wkbCompoundCurve)) - @test_logs (:warn, "unknown geometry type") GeoInterface.geotype(AG.creategeom(GDAL.wkbCurvePolygon)) - @test_logs (:warn, "unknown geometry type") GeoInterface.geotype(AG.creategeom(GDAL.wkbMultiSurface)) - @test_logs (:warn, "unknown geometry type") GeoInterface.geotype(AG.creategeom(GDAL.wkbPolyhedralSurface)) - @test_logs (:warn, "unknown geometry type") GeoInterface.geotype(AG.creategeom(GDAL.wkbTIN)) - @test_logs (:warn, "unknown geometry type") GeoInterface.geotype(AG.creategeom(GDAL.wkbTriangle)) -end +@testset "test_geometry.jl" begin + @testset "Incomplete GeoInterface geometries" begin + @test_logs (:warn, "unknown geometry type") GeoInterface.geotype( + AG.creategeom(AG.wkbCircularString), + ) + @test_logs (:warn, "unknown geometry type") GeoInterface.geotype( + AG.creategeom(AG.wkbCompoundCurve), + ) + @test_logs (:warn, "unknown geometry type") GeoInterface.geotype( + AG.creategeom(AG.wkbCurvePolygon), + ) + @test_logs (:warn, "unknown geometry type") GeoInterface.geotype( + AG.creategeom(AG.wkbMultiSurface), + ) + @test_logs (:warn, "unknown geometry type") GeoInterface.geotype( + AG.creategeom(AG.wkbPolyhedralSurface), + ) + @test_logs (:warn, "unknown geometry type") GeoInterface.geotype( + AG.creategeom(AG.wkbTIN), + ) + @test_logs (:warn, "unknown geometry type") GeoInterface.geotype( + AG.creategeom(AG.wkbTriangle), + ) + end + + @testset "Create a Point" begin + # Method 1 + AG.createpoint(100, 70) do point + @test GeoInterface.geotype(point) == :Point + @test isapprox( + GeoInterface.coordinates(point), + [100, 70], + atol = 1e-6, + ) + @test AG.geomdim(point) == 0 + @test AG.getcoorddim(point) == 2 + AG.setcoorddim!(point, 3) + @test AG.getcoorddim(point) == 3 + @test AG.isvalid(point) == true + @test AG.issimple(point) == true + @test AG.isring(point) == false + @test AG.getz(point, 0) == 0 + + @test sprint(print, AG.envelope(point)) == + "GDAL.OGREnvelope(100.0, 100.0, 70.0, 70.0)" + @test sprint(print, AG.envelope3d(point)) == + "GDAL.OGREnvelope3D(100.0, 100.0, 70.0, 70.0, 0.0, 0.0)" + @test AG.toISOWKB(point, AG.wkbNDR) == UInt8[ + 0x01, + 0xe9, + 0x03, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x59, + 0x40, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x80, + 0x51, + 0x40, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + ] + @test AG.toISOWKB(point, AG.wkbXDR) == UInt8[ + 0x00, + 0x00, + 0x00, + 0x03, + 0xe9, + 0x40, + 0x59, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x40, + 0x51, + 0x80, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + ] + @test AG.toKML(point, "relativeToGround") == + "relativeToGround" * + "100,70,0" + @test AG.toKML(point, "clampToGround") == + "clampToGround" * + "100,70,0" + @test AG.toKML(point) == + "100,70,0" + @test AG.toJSON(point) == + "{ \"type\": \"Point\", \"coordinates\": " * + "[ 100.0, 70.0, 0.0 ] }" + @test startswith( + AG.toJSON(point, SIGNIFICANT_FIGURES = 1), + "{ \"type\": \"Point\", \"coordinates\": [", + ) + @test startswith( + AG.toJSON(point, ["SIGNIFICANT_FIGURES=1"]), + "{ \"type\": \"Point\", \"coordinates\": [", + ) + AG.createpoint(100, 70, 0) do point2 + @test isapprox( + GeoInterface.coordinates(point2), + [100, 70, 0], + atol = 1e-6, + ) + @test AG.equals(point, point2) == true + end + AG.createpoint((100, 70, 0)) do point3 + @test AG.equals(point, point3) == true + end + AG.createpoint([100, 70, 0]) do point4 + @test AG.equals(point, point4) == true + end + point5 = AG.createpoint([100, 70, 0]) + @test AG.equals(point, point5) == true + AG.flattento2d!(point) + @test AG.getcoorddim(point) == 2 + @test AG.getnonlineargeomflag() == true + AG.setnonlineargeomflag!(false) + @test AG.getnonlineargeomflag() == false + AG.setnonlineargeomflag!(true) + @test AG.getnonlineargeomflag() == true + AG.closerings!(point) + @test AG.toJSON(point) == + "{ \"type\": \"Point\", \"coordinates\": [ 100.0, 70.0 ] }" + end -@testset "Create a Point" begin - # Method 1 - AG.createpoint(100, 70) do point - @test GeoInterface.geotype(point) == :Point - @test isapprox(GeoInterface.coordinates(point), [100,70], atol=1e-6) + # Method 2 + point = AG.createpoint(100, 70) @test AG.geomdim(point) == 0 @test AG.getcoorddim(point) == 2 AG.setcoorddim!(point, 3) @@ -26,32 +169,85 @@ end @test AG.issimple(point) == true @test AG.isring(point) == false @test AG.getz(point, 0) == 0 - @test typeof(point) == AG.Geometry{GDAL.wkbPoint} - - @test sprint(print, AG.envelope(point)) == "GDAL.OGREnvelope(100.0, 100.0, 70.0, 70.0)" - @test sprint(print, AG.envelope3d(point)) == "GDAL.OGREnvelope3D(100.0, 100.0, 70.0, 70.0, 0.0, 0.0)" - @test AG.toISOWKB(point, GDAL.wkbNDR) == UInt8[0x01,0xe9,0x03,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x59,0x40,0x00,0x00,0x00,0x00,0x00,0x80, - 0x51,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00] - @test AG.toISOWKB(point, GDAL.wkbXDR) == UInt8[0x00,0x00,0x00,0x03,0xe9, - 0x40,0x59,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x51,0x80,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00] - @test AG.toKML(point, "relativeToGround") == "relativeToGround100,70,0" - @test AG.toKML(point, "clampToGround") == "clampToGround100,70,0" - @test AG.toKML(point) == "100,70,0" - @test AG.toJSON(point) == "{ \"type\": \"Point\", \"coordinates\": [ 100.0, 70.0, 0.0 ] }" - AG.createpoint(100,70,0) do point2 - @test isapprox(GeoInterface.coordinates(point2), [100,70,0], atol=1e-6) - @test AG.equals(point, point2) == true - end - AG.createpoint((100,70,0)) do point3 - @test AG.equals(point, point3) == true - end - AG.createpoint([100,70,0]) do point4 - @test AG.equals(point, point4) == true - end - point5 = AG.createpoint([100,70,0]) - @test AG.equals(point, point5) == true + @test typeof(point) == AG.IGeometry{AG.wkbPoint} + @test sprint(print, AG.envelope(point)) == + "GDAL.OGREnvelope(100.0, 100.0, 70.0, 70.0)" + @test sprint(print, AG.envelope3d(point)) == + "GDAL.OGREnvelope3D(100.0, 100.0, 70.0, 70.0, 0.0, 0.0)" + @test AG.toISOWKB(point, AG.wkbNDR) == UInt8[ + 0x01, + 0xe9, + 0x03, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x59, + 0x40, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x80, + 0x51, + 0x40, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + ] + @test AG.toISOWKB(point, AG.wkbXDR) == UInt8[ + 0x00, + 0x00, + 0x00, + 0x03, + 0xe9, + 0x40, + 0x59, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x40, + 0x51, + 0x80, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + ] + @test AG.toKML(point, "relativeToGround") == + "relativeToGround" * + "100,70,0" + @test AG.toKML(point, "clampToGround") == + "clampToGround" * + "100,70,0" + @test AG.toKML(point) == + "100,70,0" + @test AG.toJSON(point) == + "{ \"type\": \"Point\", \"coordinates\": [ 100.0, 70.0, 0.0 ] }" + @test AG.equals(point, AG.createpoint(100, 70, 0)) == true + @test AG.equals(point, AG.createpoint((100, 70, 0))) == true AG.flattento2d!(point) @test AG.getcoorddim(point) == 2 @test AG.getnonlineargeomflag() == true @@ -60,266 +256,552 @@ end AG.setnonlineargeomflag!(true) @test AG.getnonlineargeomflag() == true AG.closerings!(point) - @test AG.toJSON(point) == "{ \"type\": \"Point\", \"coordinates\": [ 100.0, 70.0 ] }" + @test AG.toJSON(point) == + "{ \"type\": \"Point\", \"coordinates\": [ 100.0, 70.0 ] }" end - # Method 2 - point = AG.createpoint(100, 70) - @test AG.geomdim(point) == 0 - @test AG.getcoorddim(point) == 2 - AG.setcoorddim!(point, 3) - @test AG.getcoorddim(point) == 3 - @test AG.isvalid(point) == true - @test AG.issimple(point) == true - @test AG.isring(point) == false - @test AG.getz(point, 0) == 0 - @test sprint(print, AG.envelope(point)) == "GDAL.OGREnvelope(100.0, 100.0, 70.0, 70.0)" - @test sprint(print, AG.envelope3d(point)) == "GDAL.OGREnvelope3D(100.0, 100.0, 70.0, 70.0, 0.0, 0.0)" - @test AG.toISOWKB(point, GDAL.wkbNDR) == UInt8[0x01,0xe9,0x03,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x59,0x40,0x00,0x00,0x00,0x00,0x00,0x80, - 0x51,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00] - @test AG.toISOWKB(point, GDAL.wkbXDR) == UInt8[0x00,0x00,0x00,0x03,0xe9, - 0x40,0x59,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x51,0x80,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00] - @test AG.toKML(point, "relativeToGround") == "relativeToGround100,70,0" - @test AG.toKML(point, "clampToGround") == "clampToGround100,70,0" - @test AG.toKML(point) == "100,70,0" - @test AG.toJSON(point) == "{ \"type\": \"Point\", \"coordinates\": [ 100.0, 70.0, 0.0 ] }" - @test AG.equals(point, AG.createpoint(100,70,0)) == true - @test AG.equals(point, AG.createpoint((100,70,0))) == true - AG.flattento2d!(point) - @test AG.getcoorddim(point) == 2 - @test AG.getnonlineargeomflag() == true - AG.setnonlineargeomflag!(false) - @test AG.getnonlineargeomflag() == false - AG.setnonlineargeomflag!(true) - @test AG.getnonlineargeomflag() == true - AG.closerings!(point) - @test AG.toJSON(point) == "{ \"type\": \"Point\", \"coordinates\": [ 100.0, 70.0 ] }" -end - -@testset "Testing construction of complex geometries" begin - @test AG.toWKT(AG.createlinestring([1.,2.,3.], [4.,5.,6.])) == "LINESTRING (1 4,2 5,3 6)" - AG.createlinestring([1.,2.,3.], [4.,5.,6.]) do geom - @test GeoInterface.geotype(geom) == :LineString - @test isapprox(GeoInterface.coordinates(geom), [[1,4],[2,5],[3,6]], atol=1e-6) - @test AG.toWKT(geom) == "LINESTRING (1 4,2 5,3 6)" - AG.closerings!(geom) - @test AG.toWKT(geom) == "LINESTRING (1 4,2 5,3 6)" - AG.setpoint!(geom, 1, 10, 10) - @test AG.toWKT(geom) == "LINESTRING (1 4,10 10,3 6)" - @test GFT.val(convert(GFT.WellKnownText, geom)) == AG.toWKT(geom) - @test typeof(geom) == AG.Geometry{GDAL.wkbLineString} - end - AG.createlinestring([1.,2.,3.], [4.,5.,6.], [7.,8.,9.]) do geom - @test AG.toWKT(geom) == "LINESTRING (1 4 7,2 5 8,3 6 9)" - AG.setpoint!(geom, 1, 10, 10, 10) - @test AG.toWKT(geom) == "LINESTRING (1 4 7,10 10 10,3 6 9)" - AG.addpoint!(geom, 11, 11, 11) - @test AG.toWKT(geom) == "LINESTRING (1 4 7,10 10 10,3 6 9,11 11 11)" - end + @testset "Testing construction of complex geometries" begin + @test AG.toWKT(AG.createlinestring([1.0, 2.0, 3.0], [4.0, 5.0, 6.0])) == + "LINESTRING (1 4,2 5,3 6)" + AG.createlinestring([1.0, 2.0, 3.0], [4.0, 5.0, 6.0]) do geom + @test GeoInterface.geotype(geom) == :LineString + @test isapprox( + GeoInterface.coordinates(geom), + [[1, 4], [2, 5], [3, 6]], + atol = 1e-6, + ) + @test AG.toWKT(geom) == "LINESTRING (1 4,2 5,3 6)" + AG.closerings!(geom) + @test AG.toWKT(geom) == "LINESTRING (1 4,2 5,3 6)" + AG.setpoint!(geom, 1, 10, 10) + @test AG.toWKT(geom) == "LINESTRING (1 4,10 10,3 6)" + @test GFT.val(convert(GFT.WellKnownText, geom)) == AG.toWKT(geom) + @test typeof(geom) == AG.Geometry{AG.wkbLineString} + end + AG.createlinestring( + [1.0, 2.0, 3.0], + [4.0, 5.0, 6.0], + [7.0, 8.0, 9.0], + ) do geom + @test AG.toWKT(geom) == "LINESTRING (1 4 7,2 5 8,3 6 9)" + AG.setpoint!(geom, 1, 10, 10, 10) + @test AG.toWKT(geom) == "LINESTRING (1 4 7,10 10 10,3 6 9)" + AG.addpoint!(geom, 11, 11, 11) + @test AG.toWKT(geom) == "LINESTRING (1 4 7,10 10 10,3 6 9,11 11 11)" + end - @test AG.toWKT(AG.createlinearring([1.,2.,3.], [4.,5.,6.])) == "LINEARRING (1 4,2 5,3 6)" - AG.createlinearring([1.,2.,3.], [4.,5.,6.]) do geom - # @test GeoInterface.geotype(geom) == :LinearRing - @test isapprox(GeoInterface.coordinates(geom), [[1,4],[2,5],[3,6]], atol=1e-6) - @test AG.toWKT(geom) == "LINEARRING (1 4,2 5,3 6)" - AG.setpointcount!(geom, 5) - @test AG.toWKT(geom) == "LINEARRING (1 4,2 5,3 6,0 0,0 0)" - AG.empty!(geom) - @test AG.toWKT(geom) == "LINEARRING EMPTY" - @test typeof(geom) == AG.Geometry{GDAL.wkbLineString} # this seems odd - end - AG.createlinearring([1.,2.,3.], [4.,5.,6.], [7.,8.,9.]) do geom - @test AG.toWKT(geom) == "LINEARRING (1 4 7,2 5 8,3 6 9)" - AG.closerings!(geom) - @test AG.toWKT(geom) == "LINEARRING (1 4 7,2 5 8,3 6 9,1 4 7)" - end + @test AG.toWKT(AG.createlinearring([1.0, 2.0, 3.0], [4.0, 5.0, 6.0])) == + "LINEARRING (1 4,2 5,3 6)" + AG.createlinearring([1.0, 2.0, 3.0], [4.0, 5.0, 6.0]) do geom + @test GeoInterface.geotype(geom) == :LineString + @test isapprox( + GeoInterface.coordinates(geom), + [[1, 4], [2, 5], [3, 6]], + atol = 1e-6, + ) + @test AG.toWKT(geom) == "LINEARRING (1 4,2 5,3 6)" + AG.setpointcount!(geom, 5) + @test AG.toWKT(geom) == "LINEARRING (1 4,2 5,3 6,0 0,0 0)" + AG.empty!(geom) + @test AG.toWKT(geom) == "LINEARRING EMPTY" + @test typeof(geom) == AG.Geometry{AG.wkbLineString} # this seems odd + end + AG.createlinearring( + [1.0, 2.0, 3.0], + [4.0, 5.0, 6.0], + [7.0, 8.0, 9.0], + ) do geom + @test AG.toWKT(geom) == "LINEARRING (1 4 7,2 5 8,3 6 9)" + AG.closerings!(geom) + @test AG.toWKT(geom) == "LINEARRING (1 4 7,2 5 8,3 6 9,1 4 7)" + end - @test AG.toWKT(AG.createpolygon([1.,2.,3.], [4.,5.,6.])) == "POLYGON ((1 4,2 5,3 6))" - AG.createpolygon([1.,2.,3.], [4.,5.,6.]) do geom - @test GeoInterface.geotype(geom) == :Polygon - @test isapprox(GeoInterface.coordinates(geom), [[[1,4],[2,5],[3,6]]], atol=1e-6) - @test AG.toWKT(geom) == "POLYGON ((1 4,2 5,3 6))" - @test typeof(geom) == AG.Geometry{GDAL.wkbPolygon} - end - AG.createpolygon([1.,2.,3.], [4.,5.,6.], [7.,8.,9.]) do geom - @test AG.toWKT(geom) == "POLYGON ((1 4 7,2 5 8,3 6 9))" - AG.closerings!(geom) - @test AG.toWKT(geom) == "POLYGON ((1 4 7,2 5 8,3 6 9,1 4 7))" - end + @test AG.toWKT(AG.createpolygon([1.0, 2.0, 3.0], [4.0, 5.0, 6.0])) == + "POLYGON ((1 4,2 5,3 6))" + AG.createpolygon([1.0, 2.0, 3.0], [4.0, 5.0, 6.0]) do geom + @test GeoInterface.geotype(geom) == :Polygon + @test isapprox( + GeoInterface.coordinates(geom), + [[[1, 4], [2, 5], [3, 6]]], + atol = 1e-6, + ) + @test AG.toWKT(geom) == "POLYGON ((1 4,2 5,3 6))" + @test typeof(geom) == AG.Geometry{AG.wkbPolygon} + end + AG.createpolygon( + [1.0, 2.0, 3.0], + [4.0, 5.0, 6.0], + [7.0, 8.0, 9.0], + ) do geom + @test AG.toWKT(geom) == "POLYGON ((1 4 7,2 5 8,3 6 9))" + AG.closerings!(geom) + @test AG.toWKT(geom) == "POLYGON ((1 4 7,2 5 8,3 6 9,1 4 7))" + end - @test AG.toWKT(AG.createmultipoint([1.,2.,3.], [4.,5.,6.])) == "MULTIPOINT (1 4,2 5,3 6)" - AG.createmultipoint([1.,2.,3.], [4.,5.,6.]) do geom - @test GeoInterface.geotype(geom) == :MultiPoint - @test isapprox(GeoInterface.coordinates(geom), [[1,4],[2,5],[3,6]], atol=1e-6) - @test AG.toWKT(geom) == "MULTIPOINT (1 4,2 5,3 6)" - @test typeof(geom) == AG.Geometry{GDAL.wkbMultiPoint} - end - AG.createmultipoint([1.,2.,3.], [4.,5.,6.], [7.,8.,9.]) do geom - @test AG.toWKT(geom) == "MULTIPOINT (1 4 7,2 5 8,3 6 9)" - end + @test AG.toWKT(AG.createmultipoint([1.0, 2.0, 3.0], [4.0, 5.0, 6.0])) == + "MULTIPOINT (1 4,2 5,3 6)" + AG.createmultipoint([1.0, 2.0, 3.0], [4.0, 5.0, 6.0]) do geom + @test GeoInterface.geotype(geom) == :MultiPoint + @test isapprox( + GeoInterface.coordinates(geom), + [[1, 4], [2, 5], [3, 6]], + atol = 1e-6, + ) + @test AG.toWKT(geom) == "MULTIPOINT (1 4,2 5,3 6)" + @test typeof(geom) == AG.Geometry{AG.wkbMultiPoint} + end + AG.createmultipoint( + [1.0, 2.0, 3.0], + [4.0, 5.0, 6.0], + [7.0, 8.0, 9.0], + ) do geom + @test AG.toWKT(geom) == "MULTIPOINT (1 4 7,2 5 8,3 6 9)" + end - @test AG.toWKT(AG.createmultipolygon(Vector{Vector{Tuple{Cdouble,Cdouble}}}[ - Vector{Tuple{Cdouble,Cdouble}}[ - [(0,0),(0,4),(4,4),(4,0)], - [(1,1),(1,3),(3,3),(3,1)]], - Vector{Tuple{Cdouble,Cdouble}}[ - [(10,0),(10,4),(14,4),(14,0)], - [(11,1),(11,3),(13,3),(13,1)]]] - )) == "MULTIPOLYGON (((0 0,0 4,4 4,4 0),(1 1,1 3,3 3,3 1)),((10 0,10 4,14 4,14 0),(11 1,11 3,13 3,13 1)))" - AG.createmultipolygon(Vector{Vector{Tuple{Cdouble,Cdouble}}}[ - Vector{Tuple{Cdouble,Cdouble}}[ - [(0,0),(0,4),(4,4),(4,0)], - [(1,1),(1,3),(3,3),(3,1)]], - Vector{Tuple{Cdouble,Cdouble}}[ - [(10,0),(10,4),(14,4),(14,0)], - [(11,1),(11,3),(13,3),(13,1)]]]) do geom - @test GeoInterface.geotype(geom) == :MultiPolygon - @test isapprox( - GeoInterface.coordinates(geom), [ - [[[0,0],[0,4],[4,4],[4,0]], [[1,1],[1,3],[3,3],[3,1]]], - [[[10,0],[10,4],[14,4],[14,0]], [[11,1],[11,3],[13,3],[13,1]]] + @test AG.toWKT( + AG.createmultipolygon( + Vector{Vector{Tuple{Cdouble,Cdouble}}}[ + Vector{Tuple{Cdouble,Cdouble}}[ + [(0, 0), (0, 4), (4, 4), (4, 0)], + [(1, 1), (1, 3), (3, 3), (3, 1)], + ], + Vector{Tuple{Cdouble,Cdouble}}[ + [(10, 0), (10, 4), (14, 4), (14, 0)], + [(11, 1), (11, 3), (13, 3), (13, 1)], + ], + ], + ), + ) == + "MULTIPOLYGON (" * + "((0 0,0 4,4 4,4 0),(1 1,1 3,3 3,3 1))," * + "((10 0,10 4,14 4,14 0),(11 1,11 3,13 3,13 1)))" + AG.createmultipolygon( + Vector{Vector{Tuple{Cdouble,Cdouble}}}[ + Vector{Tuple{Cdouble,Cdouble}}[ + [(0, 0), (0, 4), (4, 4), (4, 0)], + [(1, 1), (1, 3), (3, 3), (3, 1)], + ], + Vector{Tuple{Cdouble,Cdouble}}[ + [(10, 0), (10, 4), (14, 4), (14, 0)], + [(11, 1), (11, 3), (13, 3), (13, 1)], + ], ], - atol=1e-6 - ) - @test AG.toWKT(geom) == "MULTIPOLYGON (((0 0,0 4,4 4,4 0),(1 1,1 3,3 3,3 1)),((10 0,10 4,14 4,14 0),(11 1,11 3,13 3,13 1)))" - @test typeof(geom) == AG.Geometry{GDAL.wkbMultiPolygon} - end + ) do geom + @test GeoInterface.geotype(geom) == :MultiPolygon + @test isapprox( + GeoInterface.coordinates(geom), + [ + [ + [[0, 0], [0, 4], [4, 4], [4, 0]], + [[1, 1], [1, 3], [3, 3], [3, 1]], + ], + [ + [[10, 0], [10, 4], [14, 4], [14, 0]], + [[11, 1], [11, 3], [13, 3], [13, 1]], + ], + ], + atol = 1e-6, + ) + @test AG.toWKT(geom) == + "MULTIPOLYGON (" * + "((0 0,0 4,4 4,4 0),(1 1,1 3,3 3,3 1))," * + "((10 0,10 4,14 4,14 0),(11 1,11 3,13 3,13 1)))" + @test typeof(geom) == AG.Geometry{AG.wkbMultiPolygon} + end + + AG.fromWKT( + "CURVEPOLYGON (" * + "CIRCULARSTRING (-2 0,-1 -1,0 0,1 -1,2 0,0 2,-2 0)," * + "(-1 0,0 0.5,1 0,0 1,-1 0))", + ) do geom + @test typeof(geom) == AG.Geometry{AG.wkbCurvePolygon} + @test AG.toWKT(AG.curvegeom(AG.lineargeom(geom, 0.5))) == + "CURVEPOLYGON (" * + "CIRCULARSTRING (-2 0,-1 -1,0 0,1 -1,2 0,0 2,-2 0)," * + "(-1 0,0.0 0.5,1 0,0 1,-1 0))" + AG.lineargeom(geom, 0.5) do lgeom + @test typeof(lgeom) == AG.Geometry{AG.wkbPolygon} + AG.curvegeom(lgeom) do clgeom + @test AG.toWKT(clgeom) == + "CURVEPOLYGON (" * + "CIRCULARSTRING (-2 0,-1 -1,0 0,1 -1,2 0,0 2,-2 0)," * + "(-1 0,0.0 0.5,1 0,0 1,-1 0))" + @test typeof(clgeom) == AG.Geometry{AG.wkbCurvePolygon} + end + @test AG.ngeom( + AG.polygonize(AG.forceto(lgeom, AG.wkbMultiLineString)), + ) == 2 + AG.forceto(lgeom, AG.wkbMultiLineString) do mlsgeom + @test typeof(mlsgeom) == AG.Geometry{AG.wkbMultiLineString} + AG.polygonize(mlsgeom) do plgeom + @test AG.ngeom(plgeom) == 2 + @test typeof(plgeom) == + AG.Geometry{AG.wkbGeometryCollection} + end + end + end - AG.fromWKT("CURVEPOLYGON(CIRCULARSTRING(-2 0,-1 -1,0 0,1 -1,2 0,0 2,-2 0),(-1 0,0 0.5,1 0,0 1,-1 0))") do geom - @test typeof(geom) == AG.Geometry{GDAL.wkbCurvePolygon} - @test AG.toWKT(AG.curvegeom(AG.lineargeom(geom, 0.5))) == "CURVEPOLYGON (CIRCULARSTRING (-2 0,-1 -1,0 0,1 -1,2 0,0 2,-2 0),(-1 0,0.0 0.5,1 0,0 1,-1 0))" - AG.lineargeom(geom, 0.5) do lgeom - @test typeof(lgeom) == AG.Geometry{GDAL.wkbPolygon} - AG.curvegeom(lgeom) do clgeom - @test AG.toWKT(clgeom) == "CURVEPOLYGON (CIRCULARSTRING (-2 0,-1 -1,0 0,1 -1,2 0,0 2,-2 0),(-1 0,0.0 0.5,1 0,0 1,-1 0))" - @test typeof(clgeom) == AG.Geometry{GDAL.wkbCurvePolygon} + @test startswith( + AG.toWKT( + AG.curvegeom( + AG.lineargeom(geom, 0.5, ADD_INTERMEDIATE_POINT = "NO"), + ), + ), + "CURVEPOLYGON (CIRCULARSTRING (", + ) + AG.lineargeom(geom, 0.5, ADD_INTERMEDIATE_POINT = "NO") do lgeom + AG.curvegeom(lgeom) do clgeom + @test startswith( + AG.toWKT(clgeom), + "CURVEPOLYGON (CIRCULARSTRING (", + ) + end + @test AG.ngeom( + AG.polygonize(AG.forceto(lgeom, AG.wkbMultiLineString)), + ) == 2 + AG.forceto(lgeom, AG.wkbMultiLineString) do mlsgeom + AG.polygonize(mlsgeom) do plgeom + @test AG.ngeom(plgeom) == 2 + end + end end - @test AG.ngeom(AG.polygonize(AG.forceto(lgeom, GDAL.wkbMultiLineString))) == 2 - AG.forceto(lgeom, GDAL.wkbMultiLineString) do mlsgeom - @test typeof(mlsgeom) == AG.Geometry{GDAL.wkbMultiLineString} - AG.polygonize(mlsgeom) do plgeom - @test AG.ngeom(plgeom) == 2 - @test typeof(plgeom) == AG.Geometry{GDAL.wkbGeometryCollection} + + @test startswith( + AG.toWKT( + AG.curvegeom( + AG.lineargeom(geom, ["ADD_INTERMEDIATE_POINT=NO"], 0.5), + ), + ), + "CURVEPOLYGON (CIRCULARSTRING (", + ) + AG.lineargeom(geom, ["ADD_INTERMEDIATE_POINT=NO"], 0.5) do lgeom + AG.curvegeom(lgeom) do clgeom + @test startswith( + AG.toWKT(clgeom), + "CURVEPOLYGON (CIRCULARSTRING (", + ) + end + @test AG.ngeom( + AG.polygonize(AG.forceto(lgeom, AG.wkbMultiLineString)), + ) == 2 + AG.forceto(lgeom, AG.wkbMultiLineString) do mlsgeom + AG.polygonize(mlsgeom) do plgeom + @test AG.ngeom(plgeom) == 2 + end end end end end -end -@testset "Testing remaining methods for geometries" begin - geom1 = AG.createmultipolygon(Vector{Vector{Tuple{Cdouble,Cdouble}}}[ - Vector{Tuple{Cdouble,Cdouble}}[ - [(0,0),(0,4),(4,4),(4,0)], - [(1,1),(1,3),(3,3),(3,1)]], - Vector{Tuple{Cdouble,Cdouble}}[ - [(10,0),(10,4),(14,4),(14,0)], - [(11,1),(11,3),(13,3),(13,1)]]]) - geom2 = AG.createmultipoint([1.,2.,3.], [4.,5.,6.], [7.,8.,9.]) + @testset "Testing remaining methods for geometries" begin + geom1 = AG.createmultipolygon( + Vector{Vector{Tuple{Cdouble,Cdouble}}}[ + Vector{Tuple{Cdouble,Cdouble}}[ + [(0, 0), (0, 4), (4, 4), (4, 0)], + [(1, 1), (1, 3), (3, 3), (3, 1)], + ], + Vector{Tuple{Cdouble,Cdouble}}[ + [(10, 0), (10, 4), (14, 4), (14, 0)], + [(11, 1), (11, 3), (13, 3), (13, 1)], + ], + ], + ) + geom2 = AG.createmultipoint( + [1.0, 2.0, 3.0], + [4.0, 5.0, 6.0], + [7.0, 8.0, 9.0], + ) - AG.closerings!(geom1) - @test AG.disjoint(geom1, geom2) == false - @test AG.touches(geom1, geom2) == true - @test AG.crosses(geom1, geom2) == false - @test AG.overlaps(geom1, geom2) == false + AG.closerings!(geom1) + @test AG.disjoint(geom1, geom2) == false + @test AG.touches(geom1, geom2) == true + @test AG.crosses(geom1, geom2) == false + @test AG.overlaps(geom1, geom2) == false - @test AG.toWKT(AG.boundary(geom2)) == "GEOMETRYCOLLECTION EMPTY" - AG.boundary(geom2) do result - @test AG.toWKT(result) == "GEOMETRYCOLLECTION EMPTY" - end + @test AG.toWKT(AG.boundary(geom2)) == "GEOMETRYCOLLECTION EMPTY" + AG.boundary(geom2) do result + @test AG.toWKT(result) == "GEOMETRYCOLLECTION EMPTY" + end - @test AG.toWKT(AG.union(geom1, geom2)) == "GEOMETRYCOLLECTION (POLYGON ((0 4 8,4 4 8,4 0 8,0 0 8,0 4 8),(3 1 8,3 3 8,1 3 8,1 1 8,3 1 8)),POLYGON ((10 4 8,14 4 8,14 0 8,10 0 8,10 4 8),(13 1 8,13 3 8,11 3 8,11 1 8,13 1 8)),POINT (2 5 8),POINT (3 6 9))" - AG.union(geom1, geom2) do result - @test AG.toWKT(result) == "GEOMETRYCOLLECTION (POLYGON ((0 4 8,4 4 8,4 0 8,0 0 8,0 4 8),(3 1 8,3 3 8,1 3 8,1 1 8,3 1 8)),POLYGON ((10 4 8,14 4 8,14 0 8,10 0 8,10 4 8),(13 1 8,13 3 8,11 3 8,11 1 8,13 1 8)),POINT (2 5 8),POINT (3 6 9))" - @test AG.hascurvegeom(result, true) == false - @test AG.hascurvegeom(result, false) == false - end + @test AG.toWKT(AG.union(geom1, geom2)) == + "GEOMETRYCOLLECTION (" * + "POLYGON (" * + "(0 4 8,4 4 8,4 0 8,0 0 8,0 4 8)," * + "(3 1 8,3 3 8,1 3 8,1 1 8,3 1 8))," * + "POLYGON (" * + "(10 4 8,14 4 8,14 0 8,10 0 8,10 4 8)," * + "(13 1 8,13 3 8,11 3 8,11 1 8,13 1 8))," * + "POINT (2 5 8),POINT (3 6 9))" + AG.union(geom1, geom2) do result + @test AG.toWKT(result) == + "GEOMETRYCOLLECTION (" * + "POLYGON (" * + "(0 4 8,4 4 8,4 0 8,0 0 8,0 4 8)," * + "(3 1 8,3 3 8,1 3 8,1 1 8,3 1 8))," * + "POLYGON (" * + "(10 4 8,14 4 8,14 0 8,10 0 8,10 4 8)," * + "(13 1 8,13 3 8,11 3 8,11 1 8,13 1 8))," * + "POINT (2 5 8),POINT (3 6 9))" + @test AG.hascurvegeom(result, true) == false + @test AG.hascurvegeom(result, false) == false + end - @test AG.toWKT(AG.difference(geom1, geom2)) == "MULTIPOLYGON (((0 4 8,4 4 8,4 0 8,0 0 8,0 4 8),(3 1 8,3 3 8,1 3 8,1 1 8,3 1 8)),((10 4 8,14 4 8,14 0 8,10 0 8,10 4 8),(13 1 8,13 3 8,11 3 8,11 1 8,13 1 8)))" - AG.difference(geom1, geom2) do result - @test AG.toWKT(result) == "MULTIPOLYGON (((0 4 8,4 4 8,4 0 8,0 0 8,0 4 8),(3 1 8,3 3 8,1 3 8,1 1 8,3 1 8)),((10 4 8,14 4 8,14 0 8,10 0 8,10 4 8),(13 1 8,13 3 8,11 3 8,11 1 8,13 1 8)))" - AG.segmentize!(result, 20) - @test AG.toWKT(result) == "MULTIPOLYGON (((0 4 8,4 4 8,4 0 8,0 0 8,0 4 8),(3 1 8,3 3 8,1 3 8,1 1 8,3 1 8)),((10 4 8,14 4 8,14 0 8,10 0 8,10 4 8),(13 1 8,13 3 8,11 3 8,11 1 8,13 1 8)))" - AG.segmentize!(result, 2) - @test AG.toWKT(result) == "MULTIPOLYGON (((0 4 8,2 4 8,4 4 8,4 2 8,4 0 8,2 0 8,0 0 8,0 2 8,0 4 8),(3 1 8,3 3 8,1 3 8,1 1 8,3 1 8)),((10 4 8,12 4 8,14 4 8,14 2 8,14 0 8,12 0 8,10 0 8,10 2 8,10 4 8),(13 1 8,13 3 8,11 3 8,11 1 8,13 1 8)))" - @test typeof(result) == AG.Geometry{GDAL.wkbMultiPolygon25D} - end + @test AG.toWKT(AG.difference(geom1, geom2)) == + "MULTIPOLYGON (" * + "((0 4 8,4 4 8,4 0 8,0 0 8,0 4 8)," * + "(3 1 8,3 3 8,1 3 8,1 1 8,3 1 8))," * + "((10 4 8,14 4 8,14 0 8,10 0 8,10 4 8)," * + "(13 1 8,13 3 8,11 3 8,11 1 8,13 1 8)))" + AG.difference(geom1, geom2) do result + @test AG.toWKT(result) == + "MULTIPOLYGON (" * + "((0 4 8,4 4 8,4 0 8,0 0 8,0 4 8)," * + "(3 1 8,3 3 8,1 3 8,1 1 8,3 1 8))," * + "((10 4 8,14 4 8,14 0 8,10 0 8,10 4 8)," * + "(13 1 8,13 3 8,11 3 8,11 1 8,13 1 8)))" + AG.segmentize!(result, 20) + @test AG.toWKT(result) == + "MULTIPOLYGON (" * + "((0 4 8,4 4 8,4 0 8,0 0 8,0 4 8)," * + "(3 1 8,3 3 8,1 3 8,1 1 8,3 1 8))," * + "((10 4 8,14 4 8,14 0 8,10 0 8,10 4 8)," * + "(13 1 8,13 3 8,11 3 8,11 1 8,13 1 8)))" + AG.segmentize!(result, 2) + @test AG.toWKT(result) == + "MULTIPOLYGON (" * + "(" * + "(" * + "0 4 8," * + "2 4 8," * + "4 4 8," * + "4 2 8," * + "4 0 8," * + "2 0 8," * + "0 0 8," * + "0 2 8," * + "0 4 8)," * + "(" * + "3 1 8," * + "3 3 8," * + "1 3 8," * + "1 1 8," * + "3 1 8))," * + "(" * + "(" * + "10 4 8," * + "12 4 8," * + "14 4 8," * + "14 2 8," * + "14 0 8," * + "12 0 8," * + "10 0 8," * + "10 2 8," * + "10 4 8)," * + "(" * + "13 1 8," * + "13 3 8," * + "11 3 8," * + "11 1 8," * + "13 1 8)))" + @test typeof(result) == AG.Geometry{AG.wkbMultiPolygon25D} + end - @test AG.toWKT(AG.symdifference(geom1, geom2)) == "GEOMETRYCOLLECTION (POLYGON ((0 4 8,4 4 8,4 0 8,0 0 8,0 4 8),(3 1 8,3 3 8,1 3 8,1 1 8,3 1 8)),POLYGON ((10 4 8,14 4 8,14 0 8,10 0 8,10 4 8),(13 1 8,13 3 8,11 3 8,11 1 8,13 1 8)),POINT (2 5 8),POINT (3 6 9))" - AG.symdifference(geom1, geom2) do result - @test GeoInterface.geotype(result) == :GeometryCollection - @test AG.toWKT(result) == "GEOMETRYCOLLECTION (POLYGON ((0 4 8,4 4 8,4 0 8,0 0 8,0 4 8),(3 1 8,3 3 8,1 3 8,1 1 8,3 1 8)),POLYGON ((10 4 8,14 4 8,14 0 8,10 0 8,10 4 8),(13 1 8,13 3 8,11 3 8,11 1 8,13 1 8)),POINT (2 5 8),POINT (3 6 9))" - AG.removegeom!(result, 1) - @test AG.toWKT(result) == "GEOMETRYCOLLECTION (POLYGON ((0 4 8,4 4 8,4 0 8,0 0 8,0 4 8),(3 1 8,3 3 8,1 3 8,1 1 8,3 1 8)),POINT (2 5 8),POINT (3 6 9))" - AG.removeallgeoms!(result) - @test AG.toWKT(result) == "GEOMETRYCOLLECTION EMPTY" - @test typeof(result) == AG.Geometry{GDAL.wkbGeometryCollection25D} - end + @test AG.toWKT(AG.symdifference(geom1, geom2)) == + "GEOMETRYCOLLECTION (" * + "POLYGON (" * + "(0 4 8,4 4 8,4 0 8,0 0 8,0 4 8)," * + "(3 1 8,3 3 8,1 3 8,1 1 8,3 1 8))," * + "POLYGON (" * + "(10 4 8,14 4 8,14 0 8,10 0 8,10 4 8)," * + "(13 1 8,13 3 8,11 3 8,11 1 8,13 1 8))," * + "POINT (2 5 8),POINT (3 6 9))" + AG.symdifference(geom1, geom2) do result + @test GeoInterface.geotype(result) == :GeometryCollection + @test AG.toWKT(result) == + "GEOMETRYCOLLECTION (" * + "POLYGON (" * + "(0 4 8,4 4 8,4 0 8,0 0 8,0 4 8)," * + "(3 1 8,3 3 8,1 3 8,1 1 8,3 1 8))," * + "POLYGON (" * + "(10 4 8,14 4 8,14 0 8,10 0 8,10 4 8)," * + "(13 1 8,13 3 8,11 3 8,11 1 8,13 1 8))," * + "POINT (2 5 8)," * + "POINT (3 6 9))" + AG.removegeom!(result, 1) + @test AG.toWKT(result) == + "GEOMETRYCOLLECTION (" * + "POLYGON (" * + "(0 4 8,4 4 8,4 0 8,0 0 8,0 4 8)," * + "(3 1 8,3 3 8,1 3 8,1 1 8,3 1 8))," * + "POINT (2 5 8)," * + "POINT (3 6 9))" + AG.removeallgeoms!(result) + @test AG.toWKT(result) == "GEOMETRYCOLLECTION EMPTY" + @test typeof(result) == AG.Geometry{AG.wkbGeometryCollection25D} + end - geom3 = AG.fromWKT("GEOMETRYCOLLECTION (POINT (2 5 8),POLYGON ((0 0 8,0 4 8,4 4 8,4 0 8,0 0 8),(1 1 8,3 1 8,3 3 8,1 3 8,1 1 8)),POLYGON ((10 0 8,10 4 8,14 4 8,14 0 8,10 0 8),(11 1 8,13 1 8,13 3 8,11 3 8,11 1 8)), POINT EMPTY)") - AG.clone(geom3) do geom4 - @test sprint(print, AG.clone(geom3)) == "Geometry: GEOMETRYCOLLECTION (POINT (2 5 8),POLYGON ((0 0 8, ... MPTY)" - @test sprint(print, AG.clone(geom4)) == "Geometry: GEOMETRYCOLLECTION (POINT (2 5 8),POLYGON ((0 0 8, ... MPTY)" - @test typeof(geom4) == AG.Geometry{GDAL.wkbGeometryCollection25D} - end - AG.clone(AG.getgeom(geom3, 3)) do geom4 - @test sprint(print, geom4) == "Geometry: POINT EMPTY" - end + geom3 = AG.fromWKT( + "GEOMETRYCOLLECTION (" * + "POINT (2 5 8)," * + "POLYGON (" * + "(0 0 8,0 4 8,4 4 8,4 0 8,0 0 8)," * + "(1 1 8,3 1 8,3 3 8,1 3 8,1 1 8))," * + "POLYGON (" * + "(10 0 8,10 4 8,14 4 8,14 0 8,10 0 8)," * + "(11 1 8,13 1 8,13 3 8,11 3 8,11 1 8))," * + "POINT EMPTY)", + ) + AG.clone(geom3) do geom4 + @test sprint(print, AG.clone(geom3)) == + "Geometry: GEOMETRYCOLLECTION (" * + "POINT (2 5 8)," * + "POLYGON ((0 0 8," * + " ... MPTY)" + @test sprint(print, AG.clone(geom4)) == + "Geometry: GEOMETRYCOLLECTION (" * + "POINT (2 5 8)," * + "POLYGON ((0 0 8," * + " ... MPTY)" + @test typeof(geom4) == AG.Geometry{AG.wkbGeometryCollection25D} + end + AG.clone(AG.getgeom(geom3, 3)) do geom4 + @test sprint(print, geom4) == "Geometry: POINT EMPTY" + end + + @test AG.toISOWKT(geom3) == + "GEOMETRYCOLLECTION Z (" * + "POINT Z (2 5 8)," * + "POLYGON Z (" * + "(0 0 8,0 4 8,4 4 8,4 0 8,0 0 8)," * + "(1 1 8,3 1 8,3 3 8,1 3 8,1 1 8))," * + "POLYGON Z (" * + "(10 0 8,10 4 8,14 4 8,14 0 8,10 0 8)," * + "(11 1 8,13 1 8,13 3 8,11 3 8,11 1 8))," * + "POINT Z EMPTY)" + # the JSON driver in GDAL 3.0 does not handle null geometries well yet + AG.removegeom!(geom3, AG.ngeom(geom3) - 1) + @test AG.toJSON(geom3) == + """{ "type": "GeometryCollection", "geometries": [ """ * + """{ "type": "Point", "coordinates": [ 2.0, 5.0, 8.0 ] }, """ * + """{ "type": "Polygon", "coordinates": [ """ * + "[ " * + "[ 0.0, 0.0, 8.0 ], " * + "[ 0.0, 4.0, 8.0 ], " * + "[ 4.0, 4.0, 8.0 ], " * + "[ 4.0, 0.0, 8.0 ], " * + "[ 0.0, 0.0, 8.0 ] ], " * + "[ " * + "[ 1.0, 1.0, 8.0 ], " * + "[ 3.0, 1.0, 8.0 ], " * + "[ 3.0, 3.0, 8.0 ], " * + "[ 1.0, 3.0, 8.0 ], " * + "[ 1.0, 1.0, 8.0 ] ] ] }, " * + """{ "type": "Polygon", "coordinates": [ """ * + "[ " * + "[ 10.0, 0.0, 8.0 ], " * + "[ 10.0, 4.0, 8.0 ], " * + "[ 14.0, 4.0, 8.0 ], " * + "[ 14.0, 0.0, 8.0 ], " * + "[ 10.0, 0.0, 8.0 ] ], " * + "[ " * + "[ 11.0, 1.0, 8.0 ], " * + "[ 13.0, 1.0, 8.0 ], " * + "[ 13.0, 3.0, 8.0 ], " * + "[ 11.0, 3.0, 8.0 ], " * + "[ 11.0, 1.0, 8.0 ] ] ] } ] }" - @test AG.toISOWKT(geom3) == "GEOMETRYCOLLECTION Z (POINT Z (2 5 8),POLYGON Z ((0 0 8,0 4 8,4 4 8,4 0 8,0 0 8),(1 1 8,3 1 8,3 3 8,1 3 8,1 1 8)),POLYGON Z ((10 0 8,10 4 8,14 4 8,14 0 8,10 0 8),(11 1 8,13 1 8,13 3 8,11 3 8,11 1 8)),POINT Z EMPTY)" - AG.removegeom!(geom3, AG.ngeom(geom3)-1) # the JSON driver in GDAL 3.0 does not handle null geometries well yet - @test AG.toJSON(geom3) == """{ "type": "GeometryCollection", "geometries": [ { "type": "Point", "coordinates": [ 2.0, 5.0, 8.0 ] }, { "type": "Polygon", "coordinates": [ [ [ 0.0, 0.0, 8.0 ], [ 0.0, 4.0, 8.0 ], [ 4.0, 4.0, 8.0 ], [ 4.0, 0.0, 8.0 ], [ 0.0, 0.0, 8.0 ] ], [ [ 1.0, 1.0, 8.0 ], [ 3.0, 1.0, 8.0 ], [ 3.0, 3.0, 8.0 ], [ 1.0, 3.0, 8.0 ], [ 1.0, 1.0, 8.0 ] ] ] }, { "type": "Polygon", "coordinates": [ [ [ 10.0, 0.0, 8.0 ], [ 10.0, 4.0, 8.0 ], [ 14.0, 4.0, 8.0 ], [ 14.0, 0.0, 8.0 ], [ 10.0, 0.0, 8.0 ] ], [ [ 11.0, 1.0, 8.0 ], [ 13.0, 1.0, 8.0 ], [ 13.0, 3.0, 8.0 ], [ 11.0, 3.0, 8.0 ], [ 11.0, 1.0, 8.0 ] ] ] } ] }""" + AG.createmultilinestring([[ + [1.0, 4.0], + [2.0, 5.0], + [3.0, 6.0], + [1.0, 4.0], + ]]) do geom4 + @test AG.toWKT(geom4) == "MULTILINESTRING ((1 4,2 5,3 6,1 4))" + @test AG.toWKT(AG.polygonfromedges(geom4, 0.1)) == + "POLYGON ((1 4,2 5,3 6,1 4))" + AG.polygonfromedges(geom4, 0.1) do geom5 + @test AG.toWKT(geom5) == "POLYGON ((1 4,2 5,3 6,1 4))" + end + end - AG.createmultilinestring([[[1.,4.], [2.,5.], [3.,6.], [1.,4.]]]) do geom4 - @test AG.toWKT(geom4) == "MULTILINESTRING ((1 4,2 5,3 6,1 4))" - @test AG.toWKT(AG.polygonfromedges(geom4, 0.1)) == "POLYGON ((1 4,2 5,3 6,1 4))" - AG.polygonfromedges(geom4, 0.1) do geom5 - @test AG.toWKT(geom5) == "POLYGON ((1 4,2 5,3 6,1 4))" + @test AG.getgeomtype(AG.getgeom(geom3, 0)) == AG.wkbPoint25D + @test AG.getgeomtype(AG.getgeom(geom3, 1)) == AG.wkbPolygon25D + @test AG.getgeomtype(AG.getgeom(geom3, 2)) == AG.wkbPolygon25D + @test sprint(print, AG.getgeom(geom3, 3)) == "NULL Geometry" + @test sprint(print, AG.getgeom(AG.IGeometry(), 3)) == "NULL Geometry" + AG.getgeom(geom3, 0) do geom4 + @test AG.getgeomtype(geom4) == AG.wkbPoint25D + end + AG.getgeom(geom3, 1) do geom4 + @test AG.getgeomtype(geom4) == AG.wkbPolygon25D + end + AG.getgeom(geom3, 2) do geom4 + @test AG.getgeomtype(geom4) == AG.wkbPolygon25D + end + AG.getgeom(geom3, 3) do geom4 + @test sprint(print, geom4) == "NULL Geometry" + end + AG.getgeom(AG.IGeometry(), 0) do geom + @test sprint(print, geom) == "NULL Geometry" end end - @test AG.getgeomtype(AG.getgeom(geom3, 0)) == GDAL.wkbPoint25D - @test AG.getgeomtype(AG.getgeom(geom3, 1)) == GDAL.wkbPolygon25D - @test AG.getgeomtype(AG.getgeom(geom3, 2)) == GDAL.wkbPolygon25D - @test sprint(print, AG.getgeom(geom3, 3)) == "NULL Geometry" - AG.getgeom(geom3, 0) do geom4 - @test AG.getgeomtype(geom4) == GDAL.wkbPoint25D - end - AG.getgeom(geom3, 1) do geom4 - @test AG.getgeomtype(geom4) == GDAL.wkbPolygon25D - end - AG.getgeom(geom3, 2) do geom4 - @test AG.getgeomtype(geom4) == GDAL.wkbPolygon25D - end - AG.getgeom(geom3, 3) do geom4 - @test sprint(print, geom4) == "NULL Geometry" - end -end + @testset "Spatial Reference Systems" begin + @test sprint(print, AG.getspatialref(AG.IGeometry())) == + "NULL Spatial Reference System" + AG.getspatialref(AG.IGeometry()) do spatialref + @test sprint(print, spatialref) == "NULL Spatial Reference System" + end -@testset "Spatial Reference Systems" begin - AG.read("data/point.geojson") do dataset - layer = AG.getlayer(dataset, 0) - AG.nextfeature(layer) do feature - geom = AG.getgeom(feature) - @test AG.toPROJ4(AG.getspatialref(geom)) == "+proj=longlat +datum=WGS84 +no_defs" + AG.createpoint(100, 70, 0) do geom + @test sprint(print, AG.getspatialref(geom)) == + "NULL Spatial Reference System" AG.getspatialref(geom) do spatialref - @test AG.toPROJ4(spatialref) == "+proj=longlat +datum=WGS84 +no_defs" + @test sprint(print, spatialref) == + "NULL Spatial Reference System" end end - AG.createpoint(1,2) do point - @test sprint(print, AG.getspatialref(point)) == "NULL Spatial Reference System" - AG.getspatialref(point) do spatialref - @test sprint(print, spatialref) == "NULL Spatial Reference System" + + AG.read("data/point.geojson") do dataset + layer = AG.getlayer(dataset, 0) + AG.nextfeature(layer) do feature + geom = AG.getgeom(feature) + @test AG.toPROJ4(AG.getspatialref(geom)) == + "+proj=longlat +datum=WGS84 +no_defs" + AG.getspatialref(geom) do spatialref + @test AG.toPROJ4(spatialref) == + "+proj=longlat +datum=WGS84 +no_defs" + end + end + AG.createpoint(1, 2) do point + @test sprint(print, AG.getspatialref(point)) == + "NULL Spatial Reference System" + AG.getspatialref(point) do spatialref + @test sprint(print, spatialref) == + "NULL Spatial Reference System" + end + end + end + + AG.importEPSG(2927) do source + AG.importEPSG(4326) do target + AG.createcoordtrans(source, target) do transform + AG.fromWKT("POINT (1120351.57 741921.42)") do point + @test AG.toWKT(point) == "POINT (1120351.57 741921.42)" + AG.transform!(point, transform) + @test GeoInterface.coordinates(point) ≈ + [47.3488070138318, -122.5981499431438] + end + end end end end - AG.importEPSG(2927) do source; AG.importEPSG(4326) do target - AG.createcoordtrans(source, target) do transform - AG.fromWKT("POINT (1120351.57 741921.42)") do point - @test AG.toWKT(point) == "POINT (1120351.57 741921.42)" - AG.transform!(point, transform) - @test GeoInterface.coordinates(point) ≈ [47.3488070138318, -122.5981499431438] - end end end end + @testset "Cloning NULL geometries" begin + geom = AG.IGeometry() + @test sprint(print, AG.clone(geom)) == "NULL Geometry" + AG.clone(geom) do g + @test sprint(print, g) == "NULL Geometry" + end + end end diff --git a/test/test_geos_operations.jl b/test/test_geos_operations.jl index 116e9654..5f8e7d58 100644 --- a/test/test_geos_operations.jl +++ b/test/test_geos_operations.jl @@ -1,221 +1,283 @@ using Test -import ArchGDAL; const AG = ArchGDAL +import ArchGDAL; +const AG = ArchGDAL; -function equivalent_to_wkt(geom::ArchGDAL.Geometry, wkt::String) - fromWKT(wkt) do test_geom - @test toWKT(geom) == toWKT(test_geom) +@testset "test_geos_operations.jl" begin + function equivalent_to_wkt(geom::AG.Geometry, wkt::String) + fromWKT(wkt) do test_geom + @test toWKT(geom) == toWKT(test_geom) + end end -end -@testset "Interpolation along a LineString" begin - AG.createlinestring([(8.,1.),(9.,1.),(9.,2.),(8.,2.)]) do ls - for (dist,dest) in [(1.0,(9,1)), (2.0,(9,2)), - (1.5,(9.0,1.5)),(2.5,(8.5,2.0))] - AG.pointalongline(ls, dist) do pt1 - AG.createpoint(dest) do pt2 - @test AG.toWKT(pt1) == AG.toWKT(pt2) + @testset "Interpolation along a LineString" begin + AG.createlinestring([ + (8.0, 1.0), + (9.0, 1.0), + (9.0, 2.0), + (8.0, 2.0), + ]) do ls + for (dist, dest) in [ + (1.0, (9, 1)), + (2.0, (9, 2)), + (1.5, (9.0, 1.5)), + (2.5, (8.5, 2.0)), + ] + AG.pointalongline(ls, dist) do pt1 + AG.createpoint(dest) do pt2 + @test AG.toWKT(pt1) == AG.toWKT(pt2) + end + end + @test AG.toWKT(AG.pointalongline(ls, dist)) == + AG.toWKT(AG.createpoint(dest)) end - end - @test AG.toWKT(AG.pointalongline(ls, dist)) == AG.toWKT(AG.createpoint(dest)) end end -end - -@testset "Contains operation" begin - AG.fromWKT("POLYGON EMPTY") do g1 - AG.fromWKT("POLYGON EMPTY") do g2 - @test AG.contains(g1, g2) == false - @test AG.contains(g2, g1) == false - end - end - AG.fromWKT("POLYGON((1 1,1 5,5 5,5 1,1 1))") do g1 - AG.fromWKT("POINT(2 2)") do g2 - @test AG.contains(g1, g2) == true - @test AG.contains(g2, g1) == false - end - end + @testset "Contains operation" begin + AG.fromWKT("POLYGON EMPTY") do g1 + AG.fromWKT("POLYGON EMPTY") do g2 + @test AG.contains(g1, g2) == false + @test AG.contains(g2, g1) == false + end + end - AG.fromWKT("MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0)))") do g1 - AG.fromWKT("POLYGON((1 1,1 2,2 2,2 1,1 1))") do g2 - @test AG.contains(g1, g2) == true - @test AG.contains(g2, g1) == false - end - end -end + AG.fromWKT("POLYGON((1 1,1 5,5 5,5 1,1 1))") do g1 + AG.fromWKT("POINT(2 2)") do g2 + @test AG.contains(g1, g2) == true + @test AG.contains(g2, g1) == false + end + end -@testset "Convex Hull" begin - AG.fromWKT("MULTIPOINT (130 240, 130 240, 130 240, 570 240, 570 240, 570 240, 650 240)") do input - AG.fromWKT("LINESTRING (130 240, 650 240)") do expected - AG.convexhull(input) do output - @test AG.isempty(output) == false - @test AG.toWKT(output) == AG.toWKT(expected) + AG.fromWKT("MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0)))") do g1 + AG.fromWKT("POLYGON((1 1,1 2,2 2,2 1,1 1))") do g2 + @test AG.contains(g1, g2) == true + @test AG.contains(g2, g1) == false + end end - @test AG.toWKT(AG.convexhull(input)) == AG.toWKT(expected) - end end -end -@testset "Delaunay Triangulation" begin - AG.fromWKT("POLYGON EMPTY") do g1 - AG.delaunaytriangulation(g1,0,true) do g2 - @test AG.isempty(g1) == true - @test AG.isempty(g2) == true - @test AG.toWKT(g2) == "MULTILINESTRING EMPTY" + @testset "Convex Hull" begin + AG.fromWKT( + "MULTIPOINT (130 240, 130 240, 130 240, 570 240, 570 240, 570 240, 650 240)", + ) do input + AG.fromWKT("LINESTRING (130 240, 650 240)") do expected + AG.convexhull(input) do output + @test AG.isempty(output) == false + @test AG.toWKT(output) == AG.toWKT(expected) + end + @test AG.toWKT(AG.convexhull(input)) == AG.toWKT(expected) + end end - @test AG.toWKT(AG.delaunaytriangulation(g1,0,true)) == "MULTILINESTRING EMPTY" end - AG.fromWKT("POINT(0 0)") do g1 - AG.delaunaytriangulation(g1,0,false) do g2 - @test AG.isempty(g2) == true - @test AG.toWKT(g2) == "GEOMETRYCOLLECTION EMPTY" + + @testset "Delaunay Triangulation" begin + AG.fromWKT("POLYGON EMPTY") do g1 + AG.delaunaytriangulation(g1, 0, true) do g2 + @test AG.isempty(g1) == true + @test AG.isempty(g2) == true + @test AG.toWKT(g2) == "MULTILINESTRING EMPTY" + end + @test AG.toWKT(AG.delaunaytriangulation(g1, 0, true)) == + "MULTILINESTRING EMPTY" end - @test AG.toWKT(AG.delaunaytriangulation(g1,0,false)) == "GEOMETRYCOLLECTION EMPTY" - end - AG.fromWKT("MULTIPOINT(0 0, 5 0, 10 0)") do g1 - AG.delaunaytriangulation(g1,0,false) do g2 - @test AG.toWKT(g2) == "GEOMETRYCOLLECTION EMPTY" + AG.fromWKT("POINT(0 0)") do g1 + AG.delaunaytriangulation(g1, 0, false) do g2 + @test AG.isempty(g2) == true + @test AG.toWKT(g2) == "GEOMETRYCOLLECTION EMPTY" + end + @test AG.toWKT(AG.delaunaytriangulation(g1, 0, false)) == + "GEOMETRYCOLLECTION EMPTY" end - AG.delaunaytriangulation(g1,0,true) do g2 - @test AG.toWKT(g2) == "MULTILINESTRING ((5 0,10 0),(0 0,5 0))" + AG.fromWKT("MULTIPOINT(0 0, 5 0, 10 0)") do g1 + AG.delaunaytriangulation(g1, 0, false) do g2 + @test AG.toWKT(g2) == "GEOMETRYCOLLECTION EMPTY" + end + AG.delaunaytriangulation(g1, 0, true) do g2 + @test AG.toWKT(g2) == "MULTILINESTRING ((5 0,10 0),(0 0,5 0))" + end end - end - AG.fromWKT("MULTIPOINT(0 0, 10 0, 10 10, 11 10)") do g1 - AG.delaunaytriangulation(g1,2.0,true) do g2 - @test AG.toWKT(g2) == "MULTILINESTRING ((0 0,10 10),(0 0,10 0),(10 0,10 10))" + AG.fromWKT("MULTIPOINT(0 0, 10 0, 10 10, 11 10)") do g1 + AG.delaunaytriangulation(g1, 2.0, true) do g2 + @test AG.toWKT(g2) == + "MULTILINESTRING ((0 0,10 10),(0 0,10 0),(10 0,10 10))" + end end end -end -@testset "Distance" begin - AG.fromWKT("POINT(10 10)") do g1 - AG.fromWKT("POINT(3 6)") do g2 - @test AG.distance(g1, g2) ≈ 8.06225774829855 atol=1e-12 - end + @testset "Distance" begin + AG.fromWKT("POINT(10 10)") do g1 + AG.fromWKT("POINT(3 6)") do g2 + @test AG.distance(g1, g2) ≈ 8.06225774829855 atol = 1e-12 + end + end end -end -function test_method(f::Function, wkt1::AbstractString, wkt2::AbstractString) - AG.fromWKT(wkt1) do geom - f(geom) do result - @test AG.toWKT(result) == wkt2 + function test_method( + f::Function, + wkt1::AbstractString, + wkt2::AbstractString, + ) + AG.fromWKT(wkt1) do geom + f(geom) do result + @test AG.toWKT(result) == wkt2 + end + @test AG.toWKT(f(geom)) == wkt2 end - @test AG.toWKT(f(geom)) == wkt2 end -end -@testset "Centroid" begin - test_method(AG.centroid, "POINT(10 0)", "POINT (10 0)") - test_method(AG.centroid, "LINESTRING(0 0, 10 0)", "POINT (5 0)") - test_method(AG.centroid, "POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))", "POINT (5 5)") - test_method(AG.centroid, "LINESTRING EMPTY", "POINT EMPTY") -end + @testset "Centroid" begin + test_method(AG.centroid, "POINT(10 0)", "POINT (10 0)") + test_method(AG.centroid, "LINESTRING(0 0, 10 0)", "POINT (5 0)") + test_method( + AG.centroid, + "POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))", + "POINT (5 5)", + ) + test_method(AG.centroid, "LINESTRING EMPTY", "POINT EMPTY") + end -@testset "Point on Surface" begin - test_method(AG.pointonsurface, "POINT(10 0)", "POINT (10 0)") - test_method(AG.pointonsurface, "LINESTRING(0 0, 5 0, 10 0)", "POINT (5 0)") - test_method(AG.pointonsurface, "POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))", "POINT (5 5)") - test_method(AG.pointonsurface, "LINESTRING EMPTY", "POINT EMPTY") - test_method(AG.pointonsurface, "LINESTRING(0 0, 0 0)", "POINT (0 0)") -end + @testset "Point on Surface" begin + test_method(AG.pointonsurface, "POINT(10 0)", "POINT (10 0)") + test_method( + AG.pointonsurface, + "LINESTRING(0 0, 5 0, 10 0)", + "POINT (5 0)", + ) + test_method( + AG.pointonsurface, + "POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))", + "POINT (5 5)", + ) + test_method(AG.pointonsurface, "LINESTRING EMPTY", "POINT EMPTY") + test_method(AG.pointonsurface, "LINESTRING(0 0, 0 0)", "POINT (0 0)") + end -function test_method(f::Function, wkt1::AbstractString, - wkt2::AbstractString, wkt3::AbstractString) - AG.fromWKT(wkt1) do geom1 - AG.fromWKT(wkt2) do geom2 - f(geom1, geom2) do result - @test AG.toWKT(result) == wkt3 + function test_method( + f::Function, + wkt1::AbstractString, + wkt2::AbstractString, + wkt3::AbstractString, + ) + AG.fromWKT(wkt1) do geom1 + AG.fromWKT(wkt2) do geom2 + f(geom1, geom2) do result + @test AG.toWKT(result) == wkt3 + end + @test AG.toWKT(f(geom1, geom2)) == wkt3 + end end - @test AG.toWKT(f(geom1, geom2)) == wkt3 - end end -end -@testset "Intersection" begin - test_method(AG.intersection, - "POLYGON EMPTY", - "POLYGON EMPTY", - "POLYGON EMPTY") - test_method(AG.intersection, - "POLYGON((1 1,1 5,5 5,5 1,1 1))", - "POINT(2 2)", - "POINT (2 2)") - test_method(AG.intersection, - "MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0)))", - "POLYGON((-1 1,-1 2,2 2,2 1,-1 1))", - "POLYGON ((0 2,2 2,2 1,0 1,0 2))") - test_method(AG.intersection, - "MULTIPOLYGON(((0 0,5 10,10 0,0 0),(1 1,1 2,2 2,2 1,1 1),(100 100,100 102,102 102,102 100,100 100)))", - "POLYGON((0 1,0 2,10 2,10 1,0 1))", - "GEOMETRYCOLLECTION (POLYGON ((0.5 1.0,1 2,1 1,0.5 1.0)),POLYGON ((2 2,9 2,9.5 1.0,2 1,2 2)),LINESTRING (2 1,1 1),LINESTRING (1 2,2 2))") -end - -function test_predicate(f::Function, wkt1, wkt2, result::Bool) - AG.fromWKT(wkt1) do geom1 - AG.fromWKT(wkt2) do geom2 - @test f(geom1, geom2) == result + @testset "Intersection" begin + test_method( + AG.intersection, + "POLYGON EMPTY", + "POLYGON EMPTY", + "POLYGON EMPTY", + ) + test_method( + AG.intersection, + "POLYGON((1 1,1 5,5 5,5 1,1 1))", + "POINT(2 2)", + "POINT (2 2)", + ) + test_method( + AG.intersection, + "MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0)))", + "POLYGON((-1 1,-1 2,2 2,2 1,-1 1))", + "POLYGON ((0 2,2 2,2 1,0 1,0 2))", + ) + test_method( + AG.intersection, + "MULTIPOLYGON(((0 0,5 10,10 0,0 0),(1 1,1 2,2 2,2 1,1 1),(100 100,100 102,102 102,102 100,100 100)))", + "POLYGON((0 1,0 2,10 2,10 1,0 1))", + "GEOMETRYCOLLECTION (POLYGON ((0.5 1.0,1 2,1 1,0.5 1.0)),POLYGON ((2 2,9 2,9.5 1.0,2 1,2 2)),LINESTRING (2 1,1 1),LINESTRING (1 2,2 2))", + ) end + + function test_predicate(f::Function, wkt1, wkt2, result::Bool) + AG.fromWKT(wkt1) do geom1 + AG.fromWKT(wkt2) do geom2 + @test f(geom1, geom2) == result + end + end end -end -@testset "Intersects" begin - test_predicate(AG.intersects, - "POLYGON EMPTY", - "POLYGON EMPTY", - false) - test_predicate(AG.intersects, - "POLYGON((1 1,1 5,5 5,5 1,1 1))", - "POINT(2 2)", - true) - test_predicate(AG.intersects, - "POINT(2 2)", - "POLYGON((1 1,1 5,5 5,5 1,1 1))", - true) - test_predicate(AG.intersects, - "MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0)))", - "POLYGON((1 1,1 2,2 2,2 1,1 1))", - true) - test_predicate(AG.intersects, - "POLYGON((1 1,1 2,2 2,2 1,1 1))", - "MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0)))", - true) -end + @testset "Intersects" begin + test_predicate(AG.intersects, "POLYGON EMPTY", "POLYGON EMPTY", false) + test_predicate( + AG.intersects, + "POLYGON((1 1,1 5,5 5,5 1,1 1))", + "POINT(2 2)", + true, + ) + test_predicate( + AG.intersects, + "POINT(2 2)", + "POLYGON((1 1,1 5,5 5,5 1,1 1))", + true, + ) + test_predicate( + AG.intersects, + "MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0)))", + "POLYGON((1 1,1 2,2 2,2 1,1 1))", + true, + ) + test_predicate( + AG.intersects, + "POLYGON((1 1,1 2,2 2,2 1,1 1))", + "MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0)))", + true, + ) + end -@testset "Within" begin - test_predicate(AG.within, - "POLYGON EMPTY", - "POLYGON EMPTY", - false) - test_predicate(AG.within, - "POLYGON((1 1,1 5,5 5,5 1,1 1))", - "POINT(2 2)", - false) - test_predicate(AG.within, - "POINT(2 2)", - "POLYGON((1 1,1 5,5 5,5 1,1 1))", - true) - test_predicate(AG.within, - "MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0)))", - "POLYGON((1 1,1 2,2 2,2 1,1 1))", - false) - test_predicate(AG.within, - "POLYGON((1 1,1 2,2 2,2 1,1 1))", - "MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0)))", - true) -end + @testset "Within" begin + test_predicate(AG.within, "POLYGON EMPTY", "POLYGON EMPTY", false) + test_predicate( + AG.within, + "POLYGON((1 1,1 5,5 5,5 1,1 1))", + "POINT(2 2)", + false, + ) + test_predicate( + AG.within, + "POINT(2 2)", + "POLYGON((1 1,1 5,5 5,5 1,1 1))", + true, + ) + test_predicate( + AG.within, + "MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0)))", + "POLYGON((1 1,1 2,2 2,2 1,1 1))", + false, + ) + test_predicate( + AG.within, + "POLYGON((1 1,1 2,2 2,2 1,1 1))", + "MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0)))", + true, + ) + end -@testset "Simplify" begin - AG.fromWKT("POLYGON((56.528666666700 25.2101666667, 56.529000000000 25.2105000000, 56.528833333300 25.2103333333, 56.528666666700 25.2101666667))") do g1 - AG.simplify(g1, 0.0) do g2 - @test AG.toWKT(g2) == "POLYGON EMPTY" + @testset "Simplify" begin + AG.fromWKT( + "POLYGON((56.528666666700 25.2101666667, 56.529000000000 25.2105000000, 56.528833333300 25.2103333333, 56.528666666700 25.2101666667))", + ) do g1 + AG.simplify(g1, 0.0) do g2 + @test AG.toWKT(g2) == "POLYGON EMPTY" + end + @test AG.toWKT(AG.simplify(g1, 0.0)) == "POLYGON EMPTY" end - @test AG.toWKT(AG.simplify(g1, 0.0)) == "POLYGON EMPTY" - end - AG.fromWKT("POLYGON((56.528666666700 25.2101666667, 56.529000000000 25.2105000000, 56.528833333300 25.2103333333, 56.528666666700 25.2101666667))") do g1 - AG.simplifypreservetopology(g1, 43.2) do g2 - @test AG.toWKT(g2) == "POLYGON ((56.5286666667 25.2101666667,56.529 25.2105,56.5288333333 25.2103333333,56.5286666667 25.2101666667))" + AG.fromWKT( + "POLYGON((56.528666666700 25.2101666667, 56.529000000000 25.2105000000, 56.528833333300 25.2103333333, 56.528666666700 25.2101666667))", + ) do g1 + AG.simplifypreservetopology(g1, 43.2) do g2 + @test AG.toWKT(g2) == + "POLYGON ((56.5286666667 25.2101666667,56.529 25.2105,56.5288333333 25.2103333333,56.5286666667 25.2101666667))" + end + @test AG.toWKT(AG.simplifypreservetopology(g1, 43.2)) == + "POLYGON ((56.5286666667 25.2101666667,56.529 25.2105,56.5288333333 25.2103333333,56.5286666667 25.2101666667))" end - @test AG.toWKT(AG.simplifypreservetopology(g1, 43.2)) == "POLYGON ((56.5286666667 25.2101666667,56.529 25.2105,56.5288333333 25.2103333333,56.5286666667 25.2101666667))" end end diff --git a/test/test_geotransform.jl b/test/test_geotransform.jl new file mode 100644 index 00000000..8ec5814d --- /dev/null +++ b/test/test_geotransform.jl @@ -0,0 +1,40 @@ +using Test +import ArchGDAL; +const AG = ArchGDAL; + +@testset "test_geotransform.jl" begin + function f(gt, pixel, line) + return [ + gt[1] + pixel * gt[2] + line * gt[3], + gt[4] + pixel * gt[5] + line * gt[6], + ] + end + gt1 = [ + -1111950.519667, + 463.3127165279167, + 0.0, + 6671703.118, + 0.0, + -463.3127165279165, + ] + for (pixel, line) in ((0.0, 0.0), (3.0, 2.0), (15.5, 9.9)) + @test AG.applygeotransform(gt1, pixel, line) ≈ f(gt1, pixel, line) + end + + gt2 = AG.invgeotransform(gt1) + @test gt2 ≈ [ + 2400.0, + 0.0021583694216166533, + 0.0, + 14400.0, + 0.0, + -0.002158369421616654, + ] + + gt3 = AG.composegeotransform(gt1, gt2) + @test gt3 ≈ [0.0, 1.0, 0.0, 0.0, 0.0, 1.0] + + for (pixel, line) in ((0.0, 0.0), (3.0, 2.0), (15.5, 9.9)) + @test AG.applygeotransform(gt3, pixel, line) ≈ [pixel, line] + end +end diff --git a/test/test_images.jl b/test/test_images.jl new file mode 100644 index 00000000..c52268d7 --- /dev/null +++ b/test/test_images.jl @@ -0,0 +1,174 @@ +using Test +import ArchGDAL; +const AG = ArchGDAL; +import ImageCore +import ColorTypes + +@testset "test_images.jl" begin + @testset "Test Gray colors" begin + @test eltype(AG.imread("data/utmsmall.tif")) == + ColorTypes.Gray{ImageCore.N0f8} + end + + AG.read("gdalworkshop/world.tif") do dataset + @testset "Test RGB colors" begin + @test eltype(AG.imread(dataset)) == ColorTypes.RGB{ImageCore.N0f8} + end + end + + AG.create( + AG.getdriver("MEM"), + width = 2, + height = 2, + nbands = 4, + dtype = UInt8, + ) do dataset + @testset "imview" begin + @test eltype(AG.imview(AG.GCI_GrayIndex, ones(UInt8, 2, 2))) == + ColorTypes.Gray{ImageCore.N0f8} + @test eltype(AG.imview(AG.GCI_Undefined, ones(UInt8, 2, 2))) == + ColorTypes.Gray{ImageCore.N0f8} + @test eltype(AG.imview(AG.GCI_RedBand, ones(UInt8, 2, 2))) == + ColorTypes.RGB{ImageCore.N0f8} + @test eltype(AG.imview(AG.GCI_GreenBand, ones(UInt8, 2, 2))) == + ColorTypes.RGB{ImageCore.N0f8} + @test eltype(AG.imview(AG.GCI_BlueBand, ones(UInt8, 2, 2))) == + ColorTypes.RGB{ImageCore.N0f8} + @test_throws ErrorException AG.imview(AG.GCI_Max, ones(UInt8, 2, 2)) + @test eltype(AG.imview(AG.GPI_Gray, ones(UInt8, 2, 2))) == + ColorTypes.Gray{ImageCore.N0f8} + @test eltype( + AG.imview( + AG.GPI_Gray, + ones(UInt8, 2, 2), + ones(UInt8, 2, 2), + ones(UInt8, 2, 2), + ), + ) == ColorTypes.Gray{ImageCore.N0f8} + @test_throws ErrorException AG.imview(AG.GPI_HLS, ones(UInt8, 2, 2)) + @test_throws ErrorException AG.imview( + AG.GPI_HLS, + ones(UInt8, 2, 2), + ones(UInt8, 2, 2), + ones(UInt8, 2, 2), + ) + @test_throws ErrorException AG.imview( + AG.GPI_HLS, + ones(UInt8, 2, 2), + ones(UInt8, 2, 2), + ones(UInt8, 2, 2), + ones(UInt8, 2, 2), + ) + end + + @testset "imread(color, ..., xoffset, yoffset, xsize, ysize)" begin + @test eltype( + AG.imread(AG.GCI_GrayIndex, AG.getband(dataset, 1), 0, 0, 2, 2), + ) == ColorTypes.Gray{ImageCore.N0f8} + @test eltype( + AG.imread(AG.GPI_Gray, AG.getband(dataset, 1), 0, 0, 2, 2), + ) == ColorTypes.Gray{ImageCore.N0f8} + @test eltype(AG.imread(AG.GCI_GrayIndex, dataset, 1, 0, 0, 2, 2)) == + ColorTypes.Gray{ImageCore.N0f8} + @test eltype(AG.imread(AG.GPI_Gray, dataset, 1, 0, 0, 2, 2)) == + ColorTypes.Gray{ImageCore.N0f8} + end + + @testset "imread(color, ..., rows::UnitRange, cols::UnitRange)" begin + @test eltype( + AG.imread(AG.GCI_GrayIndex, AG.getband(dataset, 1), 1:2, 1:2), + ) == ColorTypes.Gray{ImageCore.N0f8} + @test eltype( + AG.imread(AG.GPI_Gray, AG.getband(dataset, 1), 1:2, 1:2), + ) == ColorTypes.Gray{ImageCore.N0f8} + @test eltype(AG.imread(AG.GCI_GrayIndex, dataset, 1, 1:2, 1:2)) == + ColorTypes.Gray{ImageCore.N0f8} + @test eltype(AG.imread(AG.GPI_Gray, dataset, 1, 1:2, 1:2)) == + ColorTypes.Gray{ImageCore.N0f8} + end + + @testset "imread(color, ...)" begin + @test eltype(AG.imread(AG.GCI_GrayIndex, AG.getband(dataset, 1))) == + ColorTypes.Gray{ImageCore.N0f8} + @test eltype(AG.imread(AG.GPI_Gray, AG.getband(dataset, 1))) == + ColorTypes.Gray{ImageCore.N0f8} + @test eltype(AG.imread(AG.GCI_GrayIndex, dataset, 1)) == + ColorTypes.Gray{ImageCore.N0f8} + @test eltype(AG.imread(AG.GPI_Gray, dataset, 1)) == + ColorTypes.Gray{ImageCore.N0f8} + end + + @testset "Test RGBA colors" begin + AG.setcolorinterp!(AG.getband(dataset, 1), AG.GCI_RedBand) + AG.setcolorinterp!(AG.getband(dataset, 2), AG.GCI_GreenBand) + AG.setcolorinterp!(AG.getband(dataset, 3), AG.GCI_BlueBand) + AG.setcolorinterp!(AG.getband(dataset, 4), AG.GCI_AlphaBand) + @test eltype(AG.imread(AG.getband(dataset, 1))) == + ColorTypes.RGB{ImageCore.N0f8} + @test eltype(AG.imread(dataset, 1)) == + ColorTypes.RGB{ImageCore.N0f8} + @test eltype(AG.imread(AG.getband(dataset, 1), 0, 0, 2, 2)) == + ColorTypes.RGB{ImageCore.N0f8} + @test eltype(AG.imread(dataset, 1, 0, 0, 2, 2)) == + ColorTypes.RGB{ImageCore.N0f8} + @test eltype(AG.imread(AG.getband(dataset, 1), 1:2, 1:2)) == + ColorTypes.RGB{ImageCore.N0f8} + @test eltype(AG.imread(dataset, 1, 1:2, 1:2)) == + ColorTypes.RGB{ImageCore.N0f8} + @test eltype(AG.imread(AG.getband(dataset, 1))) == + ColorTypes.RGB{ImageCore.N0f8} + @test eltype(AG.imread(dataset, 1)) == + ColorTypes.RGB{ImageCore.N0f8} + @test eltype(AG.imread(dataset, 1:4, 0, 0, 2, 2)) == + ColorTypes.RGBA{ImageCore.N0f8} + @test eltype(AG.imread(dataset, 1:4, 1:2, 1:2)) == + ColorTypes.RGBA{ImageCore.N0f8} + @test eltype(AG.imread(dataset)) == ColorTypes.RGBA{ImageCore.N0f8} + end + + @testset "Test HSL colors" begin + AG.setcolorinterp!(AG.getband(dataset, 1), AG.GCI_HueBand) + AG.setcolorinterp!(AG.getband(dataset, 2), AG.GCI_SaturationBand) + AG.setcolorinterp!(AG.getband(dataset, 3), AG.GCI_LightnessBand) + @test_throws ErrorException AG.imread(dataset, 1:3) + end + + @testset "Test ColorTable colors" begin + AG.setcolorinterp!(AG.getband(dataset, 1), AG.GCI_PaletteIndex) + AG.setcolorinterp!(AG.getband(dataset, 2), AG.GCI_PaletteIndex) + AG.setcolorinterp!(AG.getband(dataset, 3), AG.GCI_PaletteIndex) + AG.setcolorinterp!(AG.getband(dataset, 4), AG.GCI_PaletteIndex) + + AG.createcolortable(AG.GPI_RGB) do ct + AG.setcolorentry!( + ct, + typemax(UInt8), + GDAL.GDALColorEntry(0, 0, 0, 0), + ) + AG.setcolortable!(AG.getband(dataset, 1), ct) + AG.setcolortable!(AG.getband(dataset, 2), ct) + AG.setcolortable!(AG.getband(dataset, 3), ct) + return AG.setcolortable!(AG.getband(dataset, 4), ct) + end + @test eltype(AG.imread(dataset)) == ColorTypes.RGBA{ImageCore.N0f8} + + AG.createcolortable(AG.GPI_Gray) do ct + AG.setcolorentry!( + ct, + typemax(UInt8), + GDAL.GDALColorEntry(0, 0, 0, 0), + ) + return AG.setcolortable!(AG.getband(dataset, 4), ct) + end + @test eltype(AG.imread(dataset, 4)) == + ColorTypes.Gray{ImageCore.N0f8} + + AG.createcolortable(AG.GPI_CMYK) do ct # CMYK not supported yet + AG.setcolortable!(AG.getband(dataset, 1), ct) + AG.setcolortable!(AG.getband(dataset, 2), ct) + return AG.setcolortable!(AG.getband(dataset, 3), ct) + end + @test_throws ErrorException AG.imread(dataset, 1:3) + end + end +end diff --git a/test/test_iterators.jl b/test/test_iterators.jl index e7040603..96f812fd 100644 --- a/test/test_iterators.jl +++ b/test/test_iterators.jl @@ -1,13 +1,16 @@ using Test -import ArchGDAL; const AG = ArchGDAL +import ArchGDAL; +const AG = ArchGDAL; -@testset "Iterator interface Window Iterator" begin - ds = AG.readraster("ospy/data4/aster.img") - band = AG.getband(ds, 1) - window = AG.windows(band) - @test Base.IteratorSize(window) == Base.HasShape{2}() - @test Base.IteratorEltype(window) == Base.HasEltype() - @test eltype(window) == Tuple{UnitRange{Int},UnitRange{Int}} - @test size(window) == (79, 89) - @test length(window) == 7031 +@testset "test_iterators.jl" begin + @testset "Iterator interface Window Iterator" begin + ds = AG.readraster("ospy/data4/aster.img") + band = AG.getband(ds, 1) + window = AG.windows(band) + @test Base.IteratorSize(window) == Base.HasShape{2}() + @test Base.IteratorEltype(window) == Base.HasEltype() + @test eltype(window) == Tuple{UnitRange{Int},UnitRange{Int}} + @test size(window) == (79, 89) + @test length(window) == 7031 + end end diff --git a/test/test_ospy_examples.jl b/test/test_ospy_examples.jl index 5c827b12..3913bb71 100644 --- a/test/test_ospy_examples.jl +++ b/test/test_ospy_examples.jl @@ -1,466 +1,506 @@ using Test using Statistics -import GDAL -import ArchGDAL; const AG = ArchGDAL - -""" -function to copy fields (not the data) from one layer to another -parameters: - fromLayer: layer object that contains the fields to copy - toLayer: layer object to copy the fields into -""" -function copyfields(fromlayer, tolayer) - featuredefn = AG.layerdefn(fromlayer) - for i in 0:(AG.nfield(featuredefn)-1) - fd = AG.getfielddefn(featuredefn, i) - if AG.gettype(fd) == OFTReal - # to deal with errors like - # ERROR: GDALError (Warning, code 1): - # Value 18740682.1600000001 of field SHAPE_AREA of - # feature 1 not successfully written. Possibly due - # to too larger number with respect to field width - fwidth = AG.getwidth(fd) - if fwidth != 0 - AG.setwidth!(fd, fwidth+1) - end end - AG.createfield!(tolayer, fd) - end -end - -""" -function to copy attributes from one feature to another -(this assumes the features have the same attribute fields!) -parameters: - fromFeature: feature object that contains the data to copy - toFeature: feature object that the data is to be copied into -""" -function copyattributes(fromfeature, tofeature) - for i in 0:(AG.nfield(fromfeature)-1) - if AG.isfieldset(fromfeature, i) - try - AG.setfield!(tofeature, i, AG.getfield(fromfeature, i)) - catch - println(fromfeature) - println(tofeature) - println("$i: $(AG.getfield(fromfeature, i))") - end end end -end - -# function reproject(inFN, inEPSG, outEPSG) -# AG.fromEPSG(inEPSG) do inspatialref -# AG.fromEPSG(outEPSG) do outspatialref -# AG.createcoordtrans(inspatialref, outspatialref) do coordtrans -# AG.read(inFN) do inDS -# AG.create("", "MEMORY") do outDS -# inlayer = AG.getlayer(inDS, 0) -# outlayer = AG.createlayer( -# name = "outlayer", -# dataset = outDS, -# geom = AG.getgeomtype(AG.layerdefn(inlayer))) -# copyfields(inlayer, outlayer) -# featuredefn = AG.layerdefn(outlayer) -# for infeature in inlayer -# geom = AG.getgeom(infeature) -# AG.createfeature(featuredefn) do outfeature -# AG.setgeom!(outfeature, AG.transform!(geom, coordtrans)) -# copyattributes(infeature, outfeature) -# AG.createfeature(outlayer, outfeature) -# end end -# println(outlayer) -# end -# end -# end -# println(AG.toWKT(AG.morphtoESRI!(outspatialref))) -# end -# end -# end - -@testset "Homework 1" begin -AG.read("ospy/data1/sites.shp") do input - #reference: http://www.gis.usu.edu/~chrisg/python/2009/lectures/ospy_hw1a.py - for (i,feature) in enumerate(AG.getlayer(input, 0)) - id = AG.getfield(feature, 0); cover = AG.getfield(feature, 1) - (x,y) = AG.getpoint(AG.getgeom(feature, 0), 0) - @test id == i - @test 4e5 <= x <= 5e5 - @test 4.5e6 <= y <= 5e6 - @test cover in ("shrubs", "trees", "rocks", "grass", "bare", "water") +import ArchGDAL; +const AG = ArchGDAL; + +@testset "test_ospy_examples.jl" begin + """ + function to copy fields (not the data) from one layer to another + parameters: + fromLayer: layer object that contains the fields to copy + toLayer: layer object to copy the fields into + """ + function copyfields(fromlayer, tolayer) + featuredefn = AG.layerdefn(fromlayer) + for i in 0:(AG.nfield(featuredefn)-1) + fd = AG.getfielddefn(featuredefn, i) + if AG.gettype(fd) == OFTReal + # to deal with errors like + # ERROR: GDALError (Warning, code 1): + # Value 18740682.1600000001 of field SHAPE_AREA of + # feature 1 not successfully written. Possibly due + # to too larger number with respect to field width + fwidth = AG.getwidth(fd) + if fwidth != 0 + AG.setwidth!(fd, fwidth + 1) + end + end + AG.createfield!(tolayer, fd) + end end - #reference: http://www.gis.usu.edu/~chrisg/python/2009/lectures/ospy_hw1b.py - # version 1 - AG.create(AG.getdriver("MEMORY")) do output - inlayer = AG.getlayer(input, 0) - outlayer = AG.createlayer( - name = "hw1b", - dataset = output, - geom = GDAL.wkbPoint - ) - inlayerdefn = AG.layerdefn(inlayer) - AG.addfielddefn!(outlayer, AG.getfielddefn(inlayerdefn, 0)) - AG.addfielddefn!(outlayer, AG.getfielddefn(inlayerdefn, 1)) - for infeature in inlayer - id = AG.getfield(infeature, 0) - @test AG.asint64(infeature, 0) == id - cover = AG.getfield(infeature, 1) - if cover == "trees" - AG.createfeature(outlayer) do outfeature - AG.setgeom!(outfeature, AG.getgeom(infeature)) - AG.setfield!(outfeature, 0, id) - AG.setfield!(outfeature, 1, cover) - end end end - @test sprint(print, output) == """ - GDAL Dataset (Driver: Memory/Memory) - File(s): - - Number of feature layers: 1 - Layer 0: hw1b (wkbPoint) - """ + """ + function to copy attributes from one feature to another + (this assumes the features have the same attribute fields!) + parameters: + fromFeature: feature object that contains the data to copy + toFeature: feature object that the data is to be copied into + """ + function copyattributes(fromfeature, tofeature) + for i in 0:(AG.nfield(fromfeature)-1) + if AG.isfieldset(fromfeature, i) + try + AG.setfield!(tofeature, i, AG.getfield(fromfeature, i)) + catch + println(fromfeature) + println(tofeature) + println("$i: $(AG.getfield(fromfeature, i))") + end + end + end end - # version 2 - AG.create(AG.getdriver("MEMORY")) do output - AG.executesql(input, """SELECT * FROM sites - WHERE cover = 'trees' """) do results - @test sprint(print, results) == """ - Layer: sites - Geometry 0 (_ogr_geometry_): [wkbPoint], POINT (449959.840851...), ... - Field 0 (ID): [OFTInteger], 2, 6, 9, 14, 19, 20, 22, 26, 34, 36, 41 - Field 1 (COVER): [OFTString], trees, trees, trees, trees, trees, trees, ... - """ - AG.copy(results, name = "hw1b", dataset = output) - end - @test sprint(print, output) == """ - GDAL Dataset (Driver: Memory/Memory) - File(s): + # function reproject(inFN, inEPSG, outEPSG) + # AG.fromEPSG(inEPSG) do inspatialref + # AG.fromEPSG(outEPSG) do outspatialref + # AG.createcoordtrans(inspatialref, outspatialref) do coordtrans + # AG.read(inFN) do inDS + # AG.create("", "MEMORY") do outDS + # inlayer = AG.getlayer(inDS, 0) + # outlayer = AG.createlayer( + # name = "outlayer", + # dataset = outDS, + # geom = AG.getgeomtype(AG.layerdefn(inlayer))) + # copyfields(inlayer, outlayer) + # featuredefn = AG.layerdefn(outlayer) + # for infeature in inlayer + # geom = AG.getgeom(infeature) + # AG.createfeature(featuredefn) do outfeature + # AG.setgeom!(outfeature, AG.transform!(geom, coordtrans)) + # copyattributes(infeature, outfeature) + # AG.createfeature(outlayer, outfeature) + # end end + # println(outlayer) + # end + # end + # end + # println(AG.toWKT(AG.morphtoESRI!(outspatialref))) + # end + # end + # end + + @testset "Homework 1" begin + AG.read("ospy/data1/sites.shp") do input + #reference: http://www.gis.usu.edu/~chrisg/python/2009/lectures/ospy_hw1a.py + for (i, feature) in enumerate(AG.getlayer(input, 0)) + id = AG.getfield(feature, 0) + cover = AG.getfield(feature, 1) + (x, y) = AG.getpoint(AG.getgeom(feature, 0), 0) + @test id == i + @test 4e5 <= x <= 5e5 + @test 4.5e6 <= y <= 5e6 + @test cover in + ("shrubs", "trees", "rocks", "grass", "bare", "water") + end - Number of feature layers: 1 - Layer 0: hw1b (wkbPoint) - """ - end -end -end - -@testset "Homework 2" begin - # http://www.gis.usu.edu/~chrisg/python/2009/lectures/ospy_hw2a.py - open("ospy/data2/ut_counties.txt", "r") do file - AG.create(AG.getdriver("MEMORY")) do output - layer = AG.createlayer( - name = "hw2a", - dataset = output, - geom = GDAL.wkbPolygon - ) - @test sprint(print, layer) == """ - Layer: hw2a - Geometry 0 (): [wkbPolygon] - """ - AG.createfielddefn("name", GDAL.OFTString) do fielddefn - AG.setwidth!(fielddefn, 30) - AG.addfielddefn!(layer, fielddefn) - end - @test sprint(print, layer) == """ - Layer: hw2a - Geometry 0 (): [wkbPolygon] - Field 0 (name): [OFTString] - """ - for line in readlines(file) - (name, coords) = split(line, ":") - coordlist = split(coords, ",") - AG.createfeature(layer) do feature - AG.setfield!(feature, 0, name) - AG.createpolygon() do poly - ring = AG.createlinearring() - for xy in map(split, coordlist) - AG.addpoint!(ring, parse(Float64, xy[1]), - parse(Float64, xy[2])) + #reference: http://www.gis.usu.edu/~chrisg/python/2009/lectures/ospy_hw1b.py + # version 1 + AG.create(AG.getdriver("MEMORY")) do output + inlayer = AG.getlayer(input, 0) + outlayer = AG.createlayer( + name = "hw1b", + dataset = output, + geom = AG.wkbPoint, + ) + inlayerdefn = AG.layerdefn(inlayer) + AG.addfielddefn!(outlayer, AG.getfielddefn(inlayerdefn, 0)) + AG.addfielddefn!(outlayer, AG.getfielddefn(inlayerdefn, 1)) + for infeature in inlayer + id = AG.getfield(infeature, 0) + @test AG.asint64(infeature, 0) == id + cover = AG.getfield(infeature, 1) + if cover == "trees" + AG.createfeature(outlayer) do outfeature + AG.setgeom!(outfeature, AG.getgeom(infeature)) + AG.setfield!(outfeature, 0, id) + return AG.setfield!(outfeature, 1, cover) + end end - AG.addgeom!(poly, ring) - AG.setgeom!(feature, poly) - end end end - @test sprint(print, layer) == """ - Layer: hw2a - Geometry 0 (): [wkbPolygon], POLYGON ((-111.50278...), ... - Field 0 (name): [OFTString], Cache, Box Elder, Rich, Weber, Morgan, ... - """ - - # input = output - # # http://www.gis.usu.edu/~chrisg/python/2009/lectures/ospy_hw2b.py - # AG.fromEPSG(4269) do inspatialref - # AG.fromEPSG(26912) do outspatialref - # AG.createcoordtrans(inspatialref, outspatialref) do coordtrans - # AG.create("", "MEMORY") do output - # inlayer = AG.getlayer(input, 0) - # outlayer = AG.createlayer( - # name = "hw2b", - # dataset = output, - # geom = GDAL.wkbPolygon - # ) - # infeaturedefn = AG.layerdefn(inlayer) - # nameindex = AG.findfieldindex(infeaturedefn, "name") - # fielddefn = AG.getfielddefn(infeaturedefn, nameindex) - # AG.createfield!(outlayer, fielddefn) - # for infeature in inlayer - # AG.createfeature(outlayer) do outfeature - # geom = AG.getgeom(infeature) - # AG.setgeom!(outfeature, AG.transform!(geom, coordtrans)) - # AG.setfield!(outfeature,0,AG.getfield(infeature, nameindex)) - # println(outfeature) - # end end - # println(layer) - # end - # end - # println(AG.toWKT(AG.morphtoESRI!(outspatialref))) - # end - # end - end - end -end + end + @test sprint(print, output) == """ + GDAL Dataset (Driver: Memory/Memory) + File(s): -@testset "Homework 3" begin - #reference: http://www.gis.usu.edu/~chrisg/python/2009/lectures/ospy_hw3a.py - AG.read("ospy/data1/sites.shp") do sitesDS - AG.read("ospy/data3/cache_towns.shp") do townsDS - siteslayer = AG.getlayer(sitesDS, 0) - townslayer = AG.getlayer(townsDS, 0) - AG.setattributefilter!(townslayer, "NAME = 'Nibley'") - AG.getfeature(townslayer, 0) do nibleyFeature - AG.buffer(AG.getgeom(nibleyFeature), 1500) do bufferGeom - AG.setspatialfilter!(siteslayer, bufferGeom) - @test [AG.getfield(f, "ID") for f in siteslayer] == [26] - end end end end - - #reference: http://www.gis.usu.edu/~chrisg/python/2009/lectures/ospy_hw3b.py - # commented out until https://github.com/visr/GDAL.jl/issues/30 is resolved - # for inFN in readdir("./ospy/data3/") - # if endswith(inFN, ".shp") - # reproject("./ospy/data3/$(inFN)", 26912, 4269) - # end end -end + Number of feature layers: 1 + Layer 0: hw1b (wkbPoint) + """ + end -AG.read("ospy/data4/aster.img") do ds - #reference: http://www.gis.usu.edu/~chrisg/python/2009/lectures/ospy_hw4a.py - @testset "Homework 4a" begin - AG.read("ospy/data1/sites.shp") do shp - shplayer = AG.getlayer(shp, 0) - id = AG.findfieldindex(AG.layerdefn(shplayer), "ID") - - transform = AG.getgeotransform(ds) - xOrigin = transform[1]; yOrigin = transform[4] - pixelWidth = transform[2]; pixelHeight = transform[6] - - results = fill(0, 42, 3) - for (i,feature) in enumerate(shplayer) - geom = AG.getgeom(feature) - x = AG.getx(geom, 0); y = AG.gety(geom, 0) - # compute pixel offset - xOffset = round(Int, (x - xOrigin) / pixelWidth) - yOffset = round(Int, (y - yOrigin) / pixelHeight) - # create a string to print out - @test AG.getfield(feature, id) == i - for j in 1:AG.nraster(ds) - data = AG.read(ds, j, xOffset, yOffset, 1, 1) - results[i,j] = data[1,1] + # version 2 + AG.create(AG.getdriver("MEMORY")) do output + AG.executesql( + input, + """SELECT id, cover FROM sites + WHERE cover = 'trees' """, + ) do results + @test sprint(print, results) == """ + Layer: sites + Geometry 0 (): [wkbPoint], POINT (449959.840851...), ... + Field 0 (id): [OFTInteger], 2, 6, 9, 14, 19, 20, 22, 26, 34, 36, 41 + Field 1 (cover): [OFTString], trees, trees, trees, trees, trees, trees, ... + """ + return AG.copy(results, name = "hw1b", dataset = output) end + @test sprint(print, output) == """ + GDAL Dataset (Driver: Memory/Memory) + File(s): + + Number of feature layers: 1 + Layer 0: hw1b (wkbPoint) + """ end - @test maximum(results) == 100 - @test minimum(results) == 0 - @test mean(results) ≈ 64.98412698412699 - @test std(results) ≈ 22.327734905980627 end end - #reference: http://www.gis.usu.edu/~chrisg/python/2009/lectures/ospy_hw4b.py - @testset "Homework 4b" begin - @testset "version 1" begin - count = 0 - total = 0 - data = AG.read(ds, 1) - for (cols,rows) in AG.windows(AG.getband(ds, 1)) - window = data[cols, rows] - count = count + sum(window .> 0) - total = total + sum(window) + @testset "Homework 2" begin + # http://www.gis.usu.edu/~chrisg/python/2009/lectures/ospy_hw2a.py + open("ospy/data2/ut_counties.txt", "r") do file + AG.create(AG.getdriver("MEMORY")) do output + layer = AG.createlayer( + name = "hw2a", + dataset = output, + geom = AG.wkbPolygon, + ) + @test sprint(print, layer) == """ + Layer: hw2a + Geometry 0 (): [wkbPolygon] + """ + AG.createfielddefn("name", AG.OFTString) do fielddefn + AG.setwidth!(fielddefn, 30) + return AG.addfielddefn!(layer, fielddefn) + end + @test sprint(print, layer) == """ + Layer: hw2a + Geometry 0 (): [wkbPolygon] + Field 0 (name): [OFTString] + """ + for line in readlines(file) + (name, coords) = split(line, ":") + coordlist = split(coords, ",") + AG.createfeature(layer) do feature + AG.setfield!(feature, 0, name) + AG.createpolygon() do poly + ring = AG.createlinearring() + for xy in map(split, coordlist) + AG.addpoint!( + ring, + parse(Float64, xy[1]), + parse(Float64, xy[2]), + ) + end + AG.addgeom!(poly, ring) + return AG.setgeom!(feature, poly) + end + end + end + @test sprint(print, layer) == """ + Layer: hw2a + Geometry 0 (): [wkbPolygon], POLYGON ((-111.50278...), ... + Field 0 (name): [OFTString], Cache, Box Elder, Rich, Weber, Morgan, ... + """ + + # input = output + # # http://www.gis.usu.edu/~chrisg/python/2009/lectures/ospy_hw2b.py + # AG.fromEPSG(4269) do inspatialref + # AG.fromEPSG(26912) do outspatialref + # AG.createcoordtrans(inspatialref, outspatialref) do coordtrans + # AG.create("", "MEMORY") do output + # inlayer = AG.getlayer(input, 0) + # outlayer = AG.createlayer( + # name = "hw2b", + # dataset = output, + # geom = AG.wkbPolygon + # ) + # infeaturedefn = AG.layerdefn(inlayer) + # nameindex = AG.findfieldindex(infeaturedefn, "name") + # fielddefn = AG.getfielddefn(infeaturedefn, nameindex) + # AG.createfield!(outlayer, fielddefn) + # for infeature in inlayer + # AG.createfeature(outlayer) do outfeature + # geom = AG.getgeom(infeature) + # AG.setgeom!(outfeature, AG.transform!(geom, coordtrans)) + # AG.setfield!(outfeature,0,AG.getfield(infeature, nameindex)) + # println(outfeature) + # end end + # println(layer) + # end + # end + # println(AG.toWKT(AG.morphtoESRI!(outspatialref))) + # end + # end end - @test total / count ≈ 76.33891347095299 - @test total / (AG.height(ds) * AG.width(ds)) ≈ 47.55674749653172 end + end - @testset "version 2" begin - band = AG.getband(ds, 1) - count = 0 - total = 0 - buffer = Array{AG.pixeltype(band)}(undef, AG.blocksize(band)...) - for (cols,rows) in AG.windows(band) - AG.rasterio!(band, buffer, rows, cols) - data = buffer[1:length(cols),1:length(rows)] - count += sum(data .> 0) - total += sum(data) + @testset "Homework 3" begin + #reference: http://www.gis.usu.edu/~chrisg/python/2009/lectures/ospy_hw3a.py + AG.read("ospy/data1/sites.shp") do sitesDS + AG.read("ospy/data3/cache_towns.shp") do townsDS + siteslayer = AG.getlayer(sitesDS, 0) + townslayer = AG.getlayer(townsDS, 0) + AG.setattributefilter!(townslayer, "NAME = 'Nibley'") + AG.getfeature(townslayer, 0) do nibleyFeature + AG.buffer(AG.getgeom(nibleyFeature), 1500) do bufferGeom + AG.setspatialfilter!(siteslayer, bufferGeom) + @test [AG.getfield(f, "ID") for f in siteslayer] == [26] + end + end end - @test total / count ≈ 76.33891347095299 - @test total / (AG.height(ds) * AG.width(ds)) ≈ 47.55674749653172 end - @testset "version 3" begin - count = 0 - total = 0 - # BufferIterator uses a single buffer, so this loop cannot be parallelized - for data in AG.bufferwindows(AG.getband(ds, 1)) - count += sum(data .> 0) - total += sum(data) + #reference: http://www.gis.usu.edu/~chrisg/python/2009/lectures/ospy_hw3b.py + # commented out until https://github.com/visr/GDAL.jl/issues/30 is resolved + # for inFN in readdir("./ospy/data3/") + # if endswith(inFN, ".shp") + # reproject("./ospy/data3/$(inFN)", 26912, 4269) + # end end + end + + AG.read("ospy/data4/aster.img") do ds + #reference: http://www.gis.usu.edu/~chrisg/python/2009/lectures/ospy_hw4a.py + @testset "Homework 4a" begin + AG.read("ospy/data1/sites.shp") do shp + shplayer = AG.getlayer(shp, 0) + id = AG.findfieldindex(AG.layerdefn(shplayer), "ID") + + transform = AG.getgeotransform(ds) + xOrigin = transform[1] + yOrigin = transform[4] + pixelWidth = transform[2] + pixelHeight = transform[6] + + results = fill(0, 42, 3) + for (i, feature) in enumerate(shplayer) + geom = AG.getgeom(feature) + x = AG.getx(geom, 0) + y = AG.gety(geom, 0) + # compute pixel offset + xOffset = round(Int, (x - xOrigin) / pixelWidth) + yOffset = round(Int, (y - yOrigin) / pixelHeight) + # create a string to print out + @test AG.getfield(feature, id) == i + for j in 1:AG.nraster(ds) + data = AG.read(ds, j, xOffset, yOffset, 1, 1) + results[i, j] = data[1, 1] + end + end + @test maximum(results) == 100 + @test minimum(results) == 0 + @test mean(results) ≈ 64.98412698412699 + @test std(results) ≈ 22.327734905980627 end - @test total / count ≈ 76.33891347095299 - @test total / (AG.height(ds) * AG.width(ds)) ≈ 47.55674749653172 end - end - #reference: http://www.gis.usu.edu/~chrisg/python/2009/lectures/ospy_hw5a.py - @testset "Homework 5a" begin - @time begin - rows = AG.height(ds); cols = AG.width(ds); bands = AG.nraster(ds) + #reference: http://www.gis.usu.edu/~chrisg/python/2009/lectures/ospy_hw4b.py + @testset "Homework 4b" begin + @testset "version 1" begin + count = 0 + total = 0 + data = AG.read(ds, 1) + for (cols, rows) in AG.windows(AG.getband(ds, 1)) + window = data[cols, rows] + count = count + sum(window .> 0) + total = total + sum(window) + end + @test total / count ≈ 76.33891347095299 + @test total / (AG.height(ds) * AG.width(ds)) ≈ 47.55674749653172 + end + + @testset "version 2" begin + band = AG.getband(ds, 1) + count = 0 + total = 0 + buffer = Array{AG.pixeltype(band)}(undef, AG.blocksize(band)...) + for (cols, rows) in AG.windows(band) + AG.rasterio!(band, buffer, rows, cols) + data = buffer[1:length(cols), 1:length(rows)] + count += sum(data .> 0) + total += sum(data) + end + @test total / count ≈ 76.33891347095299 + @test total / (AG.height(ds) * AG.width(ds)) ≈ 47.55674749653172 + end - # get the band and block sizes - inband2 = AG.getband(ds, 2); inband3 = AG.getband(ds, 3) - (xbsize, ybsize) = AG.blocksize(inband2) + @testset "version 3" begin + count = 0 + total = 0 + # BufferIterator uses a single buffer, so this loop cannot be parallelized + for data in AG.bufferwindows(AG.getband(ds, 1)) + count += sum(data .> 0) + total += sum(data) + end + @test total / count ≈ 76.33891347095299 + @test total / (AG.height(ds) * AG.width(ds)) ≈ 47.55674749653172 + end + end - buffer2 = Array{Float32}(undef, ybsize, xbsize) - buffer3 = Array{Float32}(undef, ybsize, xbsize) - ndvi = Array{Float32}(undef, ybsize, xbsize) - AG.create( + #reference: http://www.gis.usu.edu/~chrisg/python/2009/lectures/ospy_hw5a.py + @testset "Homework 5a" begin + @time begin + rows = AG.height(ds) + cols = AG.width(ds) + bands = AG.nraster(ds) + + # get the band and block sizes + inband2 = AG.getband(ds, 2) + inband3 = AG.getband(ds, 3) + (xbsize, ybsize) = AG.blocksize(inband2) + + buffer2 = Array{Float32}(undef, ybsize, xbsize) + buffer3 = Array{Float32}(undef, ybsize, xbsize) + ndvi = Array{Float32}(undef, ybsize, xbsize) + AG.create( AG.getdriver("MEM"), - width = cols, - height = rows, - nbands = 1, - dtype = Float32 + width = cols, + height = rows, + nbands = 1, + dtype = Float32, ) do outDS - for ((i,j),(nrows,ncols)) in AG.blocks(inband2) - AG.rasterio!(inband2, buffer2, j, i, ncols, nrows) - AG.rasterio!(inband3, buffer3, j, i, ncols, nrows) - data2 = buffer2[1:nrows,1:ncols] - data3 = buffer3[1:nrows,1:ncols] - for row in 1:nrows, col in 1:ncols - denominator = data2[row, col] + data3[row, col] - if denominator > 0 - numerator = data3[row, col] - data2[row, col] - ndvi[row, col] = numerator / denominator - else - ndvi[row, col] = -99 + for ((i, j), (nrows, ncols)) in AG.blocks(inband2) + AG.rasterio!(inband2, buffer2, j, i, ncols, nrows) + AG.rasterio!(inband3, buffer3, j, i, ncols, nrows) + data2 = buffer2[1:nrows, 1:ncols] + data3 = buffer3[1:nrows, 1:ncols] + for row in 1:nrows, col in 1:ncols + denominator = data2[row, col] + data3[row, col] + if denominator > 0 + numerator = data3[row, col] - data2[row, col] + ndvi[row, col] = numerator / denominator + else + ndvi[row, col] = -99 + end end + # write the data + AG.write!(outDS, ndvi, 1, j, i, ncols, nrows) end - # write the data - AG.write!(outDS, ndvi, 1, j, i, ncols, nrows) + @test sprint(print, outDS) == """ + GDAL Dataset (Driver: MEM/In Memory Raster) + File(s): + + Dataset (width x height): 5665 x 5033 (pixels) + Number of raster bands: 1 + [GA_Update] Band 1 (Undefined): 5665 x 5033 (Float32) + """ + # flush data to disk, set the NoData value and calculate stats + outband = AG.getband(outDS, 1) + @test sprint(print, outband) == """ + [GA_Update] Band 1 (Undefined): 5665 x 5033 (Float32) + blocksize: 5665×1, nodata: nothing, units: 1.0px + 0.0 + overviews: """ + AG.setnodatavalue!(outband, -99) + # georeference the image and set the projection + AG.setgeotransform!(outDS, AG.getgeotransform(ds)) + return AG.setproj!(outDS, AG.getproj(ds)) + + # build pyramids + # gdal.SetConfigOption('HFA_USE_RRD', 'YES') + # AG.buildoverviews!(outDS, + # Cint[2,4,8,16,32,64,128], # overview list + # # bandlist (omit to include all bands) + # resampling="NEAREST") # resampling method end - @test sprint(print, outDS) == """ - GDAL Dataset (Driver: MEM/In Memory Raster) - File(s): + end + end + end - Dataset (width x height): 5665 x 5033 (pixels) - Number of raster bands: 1 - [GA_Update] Band 1 (Undefined): 5665 x 5033 (Float32) - """ - # flush data to disk, set the NoData value and calculate stats - outband = AG.getband(outDS, 1) - @test sprint(print, outband) == """ - [GA_Update] Band 1 (Undefined): 5665 x 5033 (Float32) - blocksize: 5665×1, nodata: nothing, units: 1.0px + 0.0 - overviews: """ - AG.setnodatavalue!(outband, -99) - # georeference the image and set the projection - AG.setgeotransform!(outDS, AG.getgeotransform(ds)) - AG.setproj!(outDS, AG.getproj(ds)) - - # build pyramids - # gdal.SetConfigOption('HFA_USE_RRD', 'YES') - # AG.buildoverviews!(outDS, - # Cint[2,4,8,16,32,64,128], # overview list - # # bandlist (omit to include all bands) - # resampling="NEAREST") # resampling method -end end end end - -#reference: http://www.gis.usu.edu/~chrisg/python/2009/lectures/ospy_hw5b.py -@testset "Homework 5b" begin - AG.read("ospy/data5/doq1.img") do ds1 - AG.read("ospy/data5/doq2.img") do ds2 - # read in doq1 and get info about it - band1 = AG.getband(ds1, 1) - rows1 = AG.height(ds1); cols1 = AG.width(ds1) - - # get the corner coordinates for doq1 - transform1 = AG.getgeotransform(ds1) - minX1 = transform1[1]; maxY1 = transform1[4] - pixelWidth1 = transform1[2]; pixelHeight1 = transform1[6] - maxX1 = minX1 + (cols1 * pixelWidth1) - minY1 = maxY1 + (rows1 * pixelHeight1) - - # read in doq2 and get info about it - band2 = AG.getband(ds2, 1) - rows2 = AG.height(ds2); cols2 = AG.width(ds2) - - # get the corner coordinates for doq1 - transform2 = AG.getgeotransform(ds2) - minX2 = transform1[1]; maxY2 = transform1[4] - pixelWidth2 = transform1[2]; pixelHeight2 = transform1[6] - maxX2 = minX2 + (cols2 * pixelWidth2) - minY2 = maxY2 + (rows2 * pixelHeight2) - - # get the corner coordinates for the output - minX = min(minX1, minX2); maxX = max(maxX1, maxX2) - minY = min(minY1, minY2); maxY = max(maxY1, maxY2) - - # get the number of rows and columns for the output - cols = round(Int, (maxX - minX) / pixelWidth1) - rows = round(Int, (maxY - minY) / abs(pixelHeight1)) - - # compute the origin (upper left) offset for doq1 - xOffset1 = round(Int, (minX1 - minX) / pixelWidth1) - yOffset1 = round(Int, (maxY1 - maxY) / pixelHeight1) - - # compute the origin (upper left) offset for doq2 - xOffset2 = round(Int, (minX2 - minX) / pixelWidth1) - yOffset2 = round(Int, (maxY2 - maxY) / pixelHeight1) - - dtype = AG.pixeltype(band1) - data1 = Array{dtype}(undef, rows, cols) - data2 = Array{dtype}(undef, rows, cols) - # create the output image - AG.create( + #reference: http://www.gis.usu.edu/~chrisg/python/2009/lectures/ospy_hw5b.py + @testset "Homework 5b" begin + AG.read("ospy/data5/doq1.img") do ds1 + AG.read("ospy/data5/doq2.img") do ds2 + # read in doq1 and get info about it + band1 = AG.getband(ds1, 1) + rows1 = AG.height(ds1) + cols1 = AG.width(ds1) + + # get the corner coordinates for doq1 + transform1 = AG.getgeotransform(ds1) + minX1 = transform1[1] + maxY1 = transform1[4] + pixelWidth1 = transform1[2] + pixelHeight1 = transform1[6] + maxX1 = minX1 + (cols1 * pixelWidth1) + minY1 = maxY1 + (rows1 * pixelHeight1) + + # read in doq2 and get info about it + band2 = AG.getband(ds2, 1) + rows2 = AG.height(ds2) + cols2 = AG.width(ds2) + + # get the corner coordinates for doq1 + transform2 = AG.getgeotransform(ds2) + minX2 = transform1[1] + maxY2 = transform1[4] + pixelWidth2 = transform1[2] + pixelHeight2 = transform1[6] + maxX2 = minX2 + (cols2 * pixelWidth2) + minY2 = maxY2 + (rows2 * pixelHeight2) + + # get the corner coordinates for the output + minX = min(minX1, minX2) + maxX = max(maxX1, maxX2) + minY = min(minY1, minY2) + maxY = max(maxY1, maxY2) + + # get the number of rows and columns for the output + cols = round(Int, (maxX - minX) / pixelWidth1) + rows = round(Int, (maxY - minY) / abs(pixelHeight1)) + + # compute the origin (upper left) offset for doq1 + xOffset1 = round(Int, (minX1 - minX) / pixelWidth1) + yOffset1 = round(Int, (maxY1 - maxY) / pixelHeight1) + + # compute the origin (upper left) offset for doq2 + xOffset2 = round(Int, (minX2 - minX) / pixelWidth1) + yOffset2 = round(Int, (maxY2 - maxY) / pixelHeight1) + + dtype = AG.pixeltype(band1) + data1 = Array{dtype}(undef, rows, cols) + data2 = Array{dtype}(undef, rows, cols) + # create the output image + AG.create( AG.getdriver("MEM"), - width = cols, - height = rows, - nbands = 1, - dtype = AG.pixeltype(band1) + width = cols, + height = rows, + nbands = 1, + dtype = AG.pixeltype(band1), ) do dsout - # read in doq1 and write it to the output - AG.rasterio!(band1, data1, 0, 0, cols1, rows1) - AG.write!(dsout, data1, 1, xOffset1, yOffset1, cols, rows) - - # read in doq2 and write it to the output - AG.rasterio!(band2, data2, 0, 0, cols2, rows2) - AG.write!(dsout, data2, 1, xOffset2, yOffset2, cols, rows) - - @test sprint(print, dsout) == """ - GDAL Dataset (Driver: MEM/In Memory Raster) - File(s): - - Dataset (width x height): 4500 x 3000 (pixels) - Number of raster bands: 1 - [GA_Update] Band 1 (Undefined): 4500 x 3000 (UInt8) - """ - # compute statistics for the output - bandout = AG.getband(dsout, 1) - @test sprint(print, bandout) == """ - [GA_Update] Band 1 (Undefined): 4500 x 3000 (UInt8) - blocksize: 4500×1, nodata: nothing, units: 1.0px + 0.0 - overviews: """ - - # set the geotransform and projection on the output - geotransform = [minX, pixelWidth1, 0, maxY, 0, pixelHeight1] - AG.setgeotransform!(dsout, geotransform) - AG.setproj!(dsout, AG.getproj(ds1)) - - # build pyramids for the output - # gdal.SetConfigOption('HFA_USE_RRD', 'YES') - # buildoverviews not supported for in-memory rasters - # AG.buildoverviews!(dsout, - # Cint[2,4,8,16], # overview list - # # bandlist (omit to include all bands) - # resampling="NEAREST") # resampling method -end end end end + # read in doq1 and write it to the output + AG.rasterio!(band1, data1, 0, 0, cols1, rows1) + AG.write!(dsout, data1, 1, xOffset1, yOffset1, cols, rows) + + # read in doq2 and write it to the output + AG.rasterio!(band2, data2, 0, 0, cols2, rows2) + AG.write!(dsout, data2, 1, xOffset2, yOffset2, cols, rows) + + @test sprint(print, dsout) == """ + GDAL Dataset (Driver: MEM/In Memory Raster) + File(s): + + Dataset (width x height): 4500 x 3000 (pixels) + Number of raster bands: 1 + [GA_Update] Band 1 (Undefined): 4500 x 3000 (UInt8) + """ + # compute statistics for the output + bandout = AG.getband(dsout, 1) + @test sprint(print, bandout) == """ + [GA_Update] Band 1 (Undefined): 4500 x 3000 (UInt8) + blocksize: 4500×1, nodata: nothing, units: 1.0px + 0.0 + overviews: """ + + # set the geotransform and projection on the output + geotransform = [minX, pixelWidth1, 0, maxY, 0, pixelHeight1] + AG.setgeotransform!(dsout, geotransform) + return AG.setproj!(dsout, AG.getproj(ds1)) + + # build pyramids for the output + # gdal.SetConfigOption('HFA_USE_RRD', 'YES') + # buildoverviews not supported for in-memory rasters + # AG.buildoverviews!(dsout, + # Cint[2,4,8,16], # overview list + # # bandlist (omit to include all bands) + # resampling="NEAREST") # resampling method + end + end + end + end +end diff --git a/test/test_rasterattrtable.jl b/test/test_rasterattrtable.jl index 50b580c9..38629791 100644 --- a/test/test_rasterattrtable.jl +++ b/test/test_rasterattrtable.jl @@ -1,125 +1,122 @@ using Test import GDAL -import ArchGDAL; const AG = ArchGDAL +import ArchGDAL; +const AG = ArchGDAL; -@testset "Testing Raster Attribute Tables" begin - AG.createRAT() do rat - @test AG.ncolumn(rat) == 0 - @test AG.nrow(rat) == 0 - @test AG.changesarewrittentofile(rat) == false - - AG.createcolumn!(rat, "col1", GDAL.GFT_Integer, GDAL.GFU_Generic) - @test AG.ncolumn(rat) == 1 - @test AG.columnname(rat, 0) == "col1" - @test AG.columnusage(rat, 0) == GDAL.GFU_Generic - @test AG.columntype(rat, 0) == GDAL.GFT_Integer - @test AG.findcolumnindex(rat, GDAL.GFU_Generic) == 0 - @test AG.findcolumnindex(rat, GDAL.GFU_Red) == -1 - - AG.createcolumn!(rat, "col2", GDAL.GFT_Real, GDAL.GFU_MinMax) - AG.createcolumn!(rat, "col3", GDAL.GFT_String, GDAL.GFU_PixelCount) - @test AG.ncolumn(rat) == 3 - @test AG.nrow(rat) == 0 - AG.setrowcount!(rat, 5) - @test AG.nrow(rat) == 5 +@testset "test_rasterattrtable.jl" begin + @testset "Testing Raster Attribute Tables" begin + AG.createRAT() do rat + @test AG.ncolumn(rat) == 0 + @test AG.nrow(rat) == 0 + @test AG.changesarewrittentofile(rat) == false - @test AG.attributeio!(rat, GDAL.GF_Read, 0, 0, 5, Array{Cint}(undef, 5)) == - fill(0,5) - @test AG.attributeio!(rat, GDAL.GF_Read, 0, 0, 5, Array{Float64}(undef, 5)) == - fill(0,5) - @test AG.attributeio!(rat, GDAL.GF_Read, 1, 0, 5, Array{Float64}(undef, 5)) == - fill(0,5) + AG.createcolumn!(rat, "col1", AG.GFT_Integer, AG.GFU_Generic) + @test AG.ncolumn(rat) == 1 + @test AG.columnname(rat, 0) == "col1" + @test AG.columnusage(rat, 0) == AG.GFU_Generic + @test AG.columntype(rat, 0) == AG.GFT_Integer + @test AG.findcolumnindex(rat, AG.GFU_Generic) == 0 + @test AG.findcolumnindex(rat, AG.GFU_Red) == -1 - @test AG.asstring(rat, 2, 0) == "0" - @test AG.asint(rat, 2, 0) == 0 - @test AG.asdouble(rat, 2, 0) == 0 - - AG.setvalue!(rat, 2, 0, "2") - @test AG.asstring(rat, 2, 0) == "2" - @test AG.asint(rat, 2, 0) == 2 - @test AG.asdouble(rat, 2, 0) == 2 + AG.createcolumn!(rat, "col2", AG.GFT_Real, AG.GFU_MinMax) + AG.createcolumn!(rat, "col3", AG.GFT_String, AG.GFU_PixelCount) + @test AG.ncolumn(rat) == 3 + @test AG.nrow(rat) == 0 + AG.setrowcount!(rat, 5) + @test AG.nrow(rat) == 5 - AG.setvalue!(rat, 2, 0, 3) - @test AG.asstring(rat, 2, 0) == "3" - @test AG.asint(rat, 2, 0) == 3 - @test AG.asdouble(rat, 2, 0) == 3 - - AG.setvalue!(rat, 2, 0, 4.5) - @test AG.asstring(rat, 2, 0) == "4" - @test AG.asint(rat, 2, 0) == 4 - @test AG.asdouble(rat, 2, 0) == 4 + @test AG.attributeio!( + rat, + AG.GF_Read, + 0, + 0, + 5, + Array{Cint}(undef, 5), + ) == fill(0, 5) + @test AG.attributeio!( + rat, + AG.GF_Read, + 0, + 0, + 5, + Array{Float64}(undef, 5), + ) == fill(0, 5) + @test AG.attributeio!( + rat, + AG.GF_Read, + 1, + 0, + 5, + Array{Float64}(undef, 5), + ) == fill(0, 5) - @test AG.asstring(rat, 2, 1) == "0" - @test AG.asstring(rat, 2, 2) == "" + @test AG.asstring(rat, 2, 0) == "0" + @test AG.asint(rat, 2, 0) == 0 + @test AG.asdouble(rat, 2, 0) == 0 - @test AG.asstring(rat, 0, 2) == "" - @test AG.asstring(rat, 2, 2) == "" - @test AG.asstring(rat, 4, 2) == "" - AG.attributeio!(rat, GDAL.GF_Write, 2, 0, 5, fill("abc",5)) - @test AG.asstring(rat, 0, 2) == "abc" - @test AG.asstring(rat, 2, 2) == "abc" - @test AG.asstring(rat, 4, 2) == "abc" - - AG.clone(rat) do ratclone - @test AG.asstring(ratclone, 0, 2) == "abc" - @test AG.asstring(ratclone, 2, 2) == "abc" - @test AG.asstring(ratclone, 4, 2) == "abc" - @test AG.ncolumn(ratclone) == 3 - @test AG.nrow(ratclone) == 5 - @test AG.findcolumnindex(ratclone, GDAL.GFU_Generic) == 0 - @test AG.findcolumnindex(ratclone, GDAL.GFU_Red) == -1 - end + AG.setvalue!(rat, 2, 0, "2") + @test AG.asstring(rat, 2, 0) == "2" + @test AG.asint(rat, 2, 0) == 2 + @test AG.asdouble(rat, 2, 0) == 2 - AG.setlinearbinning!(rat, 0, 10) - @test AG.getlinearbinning(rat) == (0,10) - AG.setlinearbinning!(rat, -1.5, 12.0) - @test AG.getlinearbinning(rat) == (-1.5,12.0) - - @test AG.findrowindex(rat, 0) == 0 - @test AG.findrowindex(rat, -1) == 0 - @test AG.findrowindex(rat, -1.5) == 0 - @test AG.findrowindex(rat, 7.5) == 0 - @test AG.findrowindex(rat, 12) == 1 - @test AG.findrowindex(rat, 13) == 1 - end -end + AG.setvalue!(rat, 2, 0, 3) + @test AG.asstring(rat, 2, 0) == "3" + @test AG.asint(rat, 2, 0) == 3 + @test AG.asdouble(rat, 2, 0) == 3 + + AG.setvalue!(rat, 2, 0, 4.5) + @test AG.asstring(rat, 2, 0) == "4" + @test AG.asint(rat, 2, 0) == 4 + @test AG.asdouble(rat, 2, 0) == 4 + + @test AG.asstring(rat, 2, 1) == "0" + @test AG.asstring(rat, 2, 2) == "" + + @test AG.asstring(rat, 0, 2) == "" + @test AG.asstring(rat, 2, 2) == "" + @test AG.asstring(rat, 4, 2) == "" + AG.attributeio!(rat, AG.GF_Write, 2, 0, 5, fill("abc", 5)) + @test AG.asstring(rat, 0, 2) == "abc" + @test AG.asstring(rat, 2, 2) == "abc" + @test AG.asstring(rat, 4, 2) == "abc" -@testset ("Testing Color Tables") begin - AG.createcolortable(GDAL.GPI_RGB) do ct - @test AG.paletteinterp(ct) == GDAL.GPI_RGB - @test AG.ncolorentry(ct) == 0 - AG.createcolorramp!(ct, 128, GDAL.GDALColorEntry(0,0,0,0), - 255, GDAL.GDALColorEntry(0,0,255,0)) - @test AG.ncolorentry(ct) == 256 - @test sprint(print, AG.getcolorentry(ct, 0)) == "GDAL.GDALColorEntry(0, 0, 0, 0)" - @test sprint(print, AG.getcolorentry(ct, 128)) == "GDAL.GDALColorEntry(0, 0, 0, 0)" - @test sprint(print, AG.getcolorentry(ct, 200)) == "GDAL.GDALColorEntry(0, 0, 144, 0)" - @test sprint(print, AG.getcolorentry(ct, 255)) == "GDAL.GDALColorEntry(0, 0, 255, 0)" + AG.clone(rat) do ratclone + @test AG.asstring(ratclone, 0, 2) == "abc" + @test AG.asstring(ratclone, 2, 2) == "abc" + @test AG.asstring(ratclone, 4, 2) == "abc" + @test AG.ncolumn(ratclone) == 3 + @test AG.nrow(ratclone) == 5 + @test AG.findcolumnindex(ratclone, AG.GFU_Generic) == 0 + @test AG.findcolumnindex(ratclone, AG.GFU_Red) == -1 + end + + AG.setlinearbinning!(rat, 0, 10) + @test AG.getlinearbinning(rat) == (0, 10) + AG.setlinearbinning!(rat, -1.5, 12.0) + @test AG.getlinearbinning(rat) == (-1.5, 12.0) - @test sprint(print, AG.getcolorentryasrgb(ct, 0)) == "GDAL.GDALColorEntry(0, 0, 0, 0)" - @test sprint(print, AG.getcolorentryasrgb(ct, 128)) == "GDAL.GDALColorEntry(0, 0, 0, 0)" - @test sprint(print, AG.getcolorentryasrgb(ct, 200)) == "GDAL.GDALColorEntry(0, 0, 144, 0)" - @test sprint(print, AG.getcolorentryasrgb(ct, 255)) == "GDAL.GDALColorEntry(0, 0, 255, 0)" - - AG.setcolorentry!(ct, 255, GDAL.GDALColorEntry(0,0,100,0)) - @test sprint(print, AG.getcolorentry(ct, 255)) == "GDAL.GDALColorEntry(0, 0, 100, 0)" + @test AG.findrowindex(rat, 0) == 0 + @test AG.findrowindex(rat, -1) == 0 + @test AG.findrowindex(rat, -1.5) == 0 + @test AG.findrowindex(rat, 7.5) == 0 + @test AG.findrowindex(rat, 12) == 1 + @test AG.findrowindex(rat, 13) == 1 + end + end - AG.clone(ct) do ctclone - @test AG.paletteinterp(ctclone) == GDAL.GPI_RGB - @test AG.ncolorentry(ctclone) == 256 - @test sprint(print, AG.getcolorentry(ctclone, 0)) == "GDAL.GDALColorEntry(0, 0, 0, 0)" - @test sprint(print, AG.getcolorentry(ctclone, 128)) == "GDAL.GDALColorEntry(0, 0, 0, 0)" - @test sprint(print, AG.getcolorentry(ctclone, 200)) == "GDAL.GDALColorEntry(0, 0, 144, 0)" - @test sprint(print, AG.getcolorentry(ctclone, 255)) == "GDAL.GDALColorEntry(0, 0, 100, 0)" - - AG.createRAT(ctclone) do rat - ct2 = AG.toColorTable(rat) - @test AG.paletteinterp(ct2) == GDAL.GPI_RGB - @test AG.ncolorentry(ct2) == 256 - @test sprint(print, AG.getcolorentry(ct2, 0)) == "GDAL.GDALColorEntry(0, 0, 0, 0)" - @test sprint(print, AG.getcolorentry(ct2, 128)) == "GDAL.GDALColorEntry(0, 0, 0, 0)" - @test sprint(print, AG.getcolorentry(ct2, 200)) == "GDAL.GDALColorEntry(0, 0, 144, 0)" - @test sprint(print, AG.getcolorentry(ct2, 255)) == "GDAL.GDALColorEntry(0, 0, 100, 0)" + @testset ("Testing Color Tables") begin + AG.createcolortable(AG.GPI_RGB) do ct + @test sprint(print, ct) == "ColorTable[GPI_RGB]" + @test AG.paletteinterp(ct) == AG.GPI_RGB + AG.clone(ct) do ct1 + @test sprint(print, ct1) == "ColorTable[GPI_RGB]" + end + AG.clone(AG.ColorTable(C_NULL)) do ct2 + @test sprint(print, ct2) == "NULL ColorTable" + end + AG.createRAT(ct) do rat + @test sprint(print, AG.toColorTable(rat, 0)) == + "ColorTable[GPI_RGB]" end end end diff --git a/test/test_rasterband.jl b/test/test_rasterband.jl index e10a15b9..059ba621 100644 --- a/test/test_rasterband.jl +++ b/test/test_rasterband.jl @@ -1,136 +1,146 @@ using Test import GDAL -import ArchGDAL; const AG = ArchGDAL +import ArchGDAL; +const AG = ArchGDAL; -@testset "Test methods for rasterband" begin - AG.read("data/utmsmall.tif") do dataset - ds_result = """ - GDAL Dataset (Driver: GTiff/GeoTIFF) - File(s): - data/utmsmall.tif +@testset "test_rasterband.jl" begin + @testset "Test methods for rasterband" begin + AG.read("data/utmsmall.tif") do dataset + ds_result = """ + GDAL Dataset (Driver: GTiff/GeoTIFF) + File(s): + data/utmsmall.tif - Dataset (width x height): 100 x 100 (pixels) - Number of raster bands: 1 - [GA_ReadOnly] Band 1 (Gray): 100 x 100 (UInt8) - """ - @test sprint(print, dataset) == ds_result - rb = AG.getband(dataset, 1) - sprint(print, rb) == """ - [GA_ReadOnly] Band 1 (Gray): 100 x 100 (UInt8) - blocksize: 100×81, nodata: -1.0e10, units: 1.0px + 0.0 - overviews: """ - @test sprint(print, AG.getdataset(rb)) == ds_result + Dataset (width x height): 100 x 100 (pixels) + Number of raster bands: 1 + [GA_ReadOnly] Band 1 (Gray): 100 x 100 (UInt8) + """ + @test sprint(print, dataset) == ds_result + rb = AG.getband(dataset, 1) + sprint(print, rb) == """ + [GA_ReadOnly] Band 1 (Gray): 100 x 100 (UInt8) + blocksize: 100×81, nodata: -1.0e10, units: 1.0px + 0.0 + overviews: """ + @test sprint(print, AG.getdataset(rb)) == ds_result - @test AG.getunittype(rb) == "" - AG.setunittype!(rb,"ft") - @test AG.getunittype(rb) == "ft" - AG.setunittype!(rb,"") - @test AG.getunittype(rb) == "" + @test AG.getunittype(rb) == "" + AG.setunittype!(rb, "ft") + @test AG.getunittype(rb) == "ft" + AG.setunittype!(rb, "") + @test AG.getunittype(rb) == "" - @test AG.getoffset(rb) == 0 - AG.setoffset!(rb, 10) - @test AG.getoffset(rb) ≈ 10 - AG.setoffset!(rb, 0) - @test AG.getoffset(rb) ≈ 0 + @test AG.getoffset(rb) == 0 + AG.setoffset!(rb, 10) + @test AG.getoffset(rb) ≈ 10 + AG.setoffset!(rb, 0) + @test AG.getoffset(rb) ≈ 0 - @test AG.getscale(rb) == 1 - AG.setscale!(rb, 0.5) - @test AG.getscale(rb) ≈ 0.5 - AG.setscale!(rb, 2) - @test AG.getscale(rb) ≈ 2 - AG.setscale!(rb, 1) - @test AG.getscale(rb) ≈ 1 + @test AG.getscale(rb) == 1 + AG.setscale!(rb, 0.5) + @test AG.getscale(rb) ≈ 0.5 + AG.setscale!(rb, 2) + @test AG.getscale(rb) ≈ 2 + AG.setscale!(rb, 1) + @test AG.getscale(rb) ≈ 1 - @test AG.getnodatavalue(rb) === nothing - AG.setnodatavalue!(rb, -100) - @test AG.getnodatavalue(rb) ≈ -100 - AG.deletenodatavalue!(rb) - @test AG.getnodatavalue(rb) === nothing + @test isnothing(AG.getnodatavalue(rb)) + AG.setnodatavalue!(rb, -100) + @test AG.getnodatavalue(rb) ≈ -100 + AG.deletenodatavalue!(rb) + @test isnothing(AG.getnodatavalue(rb)) - AG.copy(dataset) do dest - destband = AG.getband(dest, 1) - AG.copywholeraster!(rb, destband) - @test sprint(print, destband) == """ - [GA_Update] Band 1 (Gray): 100 x 100 (UInt8) - blocksize: 100×81, nodata: nothing, units: 1.0px + 0.0 - overviews: """ - @test AG.noverview(destband) == 0 - AG.buildoverviews!(dest, Cint[2, 4, 8]) - @test AG.noverview(destband) == 3 - @test sprint(print, destband) == """ - [GA_Update] Band 1 (Gray): 100 x 100 (UInt8) - blocksize: 100×81, nodata: nothing, units: 1.0px + 0.0 - overviews: (0) 50x50 (1) 25x25 (2) 13x13 - """ - @test AG.getcolorinterp(destband) == GDAL.GCI_GrayIndex - AG.setcolorinterp!(destband, GDAL.GCI_RedBand) - @test AG.getcolorinterp(destband) == GDAL.GCI_RedBand + AG.copy(dataset) do dest + destband = AG.getband(dest, 1) + AG.copywholeraster!(rb, destband) + @test sprint(print, destband) == """ + [GA_Update] Band 1 (Gray): 100 x 100 (UInt8) + blocksize: 100×81, nodata: nothing, units: 1.0px + 0.0 + overviews: """ + @test AG.noverview(destband) == 0 + AG.buildoverviews!(dest, Cint[2, 4, 8]) + @test AG.noverview(destband) == 3 + @test sprint(print, destband) == """ + [GA_Update] Band 1 (Gray): 100 x 100 (UInt8) + blocksize: 100×81, nodata: nothing, units: 1.0px + 0.0 + overviews: (0) 50x50 (1) 25x25 (2) 13x13 + """ + @test AG.getcolorinterp(destband) == AG.GCI_GrayIndex + AG.setcolorinterp!(destband, AG.GCI_RedBand) + @test AG.getcolorinterp(destband) == AG.GCI_RedBand - @test sprint(print, AG.sampleoverview(destband, 100)) == """ - [GA_Update] Band 1 (Gray): 13 x 13 (UInt8) - blocksize: 128×128, nodata: nothing, units: 1.0px + 0.0 - overviews: """ - @test sprint(print, AG.sampleoverview(destband, 200)) == """ - [GA_Update] Band 1 (Gray): 25 x 25 (UInt8) - blocksize: 128×128, nodata: nothing, units: 1.0px + 0.0 - overviews: """ - @test sprint(print, AG.sampleoverview(destband, 500)) == """ - [GA_Update] Band 1 (Gray): 25 x 25 (UInt8) - blocksize: 128×128, nodata: nothing, units: 1.0px + 0.0 - overviews: """ - AG.sampleoverview(destband, 1000) do result - @test sprint(print, result) == """ - [GA_Update] Band 1 (Gray): 50 x 50 (UInt8) + @test sprint(print, AG.sampleoverview(destband, 100)) == """ + [GA_Update] Band 1 (Gray): 13 x 13 (UInt8) blocksize: 128×128, nodata: nothing, units: 1.0px + 0.0 overviews: """ - end - @test sprint(print, AG.getmaskband(destband)) == """ - [GA_ReadOnly] Band 0 (Undefined): 100 x 100 (UInt8) - blocksize: 100×81, nodata: nothing, units: 1.0px + 0.0 - overviews: """ - @test AG.maskflags(destband) == 1 - @test AG.maskflaginfo(rb) == (all_valid = true, per_dataset = false, alpha = false, nodata = false) - AG.createmaskband!(destband, 3) - AG.getmaskband(destband) do maskband - @test sprint(print, maskband) == """ - [GA_Update] Band 1 (Gray): 100 x 100 (UInt8) + @test sprint(print, AG.sampleoverview(destband, 200)) == """ + [GA_Update] Band 1 (Gray): 25 x 25 (UInt8) + blocksize: 128×128, nodata: nothing, units: 1.0px + 0.0 + overviews: """ + @test sprint(print, AG.sampleoverview(destband, 500)) == """ + [GA_Update] Band 1 (Gray): 25 x 25 (UInt8) + blocksize: 128×128, nodata: nothing, units: 1.0px + 0.0 + overviews: """ + AG.sampleoverview(destband, 1000) do result + @test sprint(print, result) == """ + [GA_Update] Band 1 (Gray): 50 x 50 (UInt8) + blocksize: 128×128, nodata: nothing, units: 1.0px + 0.0 + overviews: """ + end + @test sprint(print, AG.getmaskband(destband)) == """ + [GA_ReadOnly] Band 0 (Undefined): 100 x 100 (UInt8) blocksize: 100×81, nodata: nothing, units: 1.0px + 0.0 overviews: """ - end - @test AG.maskflags(destband) == 3 - @test AG.maskflaginfo(destband) == (all_valid = true, per_dataset = true, alpha = false, nodata = false) - AG.fillraster!(destband, 3) - AG.setcategorynames!(destband, ["foo","bar"]) - @test AG.getcategorynames(destband) == ["foo", "bar"] + @test AG.maskflags(destband) == 1 + @test AG.maskflaginfo(rb) == ( + all_valid = true, + per_dataset = false, + alpha = false, + nodata = false, + ) + AG.createmaskband!(destband, 3) + AG.getmaskband(destband) do maskband + @test sprint(print, maskband) == """ + [GA_Update] Band 1 (Gray): 100 x 100 (UInt8) + blocksize: 100×81, nodata: nothing, units: 1.0px + 0.0 + overviews: """ + end + @test AG.maskflags(destband) == 3 + @test AG.maskflaginfo(destband) == ( + all_valid = true, + per_dataset = true, + alpha = false, + nodata = false, + ) + AG.fillraster!(destband, 3) + AG.setcategorynames!(destband, ["foo", "bar"]) + @test AG.getcategorynames(destband) == ["foo", "bar"] - AG.getoverview(destband, 0) do overview - AG.regenerateoverviews!(destband, [ - overview, - AG.getoverview(destband, 2) - ]) - end + AG.getoverview(destband, 0) do overview + return AG.regenerateoverviews!( + destband, + [overview, AG.getoverview(destband, 2)], + ) + end - AG.createcolortable(GDAL.GPI_RGB) do ct - AG.createcolorramp!(ct, - 128, GDAL.GDALColorEntry(0,0,0,0), - 255, GDAL.GDALColorEntry(0,0,255,0) - ) - AG.setcolortable!(destband, ct) - @test AG.ncolorentry(ct) == 256 - AG.getcolortable(destband) do ct2 - @test AG.ncolorentry(ct) == AG.ncolorentry(ct2) + AG.createRAT() do rat + AG.setdefaultRAT!(destband, rat) + @test AG.getdefaultRAT(destband).ptr != rat.ptr + end + @test AG.getdefaultRAT(destband).ptr != + GDAL.GDALRasterAttributeTableH(C_NULL) + + AG.getcolortable(destband) do ct + @test ct.ptr == GDAL.GDALColorTableH(C_NULL) + end + AG.createcolortable(AG.GPI_RGB) do ct + @test ct.ptr != GDAL.GDALColorTableH(C_NULL) + return AG.setcolortable!(destband, ct) end AG.clearcolortable!(destband) - - AG.createRAT(ct) do rat - AG.setdefaultRAT!(destband, rat) - @test AG.getdefaultRAT(destband) != C_NULL + AG.getcolortable(destband) do ct + @test ct.ptr == GDAL.GDALColorTableH(C_NULL) end end end end end - -# untested -# setcategorynames!(rasterband, names) -# getcolortable(band) do C_NULL diff --git a/test/test_rasterio.jl b/test/test_rasterio.jl index 6be9fc4b..bb37196f 100644 --- a/test/test_rasterio.jl +++ b/test/test_rasterio.jl @@ -1,205 +1,277 @@ using Test -import GDAL -import ArchGDAL; const AG = ArchGDAL - -AG.read("ospy/data4/aster.img") do ds - @testset "version 1" begin - band = AG.getband(ds, 1) - count = 0 - total = 0 - buffer = Array{AG.pixeltype(band)}(undef, AG.blocksize(band)..., 1) - for (cols,rows) in AG.windows(band) - AG.rasterio!(ds, buffer, [1], rows .- 1, cols .- 1) - data = buffer[1:length(cols),1:length(rows)] - count += sum(data .> 0) - total += sum(data) +import ArchGDAL; +const AG = ArchGDAL; + +@testset "test_rasterio.jl" begin + AG.read("ospy/data4/aster.img") do ds + @testset "version 1" begin + band = AG.getband(ds, 1) + count = 0 + total = 0 + buffer = Array{AG.pixeltype(band)}(undef, AG.blocksize(band)..., 1) + for (cols, rows) in AG.windows(band) + AG.rasterio!(ds, buffer, [1], rows, cols) + data = buffer[1:length(cols), 1:length(rows)] + count += sum(data .> 0) + total += sum(data) + end + @test total / count ≈ 76.33891347095299 + @test total / (AG.height(ds) * AG.width(ds)) ≈ 47.55674749653172 end - @test total / count ≈ 76.33891347095299 - @test total / (AG.height(ds) * AG.width(ds)) ≈ 47.55674749653172 - end - @testset "version 2" begin - band = AG.getband(ds, 1) - count = 0 - total = 0 - buffer = Array{AG.pixeltype(band)}(undef, AG.blocksize(band)..., 1) - for (cols,rows) in AG.windows(band) - AG.read!(ds, buffer, [1], rows .- 1, cols .- 1) - data = buffer[1:length(cols),1:length(rows)] - count += sum(data .> 0) - total += sum(data) + @testset "version 2" begin + band = AG.getband(ds, 1) + count = 0 + total = 0 + buffer = Array{AG.pixeltype(band)}(undef, AG.blocksize(band)..., 1) + for (cols, rows) in AG.windows(band) + AG.read!(ds, buffer, [1], rows, cols) + data = buffer[1:length(cols), 1:length(rows)] + count += sum(data .> 0) + total += sum(data) + end + @test total / count ≈ 76.33891347095299 + @test total / (AG.height(ds) * AG.width(ds)) ≈ 47.55674749653172 end - @test total / count ≈ 76.33891347095299 - @test total / (AG.height(ds) * AG.width(ds)) ≈ 47.55674749653172 - end - @testset "version 3" begin - band = AG.getband(ds, 1) - count = 0 - total = 0 - buffer = Array{AG.pixeltype(band)}(undef, AG.blocksize(band)...) - for (cols,rows) in AG.windows(band) - AG.read!(ds, buffer, 1, rows, cols) - data = buffer[1:length(cols),1:length(rows)] - count += sum(data .> 0) - total += sum(data) + @testset "version 3" begin + band = AG.getband(ds, 1) + count = 0 + total = 0 + buffer = Matrix{AG.pixeltype(band)}(undef, AG.blocksize(band)...) + for (cols, rows) in AG.windows(band) + AG.read!(ds, buffer, 1, rows, cols) + data = buffer[1:length(cols), 1:length(rows)] + count += sum(data .> 0) + total += sum(data) + end + @test total / count ≈ 76.33891347095299 + @test total / (AG.height(ds) * AG.width(ds)) ≈ 47.55674749653172 end - @test total / count ≈ 76.33891347095299 - @test total / (AG.height(ds) * AG.width(ds)) ≈ 47.55674749653172 - end - @testset "version 4" begin - band = AG.getband(ds, 1) - count = 0 - total = 0 - buffer = Array{AG.pixeltype(band)}(undef, AG.blocksize(band)...) - for (cols,rows) in AG.windows(band) - AG.read!(band, buffer, rows, cols) - data = buffer[1:length(cols),1:length(rows)] - count += sum(data .> 0) - total += sum(data) + @testset "version 4" begin + band = AG.getband(ds, 1) + count = 0 + total = 0 + buffer = Matrix{AG.pixeltype(band)}(undef, AG.blocksize(band)...) + for (cols, rows) in AG.windows(band) + AG.read!(band, buffer, rows, cols) + data = buffer[1:length(cols), 1:length(rows)] + count += sum(data .> 0) + total += sum(data) + end + @test total / count ≈ 76.33891347095299 + @test total / (AG.height(ds) * AG.width(ds)) ≈ 47.55674749653172 end - @test total / count ≈ 76.33891347095299 - @test total / (AG.height(ds) * AG.width(ds)) ≈ 47.55674749653172 - end - @testset "version 5" begin - band = AG.getband(ds, 1) - count = 0 - total = 0 - xbsize, ybsize = AG.blocksize(band) - buffer = Array{AG.pixeltype(band)}(undef, ybsize, xbsize) - for ((i,j),(nrows,ncols)) in AG.blocks(band) - # AG.rasterio!(ds,buffer,[1],i,j,nrows,ncols) - # AG.read!(band, buffer, j, i, ncols, nrows) - AG.readblock!(band, j, i, buffer) - data = buffer[1:nrows, 1:ncols] - count += sum(data .> 0) - total += sum(data) + @testset "version 5" begin + band = AG.getband(ds, 1) + count = 0 + total = 0 + xbsize, ybsize = AG.blocksize(band) + buffer = Matrix{AG.pixeltype(band)}(undef, ybsize, xbsize) + for ((i, j), (nrows, ncols)) in AG.blocks(band) + # AG.rasterio!(ds,buffer,[1],i,j,nrows,ncols) + # AG.read!(band, buffer, j, i, ncols, nrows) + AG.readblock!(band, j, i, buffer) + data = buffer[1:nrows, 1:ncols] + count += sum(data .> 0) + total += sum(data) + end + @test total / count ≈ 76.33891347095299 + @test total / (AG.height(ds) * AG.width(ds)) ≈ 47.55674749653172 end - @test total / count ≈ 76.33891347095299 - @test total / (AG.height(ds) * AG.width(ds)) ≈ 47.55674749653172 - end - @testset "version 6" begin - band = AG.getband(ds, 1) - buffer = Array{AG.pixeltype(band)}(undef, AG.width(ds), AG.height(ds), 1) - AG.rasterio!(ds, buffer, [1]) - count = sum(buffer .> 0) - total = sum(buffer) - @test total / count ≈ 76.33891347095299 - @test total / (AG.height(ds) * AG.width(ds)) ≈ 47.55674749653172 - end + @testset "version 6" begin + band = AG.getband(ds, 1) + buffer = + Array{AG.pixeltype(band)}(undef, AG.width(ds), AG.height(ds), 1) + AG.rasterio!(ds, buffer, [1]) + count = sum(buffer .> 0) + total = sum(buffer) + @test total / count ≈ 76.33891347095299 + @test total / (AG.height(ds) * AG.width(ds)) ≈ 47.55674749653172 + end - @testset "version 7" begin - band = AG.getband(ds, 1) - buffer = Array{AG.pixeltype(band)}(undef, AG.width(ds), AG.height(ds)) - AG.read!(band, buffer) - count = sum(buffer .> 0) - total = sum(buffer) - @test total / count ≈ 76.33891347095299 - @test total / (AG.height(ds) * AG.width(ds)) ≈ 47.55674749653172 - end + @testset "version 7" begin + band = AG.getband(ds, 1) + buffer = + Matrix{AG.pixeltype(band)}(undef, AG.width(ds), AG.height(ds)) + AG.read!(band, buffer) + count = sum(buffer .> 0) + total = sum(buffer) + @test total / count ≈ 76.33891347095299 + @test total / (AG.height(ds) * AG.width(ds)) ≈ 47.55674749653172 + end - @testset "version 8" begin - band = AG.getband(ds, 1) - buffer = Array{AG.pixeltype(band)}(undef, AG.width(ds), AG.height(ds)) - AG.read!(ds, buffer, 1) - count = sum(buffer .> 0) - total = sum(buffer) - @test total / count ≈ 76.33891347095299 - @test total / (AG.height(ds) * AG.width(ds)) ≈ 47.55674749653172 - end + @testset "version 8" begin + band = AG.getband(ds, 1) + buffer = + Matrix{AG.pixeltype(band)}(undef, AG.width(ds), AG.height(ds)) + AG.read!(ds, buffer, 1) + count = sum(buffer .> 0) + total = sum(buffer) + @test total / count ≈ 76.33891347095299 + @test total / (AG.height(ds) * AG.width(ds)) ≈ 47.55674749653172 + end - @testset "version 9" begin - band = AG.getband(ds, 1) - buffer = Array{AG.pixeltype(band)}(undef, AG.width(ds), AG.height(ds), 1) - AG.read!(ds, buffer, [1]) - count = sum(buffer .> 0) - total = sum(buffer) - @test total / count ≈ 76.33891347095299 - @test total / (AG.height(ds) * AG.width(ds)) ≈ 47.55674749653172 - end + @testset "version 9" begin + band = AG.getband(ds, 1) + buffer = + Array{AG.pixeltype(band)}(undef, AG.width(ds), AG.height(ds), 1) + AG.read!(ds, buffer, [1]) + count = sum(buffer .> 0) + total = sum(buffer) + @test total / count ≈ 76.33891347095299 + @test total / (AG.height(ds) * AG.width(ds)) ≈ 47.55674749653172 + end - @testset "version 10" begin - band = AG.getband(ds, 1) - buffer = Array{AG.pixeltype(band)}(undef, AG.width(ds), AG.height(ds), 3) - AG.read!(ds, buffer) - count = sum(buffer[:,:,1] .> 0) - total = sum(buffer[:,:,1]) - @test total / count ≈ 76.33891347095299 - @test total / (AG.height(ds) * AG.width(ds)) ≈ 47.55674749653172 - end + @testset "version 10" begin + band = AG.getband(ds, 1) + buffer = + Array{AG.pixeltype(band)}(undef, AG.width(ds), AG.height(ds), 3) + AG.read!(ds, buffer) + count = sum(buffer[:, :, 1] .> 0) + total = sum(buffer[:, :, 1]) + @test total / count ≈ 76.33891347095299 + @test total / (AG.height(ds) * AG.width(ds)) ≈ 47.55674749653172 + end - # check for calling with Tuple - @testset "version 11" begin - band = AG.getband(ds, 1) - count = 0 - total = 0 - buffer = Array{AG.pixeltype(band)}(undef, AG.blocksize(band)..., 1) - for (cols,rows) in AG.windows(band) - AG.rasterio!(ds, buffer, (1,), rows .- 1, cols .- 1) - data = buffer[1:length(cols),1:length(rows)] - count += sum(data .> 0) - total += sum(data) + # check for calling with Tuple + @testset "version 11" begin + band = AG.getband(ds, 1) + count = 0 + total = 0 + buffer = Array{AG.pixeltype(band)}(undef, AG.blocksize(band)..., 1) + for (cols, rows) in AG.windows(band) + AG.rasterio!(ds, buffer, (1,), rows, cols) + data = buffer[1:length(cols), 1:length(rows)] + count += sum(data .> 0) + total += sum(data) + end + @test total / count ≈ 76.33891347095299 + @test total / (AG.height(ds) * AG.width(ds)) ≈ 47.55674749653172 end - @test total / count ≈ 76.33891347095299 - @test total / (AG.height(ds) * AG.width(ds)) ≈ 47.55674749653172 - end - @testset "buffer size" begin - @test size(AG.read(ds, 1, 0, 0, 20, 10)) === (20, 10) - @test size(AG.read(ds, [1, 3], 0, 0, 20, 10)) === (20, 10, 2) - @test size(AG.read(ds, 1, 1:10, 31:50)) === (20, 10) - @test size(AG.read(ds, [1, 3], 1:10, 31:50)) === (20, 10, 2) - band = AG.getband(ds, 1) - @test size(AG.read( band, 0, 0, 20, 10)) === (20, 10) + @testset "buffer size" begin + @test size(AG.read(ds, 1, 0, 0, 20, 10)) === (20, 10) + @test size(AG.read(ds, [1, 3], 0, 0, 20, 10)) === (20, 10, 2) + @test size(AG.read(ds, 1, 1:10, 31:50)) === (20, 10) + @test size(AG.read(ds, [1, 3], 1:10, 31:50)) === (20, 10, 2) + @test size(AG.read(ds, 1:2)) == (5665, 5033, 2) + band = AG.getband(ds, 1) + @test size(AG.read(band, 0, 0, 20, 10)) === (20, 10) + end + + @testset "Writing to buffers" begin + band = AG.getband(ds, 1) + @test AG.pixeltype(band) == UInt8 + xbsize, ybsize = AG.blocksize(band) + AG.create( + AG.getdriver("MEM"), + width = AG.width(band), + height = AG.height(band), + nbands = 2, + dtype = AG.pixeltype(band), + ) do dsout + bandout = AG.getband(dsout, 1) + + @testset "writeblock!(rb::RasterBand, xoffset, yoffset, buffer)" begin + # We write everything to typemax(UInt8) + for ((i, j), (nrows, ncols)) in AG.blocks(bandout) + AG.writeblock!( + bandout, + j, + i, + fill(typemax(UInt8), ncols, nrows), + ) + end + buffer = AG.read(bandout) + nnzero = sum(buffer .> 0) + @test nnzero == AG.height(bandout) * AG.width(bandout) + @test sum(buffer) / nnzero ≈ Float64(typemax(UInt8)) + + # Now we write everything to 0 + for ((i, j), (nrows, ncols)) in AG.blocks(bandout) + AG.writeblock!(bandout, j, i, fill(0x00, ncols, nrows)) + end + @test sum(AG.read(bandout) .> 0) == 0 + end + + @testset "write!(rb::RasterBand, buffer::Matrix[, rows, cols])" begin + # We write everything to typemax(UInt8) + AG.write!( + bandout, + fill( + typemax(UInt8), + AG.height(bandout), + AG.width(bandout), + ), + ) + buffer = AG.read(bandout) + nnzero = sum(buffer .> 0) + @test nnzero == AG.height(bandout) * AG.width(bandout) + @test sum(buffer) / nnzero ≈ Float64(typemax(UInt8)) + + # Now we write everything to 0 + AG.write!( + bandout, + fill(0x00, AG.height(bandout), AG.width(bandout)), + ) + @test sum(AG.read(bandout) .> 0) == 0 + end + + @testset "write!(dataset::Dataset, buffer::Matrix, i::Integer[, rows, cols])" begin + # We write everything to typemax(UInt8) + AG.write!( + dsout, + fill( + typemax(UInt8), + AG.height(bandout), + AG.width(bandout), + ), + 1, + ) + buffer = AG.read(bandout) + nnzero = sum(buffer .> 0) + @test nnzero == AG.height(bandout) * AG.width(bandout) + @test sum(buffer) / nnzero ≈ Float64(typemax(UInt8)) + + # Now we write everything to 0 + AG.write!( + dsout, + fill(0x00, AG.height(bandout), AG.width(bandout)), + 1, + ) + @test sum(AG.read(bandout) .> 0) == 0 + end + + @testset "write!(dataset::Dataset, buffer::Array, indices[, rows, cols])" begin + # We write everything to typemax(UInt8) + AG.write!( + dsout, + fill( + typemax(UInt8), + AG.height(dsout), + AG.width(dsout), + 2, + ), + 1:2, + ) + buffer = AG.read(dsout) + nnzero = sum(buffer .> 0) + @test nnzero == 2 * AG.height(dsout) * AG.width(dsout) + @test sum(buffer) / nnzero ≈ Float64(typemax(UInt8)) + + # Now we write everything to 0 + AG.write!( + dsout, + fill(0x00, AG.height(dsout), AG.width(dsout), 2), + 1:2, + ) + @test sum(AG.read(dsout) .> 0) == 0 + end + end + end end end - -# Untested -# writeblock!(rb::RasterBand, xoffset::Integer, yoffset::Integer, buffer) -# read!(rb::RasterBand, buffer::Array{Real,2}, xoffset::Integer, yoffset::Integer, xsize::Integer, ysize::Integer) -# read!(dataset::Dataset, buffer::Array{T,2}, i::Integer, xoffset::Integer, yoffset::Integer, xsize::Integer, ysize::Integer) -# read!(dataset::Dataset, buffer::Array{T,3}, indices, xoffset::Integer, yoffset::Integer, xsize::Integer, ysize::Integer) - -# read{U <: Integer}(rb::RasterBand, rows::UnitRange{U}, cols::UnitRange{U}) -# read(dataset::Dataset, indices) -# read(dataset::Dataset) -# read{T <: Integer}(dataset::Dataset, indices::Vector{T}, xoffset::Integer, yoffset::Integer, xsize::Integer, ysize::Integer) -# read{U <: Integer}(dataset::Dataset, i::Integer, rows::UnitRange{U}, cols::UnitRange{U}) -# read{U <: Integer}(dataset::Dataset, indices, rows::UnitRange{U}, cols::UnitRange{U})update!{T <: Real}(rb::RasterBand, buffer::Array{T,2}) = - -# write!(rb::RasterBand, buffer::Array{T,2}, rows::UnitRange{U}, cols::UnitRange{U}) -# write!(dataset::Dataset, buffer::Array{T,2}, i::Integer) -# write!(dataset::Dataset, buffer::Array{T,3}, indices) -# write!(dataset::Dataset, buffer::Array{T,3}, indices, xoffset::Integer, yoffset::Integer, xsize::Integer, ysize::Integer) -# write!(dataset::Dataset, buffer::Array{T,2}, i::Integer, rows::UnitRange{U}, cols::UnitRange{U}) -# write!(dataset::Dataset, buffer::Array{T,3}, indices, rows::UnitRange{U}, cols::UnitRange{U}) - -# function rasterio!(dataset::Dataset, -# buffer::Array{$T, 3}, -# bands, -# xoffset::Integer, -# yoffset::Integer, -# xsize::Integer, -# ysize::Integer, -# access::GDALRWFlag=GF_Read, -# pxspace::Integer=0, -# linespace::Integer=0, -# bandspace::Integer=0, -# extraargs=Ptr{GDAL.GDALRasterIOExtraArg}(C_NULL)) -# (dataset == C_NULL) && error("Can't read invalid rasterband") -# xbsize, ybsize, zbsize = size(buffer) -# nband = length(bands); @assert nband == zbsize -# result = ccall((:GDALDatasetRasterIOEx,GDAL.libgdal),GDAL.CPLErr, -# (Dataset,GDAL.GDALRWFlag,Cint,Cint,Cint,Cint, -# Ptr{Cvoid},Cint,Cint,GDAL.GDALDataType,Cint, -# Ptr{Cint},GDAL.GSpacing,GDAL.GSpacing,GDAL.GSpacing, -# Ptr{GDAL.GDALRasterIOExtraArg}),dataset,access, -# xoffset,yoffset,xsize,ysize,pointer(buffer),xbsize, -# ybsize,$GT,nband,pointer(bands),pxspace,linespace, -# bandspace,extraargs) -# @cplerr result "Access in DatasetRasterIO failed." -# buffer -# end diff --git a/test/test_spatialref.jl b/test/test_spatialref.jl index de8be0e2..e8fec066 100644 --- a/test/test_spatialref.jl +++ b/test/test_spatialref.jl @@ -1,82 +1,27 @@ using Test -import GDAL -import ArchGDAL; const AG = ArchGDAL -import GeoFormatTypes; const GFT = GeoFormatTypes +import ArchGDAL; +const AG = ArchGDAL; +import GeoFormatTypes; +const GFT = GeoFormatTypes; -@testset "Test Formats for Spatial Reference Systems" begin - proj4326 = "+proj=longlat +datum=WGS84 +no_defs" - proj26912 = "+proj=utm +zone=12 +datum=NAD83 +units=m +no_defs" - wkt4326 = "GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],AUTHORITY[\"EPSG\",\"4326\"]]" - wkt26912 = "PROJCS[\"NAD83 / UTM zone 12N\",GEOGCS[\"NAD83\",DATUM[\"North_American_Datum_1983\",SPHEROID[\"GRS 1980\",6378137,298.257222101,AUTHORITY[\"EPSG\",\"7019\"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY[\"EPSG\",\"6269\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4269\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",-111],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",500000],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],AUTHORITY[\"EPSG\",\"26912\"]]" - esri4326 = "GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0],UNIT[\"Degree\",0.0174532925199433],AXIS[\"Longitude\",EAST],AXIS[\"Latitude\",NORTH]]" - esri26912 = "PROJCS[\"NAD83 / UTM zone 12N\",GEOGCS[\"NAD83\",DATUM[\"North_American_Datum_1983\",SPHEROID[\"GRS 1980\",6378137,298.257222101,AUTHORITY[\"EPSG\",\"7019\"]],AUTHORITY[\"EPSG\",\"6269\"]],PRIMEM[\"Greenwich\",0],UNIT[\"Degree\",0.0174532925199433]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",-111],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",500000],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH]]" - xml4326 = """ - GCS_WGS_1984 - - - ellipsoidal - - 6402 - - - - Geodetic latitude - - 9901 - - Lat - north - - - - - Geodetic longitude - - 9902 - - Lon - east - - - - - - - D_WGS_1984 - - - Greenwich - - 0 - - - - - - WGS_1984 - 6378137 - - 298.257223563 - - - - - - - """ - xml26912 = """ - NAD_1983_UTM_Zone_12N - - - GCS_North_American_1983 +@testset "test_spatialref.jl" begin + @testset "Test Formats for Spatial Reference Systems" begin + proj4326 = "+proj=longlat +datum=WGS84 +no_defs" + proj26912 = "+proj=utm +zone=12 +datum=NAD83 +units=m +no_defs" + wkt4326 = "GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],AUTHORITY[\"EPSG\",\"4326\"]]" + wkt26912 = "PROJCS[\"NAD83 / UTM zone 12N\",GEOGCS[\"NAD83\",DATUM[\"North_American_Datum_1983\",SPHEROID[\"GRS 1980\",6378137,298.257222101,AUTHORITY[\"EPSG\",\"7019\"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY[\"EPSG\",\"6269\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4269\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",-111],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",500000],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],AUTHORITY[\"EPSG\",\"26912\"]]" + esri4326 = "GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0],UNIT[\"Degree\",0.0174532925199433],AXIS[\"Longitude\",EAST],AXIS[\"Latitude\",NORTH]]" + esri26912 = "PROJCS[\"NAD83 / UTM zone 12N\",GEOGCS[\"NAD83\",DATUM[\"North_American_Datum_1983\",SPHEROID[\"GRS 1980\",6378137,298.257222101,AUTHORITY[\"EPSG\",\"7019\"]],AUTHORITY[\"EPSG\",\"6269\"]],PRIMEM[\"Greenwich\",0],UNIT[\"Degree\",0.0174532925199433]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",-111],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",500000],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH]]" + xml4326 = """ + GCS_WGS_1984 - + ellipsoidal 6402 - + Geodetic latitude 9901 @@ -86,7 +31,7 @@ import GeoFormatTypes; const GFT = GeoFormatTypes - + Geodetic longitude 9902 @@ -98,10 +43,10 @@ import GeoFormatTypes; const GFT = GeoFormatTypes - - D_North_American_1983 + + D_WGS_1984 - + Greenwich 0 @@ -109,152 +54,231 @@ import GeoFormatTypes; const GFT = GeoFormatTypes - - GRS_1980 + + WGS_1984 6378137 - 298.257222101 + 298.257223563 - - - - Transverse_Mercator - - - 0 - - - - -111 - - - - 0.9996 - - - - 500000 - - - - 0 - - - - - - - Cartesian - - 4400 - - - - Easting - - 9906 - - E - east - - - - - Northing - - 9907 - - N - north - - - - - - """ + """ + xml26912 = """ + NAD_1983_UTM_Zone_12N + + + GCS_North_American_1983 + + + ellipsoidal + + 6402 + + + + Geodetic latitude + + 9901 + + Lat + north + + + + + Geodetic longitude + + 9902 + + Lon + east + + + + + + + D_North_American_1983 + + + Greenwich + + 0 + + + + + + GRS_1980 + 6378137 + + 298.257222101 + + + + + + + + + + Transverse_Mercator + + + 0 + + + + -111 + + + + 0.9996 + + + + 500000 + + + + 0 + + + + + + + Cartesian + + 4400 + + + + Easting + + 9906 + + E + east + + + + + Northing + + 9907 + + N + north + + + + + + """ - @testset "PROJ4 Format" begin - AG.importPROJ4(proj4326) do spatialref - spatialref2 = AG.importPROJ4(proj26912) - @test AG.toPROJ4(spatialref2) == proj26912 - AG.importPROJ4!(spatialref2, AG.toPROJ4(spatialref)) - @test AG.toPROJ4(spatialref2) == proj4326 + @testset "PROJ4 Format" begin + AG.importPROJ4(proj4326) do spatialref + spatialref2 = AG.importPROJ4(proj26912) + @test AG.toPROJ4(spatialref2) == proj26912 + AG.importPROJ4!(spatialref2, AG.toPROJ4(spatialref)) + @test AG.toPROJ4(spatialref2) == proj4326 + end end - end - @testset "WKT Format" begin - AG.importWKT(wkt4326) do spatialref - spatialref2 = AG.importWKT(wkt26912) - @test AG.toWKT(spatialref2) == wkt26912 - AG.importWKT!(spatialref2, AG.toWKT(spatialref)) - @test AG.toWKT(spatialref2) == wkt4326 + @testset "WKT Format" begin + AG.importWKT(wkt4326) do spatialref + spatialref2 = AG.importWKT(wkt26912) + @test AG.toWKT(spatialref2) == wkt26912 + AG.importWKT!(spatialref2, AG.toWKT(spatialref)) + @test AG.toWKT(spatialref2) == wkt4326 + end end - end - @testset "ESRI Format" begin - AG.importESRI(esri4326) do spatialref - spatialref2 = AG.importESRI(esri26912) - @test AG.toWKT(spatialref2) == esri26912 - AG.importESRI!(spatialref2, AG.toWKT(spatialref)) - @test AG.toWKT(spatialref2) == esri4326 + @testset "ESRI Format" begin + AG.importESRI(esri4326) do spatialref + spatialref2 = AG.importESRI(esri26912) + @test AG.toWKT(spatialref2) == esri26912 + AG.importESRI!(spatialref2, AG.toWKT(spatialref)) + @test AG.toWKT(spatialref2) == esri4326 + end end - end - @testset "XML Format" begin - AG.importXML(xml4326) do spatialref - spatialref2 = AG.importXML(xml26912) - @test startswith(AG.toXML(spatialref2), "= v"1.6.0-" - @testset "URL Import" begin - url4326 = "http://spatialreference.org/ref/epsg/4326/ogcwkt/" - wkt4326 = "GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],AUTHORITY[\"EPSG\",\"4326\"]]" - urlsample = "http://spatialreference.org/ref/epsg/2039/esriwkt/" - wktsample = "PROJCS[\"Israel / Israeli TM Grid\",GEOGCS[\"Israel\",DATUM[\"Israel_1993\",SPHEROID[\"GRS 1980\",6378137,298.257222101,AUTHORITY[\"EPSG\",\"7019\"]],AUTHORITY[\"EPSG\",\"6141\"]],PRIMEM[\"Greenwich\",0],UNIT[\"Degree\",0.0174532925199433]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",31.7343936111111],PARAMETER[\"central_meridian\",35.2045169444444],PARAMETER[\"scale_factor\",1.0000067],PARAMETER[\"false_easting\",219529.584],PARAMETER[\"false_northing\",626907.39],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH]]" - AG.importURL(url4326) do spatialref - spatialref2 = AG.importURL(urlsample) - @test AG.toWKT(spatialref2) == wktsample - AG.importURL!(spatialref2, url4326) - @test AG.toWKT(spatialref2) == wkt4326 + if VERSION >= v"1.6.0-" + @testset "URL Import" begin + url4326 = "http://spatialreference.org/ref/epsg/4326/ogcwkt/" + wkt4326 = "GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],AUTHORITY[\"EPSG\",\"4326\"]]" + urlsample = "http://spatialreference.org/ref/epsg/2039/esriwkt/" + wktsample = "PROJCS[\"Israel / Israeli TM Grid\",GEOGCS[\"Israel\",DATUM[\"Israel_1993\",SPHEROID[\"GRS 1980\",6378137,298.257222101,AUTHORITY[\"EPSG\",\"7019\"]],AUTHORITY[\"EPSG\",\"6141\"]],PRIMEM[\"Greenwich\",0],UNIT[\"Degree\",0.0174532925199433]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",31.7343936111111],PARAMETER[\"central_meridian\",35.2045169444444],PARAMETER[\"scale_factor\",1.0000067],PARAMETER[\"false_easting\",219529.584],PARAMETER[\"false_northing\",626907.39],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH]]" + AG.importURL(url4326) do spatialref + spatialref2 = AG.importURL(urlsample) + @test AG.toWKT(spatialref2) == wktsample + AG.importURL!(spatialref2, url4326) + @test AG.toWKT(spatialref2) == wkt4326 + end end end - end - @testset "generic importCRS" begin - @test AG.toWKT(AG.importCRS(GFT.WellKnownText(GFT.CRS(), wkt4326))) == AG.toWKT(AG.importWKT(wkt4326)) - @test AG.toWKT(AG.importCRS(GFT.ESRIWellKnownText(GFT.CRS(), wkt4326))) == AG.toWKT(AG.importESRI(wkt4326)) - @test AG.toWKT(AG.importCRS(GFT.ProjString(proj4326))) == AG.toWKT(AG.importPROJ4(proj4326)) - @test AG.toWKT(AG.importCRS(GFT.EPSG(4326))) == AG.toWKT(AG.importEPSG(4326)) - @test AG.toWKT(AG.importCRS(GFT.GML(xml4326))) == AG.toWKT(AG.importXML(xml4326)) - @test AG.toWKT(AG.importCRS(GFT.KML(""))) == AG.toWKT(AG.importEPSG(4326)) + @testset "generic importCRS" begin + @test AG.toWKT( + AG.importCRS(GFT.WellKnownText(GFT.CRS(), wkt4326)), + ) == AG.toWKT(AG.importWKT(wkt4326)) + @test AG.toWKT( + AG.importCRS(GFT.ESRIWellKnownText(GFT.CRS(), wkt4326)), + ) == AG.toWKT(AG.importESRI(wkt4326)) + @test AG.toWKT(AG.importCRS(GFT.ProjString(proj4326))) == + AG.toWKT(AG.importPROJ4(proj4326)) + @test AG.toWKT(AG.importCRS(GFT.EPSG(4326))) == + AG.toWKT(AG.importEPSG(4326)) + @test AG.toWKT(AG.importCRS(GFT.EPSG(4326), order = :trad)) == + AG.toWKT(AG.importEPSG(4326)) + @test AG.toWKT(AG.importCRS(GFT.EPSG(4326), order = :compliant)) == + AG.toWKT(AG.importEPSG(4326)) + @test AG.toWKT(AG.importCRS(GFT.GML(xml4326))) == + AG.toWKT(AG.importXML(xml4326)) + @test AG.toWKT(AG.importCRS(GFT.KML(""))) == + AG.toWKT(AG.importEPSG(4326)) + @test_throws ArgumentError AG.importCRS( + GFT.EPSG(4326), + order = :unknown, + ) + end end -end -@testset "Cloning NULL SRS" begin - @test sprint(print, AG.clone(AG.ISpatialRef())) == "NULL Spatial Reference System" - AG.clone(AG.ISpatialRef()) do spatialref - @test sprint(print, spatialref) == "NULL Spatial Reference System" + @testset "Cloning NULL SRS" begin + @test sprint(print, AG.clone(AG.ISpatialRef())) == + "NULL Spatial Reference System" + AG.clone(AG.ISpatialRef()) do spatialref + @test sprint(print, spatialref) == "NULL Spatial Reference System" + end end -end -@testset "Getting and Setting Attribute Values" begin - AG.importEPSG(4326) do spatialref - @test AG.toWKT(spatialref) == "GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],AUTHORITY[\"EPSG\",\"4326\"]]" - @test sprint(print, spatialref) == "Spatial Reference System: +proj=longlat +datum=WGS84 +no_defs" - AG.setattrvalue!(spatialref, "GEOGCS|AUTHORITY|EPSG") # tests seems to be broken in gdal 3.0 - @test AG.toWKT(spatialref) == "GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],AUTHORITY[\"EPSG\",\"4326\"]]" - AG.setattrvalue!(spatialref, "GEOGCS|NEWATTRIBUTE", "7031") # tests seems to be broken in gdal 3.0 - @test AG.toWKT(spatialref) == "GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],AUTHORITY[\"EPSG\",\"4326\"]]" - @test AG.getattrvalue(spatialref, "AUTHORITY", 0) == "EPSG" - @test AG.getattrvalue(spatialref, "AUTHORITY", 1) == "4326" + @testset "Getting and Setting Attribute Values" begin + AG.importEPSG(4326) do spatialref + @test AG.toWKT(spatialref) == + "GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],AUTHORITY[\"EPSG\",\"4326\"]]" + @test sprint(print, spatialref) == + "Spatial Reference System: +proj=longlat +datum=WGS84 +no_defs" + AG.setattrvalue!(spatialref, "GEOGCS|AUTHORITY|EPSG") # tests seems to be broken in gdal 3.0 + @test AG.toWKT(spatialref) == + "GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],AUTHORITY[\"EPSG\",\"4326\"]]" + AG.setattrvalue!(spatialref, "GEOGCS|NEWATTRIBUTE", "7031") # tests seems to be broken in gdal 3.0 + @test AG.toWKT(spatialref) == + "GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],AUTHORITY[\"EPSG\",\"4326\"]]" + @test AG.getattrvalue(spatialref, "AUTHORITY", 0) == "EPSG" + @test AG.getattrvalue(spatialref, "AUTHORITY", 1) == "4326" + end end end diff --git a/test/test_styletable.jl b/test/test_styletable.jl index c98d9aec..17f0667c 100644 --- a/test/test_styletable.jl +++ b/test/test_styletable.jl @@ -1,71 +1,75 @@ using Test -import GDAL -import ArchGDAL; const AG = ArchGDAL +import ArchGDAL; +const AG = ArchGDAL; -@testset "Testing StyleTable Methods" begin - AG.createstylemanager() do sm - @test AG.initialize!(sm) == true - @test AG.npart(sm) == 0 - @test AG.initialize!(sm, "PEN(w:2px,c:#000000,id:\"mapinfo-pen-2,ogr-pen-0\")") == true - AG.getpart(sm, 0) do st - @test AG.getstylestring(st) == "PEN(w:2px,c:#000000,id:\"mapinfo-pen-2,ogr-pen-0\")" - @test AG.toRGBA(st, "#123456") == (18, 52, 86, 255) - end - @test AG.npart(sm) == 1 - @test AG.addstyle!(sm, "name1", "style1") == false - @test AG.npart(sm) == 1 - @test AG.addstyle!(sm, "name2") == false - @test AG.npart(sm) == 1 +@testset "test_styletable.jl" begin + @testset "Testing StyleTable Methods" begin + AG.createstylemanager() do sm + @test AG.initialize!(sm) == true + @test AG.npart(sm) == 0 + @test AG.initialize!( + sm, + "PEN(w:2px,c:#000000,id:\"mapinfo-pen-2,ogr-pen-0\")", + ) == true + AG.getpart(sm, 0) do st + @test AG.getstylestring(st) == + "PEN(w:2px,c:#000000,id:\"mapinfo-pen-2,ogr-pen-0\")" + @test AG.toRGBA(st, "#123456") == (18, 52, 86, 255) + end + @test AG.npart(sm) == 1 + @test AG.addstyle!(sm, "name1", "style1") == false + @test AG.npart(sm) == 1 + @test AG.addstyle!(sm, "name2") == false + @test AG.npart(sm) == 1 - AG.createstyletool(GDAL.OGRSTCBrush) do st - @test AG.gettype(st) == GDAL.OGRSTCBrush - @test AG.getunit(st) == GDAL.OGRSTUMM - AG.setunit!(st, GDAL.OGRSTUPixel, 2.0) - @test AG.getunit(st) == GDAL.OGRSTUPixel + AG.createstyletool(AG.OGRSTCBrush) do st + @test AG.gettype(st) == AG.OGRSTCBrush + @test AG.getunit(st) == AG.OGRSTUMM + AG.setunit!(st, AG.OGRSTUPixel, 2.0) + @test AG.getunit(st) == AG.OGRSTUPixel - AG.setparam!(st, 0, 0) + AG.setparam!(st, 0, 0) @test AG.asint(st, 0) == 0 @test AG.asstring(st, 0) == "0" @test AG.asdouble(st, 0) == 0 - AG.setparam!(st, 1, 12) + AG.setparam!(st, 1, 12) @test AG.asint(st, 1) == 12 @test AG.asstring(st, 1) == "12" @test AG.asdouble(st, 1) == 12 - AG.setparam!(st, 2, "foo") + AG.setparam!(st, 2, "foo") @test AG.asstring(st, 2) == "foo" - AG.setparam!(st, 3, 0.5) + AG.setparam!(st, 3, 0.5) @test AG.asdouble(st, 3) ≈ 0.5 - @test AG.npart(sm) == 1 - AG.addpart!(sm, st) - @test AG.npart(sm) == 2 - @test AG.npart(sm, "some stylestring") == 1 - end + @test AG.npart(sm) == 1 + AG.addpart!(sm, st) + @test AG.npart(sm) == 2 + @test AG.npart(sm, "some stylestring") == 1 + end - AG.createstyletable() do stbl - AG.addstyle!(stbl, "name1", "style1") - AG.addstyle!(stbl, "name2", "style2") - AG.addstyle!(stbl, "name3", "style3") - AG.addstyle!(stbl, "name4", "style4") - @test AG.findstylestring(stbl, "name3") == "style3" - @test AG.laststyle(stbl) == "" - @test AG.nextstyle(stbl) == "style1" - @test AG.nextstyle(stbl) == "style2" - AG.resetreading!(stbl) - @test AG.nextstyle(stbl) == "style1" - AG.savestyletable(stbl, "tmp/styletable.txt") + AG.createstyletable() do stbl + AG.addstyle!(stbl, "name1", "style1") + AG.addstyle!(stbl, "name2", "style2") + AG.addstyle!(stbl, "name3", "style3") + AG.addstyle!(stbl, "name4", "style4") + @test AG.findstylestring(stbl, "name3") == "style3" + @test AG.laststyle(stbl) == "" + @test AG.nextstyle(stbl) == "style1" + @test AG.nextstyle(stbl) == "style2" + AG.resetreading!(stbl) + @test AG.nextstyle(stbl) == "style1" + return AG.savestyletable(stbl, "tmp/styletable.txt") + end + AG.createstyletable() do stbl + AG.loadstyletable!(stbl, "tmp/styletable.txt") + @test AG.findstylestring(stbl, "name3") == "style3" + @test AG.laststyle(stbl) == "" + @test AG.nextstyle(stbl) == "style1" + @test AG.nextstyle(stbl) == "style2" + AG.resetreading!(stbl) + @test AG.nextstyle(stbl) == "style1" + end + return rm("tmp/styletable.txt") end - AG.createstyletable() do stbl - AG.loadstyletable!(stbl, "tmp/styletable.txt") - @test AG.findstylestring(stbl, "name3") == "style3" - @test AG.laststyle(stbl) == "" - @test AG.nextstyle(stbl) == "style1" - @test AG.nextstyle(stbl) == "style2" - AG.resetreading!(stbl) - @test AG.nextstyle(stbl) == "style1" - end - rm("tmp/styletable.txt") end end - -# Untested: initialize!(stylemanager::StyleManager, feature::Feature) \ No newline at end of file diff --git a/test/test_tables.jl b/test/test_tables.jl index 86fa75ed..753e3334 100644 --- a/test/test_tables.jl +++ b/test/test_tables.jl @@ -1,70 +1,93 @@ using Test -import ArchGDAL; const AG = ArchGDAL +import ArchGDAL; +const AG = ArchGDAL; using Tables -@testset "Tables Support" begin - dataset = AG.read(joinpath(@__DIR__, "data/point.geojson")) - dataset1 = AG.read(joinpath(@__DIR__, "data/multi_geom.csv"), options = ["GEOM_POSSIBLE_NAMES=point,linestring", "KEEP_GEOM_COLUMNS=NO"]) - dataset2 = AG.read(joinpath(@__DIR__, "data/missing_testcase.csv"), options = ["GEOM_POSSIBLE_NAMES=point,linestring", "KEEP_GEOM_COLUMNS=NO"]) - @test dataset isa ArchGDAL.IDataset - @test dataset1 isa ArchGDAL.IDataset - @test dataset2 isa ArchGDAL.IDataset - layer = AG.getlayer(dataset, 0) - layer1 = AG.getlayer(dataset1, 0) - layer2 = AG.getlayer(dataset2, 0) - gt = AG.Table(layer) - gt1 = AG.Table(layer1) - gt2 = AG.Table(layer2) +@testset "test_tables.jl" begin + @testset "Tables Support" begin + dataset = AG.read(joinpath(@__DIR__, "data/point.geojson")) + dataset1 = AG.read( + joinpath(@__DIR__, "data/multi_geom.csv"), + options = [ + "GEOM_POSSIBLE_NAMES=point,linestring", + "KEEP_GEOM_COLUMNS=NO", + ], + ) + dataset2 = AG.read( + joinpath(@__DIR__, "data/missing_testcase.csv"), + options = [ + "GEOM_POSSIBLE_NAMES=point,linestring", + "KEEP_GEOM_COLUMNS=NO", + ], + ) + @test dataset isa ArchGDAL.IDataset + @test dataset1 isa ArchGDAL.IDataset + @test dataset2 isa ArchGDAL.IDataset + layer = AG.getlayer(dataset, 0) + layer1 = AG.getlayer(dataset1, 0) + layer2 = AG.getlayer(dataset2, 0) - @testset "read layer to table" begin - @test AG.getlayer(gt) === layer - @test AG.getlayer(gt1) === layer1 - @test AG.getlayer(gt2) === layer2 - @test sprint(print, gt) == "Table with 4 features\n" - @test sprint(print, gt1) == "Table with 2 features\n" - @test sprint(print, gt2) == "Table with 9 features\n" - end + @testset "Tables methods" begin + @test Tables.schema(layer1) == Tables.Schema( + (:point, :linestring, :id, :zoom, :location), + ( + AG.IGeometry{AG.wkbUnknown}, + AG.IGeometry{AG.wkbUnknown}, + String, + String, + String, + ), + ) + @test Tables.istable(typeof(layer)) == true + @test Tables.rowaccess(typeof(layer)) == true - @testset "Tables methods" begin - @test Tables.schema(layer).names == propertynames(gt) - @test Tables.schema(layer1).names == propertynames(gt1) - @test Tables.schema(layer2).names == propertynames(gt2) - @test Tables.istable(AG.Table) == true - @test Tables.rows(gt) == AG.Table(layer) - @test Tables.rows(gt1) == AG.Table(layer1) - @test Tables.rows(gt2) == AG.Table(layer2) - end + features = collect(Tables.rows(layer1)) + @test length(features) == 2 - @testset "Misc. methods" begin - @test Base.size(gt) == 4 - @test Base.size(gt1) == 2 - @test Base.size(gt2) == 9 - @test Base.length(gt) == 4 - @test Base.length(gt1) == 2 - @test Base.length(gt2) == 9 - @test Base.IteratorSize(typeof(gt)) == Base.HasLength() - @test Base.IteratorEltype(typeof(gt1)) == Base.HasEltype() - @test propertynames(gt) == (:geometry, :FID, :pointname) - @test propertynames(gt1) == (:point, :linestring, :id, :zoom, :location) - @test propertynames(gt2) == (:point, :linestring, :id, :zoom, :location) - @test getproperty(gt, :FID) == [iterate(gt, i)[1].FID for i in 0:size(gt)-1] - @test getproperty(gt1, :zoom) == [iterate(gt1, i)[1].zoom for i in 0:size(gt1)-1] - @test sprint(print, gt2[5].linestring) == sprint(print, gt2[3].point) - @test sprint(print, gt2[9].linestring) == sprint(print, gt2[7].point) - @test collect(findall(x->x=="missing", getproperty(gt2, i)) for i in [:id, :zoom, :location]) == [[6], [4, 8], [3, 7, 8]] - @test iterate(gt, 5) === nothing - @test iterate(gt1, 3) === nothing - @test typeof([getindex(gt, i) for i in 1:size(gt)]) == typeof([iterate(gt, i)[1] for i in 0:size(gt)-1]) - @test typeof([getindex(gt1, i) for i in 1:size(gt1)]) == typeof([iterate(gt1, i)[1] for i in 0:size(gt1)-1]) + @test Tables.columnnames(features[1]) == + (:point, :linestring, :id, :zoom, :location) + @test ismissing(Tables.getcolumn(features[2], -5)) + @test ismissing(Tables.getcolumn(features[2], 0)) + @test Tables.getcolumn(features[1], 1) == "5.1" + @test Tables.getcolumn(features[1], 2) == "1.0" + @test Tables.getcolumn(features[1], 3) == "Mumbai" + @test AG.toWKT(Tables.getcolumn(features[1], 4)) == "POINT (30 10)" + @test AG.toWKT(Tables.getcolumn(features[1], 5)) == + "LINESTRING (30 10,10 30,40 40)" + @test Tables.getcolumn(features[1], :id) == "5.1" + @test Tables.getcolumn(features[1], :zoom) == "1.0" + @test Tables.getcolumn(features[1], :location) == "Mumbai" + @test AG.toWKT(Tables.getcolumn(features[1], :point)) == + "POINT (30 10)" + @test AG.toWKT(Tables.getcolumn(features[1], :linestring)) == + "LINESTRING (30 10,10 30,40 40)" + @test ismissing(Tables.getcolumn(features[1], :fake)) - AG.resetreading!(layer) - AG.resetreading!(layer1) + @test Tables.columnnames(features[2]) == + (:point, :linestring, :id, :zoom, :location) + @test ismissing(Tables.getcolumn(features[2], -5)) + @test ismissing(Tables.getcolumn(features[2], 0)) + @test Tables.getcolumn(features[2], 1) == "5.2" + @test Tables.getcolumn(features[2], 2) == "2.0" + @test Tables.getcolumn(features[2], 3) == "New Delhi" + @test AG.toWKT(Tables.getcolumn(features[2], 4)) == "POINT (35 15)" + @test AG.toWKT(Tables.getcolumn(features[2], 5)) == + "LINESTRING (35 15,15 35,45 45)" + @test Tables.getcolumn(features[2], :id) == "5.2" + @test Tables.getcolumn(features[2], :zoom) == "2.0" + @test Tables.getcolumn(features[2], :location) == "New Delhi" + @test AG.toWKT(Tables.getcolumn(features[2], :point)) == + "POINT (35 15)" + @test AG.toWKT(Tables.getcolumn(features[2], :linestring)) == + "LINESTRING (35 15,15 35,45 45)" + @test ismissing(Tables.getcolumn(features[2], :fake)) - @test AG.nextnamedtuple(layer) isa NamedTuple{(:geometry, :FID, :pointname),Tuple{ArchGDAL.IGeometry{AG.GDAL.wkbPoint},Float64,String}} - @test AG.nextnamedtuple(layer1) isa NamedTuple{(:point, :linestring, :id, :zoom, :location),Tuple{ArchGDAL.IGeometry{AG.GDAL.wkbPoint},ArchGDAL.IGeometry{AG.GDAL.wkbLineString},String,String,String}} - for i in (1,3,4) - @test AG.schema_names(layer)[i] isa Base.Generator || AG.schema_names(layer)[i] isa ArchGDAL.IFeatureDefnView - @test AG.schema_names(layer1)[i] isa Base.Generator || AG.schema_names(layer1)[i] isa ArchGDAL.IFeatureDefnView + geom_names, field_names = AG.schema_names(AG.layerdefn(layer)) + @test collect(geom_names) == [Symbol("")] + @test collect(field_names) == [:FID, :pointname] + geom_names, field_names = AG.schema_names(AG.layerdefn(layer1)) + @test collect(geom_names) == [:point, :linestring] + @test collect(field_names) == [:id, :zoom, :location] end end end diff --git a/test/test_types.jl b/test/test_types.jl index 71d1c1e8..a4680607 100644 --- a/test/test_types.jl +++ b/test/test_types.jl @@ -1,49 +1,142 @@ using Test -import GDAL -import ArchGDAL; const AG = ArchGDAL - -@testset "Testing GDAL Type Methods" begin - @testset "GDAL Open Flags" begin - @test AG.OF_ReadOnly | 0x04 == 0x04 - @test 0x06 | AG.OF_ReadOnly == 0x06 - @test AG.OF_ReadOnly | AG.OF_GNM == GDAL.GDAL_OF_READONLY | GDAL.GDAL_OF_GNM - end +import ArchGDAL; +const AG = ArchGDAL; +import ImageCore - @testset "GDAL Data Types" begin - @test AG.typesize(GDAL.GDT_UInt16) == 16 - @test AG.typename(GDAL.GDT_UInt16) == "UInt16" - @test AG.gettype("UInt16") == GDAL.GDT_UInt16 +@testset "test_types.jl" begin + @testset "Testing GDAL Type Methods" begin + @testset "GDAL Open Flags" begin + @test AG.OF_READONLY | 0x04 == 0x04 + @test 0x06 | AG.OF_READONLY == 0x06 + @test AG.OF_READONLY | AG.OF_GNM == AG.OF_READONLY | AG.OF_GNM + end - @test AG.typeunion(GDAL.GDT_UInt16, GDAL.GDT_Byte) == GDAL.GDT_UInt16 - @test AG.iscomplex(GDAL.GDT_Float32) == false - end + @testset "GDAL Data Types" begin + @test AG.typesize(AG.GDT_UInt16) == 16 + @test AG.typename(AG.GDT_UInt16) == "UInt16" + @test AG.gettype("UInt16") == AG.GDT_UInt16 - @testset "GDAL Colors and Palettes" begin - @test AG.getname(GDAL.GARIO_COMPLETE) == "COMPLETE" - @test AG.asyncstatustype("COMPLETE") == GDAL.GARIO_COMPLETE - @test AG.asyncstatustype("ERROR") == GDAL.GARIO_ERROR - @test AG.asyncstatustype("PENDING") == GDAL.GARIO_PENDING - @test AG.asyncstatustype("UPDATE") == GDAL.GARIO_UPDATE - - @test AG.colorinterp("Hue") == GDAL.GCI_HueBand - @test AG.colorinterp("Red") == GDAL.GCI_RedBand - @test AG.colorinterp("Blue") == GDAL.GCI_BlueBand - - @test AG.getname(GDAL.GPI_Gray) == "Gray" - @test AG.getname(GDAL.GPI_RGB) == "RGB" - @test AG.getname(GDAL.GPI_CMYK) == "CMYK" - @test AG.getname(GDAL.GPI_HLS) == "HLS" - end + @test AG.typeunion(AG.GDT_UInt16, AG.GDT_Byte) == AG.GDT_UInt16 + @test AG.iscomplex(AG.GDT_Float32) == false + + @test Base.convert(AG.GDALDataType, UInt8) == AG.GDT_Byte + @test_throws ErrorException Base.convert(AG.GDALDataType, Int64) + @test_throws ErrorException Base.convert(DataType, AG.GDT_TypeCount) + @test_throws MethodError Base.convert( + ImageCore.Normed, + AG.GDT_Float32, + ) + end + + @testset "GDAL Colors and Palettes" begin + @test AG.getname(AG.GARIO_COMPLETE) == "COMPLETE" + @test AG.asyncstatustype("COMPLETE") == AG.GARIO_COMPLETE + @test AG.asyncstatustype("ERROR") == AG.GARIO_ERROR + @test AG.asyncstatustype("PENDING") == AG.GARIO_PENDING + @test AG.asyncstatustype("UPDATE") == AG.GARIO_UPDATE + + @test AG.colorinterp("Hue") == AG.GCI_HueBand + @test AG.colorinterp("Red") == AG.GCI_RedBand + @test AG.colorinterp("Blue") == AG.GCI_BlueBand + + @test AG.getname(AG.GPI_Gray) == "Gray" + @test AG.getname(AG.GPI_RGB) == "RGB" + @test AG.getname(AG.GPI_CMYK) == "CMYK" + @test AG.getname(AG.GPI_HLS) == "HLS" + end + + @testset "GDAL Field Types" begin + @test AG.getname(AG.OFTString) == "String" + @test AG.getname(AG.OFTIntegerList) == "IntegerList" + @test AG.getname(AG.OFSTBoolean) == "Boolean" + @test AG.getname(AG.OFSTFloat32) == "Float32" - @testset "GDAL Field Types" begin - @test AG.getname(GDAL.OFTString) == "String" - @test AG.getname(GDAL.OFTIntegerList) == "IntegerList" - @test AG.getname(GDAL.OFSTBoolean) == "Boolean" - @test AG.getname(GDAL.OFSTFloat32) == "Float32" + @test AG.arecompatible(AG.OFTReal, AG.OFSTNone) == true + @test AG.arecompatible(AG.OFTReal, AG.OFSTBoolean) == false + @test AG.arecompatible(AG.OFTReal, AG.OFSTInt16) == false + @test AG.arecompatible(AG.OFTReal, AG.OFSTFloat32) == true + end - @test AG.arecompatible(GDAL.OFTReal, GDAL.OFSTNone) == true - @test AG.arecompatible(GDAL.OFTReal, GDAL.OFSTBoolean) == false - @test AG.arecompatible(GDAL.OFTReal, GDAL.OFSTInt16) == false - @test AG.arecompatible(GDAL.OFTReal, GDAL.OFSTFloat32) == true + @testset "Base Geometry Types" begin + @test AG.basetype(AG.wkbUnknown) == AG.wkbUnknown + @test AG.basetype(AG.wkbPoint) == AG.wkbPoint + @test AG.basetype(AG.wkbLineString) == AG.wkbLineString + @test AG.basetype(AG.wkbPolygon) == AG.wkbPolygon + @test AG.basetype(AG.wkbMultiPoint) == AG.wkbMultiPoint + @test AG.basetype(AG.wkbMultiLineString) == AG.wkbMultiLineString + @test AG.basetype(AG.wkbMultiPolygon) == AG.wkbMultiPolygon + @test AG.basetype(AG.wkbGeometryCollection) == + AG.wkbGeometryCollection + @test AG.basetype(AG.wkbCircularString) == AG.wkbCircularString + @test AG.basetype(AG.wkbCompoundCurve) == AG.wkbCompoundCurve + @test AG.basetype(AG.wkbCurvePolygon) == AG.wkbCurvePolygon + @test AG.basetype(AG.wkbMultiCurve) == AG.wkbMultiCurve + @test AG.basetype(AG.wkbMultiSurface) == AG.wkbMultiSurface + @test AG.basetype(AG.wkbCurve) == AG.wkbCurve + @test AG.basetype(AG.wkbSurface) == AG.wkbSurface + @test AG.basetype(AG.wkbPolyhedralSurface) == + AG.wkbPolyhedralSurface + @test AG.basetype(AG.wkbTIN) == AG.wkbTIN + @test AG.basetype(AG.wkbTriangle) == AG.wkbTriangle + @test AG.basetype(AG.wkbNone) == AG.wkbNone + @test AG.basetype(AG.wkbLinearRing) == AG.wkbLinearRing + @test AG.basetype(AG.wkbCircularStringZ) == AG.wkbCircularString + @test AG.basetype(AG.wkbCompoundCurveZ) == AG.wkbCompoundCurve + @test AG.basetype(AG.wkbCurvePolygonZ) == AG.wkbCurvePolygon + @test AG.basetype(AG.wkbMultiCurveZ) == AG.wkbMultiCurve + @test AG.basetype(AG.wkbMultiSurfaceZ) == AG.wkbMultiSurface + @test AG.basetype(AG.wkbCurveZ) == AG.wkbCurve + @test AG.basetype(AG.wkbSurfaceZ) == AG.wkbSurface + @test AG.basetype(AG.wkbPolyhedralSurfaceZ) == + AG.wkbPolyhedralSurface + @test AG.basetype(AG.wkbTINZ) == AG.wkbTIN + @test AG.basetype(AG.wkbTriangleZ) == AG.wkbTriangle + @test AG.basetype(AG.wkbPointM) == AG.wkbPoint + @test AG.basetype(AG.wkbLineStringM) == AG.wkbLineString + @test AG.basetype(AG.wkbPolygonM) == AG.wkbPolygon + @test AG.basetype(AG.wkbMultiPointM) == AG.wkbMultiPoint + @test AG.basetype(AG.wkbMultiLineStringM) == AG.wkbMultiLineString + @test AG.basetype(AG.wkbMultiPolygonM) == AG.wkbMultiPolygon + @test AG.basetype(AG.wkbGeometryCollectionM) == + AG.wkbGeometryCollection + @test AG.basetype(AG.wkbCircularStringM) == AG.wkbCircularString + @test AG.basetype(AG.wkbCompoundCurveM) == AG.wkbCompoundCurve + @test AG.basetype(AG.wkbCurvePolygonM) == AG.wkbCurvePolygon + @test AG.basetype(AG.wkbMultiCurveM) == AG.wkbMultiCurve + @test AG.basetype(AG.wkbMultiSurfaceM) == AG.wkbMultiSurface + @test AG.basetype(AG.wkbCurveM) == AG.wkbCurve + @test AG.basetype(AG.wkbSurfaceM) == AG.wkbSurface + @test AG.basetype(AG.wkbPolyhedralSurfaceM) == + AG.wkbPolyhedralSurface + @test AG.basetype(AG.wkbTINM) == AG.wkbTIN + @test AG.basetype(AG.wkbTriangleM) == AG.wkbTriangle + @test AG.basetype(AG.wkbPointZM) == AG.wkbPoint + @test AG.basetype(AG.wkbLineStringZM) == AG.wkbLineString + @test AG.basetype(AG.wkbPolygonZM) == AG.wkbPolygon + @test AG.basetype(AG.wkbMultiPointZM) == AG.wkbMultiPoint + @test AG.basetype(AG.wkbMultiLineStringZM) == AG.wkbMultiLineString + @test AG.basetype(AG.wkbMultiPolygonZM) == AG.wkbMultiPolygon + @test AG.basetype(AG.wkbGeometryCollectionZM) == + AG.wkbGeometryCollection + @test AG.basetype(AG.wkbCircularStringZM) == AG.wkbCircularString + @test AG.basetype(AG.wkbCompoundCurveZM) == AG.wkbCompoundCurve + @test AG.basetype(AG.wkbCurvePolygonZM) == AG.wkbCurvePolygon + @test AG.basetype(AG.wkbMultiCurveZM) == AG.wkbMultiCurve + @test AG.basetype(AG.wkbMultiSurfaceZM) == AG.wkbMultiSurface + @test AG.basetype(AG.wkbCurveZM) == AG.wkbCurve + @test AG.basetype(AG.wkbSurfaceZM) == AG.wkbSurface + @test AG.basetype(AG.wkbPolyhedralSurfaceZM) == + AG.wkbPolyhedralSurface + @test AG.basetype(AG.wkbTINZM) == AG.wkbTIN + @test AG.basetype(AG.wkbTriangleZM) == AG.wkbTriangle + @test AG.basetype(AG.wkbPoint25D) == AG.wkbPoint + @test AG.basetype(AG.wkbLineString25D) == AG.wkbLineString + @test AG.basetype(AG.wkbPolygon25D) == AG.wkbPolygon + @test AG.basetype(AG.wkbMultiPoint25D) == AG.wkbMultiPoint + @test AG.basetype(AG.wkbMultiLineString25D) == AG.wkbMultiLineString + @test AG.basetype(AG.wkbMultiPolygon25D) == AG.wkbMultiPolygon + @test AG.basetype(AG.wkbGeometryCollection25D) == + AG.wkbGeometryCollection + end end end diff --git a/test/test_utils.jl b/test/test_utils.jl index 51f06f77..61456715 100644 --- a/test/test_utils.jl +++ b/test/test_utils.jl @@ -1,10 +1,12 @@ using Test -import GDAL -import ArchGDAL; const AG = ArchGDAL +import ArchGDAL; +const AG = ArchGDAL; -@testset "metadataitem" begin - driver = AG.getdriver("DERIVED") - @test AG.metadataitem(driver, "DMD_EXTENSIONS") == "" - driver = AG.getdriver("GTiff") - @test AG.metadataitem(driver, "DMD_EXTENSIONS") == "tif tiff" +@testset "test_utils.jl" begin + @testset "metadataitem" begin + driver = AG.getdriver("DERIVED") + @test AG.metadataitem(driver, "DMD_EXTENSIONS") == "" + driver = AG.getdriver("GTiff") + @test AG.metadataitem(driver, "DMD_EXTENSIONS") == "tif tiff" + end end