TerrainGen coverage


terraingen/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
11 / 11
100.0%
Branches:
9 / 14
64.3%
Functions:
4 / 4, 1 excl
100.0%

raster/geotiff/geotiff.h
Line Branch Exec Source
1 /// @file
2 /// @brief Reading and writing GeoTIFF rasters, with their projection keys and band scaling.
3 #pragma once
4
5 #include "terraingen/raster/geo_transform.h"
6 #include "terraingen/raster/raster.h"
7
8 #include <cstddef>
9 #include <cstdint>
10 #include <filesystem>
11 #include <optional>
12 #include <stdexcept>
13 #include <string>
14 #include <vector>
15
16 namespace terraingen {
17
18 /// A GeoTIFF that cannot be read or written, with the reason.
19 class GeoTiffError : public std::runtime_error {
20 public:
21 using std::runtime_error::runtime_error;
22 };
23
24 /// A GeoTIFF's projection as raw GeoKey tags, written back unchanged.
25 ///
26 /// Projections are not interpreted.
27 struct GeoKeys {
28 14 std::vector<std::uint16_t> directory; ///< GeoKeyDirectoryTag (34735).
29 14 std::vector<double> doubleParams; ///< GeoDoubleParamsTag (34736).
30 14 std::string asciiParams; ///< GeoAsciiParamsTag (34737).
31
32 /// Whether there are no keys (the directory is empty).
33 652 bool Empty() const {
34 652 return directory.empty();
35 }
36
37 /// Whether GTRasterTypeGeoKey is PixelIsPoint: pixel coordinates name pixel centres rather
38 /// than pixel areas.
39 bool PixelIsPoint() const;
40
41 /// Whether GTModelTypeGeoKey is geographic: coordinates are angles rather than a projection's
42 /// linear units.
43 bool IsGeographic() const;
44
45 /// The value of the SHORT GeoKey `key` stored inline in the directory, if present.
46 std::optional<std::uint16_t> ShortKey(std::uint16_t key) const;
47
48
3/6
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 14 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 14 times.
14 bool operator==(const GeoKeys&) const = default;
49 };
50
51 /// Compression schemes this library reads and writes; others are reported as unsupported.
52 enum class TiffCompression {
53 None, ///< Code 1.
54 Lzw, ///< Code 5, with TIFF 6.0's early code-width change.
55 PackBits, ///< Code 32773; read only.
56 Deflate, ///< Codes 8 and 32946 (zlib streams), decoded and encoded with miniz.
57 };
58
59 /// How sample bits are interpreted.
60 enum class TiffSampleFormat { UnsignedInteger, SignedInteger, Float };
61
62 /// A band's scale and offset: a sample `s` stands for `s * scale + offset`, as GDAL's band
63 /// SCALE and OFFSET metadata say.
64 struct BandScaling {
65 9 double scale = 1.0; ///< Multiplier of the stored sample.
66 9 double offset = 0.0; ///< Added after scaling.
67
68 /// Whether the scaling leaves samples unchanged (scale 1 and offset 0).
69 37 bool Identity() const {
70
4/4
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 27 times.
✓ Branch 3 taken 1 time.
37 return scale == 1.0 && offset == 0.0;
71 }
72
73
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 9 times.
9 bool operator==(const BandScaling&) const = default;
74 };
75
76 /// The band scaling a GDAL_METADATA tag (42112) gives, one per band; scale 1 and offset 0 for
77 /// bands not mentioned.
78 ///
79 /// Reads `<Item name="SCALE" sample="n" role="scale">` and `role="offset"` items; items for bands
80 /// beyond `bands`, without a band, or whose value is not a number are ignored.
81 std::vector<BandScaling> ParseGdalBandScaling(const std::string& xml, std::size_t bands);
82
83 /// The layout and georeferencing of a GeoTIFF's first image.
84 struct GeoTiffInfo {
85 std::size_t width = 0; ///< Columns.
86 std::size_t height = 0; ///< Rows.
87 std::size_t bands = 0; ///< Samples per pixel.
88 int bitsPerSample = 0; ///< Bits of each sample.
89 /// How samples are stored.
90 TiffSampleFormat sampleFormat = TiffSampleFormat::UnsignedInteger;
91 /// How the image data is compressed.
92 TiffCompression compression = TiffCompression::None;
93 int predictor = 1; ///< 1 none, 2 horizontal differencing, 3 floating point.
94 bool tiled = false; ///< Tiles rather than strips.
95 bool bigTiff = false; ///< BigTIFF rather than classic TIFF.
96 bool bigEndian = false; ///< Big-endian ("MM") byte order.
97 /// Pixel-to-world transform, the identity without one; for PixelIsPoint shifted half a pixel
98 /// to name the first pixel's corner, as GDAL does.
99 GeoTransform transform{};
100 bool georeferenced = false; ///< Whether the file has a transform.
101 std::optional<double> noData; ///< From the GDAL_NODATA tag (42113).
102 /// One per band, from the GDAL_METADATA tag (42112); `ReadGeoTiff` returns raw samples.
103 std::vector<BandScaling> bandScaling;
104 GeoKeys geoKeys; ///< The projection keys.
105 };
106
107 /// Reads the layout and georeferencing of a GeoTIFF's first image without its pixels.
108 ///
109 /// Classic TIFF and BigTIFF in either byte order.
110 /// @throws GeoTiffError for unreadable or malformed files.
111 GeoTiffInfo ReadGeoTiffInfo(const std::filesystem::path& path);
112
113 /// A raster read from a GeoTIFF with the projection keys to write it back with.
114 struct GeoTiff {
115 Raster<float> raster; ///< The band read, with the file's transform and no-data value.
116 GeoKeys geoKeys; ///< The file's projection keys.
117 };
118
119 /// Reads one band of a GeoTIFF's first image as `float` samples, with the file's transform,
120 /// no-data value and GeoKeys.
121 ///
122 /// Strips or tiles, chunky or planar bands, 8- to 64-bit integer and 32- and 64-bit float samples,
123 /// LZW, Deflate or PackBits, with or without a predictor. Band scaling is not applied.
124 /// @param band The band, from 0.
125 /// @throws GeoTiffError for unreadable, malformed or unsupported files, or a missing band.
126 GeoTiff ReadGeoTiff(const std::filesystem::path& path, std::size_t band = 0);
127
128 /// GeoKeys for longitude and latitude in degrees on a sphere, such as a planet or moon.
129 ///
130 /// A user-defined geographic CRS with both axes `radius` and pixel-is-area rasters.
131 /// @param radius The sphere's radius in metres; positive and finite.
132 /// @throws std::invalid_argument unless `radius` is positive and finite.
133 GeoKeys GeographicSphereKeys(double radius);
134
135 /// Settings for `WriteGeoTiff`.
136 struct GeoTiffWriteOptions {
137 /// `None`, `Lzw` or `Deflate`; compressed files use the floating-point predictor.
138 TiffCompression compression = TiffCompression::Lzw;
139 /// Tile width and height in pixels; a positive multiple of 16.
140 std::uint32_t tileSize = 256;
141 /// Always write BigTIFF; otherwise BigTIFF is used only when the file could exceed 4 GiB.
142 bool bigTiff = false;
143 };
144
145 /// Writes a raster as a tiled, little-endian float32 GeoTIFF.
146 ///
147 /// The transform is written as ModelPixelScale and ModelTiepoint, or ModelTransformation when
148 /// rotated, shifted back half a pixel when `geoKeys` say PixelIsPoint; it is omitted for the
149 /// identity transform without keys. The no-data value is written as GDAL_NODATA. Edge tiles are
150 /// padded with the no-data value, or 0 without one.
151 /// @param geoKeys The projection keys, as `ReadGeoTiff` returned them.
152 /// @throws GeoTiffError for an empty raster, invalid options, or a file that cannot be written.
153 void WriteGeoTiff(const std::filesystem::path& path, const Raster<float>& raster,
154 const GeoKeys& geoKeys = {}, const GeoTiffWriteOptions& options = {});
155
156 /// Writes rasters as one GeoTIFF of as many float32 bands.
157 ///
158 /// Several bands are stored band after band (planar configuration 2), with the first band's
159 /// transform and no-data value; otherwise as `WriteGeoTiff` for one raster.
160 /// @param bands The bands; at least one, all on one grid.
161 /// @throws GeoTiffError as for one raster, or for no bands or bands on different grids.
162 void WriteGeoTiff(const std::filesystem::path& path, const std::vector<Raster<float>>& bands,
163 const GeoKeys& geoKeys = {}, const GeoTiffWriteOptions& options = {});
164
165 /// `WriteGeoTiff` for bands held elsewhere.
166 /// @param bands The bands; at least one, all on one grid.
167 /// @throws GeoTiffError as for one raster, or for no bands or bands on different grids.
168 void WriteGeoTiff(const std::filesystem::path& path, const std::vector<const Raster<float>*>& bands,
169 const GeoKeys& geoKeys = {}, const GeoTiffWriteOptions& options = {});
170
171 } // namespace terraingen
172