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 | 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 | 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 | 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 | 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 | 128487064 | if (!noData_) { | |
| 114 | 128433332 | return false; | |
| 115 | } | ||
| 116 | if constexpr (std::is_floating_point_v<T>) { | ||
| 117 | 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 | 75 | if (col >= width_ || row >= height_) { | |
| 143 | 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 |