sgsPy
structurally guided sampling
Loading...
Searching...
No Matches
quantiles.h
Go to the documentation of this file.
1/******************************************************************************
2 *
3 * Project: sgs
4 * Purpose: C++ implementation of raster stratification using quantiles
5 * Author: Joseph Meyer
6 * Date: September, 2025
7 *
8 ******************************************************************************/
9
10#include "utils/raster.h"
11#include "utils/helper.h"
12
13#include <condition_variable>
14#include <boost/asio/thread_pool.hpp>
15#include <boost/asio/post.hpp>
16#include <mkl.h>
17
22
23namespace sgs {
24namespace quantiles {
25
59 std::vector<double>& probabilities,
60 std::vector<double>& quantiles)
61{
62 std::vector<float> spProbabilities(probabilities.size());
63 std::vector<float> spQuantiles(quantiles.size());
64
65 //get float vector of probabilities
66 for (size_t i = 0; i < probabilities.size(); i++) {
67 spProbabilities[i] = static_cast<float>(probabilities[i]);
68 }
69
70 //filter out nan values
71 size_t fi = 0;
72 float nan = static_cast<float>(band.nan);
73 size_t pixelCount = static_cast<size_t>(p_raster->getWidth()) * static_cast<size_t>(p_raster->getHeight());
74 std::vector<float> filteredData(pixelCount);
75 for (size_t i = 0; i < pixelCount; i++) {
77 bool isNan = std::isnan(val) || val == nan;
78 if (!isNan) {
79 filteredData[fi] = val;
80 fi ++;
81 }
82 }
83 filteredData.resize(fi + 1);
84
85 //define variables to pass to MKL quantiles calculation function
86 //https://www.intel.com/content/www/us/en/docs/onemkl/developer-reference-c/2025-2/vslsseditquantiles.html
87 VSLSSTaskPtr task;
88 int status;
89 MKL_INT quant_order_n = spQuantiles.size();
90 MKL_INT p = 1;
91 MKL_INT nparams = filteredData.size();
92 MKL_INT xstorage = VSL_SS_MATRIX_STORAGE_ROWS;
93 float *quant_order = spProbabilities.data();
94 float *params = filteredData.data();
95 float *quants = spQuantiles.data();
96
97 //calculate quantiles using MKL
98 status = vslsSSNewTask(&task, &p, &nparams, &xstorage, params, nullptr, nullptr);
99 status = vslsSSEditQuantiles(task, &quant_order_n, quant_order, quants, nullptr, nullptr);
100 status = vslsSSCompute(task, VSL_SS_QUANTS, VSL_SS_METHOD_FAST);
101 status = vslSSDeleteTask(&task);
102
103 //assign double quantile values
104 for(size_t i = 0; i < spQuantiles.size(); i++) {
105 quantiles[i] = static_cast<double>(spQuantiles[i]);
106 }
107}
108
135 std::vector<double>& probabilities,
136 std::vector<double>& quantiles)
137{
138 //filter out nan values
139 size_t fi = 0;
140 size_t pixelCount = static_cast<size_t>(p_raster->getWidth()) * static_cast<size_t>(p_raster->getHeight());
141 std::vector<double> filteredData(pixelCount);
142 for (size_t i = 0; i < pixelCount; i++) {
144 bool isNan = std::isnan(val) || val == band.nan;
145 if (!isNan) {
146 filteredData[fi] = val;
147 fi ++;
148 }
149 }
150 filteredData.resize(fi + 1);
151
152 //define variables to pass to MKL quantiles calculation function
153 //https://www.intel.com/content/www/us/en/docs/onemkl/developer-reference-c/2025-2/vslsseditquantiles.html
154 VSLSSTaskPtr task;
155 int status;
156 MKL_INT quant_order_n = quantiles.size();
157 MKL_INT p = 1;
158 MKL_INT nparams = filteredData.size();
159 MKL_INT xstorage = VSL_SS_MATRIX_STORAGE_ROWS;
160 double *quant_order = probabilities.data();
161 double *params = filteredData.data();
162 double *quants = quantiles.data();
163
164 //calculate quantiles using MKL
165 status = vsldSSNewTask(&task, &p, &nparams, &xstorage, params, nullptr, nullptr);
166 status = vsldSSEditQuantiles(task, &quant_order_n, quant_order, quants, nullptr, nullptr);
167 status = vsldSSCompute(task, VSL_SS_QUANTS, VSL_SS_METHOD_FAST);
168 status = vslSSDeleteTask(&task);
169}
170
237 raster::GDALRasterWrapper *p_raster,
239 std::vector<double>& probabilities,
240 std::vector<double>& quantiles,
241 std::mutex& mutex,
242 std::condition_variable& cv,
243 bool& calculated,
244 double eps)
245{
246 std::vector<float> spProbabilities(probabilities.size());
247 std::vector<float> spQuantiles(quantiles.size());
248
249 //get float vector of probabilities
250 for (size_t i = 0; i < probabilities.size(); i++) {
251 spProbabilities[i] = static_cast<float>(probabilities[i]);
252 }
253
254 int xBlocks = (p_raster->getWidth() + band.xBlockSize - 1) / band.xBlockSize;
255 int yBlocks = (p_raster->getHeight() + band.yBlockSize - 1) / band.yBlockSize;
256
257 float nan = static_cast<float>(band.nan);
258 float spEps = static_cast<float>(eps);
259 void *p_buffer = VSIMalloc3(band.xBlockSize, band.yBlockSize, band.size);
260 float *p_filtered = reinterpret_cast<float *>(VSIMalloc3(band.xBlockSize, band.yBlockSize, sizeof(float)));
261
262 //define variables to pass to MKL quantiles calculation function
263 //https://www.intel.com/content/www/us/en/docs/onemkl/developer-reference-c/2025-2/vslsseditstreamquantiles.html
264 VSLSSTaskPtr task;
265 int status;
266 MKL_INT quant_order_n = spProbabilities.size();
267 MKL_INT p = 1;
268 MKL_INT n = band.xBlockSize * band.yBlockSize;
269 MKL_INT nparams = VSL_SS_SQUANTS_ZW_PARAMS_N;
270 MKL_INT xstorage = VSL_SS_MATRIX_STORAGE_ROWS;
271
272 float *quant_order = reinterpret_cast<float *>(spProbabilities.data());
273 float *quants = reinterpret_cast<float *>(spQuantiles.data());
274
275 status = vslsSSNewTask(&task, &p, &n, &xstorage, p_filtered, nullptr, nullptr);
276
277 //read and compute raster by blocks
278 bool calledEditStreamQuantiles = false;
279 for (int yBlock = 0; yBlock < yBlocks; yBlock++) {
280 for (int xBlock = 0; xBlock < xBlocks; xBlock++) {
281 int xValid, yValid;
282 band.p_mutex->lock();
283 band.p_band->GetActualBlockSize(xBlock, yBlock, &xValid, &yValid);
284 CPLErr err = band.p_band->ReadBlock(xBlock, yBlock, p_buffer);
285 band.p_mutex->unlock();
286 if (err) {
287 throw std::runtime_error("error reading block from band.");
288 }
289
290 int fi = 0;
291 for (int y = 0; y < yValid; y++) {
292 int index = y * band.xBlockSize;
293 for (int x = 0; x < xValid; x++) {
294 float val = helper::getPixelValueDependingOnType<float>(band.type, p_buffer, index);
295 bool isNan = std::isnan(val) || val == nan;
296 if (!isNan) {
297 p_filtered[fi] = val;
298 fi++;
299 }
300 index++;
301 }
302 }
303
304 if (fi == 0) {
305 continue;
306 }
307
308 n = fi;
309 if (!calledEditStreamQuantiles) {
310 //for some reason this has issues if called before the first band of data is loaded,
311 //although it only has to be called once.
312 status = vslsSSEditStreamQuantiles(task, &quant_order_n, quant_order, quants, &nparams, &spEps);
313 calledEditStreamQuantiles = true;
314 }
315 status = vslsSSCompute(task, VSL_SS_STREAM_QUANTS, VSL_SS_METHOD_SQUANTS_ZW_FAST);
316 }
317 }
318
319 //use VSL_SS_METHOD_SQUANTS_ZW (not VSL_SS_METHOD_SQUANTS_ZW_FAST) to get final estimates
320 n = 0;
321 vslsSSCompute(task, VSL_SS_STREAM_QUANTS, VSL_SS_METHOD_SQUANTS_ZW);
322 status = vslSSDeleteTask(&task);
323
324 //assign double quantile values
325 for(size_t i = 0; i < spQuantiles.size(); i++) {
326 quantiles[i] = static_cast<double>(spQuantiles[i]);
327 }
328
329 VSIFree(p_buffer);
330 VSIFree(p_filtered);
331
332 mutex.lock();
333 calculated = true;
334 mutex.unlock();
335 cv.notify_all();
336}
337
338
398 raster::GDALRasterWrapper *p_raster,
400 std::vector<double>& probabilities,
401 std::vector<double>& quantiles,
402 std::mutex& mutex,
403 std::condition_variable& cv,
404 bool& calculated,
405 double eps)
406{
407 int xBlocks = (p_raster->getWidth() + band.xBlockSize - 1) / band.xBlockSize;
408 int yBlocks = (p_raster->getHeight() + band.yBlockSize - 1) / band.yBlockSize;
409
410 void *p_buffer = VSIMalloc3(band.xBlockSize, band.yBlockSize, band.size);
411 double *p_filtered = reinterpret_cast<double *>(VSIMalloc3(band.xBlockSize, band.yBlockSize, sizeof(double)));
412
413 //define variables to pass to MKL quantiles calculation function
414 //https://www.intel.com/content/www/us/en/docs/onemkl/developer-reference-c/2025-2/vslsseditstreamquantiles.html
415 VSLSSTaskPtr task;
416 int status;
417 MKL_INT quant_order_n = probabilities.size();
418 MKL_INT p = 1;
419 MKL_INT n = band.xBlockSize * band.yBlockSize;
420 MKL_INT nparams = VSL_SS_SQUANTS_ZW_PARAMS_N;
421 MKL_INT xstorage = VSL_SS_MATRIX_STORAGE_ROWS;
422
423 double *quant_order = reinterpret_cast<double *>(probabilities.data());
424 double *quants = reinterpret_cast<double *>(quantiles.data());
425
426 vsldSSNewTask(&task, &p, &n, &xstorage, p_filtered, 0, 0);
427
428 //read and compute raster by blocks
429 bool calledEditStreamQuantiles = false;
430 for (int yBlock = 0; yBlock < yBlocks; yBlock++) {
431 for (int xBlock = 0; xBlock < xBlocks; xBlock++) {
432 int xValid, yValid;
433 band.p_mutex->lock();
434 band.p_band->GetActualBlockSize(xBlock, yBlock, &xValid, &yValid);
435 CPLErr err = band.p_band->ReadBlock(xBlock, yBlock, p_buffer);
436 band.p_mutex->unlock();
437 if (err) {
438 throw std::runtime_error("error reading block from band.");
439 }
440
441 int fi = 0;
442 for (int y = 0; y < yValid; y++) {
443 int index = y * band.xBlockSize;
444 for (int x = 0; x < xValid; x++) {
445 double val = helper::getPixelValueDependingOnType<double>(band.type, p_buffer, index);
446 bool isNan = std::isnan(val) || val == band.nan;
447 if (!isNan) {
448 p_filtered[fi] = val;
449 fi++;
450 }
451 index++;
452 }
453 }
454
455 if (fi == 0) {
456 continue;
457 }
458
459 n = fi;
460 if (!calledEditStreamQuantiles) {
461 //for some reason this has issues if called before the first band of data is loaded,
462 //although it only has to be called once.
463 status = vsldSSEditStreamQuantiles(task, &quant_order_n, quant_order, quants, &nparams, &eps);
464 calledEditStreamQuantiles = true;
465 }
466 status = vsldSSCompute(task, VSL_SS_STREAM_QUANTS, VSL_SS_METHOD_SQUANTS_ZW_FAST);
467 }
468 }
469
470 //use VSL_SS_METHOD_SQUANTS_ZW (not VSL_SS_METHOD_SQUANTS_ZW_FAST) to get final estimates
471 n = 0;
472 vsldSSCompute(task, VSL_SS_STREAM_QUANTS, VSL_SS_METHOD_SQUANTS_ZW);
473 status = vslSSDeleteTask(&task);
474
475 mutex.lock();
476 calculated = true;
477 mutex.unlock();
478 cv.notify_all();
479
480 VSIFree(p_buffer);
481 VSIFree(p_filtered);
482}
483
514inline void processMapPixel(
515 size_t index,
517 void * p_dataBuffer,
519 void * p_stratBuffer,
520 std::vector<double>& quantiles,
521 size_t multiplier,
522 bool& mapNan,
523 size_t& mapStrat)
524{
525 double val = helper::getPixelValueDependingOnType<double>(dataBand.type, p_dataBuffer, index);
526 bool isNan = std::isnan(val) || (double)val == dataBand.nan;
527 mapNan |= isNan;
528
529 size_t strat = 0;
530 if (!isNan) {
531 auto it = std::lower_bound(quantiles.begin(), quantiles.end(), val);
532 strat = (it == quantiles.end()) ? quantiles.size() : std::distance(quantiles.begin(), it);
533 }
534
535 helper::setStrataPixelDependingOnType(stratBand.type, p_stratBuffer, index, isNan, strat);
536
537 if (!mapNan) {
538 mapStrat += strat * multiplier;
539 }
540}
541
564inline void
566 size_t index,
567 void *p_data,
568 helper::RasterBandMetaData *p_dataBand,
569 void *p_strat,
570 helper::RasterBandMetaData *p_stratBand,
571 std::vector<double>& quantiles)
572{
573 double val = helper::getPixelValueDependingOnType<double>(p_dataBand->type, p_data, index);
574 bool isNan = std::isnan(val) || val == p_dataBand->nan;
575
576 size_t strat = 0;
577 if (!isNan) {
578 auto it = std::lower_bound(quantiles.begin(), quantiles.end(), val);
579 strat = (it == quantiles.end()) ? quantiles.size() : std::distance(quantiles.begin(), it);
580 }
581
582 helper::setStrataPixelDependingOnType(p_stratBand->type, p_strat, index, isNan, strat);
583}
584
712std::pair<raster::GDALRasterWrapper *, std::unordered_map<std::string, std::vector<double>>>
715 std::map<int, std::vector<double>> userProbabilites,
716 bool map,
717 std::string filename,
718 std::string tempFolder,
719 bool largeRaster,
720 int threadCount,
721 std::map<std::string, std::string> driverOptions,
722 double eps)
723{
724 GDALAllRegister();
725
726 int height = p_raster->getHeight();
727 int width = p_raster->getWidth();
728 double *geotransform = p_raster->getGeotransform();
729 std::string projection = std::string(p_raster->getDataset()->GetProjectionRef());
730
731 GDALDataset *p_dataset = nullptr;
732
733 int bandCount = userProbabilites.size();
734 std::vector<std::vector<double>> probabilities;
735 std::vector<std::string> bandNames = p_raster->getBands();
736
737 std::vector<helper::RasterBandMetaData> dataBands(bandCount);
738 std::vector<helper::RasterBandMetaData> stratBands(bandCount + map);
739 std::vector<helper::VRTBandDatasetInfo> VRTBandInfo;
740
741 bool isMEMDataset = !largeRaster && filename == "";
742 bool isVRTDataset = largeRaster && filename == "";
743
744 std::mutex dataBandMutex;
745 std::mutex stratBandMutex;
746 std::vector<std::mutex> stratBandMutexes(isVRTDataset * (bandCount + map));
747
748 std::string driver;
749 if (isMEMDataset || isVRTDataset) {
750 driver = isMEMDataset ? "MEM" : "VRT";
751 p_dataset = helper::createVirtualDataset(driver, width, height, geotransform, projection);
752 }
753 else {
754 std::filesystem::path filepath = filename;
755 std::string extension = filepath.extension().string();
756
757 if (extension == ".tif") {
758 driver = "Gtiff";
759 }
760 else {
761 throw std::runtime_error("sgs only supports .tif files right now");
762 }
763 }
764
765 //allocate, read, and initialize raster data and breaks information
766 GDALDataType stratPixelType = GDT_Int8;
767 size_t stratPixelSize = 1;
768 size_t band = 0;
769 for (auto const& [key, val] : userProbabilites) {
770 helper::RasterBandMetaData *p_dataBand = &dataBands[band];
771 helper::RasterBandMetaData *p_stratBand = &stratBands[band];
772
773 //get and store metadata from input raster band
774 GDALRasterBand *p_band = p_raster->getRasterBand(key);
775 p_dataBand->p_band = p_band;
776 p_dataBand->type = p_raster->getRasterBandType(key);
777 p_dataBand->size = p_raster->getRasterBandTypeSize(key);
778 p_dataBand->p_buffer = largeRaster ? nullptr : p_raster->getRasterBandBuffer(key);
779 p_dataBand->nan = p_band->GetNoDataValue();
780 p_dataBand->p_mutex = &dataBandMutex;
781 p_band->GetBlockSize(&p_dataBand->xBlockSize, &p_dataBand->yBlockSize);
782
783 //add probabilities
784 probabilities.push_back(val);
785
786 //update metadata of new strat raster
787 size_t maxStrata = val.size() + 1;
788 helper::setStratBandTypeAndSize(maxStrata, &p_stratBand->type, &p_stratBand->size);
789 p_stratBand->name = "strat_" + bandNames[key];
790 p_stratBand->xBlockSize = map ? dataBands[0].xBlockSize : p_dataBand->xBlockSize;
791 p_stratBand->yBlockSize = map ? dataBands[0].yBlockSize : p_dataBand->yBlockSize;
792 p_stratBand->p_mutex = isVRTDataset ? &stratBandMutexes[band] : &stratBandMutex;
793
794 //update dataset with new band information
795 if (isMEMDataset) {
796 helper::addBandToMEMDataset(p_dataset, *p_stratBand);
797 }
798 else if (isVRTDataset) {
799 helper::createVRTBandDataset(p_dataset, *p_stratBand, tempFolder, std::to_string(key), VRTBandInfo, driverOptions);
800 }
801 else {
802 if (stratPixelSize < p_stratBand->size) {
803 stratPixelSize = p_stratBand->size;
804 stratPixelType = p_stratBand->type;
805 }
806 }
807
808 band++;
809 }
810
811 //set multipliers if mapped stratification
812 std::vector<size_t> multipliers(probabilities.size(), 1);
813 if (map) {
814 //determine the stratification band index multiplier for the mapped band
815 for (int i = 0; i < bandCount - 1; i++) {
816 multipliers[i + 1] = multipliers[i] * (probabilities[i].size() + 1);
817 }
818
819 //update info of new strat raster band map
820 helper::RasterBandMetaData *p_stratBand = &stratBands.back();
821 size_t maxStrata = multipliers.back() * (probabilities.back().size() + 1);
822 helper::setStratBandTypeAndSize(maxStrata, &p_stratBand->type, &p_stratBand->size);
823 p_stratBand->name = "strat_map";
824 p_stratBand->xBlockSize = dataBands[0].xBlockSize;
825 p_stratBand->yBlockSize = dataBands[0].yBlockSize;
826 p_stratBand->p_mutex = isVRTDataset ? &stratBandMutexes.back() : &stratBandMutex;
827
828 //update dataset with band information
829 if (isMEMDataset) {
830 helper::addBandToMEMDataset(p_dataset, *p_stratBand);
831 }
832 else if (isVRTDataset) {
833 helper::createVRTBandDataset(p_dataset, *p_stratBand, tempFolder, "map", VRTBandInfo, driverOptions);
834 }
835 else {
836 if (stratPixelSize < p_stratBand->size) {
837 stratPixelSize = p_stratBand->size;
838 stratPixelType = p_stratBand->type;
839 }
840 }
841 }
842
843 //create full non-virtual dataset now that we have all required band information
844 if (!isMEMDataset && !isVRTDataset) {
845 bool useTiles = stratBands[0].xBlockSize != width &&
846 stratBands[0].yBlockSize != height;
847
848 for (size_t band = 0; band < stratBands.size(); band++) {
849 stratBands[band].size = stratPixelSize;
850 stratBands[band].type = stratPixelType;
851 stratBands[band].p_buffer = !largeRaster ? VSIMalloc3(height, width, stratPixelSize) : nullptr;
852 }
853
854 p_dataset = helper::createDataset(
855 filename,
856 driver,
857 width,
858 height,
859 geotransform,
860 projection,
861 stratBands.data(),
862 stratBands.size(),
863 useTiles,
864 driverOptions
865 );
866 }
867
868 std::vector<std::vector<double>> quantiles(probabilities.size());
869 if (largeRaster) {
870 pybind11::gil_scoped_acquire acquire;
871 boost::asio::thread_pool pool(threadCount);
872
873 //initialize synchronization variables
874 std::vector<std::mutex> mutexes(map ? 1 : bandCount);
875 std::vector<std::condition_variable> cvs(map ? 1 : bandCount);
876 bool *quantilesCalculated = reinterpret_cast<bool *>(VSIMalloc2(bandCount, sizeof(bool))); //don't use std::vector<bool> because can't reference individual bools from it
877
878 //call batch processing quantiles function depending on data type
879 for (int i = 0; i < bandCount; i++) {
880 helper::RasterBandMetaData band = dataBands[i];
881 quantiles[i].resize(probabilities[i].size());
882 quantilesCalculated[i] = false;
883 if (band.type != GDT_Float64) {
884 boost::asio::post(pool, std::bind(batchCalcSPQuantiles,
885 p_raster,
886 std::ref(band),
887 std::ref(probabilities[i]),
888 std::ref(quantiles[i]),
889 std::ref(mutexes[map ? 0 : i]),
890 std::ref(cvs[map ? 0 : i]),
891 std::ref(quantilesCalculated[i]),
892 static_cast<double>(eps)
893 ));
894 }
895 else {
896 boost::asio::post(pool, std::bind(batchCalcDPQuantiles,
897 p_raster,
898 std::ref(band),
899 std::ref(probabilities[i]),
900 std::ref(quantiles[i]),
901 std::ref(mutexes[i]),
902 std::ref(cvs[i]),
903 std::ref(quantilesCalculated[i]),
904 static_cast<double>(eps)
905 ));
906 }
907 }
908
909 //iterate through all pixels and update the stratified raster bands
910 if (map) {
911
912 //NOTE: all processing threads in the case of a mapped raster must wait
913 //until quantiles have been calculated for every band.
914 std::unique_lock lock(mutexes[0]);
915 bool allBandsQuantilesCalculated = true;
916 for (int i = 0; i < bandCount; i++) {
917 allBandsQuantilesCalculated &= quantilesCalculated[i];
918 }
919
920 while (!allBandsQuantilesCalculated) {
921 cvs[0].wait(lock);
922 allBandsQuantilesCalculated = true;
923 for (int i = 0; i < bandCount; i++) {
924 allBandsQuantilesCalculated &= quantilesCalculated[i];
925 }
926 }
927
928 //use the first raster band to determine block size
929 int xBlockSize = dataBands[0].xBlockSize;
930 int yBlockSize = dataBands[0].yBlockSize;
931
932 int xBlocks = (p_raster->getWidth() + xBlockSize - 1) / xBlockSize;
933 int yBlocks = (p_raster->getHeight() + yBlockSize - 1) / yBlockSize;
934 int chunkSize = yBlocks / threadCount;
935
936 for (int yBlockStart = 0; yBlockStart < yBlocks; yBlockStart += chunkSize) {
937 int yBlockEnd = std::min(yBlockStart + chunkSize, yBlocks);
938
939 boost::asio::post(pool, [
940 bandCount,
941 xBlockSize,
942 yBlockSize,
943 yBlockStart,
944 yBlockEnd,
945 xBlocks,
946 &dataBands,
947 &stratBands,
948 &quantiles,
949 &multipliers
950 ] {
951 std::vector<void *> dataBuffers(dataBands.size());
952 std::vector<void *> stratBuffers(stratBands.size());
953 for (size_t band = 0; band < dataBuffers.size(); band++) {
954 dataBuffers[band] = VSIMalloc3(xBlockSize, yBlockSize, dataBands[band].size);
955 stratBuffers[band] = VSIMalloc3(xBlockSize, yBlockSize, stratBands[band].size);
956 }
957 stratBuffers.back() = VSIMalloc3(xBlockSize, yBlockSize, stratBands.back().size);
958
959 for (int yBlock = yBlockStart; yBlock < yBlockEnd; yBlock++) {
960 for (int xBlock = 0; xBlock < xBlocks; xBlock++) {
961 int xValid, yValid;
962 dataBands[0].p_mutex->lock();
963 dataBands[0].p_band->GetActualBlockSize(xBlock, yBlock, &xValid, &yValid);
964 dataBands[0].p_mutex->unlock();
965
966 //read raster band data into band buffers
967 for (size_t band = 0; band < static_cast<size_t>(bandCount); band++) {
969 dataBands[band],
970 dataBuffers[band],
971 xBlockSize,
972 yBlockSize,
973 xBlock,
974 yBlock,
975 xValid,
976 yValid,
977 true //read = true
978 );
979 }
980
981 //process blocked band data
982 for (int y = 0; y < yValid; y++) {
983 size_t index = static_cast<size_t>(y * xBlockSize);
984 for (int x = 0; x < xValid; x++) {
985 bool mapNan = false;
986 size_t mapStrat = 0;
987
988 for (size_t band = 0; band < static_cast<size_t>(bandCount); band++) {
990 index,
991 dataBands[band],
992 dataBuffers[band],
993 stratBands[band],
994 stratBuffers[band],
995 quantiles[band],
996 multipliers[band],
997 mapNan,
998 mapStrat
999 );
1000 }
1001
1003 stratBands.back().type,
1004 stratBuffers.back(),
1005 index,
1006 mapNan,
1007 mapStrat
1008 );
1009
1010 index++;
1011 }
1012 }
1013
1014 //write strat band data
1015 for (size_t band = 0; band <= static_cast<size_t>(bandCount); band++) {
1017 stratBands[band],
1018 stratBuffers[band],
1019 xBlockSize,
1020 yBlockSize,
1021 xBlock,
1022 yBlock,
1023 xValid,
1024 yValid,
1025 false //read = false
1026 );
1027 }
1028 }
1029 }
1030
1031 for (size_t band = 0; band < dataBuffers.size(); band++) {
1032 VSIFree(dataBuffers[band]);
1033 VSIFree(stratBuffers[band]);
1034 }
1035 VSIFree(stratBuffers.back());
1036 });
1037 }
1038 }
1039 else {
1040 for (size_t band = 0; band < static_cast<size_t>(bandCount); band++) {
1041 helper::RasterBandMetaData* p_dataBand = &dataBands[band];
1042 helper::RasterBandMetaData* p_stratBand = &stratBands[band];
1043
1044 int xBlockSize = p_dataBand->xBlockSize;
1045 int yBlockSize = p_dataBand->yBlockSize;
1046
1047 int xBlocks = (p_raster->getWidth() + xBlockSize - 1) / xBlockSize;
1048 int yBlocks = (p_raster->getHeight() + yBlockSize - 1) / yBlockSize;
1049 int chunkSize = yBlocks / threadCount;
1050
1051 for (int yBlockStart = 0; yBlockStart < yBlocks; yBlockStart += chunkSize) {
1052 int yBlockEnd = std::min(yBlocks, yBlockStart + chunkSize);
1053 std::vector<double> *p_quantiles = &quantiles[band];
1054 std::mutex *p_mutex = &mutexes[band];
1055 std::condition_variable *p_cv = &cvs[band];
1056 bool *p_calculated = &quantilesCalculated[band];
1057
1058 boost::asio::post(pool, [
1059 xBlockSize,
1060 yBlockSize,
1061 yBlockStart,
1062 yBlockEnd,
1063 xBlocks,
1064 p_dataBand,
1065 p_stratBand,
1066 p_quantiles,
1067 p_mutex,
1068 p_cv,
1069 p_calculated
1070 ] {
1071 std::unique_lock lock(*p_mutex);
1072
1073 if (!(*p_calculated)) {
1074 p_cv->wait(lock);
1075 }
1076
1077 void *p_data = VSIMalloc3(xBlockSize, yBlockSize, p_dataBand->size);
1078 void *p_strat = VSIMalloc3(xBlockSize, yBlockSize, p_stratBand->size);
1079
1080 int xValid, yValid;
1081 for (int yBlock = yBlockStart; yBlock < yBlockEnd; yBlock++) {
1082 for (int xBlock = 0; xBlock < xBlocks; xBlock++) {
1083 p_dataBand->p_mutex->lock();
1084 p_dataBand->p_band->GetActualBlockSize(xBlock, yBlock, &xValid, &yValid);
1085 p_dataBand->p_mutex->unlock();
1086
1087 //read block into memory
1088 rasterBandIO(
1089 *p_dataBand,
1090 p_data,
1091 xBlockSize,
1092 yBlockSize,
1093 xBlock,
1094 yBlock,
1095 xValid,
1096 yValid,
1097 true //read = true
1098 );
1099
1100 //process block
1101 for (int y = 0; y < yValid; y++) {
1102 size_t index = static_cast<size_t>(y * xBlockSize);
1103 for (int x = 0; x < xValid; x++) {
1104 processPixel(index, p_data, p_dataBand, p_strat, p_stratBand, *p_quantiles);
1105 index++;
1106 }
1107 }
1108
1109 //write resulting stratifications to disk
1111 *p_stratBand,
1112 p_strat,
1113 xBlockSize,
1114 yBlockSize,
1115 xBlock,
1116 yBlock,
1117 xValid,
1118 yValid,
1119 false //read = false
1120 );
1121 }
1122 }
1123 VSIFree(p_data);
1124 VSIFree(p_strat);
1125 });
1126 }
1127 }
1128 }
1129
1130 pool.join();
1131 VSIFree(quantilesCalculated);
1132 pybind11::gil_scoped_release release;
1133 }
1134 else {
1135 //call quantiles calculation fuction depending on type
1136 for (int i = 0; i < bandCount; i++) {
1137 helper::RasterBandMetaData band = dataBands[i];
1138 quantiles[i].resize(probabilities[i].size());
1139 (band.type != GDT_Float64) ?
1140 calcSPQuantiles(p_raster, band, probabilities[i], quantiles[i]) :
1141 calcDPQuantiles(p_raster, band, probabilities[i], quantiles[i]);
1142 }
1143
1144 size_t pixelCount = static_cast<size_t>(p_raster->getWidth()) * static_cast<size_t>(p_raster->getHeight());
1145 if (map) {
1146 for (size_t index = 0; index < pixelCount; index++) {
1147 bool mapNan = false;
1148 size_t mapStrat = 0;
1149
1150 for (size_t band = 0; band < static_cast<size_t>(bandCount); band++) {
1152 index,
1153 dataBands[band],
1154 dataBands[band].p_buffer,
1155 stratBands[band],
1156 stratBands[band].p_buffer,
1157 quantiles[band],
1158 multipliers[band],
1159 mapNan,
1160 mapStrat
1161 );
1162 }
1163
1165 stratBands.back().type,
1166 stratBands.back().p_buffer,
1167 index,
1168 mapNan,
1169 mapStrat
1170 );
1171 }
1172 }
1173 else {
1174 for (size_t band = 0; band < static_cast<size_t>(bandCount); band++) {
1175 for (size_t i = 0; i < pixelCount; i++) {
1177 i,
1178 dataBands[band].p_buffer,
1179 &dataBands[band],
1180 stratBands[band].p_buffer,
1181 &stratBands[band],
1182 quantiles[band]
1183 );
1184 }
1185 }
1186 }
1187
1188 //if non-virtual dataset then write in-memory output bands to disk
1189 if (!isVRTDataset && !isMEMDataset) {
1190 CPLErr err;
1191 for (size_t band = 0; band < stratBands.size(); band++) {
1192 err = stratBands[band].p_band->RasterIO(
1193 GF_Write,
1194 0,
1195 0,
1196 width,
1197 height,
1198 stratBands[band].p_buffer,
1199 width,
1200 height,
1201 stratBands[band].type,
1202 0,
1203 0
1204 );
1205 if (err) {
1206 throw std::runtime_error("error writing band to file.");
1207 }
1208 }
1209 }
1210
1211 }
1212
1213 //close and add all of the VRT sub datasets as bands
1214 if (isVRTDataset) {
1215 for (size_t band = 0; band < VRTBandInfo.size(); band++) {
1216 GDALClose(VRTBandInfo[band].p_dataset);
1217 helper::addBandToVRTDataset(p_dataset, stratBands[band], VRTBandInfo[band]);
1218 }
1219 }
1220
1221 //if bands are in memory, populate a vector of just bands to use in GDALRasterWrapper creation
1222 std::vector<void *> buffers(stratBands.size());
1223 if (!largeRaster) {
1224 for (size_t band = 0; band < stratBands.size(); band++) {
1225 buffers[band] = stratBands[band].p_buffer;
1226 }
1227 }
1228
1229 std::unordered_map<std::string, std::vector<double>> quantileValues;
1230 for (size_t i = 0; i < dataBands.size(); i++) {
1231 quantileValues.insert({std::string(dataBands[i].p_band->GetDescription()), quantiles[i]});
1232 }
1233
1234 if (largeRaster) {
1235 return {new raster::GDALRasterWrapper(p_dataset), quantileValues};
1236 }
1237 else {
1238 return {new raster::GDALRasterWrapper(p_dataset, buffers), quantileValues};
1239 }
1240}
1241
1242} //namespace quantiles
1243} //namespace sgs
Definition raster.h:57
void * getRasterBandBuffer(int band)
Definition raster.h:706
GDALDataset * getDataset()
Definition raster.h:429
int getWidth()
Definition raster.h:467
size_t getRasterBandTypeSize(int band)
Definition raster.h:735
double * getGeotransform()
Definition raster.h:590
GDALDataType getRasterBandType(int band)
Definition raster.h:724
int getHeight()
Definition raster.h:476
GDALRasterBand * getRasterBand(int band)
Definition raster.h:696
std::vector< std::string > getBands()
Definition raster.h:575
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
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 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 setStratBandTypeAndSize(size_t maxStrata, GDALDataType *p_type, size_t *p_size)
Definition helper.h:140
void calcDPQuantiles(raster::GDALRasterWrapper *p_raster, helper::RasterBandMetaData &band, std::vector< double > &probabilities, std::vector< double > &quantiles)
Definition quantiles.h:132
void batchCalcDPQuantiles(raster::GDALRasterWrapper *p_raster, helper::RasterBandMetaData &band, std::vector< double > &probabilities, std::vector< double > &quantiles, std::mutex &mutex, std::condition_variable &cv, bool &calculated, double eps)
Definition quantiles.h:397
std::pair< raster::GDALRasterWrapper *, std::unordered_map< std::string, std::vector< double > > > quantiles(raster::GDALRasterWrapper *p_raster, std::map< int, std::vector< double > > userProbabilites, bool map, std::string filename, std::string tempFolder, bool largeRaster, int threadCount, std::map< std::string, std::string > driverOptions, double eps)
Definition quantiles.h:713
void batchCalcSPQuantiles(raster::GDALRasterWrapper *p_raster, helper::RasterBandMetaData &band, std::vector< double > &probabilities, std::vector< double > &quantiles, std::mutex &mutex, std::condition_variable &cv, bool &calculated, double eps)
Definition quantiles.h:236
void calcSPQuantiles(raster::GDALRasterWrapper *p_raster, helper::RasterBandMetaData &band, std::vector< double > &probabilities, std::vector< double > &quantiles)
Definition quantiles.h:56
void processMapPixel(size_t index, helper::RasterBandMetaData &dataBand, void *p_dataBuffer, helper::RasterBandMetaData &stratBand, void *p_stratBuffer, std::vector< double > &quantiles, size_t multiplier, bool &mapNan, size_t &mapStrat)
Definition quantiles.h:514
void processPixel(size_t index, void *p_data, helper::RasterBandMetaData *p_dataBand, void *p_strat, helper::RasterBandMetaData *p_stratBand, std::vector< double > &quantiles)
Definition quantiles.h:565
Definition map.h:22
Definition strat.h:27
Definition pca.h:23
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