11#include "coordinates.hpp"
14#include "resolution.hpp"
16#include <range/v3/all.hpp>
37template <
typename Time =
int>
class raster
42 using time_type = Time;
45 using time_descriptor = std::vector<time_type>::size_type;
48 using location_descriptor = int;
63 using decimal_degree = double;
66 using value_type = double;
70 std::vector<time_type> _times;
71 std::vector<double> _gT;
82 inline auto compute_resolution(
const std::vector<double> &gT)
const
90 const auto [lat_max, lon_min] =
origin;
91 const auto lon_max = lon_min +
width() * r.
lon();
92 const auto lat_min = lat_max +
height() * r.
lat();
101 explicit raster(
const std::filesystem::path &file,
const std::vector<time_type> &
times)
102 : _dataset(file.string()), _times(
times), _gT(_dataset.affine_transformation_coefficients()),
103 _origin(compute_origin(_gT)), _width(_dataset.
width()), _height(_dataset.
height()), _depth(_dataset.
depth()),
104 _resolution(compute_resolution(_gT)), _extent(compute_extent(_origin, _resolution)),
105 _no_data(nodata_first_band())
107 check_times_equals_depth();
129 template <
class Callable>
130 requires(std::is_invocable_r_v<std::optional<value_type>, Callable, location_descriptor, time_type>)
131 inline void to_geotiff(Callable f, time_type start, time_type end,
const std::filesystem::path &file)
const
133 export_to_geotiff(f, start, end, file);
139 inline void to_shapefile(std::vector<latlon> points,
const std::filesystem::path &file)
const
141 points_to_shapefile(points, file);
147 inline void to_shapefile(std::map<latlon, int> counts,
const std::filesystem::path &file)
const
149 counts_to_shapefile(counts, file);
153 std::ostream &
write(std::ostream &stream)
const
155 stream <<
"Origin: " <<
origin() <<
"\nWidth: " <<
width() <<
"\nHeight: " <<
height() <<
"\nDepth: " <<
depth()
160 <<
"\nNA value: " <<
NA() << std::endl;
207 inline value_type
NA() const noexcept
229 return ranges::views::iota(0,
depth());
235 inline bool is_valid(location_descriptor x)
const noexcept
237 return ( x >= 0 and x <
locations().size());
245 return x.col >= 0 and x.col <
width() and x.row >= 0 and x.row <
height() ;
253 return x.col >= 0 and x.col <
width() and x.row >= 0 and x.row <
height() ;
261 return is_in_spatial_extent(x);
269 return is_in_spatial_extent(
to_latlon(x));
275 inline bool is_valid(time_descriptor t)
const noexcept
277 return t >= 0 and t < _times.size();
285 return std::find(_times.begin(), _times.end(), t) != _times.end();
293 return t >= _times.front() and t <= _times.back();
302 inline std::optional<value_type>
at(location_descriptor x, time_descriptor t)
const noexcept
317 return [
this](location_descriptor x, time_descriptor t) {
return this->
at(x, t); };
331 return x.col *
width() + x.col;
351 const auto [lat, lon] = x;
352 const int col = (lon - _gT[0]) / _gT[1];
353 const int row = (lat - _gT[3]) / _gT[5];
376 const int col = x %
width();
377 const int row = x /
width();
390 return rowcol(c.row, c.col);
420 const auto [col, row] = x;
421 const auto lon = _gT[1] * col + _gT[2] * row + _gT[1] * 0.5 + _gT[2] * 0.5 + _gT[0];
422 const auto lat = _gT[4] * col + _gT[5] * row + _gT[4] * 0.5 + _gT[5] * 0.5 + _gT[3];
455 "latlon does not belong to spatial extent and can not be reprojected to a cell centroid.");
469 return lonlat(x.lon, x.lat);
475 inline latlon
to_latlon(
const lonlat &x)
const noexcept
477 return latlon(x.lat, x.lon);
481 inline value_type nodata_first_band()
const
483 return _dataset.get().GetRasterBand(1)->GetNoDataValue();
489 std::optional<double> read(
int col,
int row,
int band)
const
491 const int nXSize = 1;
492 auto line = (
float *)CPLMalloc(
sizeof(
float) * nXSize);
495 const auto err = _dataset.
band(band + 1).RasterIO(GF_Read, col, row, 1, 1, line, nXSize, 1, GDT_Float32, 0, 0);
498 throw std::invalid_argument(
"received negative or invalid col row or band index");
500 const double val =
static_cast<double>(*line);
502 if (val != this->
NA())
508 template <
typename Callable>
509 void export_to_geotiff(Callable f, time_type start, time_type end,
const std::filesystem::path &file)
const
512 const auto times = ranges::views::iota(start, end);
516 _dataset.get().GetDriver()->Create(file.string().c_str(),
width(),
height(),
depth, GDT_Float32, NULL);
520 for (time_descriptor t :
times)
522 auto fun = [f, t,
this](location_descriptor x) {
523 const auto o = f(x, t);
524 return o.has_value() ?
static_cast<float>(o.value()) : this->
NA();
527 auto buffer =
locations() | ranges::views::transform(fun) | ranges::to<std::vector>();
528 GDALRasterBand *sink_band = sink->GetRasterBand(band);
529 [[maybe_unused]]
const auto err2 = sink_band->RasterIO(GF_Write, 0, 0,
width(),
height(), buffer.data(),
531 sink_band->SetNoDataValue(
NA());
537 std::vector<double> geo_transform(6);
538 _dataset.get().GetGeoTransform(geo_transform.data());
539 sink->SetGeoTransform(geo_transform.data());
540 sink->SetProjection(_dataset.get().GetProjectionRef());
542 GDALClose(
static_cast<GDALDatasetH
>(sink));
546 void counts_to_shapefile(std::map<latlon, int> counts,
const std::filesystem::path &file)
const
548 std::string driver_name =
"ESRI Shapefile";
549 GDALDriver *poDriver;
551 poDriver = GetGDALDriverManager()->GetDriverByName(driver_name.c_str());
552 assert(poDriver != NULL);
555 poDS = poDriver->Create(file.string().c_str(), 0, 0, 0, GDT_Unknown, NULL);
556 assert(poDS != NULL);
559 poLayer = poDS->CreateLayer(
"sample", NULL, wkbPoint, NULL);
560 assert(poLayer != NULL);
562 OGRFieldDefn oField(
"count", OFTInteger);
563 if (poLayer->CreateField(&oField) != OGRERR_NONE)
565 throw(std::string(
"Creating sample size field failed."));
568 for (
auto const &it : counts)
570 const auto [y, x] = it.first;
571 int sample_size = it.second;
573 OGRFeature *poFeature;
574 poFeature = OGRFeature::CreateFeature(poLayer->GetLayerDefn());
576 poFeature->SetField(
"count", sample_size);
582 poFeature->SetGeometry(&pt);
584 if (poLayer->CreateFeature(poFeature) != OGRERR_NONE)
586 throw(std::string(
"Failed to create feature in shapefile."));
588 OGRFeature::DestroyFeature(poFeature);
594 void points_to_shapefile(std::vector<latlon> coords,
const std::filesystem::path &file)
const
596 std::string driver_name =
"ESRI Shapefile";
597 GDALDriver *poDriver;
599 poDriver = GetGDALDriverManager()->GetDriverByName(driver_name.c_str());
600 assert(poDriver != NULL);
603 poDS = poDriver->Create(file.string().c_str(), 0, 0, 0, GDT_Unknown, NULL);
604 assert(poDS != NULL);
607 poLayer = poDS->CreateLayer(
"sample", NULL, wkbPoint, NULL);
608 assert(poLayer != NULL);
610 for (
auto const [y, x] : coords)
612 OGRFeature *poFeature;
613 poFeature = OGRFeature::CreateFeature(poLayer->GetLayerDefn());
619 poFeature->SetGeometry(&pt);
621 if (poLayer->CreateFeature(poFeature) != OGRERR_NONE)
623 throw(std::string(
"Failed to create feature in shapefile."));
625 OGRFeature::DestroyFeature(poFeature);
631 bool is_in_spatial_extent(
const latlon &x)
const noexcept
633 const auto [lat, lon] = x;
641 void check_times_equals_depth()
643 if (_times.size() !=
depth())
646 "the size of times argument should be equal to the depth of the given dataset: size is ");
647 message += std::to_string(_times.size());
648 message +=
", should be ";
649 message += std::to_string(
depth());
651 throw std::runtime_error(message.c_str());
656 latlon compute_origin(
const std::vector<double> &gT)
const
658 return latlon(gT[3], gT[0]);
663template <
typename Time> std::ostream &
operator<<(std::ostream &os,
const raster<Time> &r)
extent of a raster grid object
Definition extent.hpp:19
constexpr value_type lon_min() const noexcept
Minimal longitude of the spatial extent.
Definition extent.hpp:89
constexpr value_type lat_min() const noexcept
Minimal latitude of the spatial extent.
Definition extent.hpp:77
constexpr value_type lon_max() const noexcept
Get the maximal longitude of the spatial extent.
Definition extent.hpp:95
constexpr value_type lat_max() const noexcept
Maximal latitude of the spatial extent.
Definition extent.hpp:83
Definition gdalcpp.hpp:229
auto & band(unsigned int i) const
Fetch a band object for a dataset, zeroth-based numbering.
Definition gdalcpp.hpp:317
Discrete spatio-temporal variations of an environmental variable.
Definition raster.hpp:38
geography::extent< decimal_degree > get_extent() const noexcept
Extent of the spatial grid.
Definition raster.hpp:183
static raster from_file(const std::filesystem::path &file, const std::vector< time_type > ×)
Read the raster from an input file in the geotiff format.
Definition raster.hpp:117
bool is_recorded(const time_type &t) const noexcept
Search for the exact time in the list of time points recorded by the raster.
Definition raster.hpp:283
auto times() const noexcept
Time descriptors (unique identifiers) of the dataset bands.
Definition raster.hpp:226
int depth() const noexcept
Depth of the spatial grid.
Definition raster.hpp:201
location_descriptor to_descriptor(const colrow &x) const noexcept
Location descriptor of the cell identified by its column/row.
Definition raster.hpp:328
std::ostream & write(std::ostream &stream) const
Raster is streamable.
Definition raster.hpp:153
latlon to_latlon(const colrow &x) const noexcept
Latitude and longitude of the cell identified by its column/row.
Definition raster.hpp:417
bool is_in_interval(const time_type &t) const noexcept
Check if the exact time point falls between the first and last date of record.
Definition raster.hpp:291
latlon to_latlon(location_descriptor x) const noexcept
Latitude and longitude of the cell to which the given coordinate belongs.
Definition raster.hpp:397
latlon origin() const noexcept
Origin of the spatial grid.
Definition raster.hpp:170
geography::resolution< decimal_degree > get_resolution() const noexcept
Resolution of the spatial grid.
Definition raster.hpp:177
auto to_view() const noexcept
Makes the raster a callable with signature std::optional<value_type> (location_descriptor x,...
Definition raster.hpp:315
rowcol to_rowcol(const latlon &x) const noexcept
Row and column of the cell to which the given coordinate belongs.
Definition raster.hpp:361
colrow to_colrow(location_descriptor x) const noexcept
Column and row of the cell to which the given descriptor belongs.
Definition raster.hpp:373
void to_shapefile(std::map< latlon, int > counts, const std::filesystem::path &file) const
Export geolocalized counts as spatial-points to a shapefile.
Definition raster.hpp:147
int height() const noexcept
Height of the spatial grid.
Definition raster.hpp:195
bool contains(const lonlat &x) const noexcept
Check if the raster contains a coordinate.
Definition raster.hpp:267
auto locations() const noexcept
Location descriptors (unique identifiers) of the grid cells.
Definition raster.hpp:219
value_type NA() const noexcept
NA value, as read in the first band of the dataset.
Definition raster.hpp:207
lonlat to_lonlat(location_descriptor x) const noexcept
Latitude and longitude of the cell to which the given coordinate belongs.
Definition raster.hpp:407
std::optional< value_type > at(location_descriptor x, time_descriptor t) const noexcept
Read the value at location x and time t.
Definition raster.hpp:302
void to_geotiff(Callable f, time_type start, time_type end, const std::filesystem::path &file) const
Export a spatio-temporal raster to a Geotiff file.
Definition raster.hpp:131
raster(const std::filesystem::path &file, const std::vector< time_type > ×)
Constructor.
Definition raster.hpp:101
location_descriptor to_descriptor(const latlon &x) const noexcept
Location descriptor of the cell to which the given coordinate belongs.
Definition raster.hpp:338
bool is_valid(time_descriptor t) const noexcept
Check if the time descriptor is a valid index.
Definition raster.hpp:275
bool is_valid(const colrow &x) const noexcept
Check if the coordinate describes a valid location of the spatial grid.
Definition raster.hpp:243
latlon to_centroid(const latlon &x) const
Reprojects a coordinate to the centroid of the cell it belongs.
Definition raster.hpp:452
rowcol to_rowcol(location_descriptor x) const noexcept
Column and row of the cell to which the given descriptor belongs.
Definition raster.hpp:386
colrow to_colrow(const latlon &x) const noexcept
Column and row of the cell to which the given coordinate belongs.
Definition raster.hpp:348
bool is_valid(const rowcol &x) const noexcept
Check if the coordinate describes a valid location of the spatial grid.
Definition raster.hpp:251
bool is_valid(location_descriptor x) const noexcept
Check if a descriptor describes a valid location of the spatial grid.
Definition raster.hpp:235
lonlat to_lonlat(const colrow &x) const noexcept
Longitude and latitude of the deme identified by its column/row.
Definition raster.hpp:440
bool contains(const latlon &x) const noexcept
Check if the raster contains a coordinate.
Definition raster.hpp:259
latlon to_latlon(const rowcol &x) const noexcept
Latitude and longitude of the cell identified by its row/column.
Definition raster.hpp:430
int width() const noexcept
Width of the spatial grid.
Definition raster.hpp:189
void to_shapefile(std::vector< latlon > points, const std::filesystem::path &file) const
Export spatial points to a shapefile.
Definition raster.hpp:139
Resolution of a spatial grid.
Definition resolution.hpp:16
constexpr value_type lat() const noexcept
Gets latitude resolution.
Definition resolution.hpp:35
constexpr value_type lon() const noexcept
Gets longitude resolution.
Definition resolution.hpp:41
Geospatial data formatting and processing.
Definition geography.hpp:17
std::ostream & operator<<(std::ostream &stream, const C &c)
GridCoordinates are streamable.
Definition coordinates.hpp:51
Grid coordinates.
Definition coordinates.hpp:82
value_type col
Column descriptor.
Definition coordinates.hpp:87
value_type row
Row descriptor.
Definition coordinates.hpp:90
Geographic coordinates.
Definition coordinates.hpp:179
Geographic coordinates.
Definition coordinates.hpp:218
Grid coordinates.
Definition coordinates.hpp:61