From fc896e209b662f5f6db7ec1c5f3bc4806a86de8f Mon Sep 17 00:00:00 2001 From: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> Date: Thu, 14 Nov 2024 11:21:43 -0300 Subject: [PATCH] Add docstrings (#7) * Add docstrings * Add docstrings for objects from 'metadata.jl' file * Update docstrings * Add docstrings for objects from 'image.jl', 'load.jl', 'save.jl' files * Add docstrings for objects from 'userutils.jl' file * [WIP] Add docstring for 'metadata' function * Add docstring for 'metadata' function * Update docstrings * Update docstrings --- src/geokeys.jl | 77 ++++++++++++++-- src/image.jl | 18 ++++ src/load.jl | 12 +++ src/metadata.jl | 225 +++++++++++++++++++++++++++++++++++++++++++++-- src/save.jl | 21 +++++ src/userutils.jl | 51 +++++++++++ 6 files changed, 391 insertions(+), 13 deletions(-) diff --git a/src/geokeys.jl b/src/geokeys.jl index d59be87..210d036 100644 --- a/src/geokeys.jl +++ b/src/geokeys.jl @@ -2,6 +2,14 @@ # Licensed under the MIT License. See LICENSE in the project root. # ----------------------------------------------------------------- +""" + GeoTIFF.GeoKeyID + +Enum of all GeoKey IDs supported by GeoTIFF. + +See [Requirements Class GeoKeyDirectoryTag](https://docs.ogc.org/is/19-008r4/19-008r4.html#_requirements_class_geokeydirectorytag) +section of the GeoTIFF specification for more details. +""" @enum GeoKeyID::UInt16 begin GTRasterTypeGeoKey = 1025 GTModelTypeGeoKey = 1024 @@ -50,11 +58,20 @@ ProjScaleAtCenterGeoKey = 3093 end -# Corresponding names in the GeoTIFF specification: -# id - KeyID -# tag - TIFFTagLocation -# count - Count -# value - ValueOffset +""" + GeoTIFF.GeoKey(id, tag, count, value) + +GeoKey Entry that is stored in [`GeoKeyDirectory`](@ref). + +Corresponding field names in the GeoTIFF specification: +* `id`: KeyID +* `tag`: TIFFTagLocation +* `count`: Count +* `value`: ValueOffset + +See [Requirements Class GeoKeyDirectoryTag](https://docs.ogc.org/is/19-008r4/19-008r4.html#_requirements_class_geokeydirectorytag) +section of the GeoTIFF specification for a explanation of each field. +""" struct GeoKey id::GeoKeyID tag::UInt16 @@ -63,14 +80,64 @@ struct GeoKey end # generic values + +""" + GeoTIFF.Undefined + +GeoKey value that indicate intentionally omitted parameters. +""" const Undefined = UInt16(0) + +""" + GeoTIFF.UserDefined + +GeoKey value that indicate user defined parameters. +""" const UserDefined = UInt16(32767) # GTRasterTypeGeoKey values + +""" + GeoTIFF.PixelIsArea + +PixelIsArea raster type, i.e each pixel of the image is a grid element. +""" const PixelIsArea = UInt16(1) + +""" + GeoTIFF.PixelIsPoint + +PixelIsPoint raster type, i.e each pixel of the image is a grid vertex. +""" const PixelIsPoint = UInt16(2) # GTModelTypeGeoKey values + +""" + GeoTIFF.Projected2D + +Projected CRS model type. +""" const Projected2D = UInt16(1) + +""" + GeoTIFF.Geographic2D + +Geographic 2D CRS model type. + +### Notes + +* GeoTIFF specification uses the term Geographic2D to mean geodetic latlon coordinates; +""" const Geographic2D = UInt16(2) + +""" + GeoTIFF.Geocentric3D + +Geocentric 3D CRS model type. + +### Notes + +* GeoTIFF specification uses the term Geocentric3D to mean Cartesian 3D coordinates; +""" const Geocentric3D = UInt16(3) diff --git a/src/image.jl b/src/image.jl index a39a012..ca81f77 100644 --- a/src/image.jl +++ b/src/image.jl @@ -2,13 +2,31 @@ # Licensed under the MIT License. See LICENSE in the project root. # ----------------------------------------------------------------- +""" + GeoTIFF.GeoTIFFImage + +Image type returned by the [`GeoTIFF.load`](@ref) function. + +See the [`GeoTIFF.tiff`](@ref) and [`GeoTIFF.metadata`](@ref) functions +to get the TIFF image and metadata respectively. +""" struct GeoTIFFImage{T,N,I<:AbstractTIFF{T,N}} <: AbstractArray{T,N} tiff::I metadata::Metadata end +""" + GeoTIFF.tiff(geotiff) + +TIFF image of a `geotiff` image. +""" tiff(geotiff::GeoTIFFImage) = geotiff.tiff +""" + GeoTIFF.metadata(geotiff) + +GeoTIFF metadata of a `geotiff` image. +""" metadata(geotiff::GeoTIFFImage) = geotiff.metadata # AbstractArray interface diff --git a/src/load.jl b/src/load.jl index a2c9a4d..713fe53 100644 --- a/src/load.jl +++ b/src/load.jl @@ -2,6 +2,18 @@ # Licensed under the MIT License. See LICENSE in the project root. # ----------------------------------------------------------------- +""" + GeoTIFF.load(fname; kwargs...) + +Load a GeoTIFF file returning an image with the processed GeoTIFF metadata. +The keyword arguments are forward to the `TiffImages.load` function. + +### Notes + +* TIFF files without GeoTIFF metadata are load with default and mandatory metadata: + * [`GeoKeyDirectory`](@ref) without GeoKeys and only with GeoTIFF version. + * [`ModelTransformation`](@ref) with `A` as identity matrix and `b` as vector of zeros. +""" function load(fname; kwargs...) tiff = TiffImages.load(fname; kwargs...) metadata = _getmetadata(tiff) diff --git a/src/metadata.jl b/src/metadata.jl index 405375d..2452f57 100644 --- a/src/metadata.jl +++ b/src/metadata.jl @@ -2,6 +2,14 @@ # Licensed under the MIT License. See LICENSE in the project root. # ----------------------------------------------------------------- +""" + GeoTIFF.GeoTag + +Enum of all tags supported by GeoTIFF. + +See [Requirements Class TIFF](https://docs.ogc.org/is/19-008r4/19-008r4.html#_requirements_class_tiff) +section of the GeoTIFF Spec for more details. +""" @enum GeoTag::UInt16 begin GeoKeyDirectoryTag = 34735 GeoDoubleParamsTag = 34736 @@ -11,12 +19,21 @@ ModelTransformationTag = 34264 end -# Corresponding names in the GeoTIFF specification: -# version - KeyDirectoryVersion -# revision - KeyRevision -# minor - MinorRevision -# nkeys - NumberOfKeys -# geokeys - Key Entry Set +""" + GeoTIFF.GeoKeyDirectory(; version=1, revision=1, minor=1, geokeys=GeoKey[]) + +The GeoKeyDirectory stores the GeoKeys and the format version. + +Corresponding field names in the GeoTIFF specification: +* `version`: KeyDirectoryVersion +* `revision`: KeyRevision +* `minor`: MinorRevision +* `nkeys`: NumberOfKeys +* `geokeys`: Key Entry Set + +See [Requirements Class GeoKeyDirectoryTag](https://docs.ogc.org/is/19-008r4/19-008r4.html#_requirements_class_geokeydirectorytag) +section of the GeoTIFF specification for a explanation of each field. +""" struct GeoKeyDirectory version::UInt16 revision::UInt16 @@ -52,30 +69,62 @@ function params(geokeydirectory::GeoKeyDirectory) params end +""" + GeoTIFF.GeoDoubleParams(params) + +The GeoDoubleParams stores the double (Float64) parameters of the GeoKeys. + +See [Requirements Class GeoDoubleParamsTag](https://docs.ogc.org/is/19-008r4/19-008r4.html#_requirements_class_geodoubleparamstag) +section of the GeoTIFF specification for more details. +""" struct GeoDoubleParams params::Vector{Float64} end params(geodoubleparams::GeoDoubleParams) = geodoubleparams.params +""" + GeoTIFF.GeoAsciiParams(params) + +The GeoAsciiParams stores the ASCII string parameters of the GeoKeys. + +See [Requirements Class GeoAsciiParamsTag](https://docs.ogc.org/is/19-008r4/19-008r4.html#_requirements_class_geoasciiparamstag) +section of the GeoTIFF specification for more details. +""" struct GeoAsciiParams params::String end params(geoasciiparams::GeoAsciiParams) = geoasciiparams.params +""" + GeoTIFF.ModelPixelScale(; x=1.0, y=-1.0, z=1.0) + +The ModelPixelScale contains the scale parameters of the raster-to-model transformation. + +See [Raster to Model Coordinate Transformation Requirements](https://docs.ogc.org/is/19-008r4/19-008r4.html#_raster_to_model_coordinate_transformation_requirements) +section of the GeoTIFF specification for more details. +""" struct ModelPixelScale x::Float64 y::Float64 z::Float64 end -ModelPixelScale(; x=1.0, y=1.0, z=1.0) = ModelPixelScale(x, y, z) +ModelPixelScale(; x=1.0, y=-1.0, z=1.0) = ModelPixelScale(x, y, z) ModelPixelScale(params::Vector{Float64}) = ModelPixelScale(params[1], params[2], params[3]) params(modelpixelscale::ModelPixelScale) = [modelpixelscale.x, modelpixelscale.y, modelpixelscale.z] +""" + GeoTIFF.ModelTiepoint(; i=0.0, j=0.0, k=0.0, x=0.0, y=0.0, z=0.0) + +The ModelTiepoint contains the tie point parameters of the raster-to-model transformation. + +See [Raster to Model Coordinate Transformation Requirements](https://docs.ogc.org/is/19-008r4/19-008r4.html#_raster_to_model_coordinate_transformation_requirements) +section of the GeoTIFF specification for more details. +""" struct ModelTiepoint i::Float64 j::Float64 @@ -92,6 +141,16 @@ ModelTiepoint(params::Vector{Float64}) = ModelTiepoint(params[1], params[2], par params(modeltiepoint::ModelTiepoint) = [modeltiepoint.i, modeltiepoint.j, modeltiepoint.k, modeltiepoint.x, modeltiepoint.y, modeltiepoint.z] +""" + GeoTIFF.ModelTransformation(; A=[1.0 0.0 0.0; 0.0 1.0 0.0; 0.0 0.0 1.0], b=[0.0, 0.0, 0.0]) + +The ModelTransformation contains the affine parameters of the raster-to-model transformation. + +For convinience, the 4x4 transformation matrix of the ModelTransformation, is splited into `A` and `b`. + +See [Raster to Model Coordinate Transformation Requirements](https://docs.ogc.org/is/19-008r4/19-008r4.html#_raster_to_model_coordinate_transformation_requirements) +section of the GeoTIFF specification for more details. +""" struct ModelTransformation A::SMatrix{3,3,Float64,9} b::SVector{3,Float64} @@ -132,6 +191,34 @@ function params(modeltransformation::ModelTransformation) [A[1, 1], A[1, 2], A[1, 3], b[1], A[2, 1], A[2, 2], A[2, 3], b[2], A[3, 1], A[3, 2], A[3, 3], b[3], 0, 0, 0, 1] end +""" + GeoTIFF.Metadata(; + geokeydirectory=GeoKeyDirectory(), + geodoubleparams=nothing, + geoasciiparams=nothing, + modelpixelscale=nothing, + modeltiepoint=nothing, + modeltransformation=ModelTransformation() + ) + +Stores all GeoTIFF format metadata. + +Corresponding field names in the GeoTIFF specification: +* `geokeydirectory`: GeoKeyDirectoryTag +* `geodoubleparams`: GeoDoubleParamsTag +* `geoasciiparams`: GeoAsciiParamsTag +* `modeltiepoint`: ModelPixelScaleTag +* `modeltiepoint`: ModelTiepointTag +* `modeltransformation`: ModelTransformationTag + +See [Requirements Class TIFF](https://docs.ogc.org/is/19-008r4/19-008r4.html#_requirements_class_tiff) +section of the GeoTIFF specification for more details. + +### Notes + +* Construct metadata manually is hard work. See the [`GeoTIFF.metadata`](@ref) function + for a more user-friendly way to construct metadata. +""" struct Metadata geokeydirectory::GeoKeyDirectory geodoubleparams::Union{GeoDoubleParams,Nothing} @@ -164,6 +251,128 @@ function Metadata(; Metadata(geokeydirectory, geodoubleparams, geoasciiparams, modelpixelscale, modeltiepoint, modeltransformation) end +""" + GeoTIFF.metadata(; [parameters...]) + +Construct a GeoTIFF metadata with parameter values. + +# Parameters + +## GeoTIFF version +* `version` (integer): KeyDirectoryVersion of the GeoTIFF (default to `1`); +* `revision` (integer): KeyRevision of the GeoTIFF (default to `1`); +* `minor` (integer): MinorRevision of the GeoTIFF (default to `1`); + * `0`: GeoTIFF 1.0 version; + * `1`: GeoTIFF 1.1 version; + +## Raster to Model transformation +* `pixelscale` (3-tuple of float): `(x, y, z)` pixel scale parameters; + * Must be set together with `tiepoint`; +* `tiepoint` (6-tuple of float): `(i, j, k, x, y, z)` tiepoint parameters; +* `transformation` (matrix of float, vector of float): `(A, b)` affine parameters (default to `A` as identity matrix and `b` as vector of zeros + if no transformation is passed); + * Should not be set if `pixelscale` and `tiepoint` have been set; + +All parameters in the following sections can receive the values +`GeoTIFF.Undefined` and `GeoTIFF.UserDefined` in addition to the allowed values, +except the string and float parameters. + +## GeoTIFF Configuration +* `rastertype` (`GeoTIFF.PixelIsArea` | `GeoTIFF.PixelIsPoint`): raster type of the GeoTIFF; +* `modeltype` (`GeoTIFF.Projected2D` | `GeoTIFF.Geographic2D` | `GeoTIFF.Geocentric3D`): model type of the GeoTIFF; + * If `GeoTIFF.Projected2D`, then `projectcrs` must be set; + * If `GeoTIFF.Geographic2D` or `GeoTIFF.Geocentric3D`, then `geodeticcrs` must be set; + +## Model CRS +* `projectcrs` (1024-32766): EPSG code of the Projected CRS; + * If `GeoTIFF.UserDefined`, then `projectedcitation`, `geodeticcrs` and `projection` must be set; +* `geodeticcrs` (1024-32766): EPSG code of the Geographic or Geocentric CRS; + * If `GeoTIFF.UserDefined`, then `geodeticcitation`, `geodeticdatum` and `geogangularunits` (for Geographic CRS) + or `geoglinearunits` (for Geocentric CRS) must be set; +* `verticalcrs` (1024-32766): EPSG code of the Vertical CRS; + * If `GeoTIFF.UserDefined`, then `verticalcitation`, `verticaldatum` and `verticalunits` must be set; + +## Citation +* `citation` (string): Description of the GeoTIFF file; +* `geodeticcitation` (string): Description of the Geodetic CRS; +* `projectedcitation` (string): Description of the Projected CRS; +* `verticalcitation` (string): Description of the Vertical CRS; + +## User Defined CRS +### Units +* `geogangularunits` (1024-32766): EPSG code of angular unit for the: + * user defined Geographic CRS; + * user defined prime meridians; + * user defined projection parameters that are angles; + * If `GeoTIFF.UserDefined`, then `geodeticcitation` and `geogangularunitsize` must be set; +* `geogazimuthunits` (1024-32766): EPSG code of angular unit for the user defined projection parameters + when these differ from the angular unit of `geogangularunits`; + * If `GeoTIFF.UserDefined`, then `geodeticcitation` and `geogangularunitsize` must be set; +* `geoglinearunits` (1024-32766): EPSG code of length unit for the: + * user defined Geocentric CRS; + * height of user defined Geographic 3D CRS; + * user defined ellipsoid axes; + * If `GeoTIFF.UserDefined`, then `geodeticcitation` and `geoglinearunitsize` must be set; +* `projlinearunits` (1024-32766): EPSG code of length unit for the: + * user defined Projected CRS; + * user defined projection parameters that are lengths; + * If `GeoTIFF.UserDefined`, then `projectedcitation` and `projlinearunitsize` must be set; +* `verticalunits` (1024-32766): EPSG code of length unit for the user defined Vertical CRS; + * `GeoTIFF.UserDefined` is not supported; + +### Unit size +* `geogangularunitsize` (float): Size of user defined Geographic angle with radian as base unit; +* `geoglinearunitsize` (float): Size of user defined Geographic length with meter as base unit; +* `projlinearunitsize` (float): Size of user defined Projected length with meter as base unit; + +### Geodetic Datum +* `geodeticdatum` (1024-32766): EPSG code of Datum for the user defined Geographic CRS; + * If `GeoTIFF.UserDefined`, then `geodeticcitation`, `primemeridian` and `ellipsoid` must be set; +* `primemeridian` (1024-32766): EPSG code of Prime Meridian for the user defined Datum; + * If `GeoTIFF.UserDefined`, then `primemeridianlongitude` must be set; +* `primemeridianlongitude` (float): Longitude angle relative to the international reference meridian + for the user defined Prime Meridian; +* `ellipsoid` (1024-32766): EPSG code of Ellipsoid for the user defined Datum; + * If `GeoTIFF.UserDefined`, then `ellipsoidsemimajoraxis` and `ellipsoidsemiminoraxis` or `ellipsoidinvflattening` must be set; +* `ellipsoidsemimajoraxis` (float): Semi-major axis of the user defined Ellipsoid; +* `ellipsoidsemiminoraxis` (float): Semi-minor axis of the user defined Ellipsoid; +* `ellipsoidinvflattening` (float): Inverse flattening of the user defined Ellipsoid; + +### Vertical Datum +* `verticaldatum` (1024-32766): EPSG code of Datum for the user defined Vertical CRS; + * If `GeoTIFF.UserDefined`, then `verticalcitation` must be set; + +### Projection +* `projection` (1024-32766): EPSG code of coordinate operation for the user defined Projected CRS; + * If `GeoTIFF.UserDefined`, then `projectedcitation`, `projmethod`, and `projlinearunits` must be set; +* `projmethod` (1-27): GeoTIFF projection code of the user defined projection; + * See [Map Projection methods](https://docs.ogc.org/is/19-008r4/19-008r4.html#_map_projection_methods) + for the full list of codes; + * All projection parameters of the passed projection method must be set; + +### Projection parameters +* `projstdparallel1` (float): First standard parallel; +* `projstdparallel2` (float): Second standard parallel; +* `projnatoriginlong` (float): Longitude of natural origin; +* `projnatoriginlat` (float): Latitude of natural origin; +* `projfalseoriginlong` (float): Longitude of false origin; +* `projfalseoriginlat` (float): Latitude of false origin; +* `projcenterlong` (float): Longitude of projection center; +* `projcenterlat` (float): Latitude of projection center; +* `projstraightvertpolelong` (float): Longitude of straight vertical pole; +* `projazimuthangle` (float): Azimuth angle east of true north of the central line passing through the projection center; +* `projfalseeasting` (float): False easting; +* `projfalsenorthing` (float): False northing; +* `projfalseorigineasting` (float): Easting coordinate of false origin; +* `projfalseoriginnorthing` (float): Northing coordinate of false origin; +* `projcentereasting` (float): Easting coordinate of projection center; +* `projcenternorthing` (float): Northing coordinate of projection center; +* `projscaleatnatorigin` (float): Scale of natural origin; +* `projscaleatcenter` (float): Scale of projection center; + +See [Requirements](https://docs.ogc.org/is/19-008r4/19-008r4.html#_requirements) +section of the GeoTIFF specification for more details. +""" function metadata(; version=1, revision=1, @@ -240,7 +449,7 @@ function metadata(; end end - # GeoTIFF Configuration GeoKeys + # GeoTIFF Configuration geokeyshort!(GTRasterTypeGeoKey, rastertype) geokeyshort!(GTModelTypeGeoKey, modeltype) diff --git a/src/save.jl b/src/save.jl index 8143825..fcb2aaa 100644 --- a/src/save.jl +++ b/src/save.jl @@ -6,15 +6,36 @@ const GeoTIFFType = Union{AbstractFloat,Signed,Unsigned} const WidePixelOrColorant = Union{WidePixel,Colorant} +""" + GeoTIFF.save(fname, geotiff) + +Save in the file `fname` a `geotiff` image returned by the [`GeoTIFF.load`](@ref) function. +""" save(fname, geotiff::GeoTIFFImage) = TiffImages.save(fname, tiff(geotiff)) +""" + GeoTIFF.save(fname, tiff; metadata=GeoTIFF.Metadata()) + +Save in the file `fname` a `tiff` image with passed GeoTIFF `metadata`. +""" function save(fname, tiff::AbstractTIFF; metadata=Metadata()) _setmetadata!(tiff, metadata) TiffImages.save(fname, tiff) end +""" + GeoTIFF.save(fname, img; metadata=GeoTIFF.Metadata()) + +Save in the file `fname` an image (array of colors) `img` with passed GeoTIFF `metadata`. +""" save(fname, img::AbstractArray{<:WidePixelOrColorant}; kwargs...) = save(fname, DenseTaggedImage(img); kwargs...) +""" + GeoTIFF.save(fname, channels...; metadata=GeoTIFF.Metadata()) + +Save in the file `fname` a multi-channel image with passed GeoTIFF `metadata`. +All `channels` must have the same size and an eltype supported by GeoTIFF, which are float, signed and unsigned integer. +""" function save(fname, channel₁::AbstractArray{T}, channelₙ::AbstractArray{T}...; kwargs...) where {T<:GeoTIFFType} CT = _colordatatype(T) colors = reinterpret(Gray{CT}, channel₁) diff --git a/src/userutils.jl b/src/userutils.jl index f628580..c45a131 100644 --- a/src/userutils.jl +++ b/src/userutils.jl @@ -2,17 +2,35 @@ # Licensed under the MIT License. See LICENSE in the project root. # ----------------------------------------------------------------- +""" + GeoTIFF.geokey(metadata, id) + +Find the GeoKey that has the `id` in the `metadata`. +If it is not stored in the metadata, `nothing` will be returned. +""" function geokey(metadata::Metadata, id::GeoKeyID) geokeys = metadata.geokeydirectory.geokeys i = findfirst(geokey -> geokey.id == id, geokeys) isnothing(i) ? nothing : geokeys[i] end +""" + GeoTIFF.geokeyvalue(metadata, id) + +Find the GeoKey that has the `id` in the `metadata` and return it value. +If it is not stored in the metadata, `nothing` will be returned. +""" function geokeyvalue(metadata::Metadata, id::GeoKeyID) gk = geokey(metadata, id) isnothing(gk) ? nothing : gk.value end +""" + GeoTIFF.geokeydouble(metadata, id) + +Find the GeoKey that has the `id` in the `metadata` and return it double parameter. +If it is not stored in the metadata, `nothing` will be returned. +""" function geokeydouble(metadata::Metadata, id::GeoKeyID) dp = metadata.geodoubleparams gk = geokey(metadata, id) @@ -23,6 +41,12 @@ function geokeydouble(metadata::Metadata, id::GeoKeyID) end end +""" + GeoTIFF.geokeyascii(metadata, id) + +Find the GeoKey that has the `id` in the metadata and return it ASCII parameter. +If it is not stored in the metadata, `nothing` will be returned. +""" function geokeyascii(metadata::Metadata, id::GeoKeyID) ap = metadata.geoasciiparams gk = geokey(metadata, id) @@ -34,10 +58,25 @@ function geokeyascii(metadata::Metadata, id::GeoKeyID) end end +""" + GeoTIFF.rastertype(metadata) + +Raster type of the GeoTIFF. If it is not stored in the `metadata`, `nothing` will be returned. +""" rastertype(metadata::Metadata) = geokeyvalue(metadata, GTRasterTypeGeoKey) +""" + GeoTIFF.modeltype(metadata) + +Model type of the GeoTIFF. If it is not stored in the `metadata`, `nothing` will be returned. +""" modeltype(metadata::Metadata) = geokeyvalue(metadata, GTModelTypeGeoKey) +""" + GeoTIFF.epsgcode(metadata) + +EPSG Code of the GeoTIFF CRS. If it is not stored in the `metadata`, `nothing` will be returned. +""" function epsgcode(metadata::Metadata) mt = modeltype(metadata) isnothing(mt) && return nothing @@ -51,6 +90,12 @@ function epsgcode(metadata::Metadata) end end +""" + GeoTIFF.affineparams2D(metadata) + +Affine 2D parameters `(A, b)` of the GeoTIFF raster-to-model transformation. +If it is not stored in the `metadata`, `nothing` will be returned. +""" function affineparams2D(metadata::Metadata) pixelscale = metadata.modelpixelscale tiepoint = metadata.modeltiepoint @@ -74,6 +119,12 @@ function affineparams2D(metadata::Metadata) end end +""" + GeoTIFF.affineparams3D(metadata) + +Affine 3D parameters `(A, b)` of the GeoTIFF raster-to-model transformation. +If it is not stored in the `metadata`, `nothing` will be returned. +""" function affineparams3D(metadata::Metadata) pixelscale = metadata.modelpixelscale tiepoint = metadata.modeltiepoint