sgsPy
structurally guided sampling
Loading...
Searching...
No Matches
helper.h
Go to the documentation of this file.
1/******************************************************************************
2 *
3 * Project: sgs
4 * Purpose: helper functions
5 * Author: Joseph Meyer
6 * Date: September, 2025
7 *
8 ******************************************************************************/
9
14
15#pragma once
16
17#include <iostream>
18#include <filesystem>
19#include <mutex>
20
21#include <boost/unordered/unordered_flat_map.hpp>
22#include <xoshiro.h>
23#include <gdal_priv.h>
24#include <ogrsf_frmts.h>
25#include <ogr_core.h>
26
27#define MAXINT8 127
28#define MAXINT16 32767
29
30namespace sgs {
31namespace helper {
32
37struct Index {
38 int x = -1;
39 int y = -1;
40};
41
46struct Field {
47 std::string fname; //field name
48 int fval; //field value
49};
50
88 GDALRasterBand *p_band = nullptr;
89 void *p_buffer = nullptr;
90 GDALDataType type = GDT_Unknown;
91 size_t size = 0;
92 std::string name = "";
93 double nan = -1;
94 int xBlockSize = -1;
95 int yBlockSize = -1;
96 std::mutex *p_mutex = nullptr;
97};
98
123 GDALDataset *p_dataset = nullptr;
124 std::string filename = "";
125
126};
127
139inline void
141 size_t maxStrata,
142 GDALDataType *p_type,
143 size_t *p_size
144) {
145 if (maxStrata <= MAXINT8) {
146 *p_type = GDT_Int8;
147 *p_size = sizeof(int8_t);
148 }
149 else if (maxStrata <= MAXINT16) {
150 *p_type = GDT_Int16;
151 *p_size = sizeof(int16_t);
152 }
153 else {
154 *p_type = GDT_Int32;
155 *p_size = sizeof(int32_t);
156 }
157}
158
171template <typename T>
172inline T
174 GDALDataType type,
175 void *p_data,
176 size_t index
177) {
178 switch (type) {
179 case GDT_Int8:
180 return static_cast<T>(((int8_t *)p_data)[index]);
181 case GDT_UInt16:
182 return static_cast<T>(((uint16_t *)p_data)[index]);
183 case GDT_Int16:
184 return static_cast<T>(((int16_t *)p_data)[index]);
185 case GDT_UInt32:
186 return static_cast<T>(((uint32_t *)p_data)[index]);
187 case GDT_Int32:
188 return static_cast<T>(((int32_t *)p_data)[index]);
189 case GDT_Float32:
190 return static_cast<T>(((float *)p_data)[index]);
191 case GDT_Float64:
192 return static_cast<T>(((double *)p_data)[index]);
193 default:
194 throw std::runtime_error("raster pixel data type not supported.");
195 }
196}
197
212inline void
214 GDALDataType type,
215 void *p_data,
216 size_t index,
217 bool isNan,
218 size_t strata
219) {
220 switch(type) {
221 case GDT_Int8:
222 reinterpret_cast<int8_t *>(p_data)[index] = isNan ?
223 static_cast<int8_t>(-1) :
224 static_cast<int8_t>(strata);
225 break;
226 case GDT_Int16:
227 reinterpret_cast<int16_t *>(p_data)[index] = isNan ?
228 static_cast<int16_t>(-1) :
229 static_cast<int16_t>(strata);
230 break;
231 case GDT_Int32:
232 reinterpret_cast<int32_t *>(p_data)[index] = isNan ?
233 static_cast<int32_t>(-1) :
234 static_cast<int32_t>(strata);
235 break;
236 default:
237 throw std::runtime_error("strata pixel data type not supported.");
238 }
239}
240
250inline void
252 switch(type) {
253 case GDT_UInt32:
254 std::cout << "**warning** the pixel type of one of the bands given is an unsigned 32 bit integer. This may result in undefined behavior if the value is not castable to a 32-bit signed integer type." << std::endl;
255 break;
256 case GDT_Float32:
257 std::cout << "**warning** the pixel type of one of the bands given is a 32 bit floating point value. This may result in undefined behavior if the value is not castable to a 32-bit signed integer type." << std::endl;
258 break;
259 case GDT_Float64:
260 std::cout << "**warning** the pixel type of one of the bands given is a 64 bit floating point value. This may result in undefined behavior if the value is not castable to a 32-bit signed integer type." << std::endl;
261 default:
262 //don't care if converting the type to int32 won't result in overflow problems
263 break;
264 }
265}
266
293inline GDALDataset *
295 std::string driverName,
296 int width,
297 int height,
298 double *geotransform,
299 std::string projection)
300{
301 GDALDriver *p_driver = GetGDALDriverManager()->GetDriverByName(driverName.c_str());
302 if (!p_driver) {
303 throw std::runtime_error("unable to find dataset driver.");
304 }
305
306 GDALDataset *p_dataset = p_driver->Create(
307 "",
308 width,
309 height,
310 0,
311 GDT_Unknown,
312 nullptr
313 );
314 if (!p_dataset) {
315 throw std::runtime_error("unable to create dataset with driver.");
316 }
317
318 CPLErr err = p_dataset->SetGeoTransform(geotransform);
319 if (err) {
320 throw std::runtime_error("error setting geotransform.");
321 }
322
323 err = p_dataset->SetProjection(projection.c_str());
324 if (err) {
325 throw std::runtime_error("error setting projection.");
326 }
327
328 return p_dataset;
329}
330
374inline GDALDataset *
376 std::string filename,
377 std::string driverName,
378 int width,
379 int height,
380 double *geotransform,
381 std::string projection,
382 RasterBandMetaData *bands,
383 size_t bandCount,
384 bool useTiles,
385 std::map<std::string, std::string>& driverOptions)
386{
387 GDALDriver *p_driver = GetGDALDriverManager()->GetDriverByName(driverName.c_str());
388 if (!p_driver) {
389 throw std::runtime_error("unable to find dataset driver.");
390 }
391
392 char **papszOptions = nullptr;
393 if (useTiles) {
394 if (driverOptions.find("TILED") == driverOptions.end()) {
395 papszOptions = CSLSetNameValue(papszOptions, "TILED", "YES");
396 }
397 if (driverOptions.find("BLOCKXSIZE") == driverOptions.end()) {
398 const char *xBlockSizeOption = std::to_string(bands[0].xBlockSize).c_str();
399 papszOptions = CSLSetNameValue(papszOptions, "BLOCKXSIZE", xBlockSizeOption);
400 }
401 if (driverOptions.find("BLOCKYSIZE") == driverOptions.end()) {
402 const char *yBlockSizeOption = std::to_string(bands[0].yBlockSize).c_str();
403 papszOptions = CSLSetNameValue(papszOptions, "BLOCKYSIZE", yBlockSizeOption);
404 }
405 }
406
407 for (auto const& [key, val] : driverOptions) {
408 papszOptions = CSLSetNameValue(papszOptions, key.c_str(), val.c_str());
409 }
410
411 GDALDataset *p_dataset = p_driver->Create(
412 filename.c_str(),
413 width,
414 height,
415 bandCount,
416 bands[0].type,
417 papszOptions
418 );
419 CSLDestroy(papszOptions);
420
421 if (!p_dataset) {
422 throw std::runtime_error("unable to create dataset with driver.");
423 }
424
425 CPLErr err = p_dataset->SetGeoTransform(geotransform);
426 if (err) {
427 throw std::runtime_error("error setting geotransform.");
428 }
429
430 err = p_dataset->SetProjection(projection.c_str());
431 if (err) {
432 throw std::runtime_error("error setting projection.");
433 }
434
435 for (size_t band = 0; band < bandCount; band++) {
436 GDALRasterBand *p_band = p_dataset->GetRasterBand(band + 1);
437 p_band->SetDescription(bands[band].name.c_str());
438 p_band->SetNoDataValue(bands[band].nan);
439 p_band->GetBlockSize(&bands[band].xBlockSize, &bands[band].yBlockSize);
440 bands[band].p_band = p_band;
441 }
442
443 return p_dataset;
444}
445
446
466inline void
468 GDALDataset *p_dataset,
469 RasterBandMetaData& band)
470{
471 //allocate data buffer if it has not been allocated yet
472 if (!band.p_buffer) {
473 band.p_buffer = VSIMalloc3(
474 p_dataset->GetRasterXSize(),
475 p_dataset->GetRasterYSize(),
476 band.size
477 );
478 }
479
480 CPLErr err;
481 char **papszOptions = nullptr;
482 std::string datapointer = std::to_string((size_t)band.p_buffer);
483 papszOptions = CSLSetNameValue(papszOptions, "DATAPOINTER", datapointer.c_str());
484
485 err = p_dataset->AddBand(band.type, papszOptions);
486 CSLDestroy(papszOptions);
487 if (err) {
488 throw std::runtime_error("unable to add band to dataset.");
489 }
490
491 GDALRasterBand *p_band = p_dataset->GetRasterBand(p_dataset->GetRasterCount());
492 p_band->SetNoDataValue(band.nan);
493 p_band->SetDescription(band.name.c_str());
494 band.p_band = p_band;
495}
496
537inline void
539 GDALDataset *p_dataset,
540 RasterBandMetaData& band,
541 std::string tempFolder,
542 std::string key,
543 std::vector<VRTBandDatasetInfo>& VRTBandInfo,
544 std::map<std::string, std::string>& driverOptions)
545{
546 std::filesystem::path tmpPath = tempFolder;
547 std::filesystem::path tmpName = "strat_breaks_" + key + ".tif";
548 tmpPath = tmpPath / tmpName;
549
551 info.filename = tmpPath.string();
552
553 bool useTiles = band.xBlockSize != p_dataset->GetRasterXSize() &&
554 band.yBlockSize != p_dataset->GetRasterYSize();
555
556 double geotransform[6];
557 CPLErr err = p_dataset->GetGeoTransform(geotransform);
558 if (err) {
559 throw std::runtime_error("unable to get geotransform from dataset.");
560 }
561
563 info.filename,
564 "GTiff",
565 p_dataset->GetRasterXSize(),
566 p_dataset->GetRasterYSize(),
567 geotransform,
568 std::string(p_dataset->GetProjectionRef()),
569 &band,
570 1,
571 useTiles,
572 driverOptions
573 );
574
575 GDALRasterBand *p_band = info.p_dataset->GetRasterBand(1);
576 p_band->SetDescription(band.name.c_str());
577 p_band->SetNoDataValue(band.nan);
578 p_band->GetBlockSize(&band.xBlockSize, &band.yBlockSize);
579 band.p_band = p_band;
580
581 VRTBandInfo.push_back(info);
582}
583
584
598inline void
600 GDALDataset *p_dataset,
601 RasterBandMetaData& band,
603) {
604 char **papszOptions = nullptr;
605 const char *filename = info.filename.c_str();
606 papszOptions = CSLSetNameValue(papszOptions, "subclass", "VRTRawRasterBand");
607 papszOptions = CSLSetNameValue(papszOptions, "SourceFilename", filename);
608
609 CPLErr err = p_dataset->AddBand(band.type, papszOptions);
610 CSLDestroy(papszOptions);
611 if (err) {
612 throw std::runtime_error("unable to add band to dataset.");
613 }
614
615 GDALRasterBand *p_VRTBand = p_dataset->GetRasterBand(p_dataset->GetRasterCount());
616 p_VRTBand->SetDescription(band.name.c_str());
617 p_VRTBand->SetNoDataValue(band.nan);
618}
619
642inline void
644 RasterBandMetaData& band,
645 void *p_buffer,
646 int xBlockSize,
647 int yBlockSize,
648 int xBlock,
649 int yBlock,
650 int xValid,
651 int yValid,
652 bool read,
653 bool threaded = true)
654{
655 CPLErr err;
656 bool useBlock = xBlockSize == band.xBlockSize &&
657 yBlockSize == band.yBlockSize;
658
659 if (threaded) {
660 band.p_mutex->lock();
661 }
662 if (useBlock && read) {
663 err = read ?
664 band.p_band->ReadBlock(xBlock, yBlock, p_buffer) :
665 band.p_band->WriteBlock(xBlock, yBlock, p_buffer);
666 }
667 else {
668 err = band.p_band->RasterIO(
669 read ? GF_Read : GF_Write,
670 xBlock * xBlockSize,
671 yBlock * yBlockSize,
672 xValid,
673 yValid,
674 p_buffer,
675 xBlockSize,
676 yBlockSize,
677 band.type,
678 0,
679 0
680 );
681 }
682 if (threaded) {
683 band.p_mutex->unlock();
684 }
685 if (err) {
686 throw read ?
687 std::runtime_error("unable to read block from raster.") :
688 std::runtime_error("unable to write block to raster.");
689 }
690}
691
699inline void
700addPoint(OGRPoint *p_point, OGRLayer *p_layer) {
701 OGRFeature *p_feature = OGRFeature::CreateFeature(p_layer->GetLayerDefn());
702 p_feature->SetGeometry(p_point);
703 p_layer->CreateFeature(p_feature);
704 OGRFeature::DestroyFeature(p_feature);
705}
706
715inline void
716addPoint(const OGRPoint *p_point, OGRLayer *p_layer) {
717 OGRFeature *p_feature = OGRFeature::CreateFeature(p_layer->GetLayerDefn());
718 p_feature->SetGeometry(p_point);
719 p_layer->CreateFeature(p_feature);
720 OGRFeature::DestroyFeature(p_feature);
721}
722
731inline void
732addPoint(OGRPoint *p_point, OGRLayer *p_layer, Field *p_field) {
733 OGRFeature *p_feature = OGRFeature::CreateFeature(p_layer->GetLayerDefn());
734 p_feature->SetGeometry(p_point);
735 p_feature->SetField(p_field->fname.c_str(), p_field->fval);
736 p_layer->CreateFeature(p_feature);
737 OGRFeature::DestroyFeature(p_feature);
738}
739
749inline void
750addPoint(const OGRPoint *p_point, OGRLayer *p_layer, Field *p_field) {
751 OGRFeature *p_feature = OGRFeature::CreateFeature(p_layer->GetLayerDefn());
752 p_feature->SetGeometry(p_point);
753 p_feature->SetField(p_field->fname.c_str(), p_field->fval);
754 p_layer->CreateFeature(p_feature);
755 OGRFeature::DestroyFeature(p_feature);
756}
757
766inline void
767addPoint(OGRPoint *p_point, OGRLayer *p_layer, std::vector<Field *> *p_fields) {
768 OGRFeature *p_feature = OGRFeature::CreateFeature(p_layer->GetLayerDefn());
769 p_feature->SetGeometry(p_point);
770 for (size_t i = 0; i < p_fields->size(); i++) {
771 p_feature->SetField((*p_fields)[i]->fname.c_str(), (*p_fields)[i]->fval);
772 }
773 p_layer->CreateFeature(p_feature);
774 OGRFeature::DestroyFeature(p_feature);
775}
776
786inline void
787addPoint(const OGRPoint *p_point, OGRLayer *p_layer, std::vector<Field *> *p_fields) {
788 OGRFeature *p_feature = OGRFeature::CreateFeature(p_layer->GetLayerDefn());
789 p_feature->SetGeometry(p_point);
790 for (size_t i = 0; i < p_fields->size(); i++) {
791 p_feature->SetField((*p_fields)[i]->fname.c_str(), (*p_fields)[i]->fval);
792 }
793 p_layer->CreateFeature(p_feature);
794 OGRFeature::DestroyFeature(p_feature);
795}
796
797
810template <typename T>
811inline T
812point2index(double xCoord, double yCoord, double *IGT, T width) {
813 T x = static_cast<T>(IGT[0] + xCoord * IGT[1] + yCoord * IGT[2]);
814 T y = static_cast<T>(IGT[3] + xCoord * IGT[4] + yCoord * IGT[5]);
815
816 T index = y * width + x;
817 return index;
818}
819
832class Variance {
833 private:
834 int64_t k = 0; //count
835 double M = 0; //running mean
836 double S = 0; //sum of squares
837 double oldM = 0;
838
839 public:
840 inline void
846 update(double x) {
847 k++;
848 oldM = M;
849
850 //update running mean
851 M = M + (x - M) / static_cast<double>(k);
852
853 //update sum of squares
854 S = S + (x - M) * (x - oldM);
855 }
856
862 inline double
864 return M;
865 }
866
873 inline double
875 double variance = S / static_cast<double>(k);
876 return std::sqrt(variance);
877 }
878
884 inline int64_t
886 return this->k;
887 }
888};
889
939inline uint64_t
940getProbabilityMultiplier(double width, double height, double pixelWidth, double pixelHeight, int startMult, int numSamples, bool useMindist, double accessibleArea) {
941 double numer = static_cast<double>(numSamples * startMult * (useMindist ? 3 : 1));
942 double denom = height * width;
943
944 if (accessibleArea != -1) {
945 double totalArea = width * pixelWidth * height * pixelHeight;
946 numer *= (totalArea / accessibleArea);
947 }
948
949 if (numer > denom) {
950 return 0;
951 }
952
953 uint8_t bits = static_cast<uint8_t>(std::ceil(std::log2(denom) - std::log2(numer)));
954 return (1 << bits) - 1;
955}
956
977private:
978 std::vector<bool> randVals;
979 size_t randValIndex = 0;
980 uint64_t multiplier = 0;
981 xso::xoshiro_4x64_plus *p_rng = nullptr;
982 bool alwaysTrue = false;
983
984public:
994 RandValController(int xBlockSize, int yBlockSize, uint64_t multiplier, xso::xoshiro_4x64_plus *p_rng) {
995 if (multiplier == 0) {
996 this->alwaysTrue = true;
997 }
998 else {
999 this->randVals.resize(xBlockSize * yBlockSize);
1000 this->randValIndex = static_cast<size_t>(xBlockSize * yBlockSize);
1001 this->multiplier = multiplier;
1002 this->p_rng = p_rng;
1003 }
1004 }
1005
1018 inline void
1020 if (alwaysTrue) {
1021 return;
1022 }
1023
1024 for (size_t i = 0; i < randValIndex; i++) {
1025 randVals[i] = (((*p_rng)() >> 11) & multiplier) == multiplier;
1026 }
1027 randValIndex = 0;
1028 }
1029
1033 inline bool
1034 next(void) {
1035 if (alwaysTrue) {
1036 return true;
1037 }
1038
1039 bool retval = randVals[randValIndex];
1040 randValIndex++;
1041 return retval;
1042 }
1043};
1044
1050 inline std::size_t operator()(const std::pair<int, int> &v) const {
1051 std::size_t h1 = std::hash<int>{}(v.first);
1052 std::size_t h2 = std::hash<int>{}(v.second);
1053 return h1 ^ (h2 + 0x9e3779b9 + (h1 << 6) + (h1 >> 2));
1054 }
1055};
1056
1061typedef boost::unordered::unordered_flat_map<std::pair<int, int>, std::vector<std::pair<double, double>>, PointHash> NeighborMap;
1062
1072inline bool is_valid_sample(double x, double y, NeighborMap& neighbor_map, float mindist, float mindist_sq) {
1073 int cx = static_cast<int>(std::floor(x / mindist));
1074 int cy = static_cast<int>(std::floor(y / mindist));
1075
1076 for (int dx = -1; dx <= 1; dx++) {
1077 for (int dy = -1; dy <= 1; dy++) {
1078 std::pair<int, int> neighbor = {cx + dx, cy + dy};
1079
1080 auto it = neighbor_map.find(neighbor);
1081 if (it == neighbor_map.end())
1082 continue;
1083
1084 for (const auto &[nx, ny] : it->second) {
1085 float dxp = x - nx;
1086 float dyp = y - ny;
1087 float dist_sq = dxp * dxp + dyp * dyp;
1088
1089 if (dist_sq < mindist_sq) {
1090 return false;
1091 }
1092 }
1093 }
1094 }
1095
1096 // add point to neighborhood
1097 neighbor_map[{cx, cy}].emplace_back(x, y);
1098 return true;
1099}
1100
1107
1108inline std::pair<double, double> sample_to_point(double *GT, Index &index) {
1109 double px = index.x + 0.5;
1110 double py = index.y + 0.5;
1111 double x = GT[0] + px * GT[1] + py * GT[2];
1112 double y = GT[3] + px * GT[4] + py * GT[5];
1113
1114 return {x, y};
1115}
1116
1124
1125inline std::pair<double, double> sample_to_point(double *GT, int xs, int ys) {
1126 double px = xs + 0.5;
1127 double py = ys + 0.5;
1128 double x = GT[0] + px * GT[1] + py * GT[2];
1129 double y = GT[3] + px * GT[4] + py * GT[5];
1130
1131 return {x, y};
1132}
1133
1134} //namespace helper
1135} //namespace sgs
void calculateRandValues(void)
Definition helper.h:1019
bool next(void)
Definition helper.h:1034
RandValController(int xBlockSize, int yBlockSize, uint64_t multiplier, xso::xoshiro_4x64_plus *p_rng)
Definition helper.h:994
Definition helper.h:832
int64_t getCount()
Definition helper.h:885
void update(double x)
Definition helper.h:846
double getStdev()
Definition helper.h:874
double getMean()
Definition helper.h:863
boost::unordered::unordered_flat_map< std::pair< int, int >, std::vector< std::pair< double, double > >, PointHash > NeighborMap
Definition helper.h:1061
uint64_t getProbabilityMultiplier(double width, double height, double pixelWidth, double pixelHeight, int startMult, int numSamples, bool useMindist, double accessibleArea)
Definition helper.h:940
void rasterBandIO(RasterBandMetaData &band, void *p_buffer, int xBlockSize, int yBlockSize, int xBlock, int yBlock, int xValid, int yValid, bool read, bool threaded=true)
Definition helper.h:643
void addBandToVRTDataset(GDALDataset *p_dataset, RasterBandMetaData &band, VRTBandDatasetInfo &info)
Definition helper.h:599
GDALDataset * createDataset(std::string filename, std::string driverName, int width, int height, double *geotransform, std::string projection, RasterBandMetaData *bands, size_t bandCount, bool useTiles, std::map< std::string, std::string > &driverOptions)
Definition helper.h:375
std::pair< double, double > sample_to_point(double *GT, Index &index)
Definition helper.h:1108
void setStrataPixelDependingOnType(GDALDataType type, void *p_data, size_t index, bool isNan, size_t strata)
Definition helper.h:213
void addBandToMEMDataset(GDALDataset *p_dataset, RasterBandMetaData &band)
Definition helper.h:467
T getPixelValueDependingOnType(GDALDataType type, void *p_data, size_t index)
Definition helper.h:173
void printTypeWarningsForInt32Conversion(GDALDataType type)
Definition helper.h:251
bool is_valid_sample(double x, double y, NeighborMap &neighbor_map, float mindist, float mindist_sq)
Definition helper.h:1072
T point2index(double xCoord, double yCoord, double *IGT, T width)
Definition helper.h:812
void createVRTBandDataset(GDALDataset *p_dataset, RasterBandMetaData &band, std::string tempFolder, std::string key, std::vector< VRTBandDatasetInfo > &VRTBandInfo, std::map< std::string, std::string > &driverOptions)
Definition helper.h:538
GDALDataset * createVirtualDataset(std::string driverName, int width, int height, double *geotransform, std::string projection)
Definition helper.h:294
void addPoint(OGRPoint *p_point, OGRLayer *p_layer)
Definition helper.h:700
void setStratBandTypeAndSize(size_t maxStrata, GDALDataType *p_type, size_t *p_size)
Definition helper.h:140
#define MAXINT8
Definition helper.h:27
#define MAXINT16
Definition helper.h:28
Definition helper.h:31
Definition pca.h:23
Definition helper.h:46
int fval
Definition helper.h:48
std::string fname
Definition helper.h:47
Definition helper.h:37
int x
Definition helper.h:38
int y
Definition helper.h:39
Definition helper.h:1049
std::size_t operator()(const std::pair< int, int > &v) const
Definition helper.h:1050
Definition helper.h:87
int xBlockSize
Definition helper.h:94
std::mutex * p_mutex
Definition helper.h:96
void * p_buffer
Definition helper.h:89
int yBlockSize
Definition helper.h:95
size_t size
Definition helper.h:91
GDALDataType type
Definition helper.h:90
double nan
Definition helper.h:93
std::string name
Definition helper.h:92
GDALRasterBand * p_band
Definition helper.h:88
Definition helper.h:122
GDALDataset * p_dataset
Definition helper.h:123
std::string filename
Definition helper.h:124