TerrainGen coverage


terraingen/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
86 / 95
90.5%
Branches:
24 / 38
63.2%
Functions:
32 / 32
100.0%

raster/raster.h
Line Branch Exec Source
1 /// @file
2 /// @brief A single-band georeferenced grid of values.
3 #pragma once
4
5 #include "terraingen/raster/geo_transform.h"
6
7 #include <cassert>
8 #include <cmath>
9 #include <cstddef>
10 #include <optional>
11 #include <span>
12 #include <stdexcept>
13 #include <type_traits>
14 #include <vector>
15
16 namespace terraingen {
17
18 /// A single-band, georeferenced grid of values stored row-major.
19 ///
20 /// Row 0 is the first row in the file, the northern edge for north-up rasters. Cells are addressed
21 /// as (column, row), matching the (x, y) order of `GeoTransform`.
22 /// @tparam T Cell type, usually `float` heights in metres.
23 template <typename T> class Raster {
24 public:
25 /// An empty raster with the identity transform and no no-data value.
26 36 Raster() = default;
27
28 /// A raster of `width` columns and `height` rows, every cell set to `fill`.
29 18843 Raster(std::size_t width, std::size_t height, T fill = T{})
30
3/3
terraingen::Raster<double>::Raster(unsigned long, unsigned long, double):
✓ Branch 1 taken 22 times.
terraingen::Raster<float>::Raster(unsigned long, unsigned long, float):
✓ Branch 1 taken 18818 times.
terraingen::Raster<int>::Raster(unsigned long, unsigned long, int):
✓ Branch 1 taken 3 times.
56529 : width_(width), height_(height), values_(width * height, fill) {}
31
32 /// The number of columns.
33 177374276 std::size_t Width() const {
34 177374276 return width_;
35 }
36
37 /// The number of rows.
38 61395429 std::size_t Height() const {
39 61395429 return height_;
40 }
41
42 /// The number of cells, `Width() * Height()`.
43 2130060 std::size_t Size() const {
44 2130060 return values_.size();
45 }
46
47 /// Whether the raster has no cells.
48 159221 bool Empty() const {
49 159221 return values_.empty();
50 }
51
52 /// Unchecked cell access; asserts `col < Width()` and `row < Height()` in debug builds.
53 158852760 T& operator()(std::size_t col, std::size_t row) {
54
6/12
terraingen::Raster<double>::operator()(unsigned long, unsigned long):
✓ Branch 0 taken 740312 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 740312 times.
✗ Branch 3 not taken.
terraingen::Raster<float>::operator()(unsigned long, unsigned long):
✓ Branch 0 taken 158112442 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 158112442 times.
✗ Branch 3 not taken.
terraingen::Raster<int>::operator()(unsigned long, unsigned long):
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
158852760 assert(col < width_ && row < height_);
55 158852760 return values_[row * width_ + col];
56 }
57
58 /// Unchecked read-only cell access; asserts in debug builds.
59 128864289 const T& operator()(std::size_t col, std::size_t row) const {
60
4/8
terraingen::Raster<double>::operator()(unsigned long, unsigned long) const:
✓ Branch 0 taken 937996 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 937996 times.
✗ Branch 3 not taken.
terraingen::Raster<float>::operator()(unsigned long, unsigned long) const:
✓ Branch 0 taken 127926293 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 127926293 times.
✗ Branch 3 not taken.
128864289 assert(col < width_ && row < height_);
61 128864289 return values_[row * width_ + col];
62 }
63
64 /// Bounds-checked cell access.
65 /// @throws std::out_of_range if `col` or `row` is outside the raster.
66 75 T& At(std::size_t col, std::size_t row) {
67 75 CheckBounds(col, row);
68 73 return values_[row * width_ + col];
69 }
70
71 /// Bounds-checked read-only cell access.
72 /// @throws std::out_of_range if `col` or `row` is outside the raster.
73 const T& At(std::size_t col, std::size_t row) const {
74 CheckBounds(col, row);
75 return values_[row * width_ + col];
76 }
77
78 /// All cells in row-major order.
79 8773866 std::span<T> Values() {
80 8773866 return values_;
81 }
82
83 /// All cells in row-major order, read-only.
84 1712093 std::span<const T> Values() const {
85 1712093 return values_;
86 }
87
88 /// The cells of one row, west to east for north-up rasters; asserts in debug builds.
89 2 std::span<T> Row(std::size_t row) {
90
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 assert(row < height_);
91 2 return std::span<T>(values_).subspan(row * width_, width_);
92 }
93
94 /// The cells of one row, read-only; asserts in debug builds.
95 std::span<const T> Row(std::size_t row) const {
96 assert(row < height_);
97 return std::span<const T>(values_).subspan(row * width_, width_);
98 }
99
100 /// The pixel-to-world transform.
101 676841 const GeoTransform& Transform() const {
102 676841 return transform_;
103 }
104
105 /// Sets the pixel-to-world transform.
106 1608 void SetTransform(const GeoTransform& transform) {
107 1608 transform_ = transform;
108 1608 }
109
110 /// Whether `value` is the no-data value; a NaN no-data value matches every NaN, and nothing
111 /// matches without a no-data value.
112 128487064 bool IsNoData(const T& value) const {
113
3/4
terraingen::Raster<double>::IsNoData(double const&) const:
✓ Branch 1 taken 80559 times.
✗ Branch 2 not taken.
terraingen::Raster<float>::IsNoData(float const&) const:
✓ Branch 1 taken 128352773 times.
✓ Branch 2 taken 53732 times.
128487064 if (!noData_) {
114 128433332 return false;
115 }
116 if constexpr (std::is_floating_point_v<T>) {
117
2/4
terraingen::Raster<double>::IsNoData(double const&) const:
✗ Branch 2 not taken.
✗ Branch 3 not taken.
terraingen::Raster<float>::IsNoData(float const&) const:
✓ Branch 2 taken 15795 times.
✓ Branch 3 taken 37937 times.
53732 if (std::isnan(*noData_)) {
118 15795 return std::isnan(value);
119 }
120 }
121 37937 return value == *noData_;
122 }
123
124 /// The value marking cells without data, if any.
125 847 const std::optional<T>& NoData() const {
126 847 return noData_;
127 }
128
129 /// Sets the value marking cells without data, or none.
130 256 void SetNoData(std::optional<T> value) {
131 256 noData_ = value;
132 256 }
133
134 /// The world coordinates of a cell's centre.
135 134085541 Vec2d CellCenter(std::size_t col, std::size_t row) const {
136 134085541 return transform_.PixelToWorld(static_cast<double>(col) + 0.5,
137 134085541 static_cast<double>(row) + 0.5);
138 }
139
140 private:
141 75 void CheckBounds(std::size_t col, std::size_t row) const {
142
4/4
✓ Branch 0 taken 74 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 73 times.
75 if (col >= width_ || row >= height_) {
143
1/1
✓ Branch 2 taken 2 times.
2 throw std::out_of_range("Raster cell out of range");
144 }
145 73 }
146
147 std::size_t width_ = 0;
148 std::size_t height_ = 0;
149 std::vector<T> values_;
150 GeoTransform transform_;
151 std::optional<T> noData_;
152 };
153
154 } // namespace terraingen
155