sgsPy
structurally guided sampling
Loading...
Searching...
No Matches
map.h
Go to the documentation of this file.
1/******************************************************************************
2 *
3 * Project: sgs
4 * Purpose: C++ implementation of raster stratification mapping
5 * Author: Joseph Meyer
6 * Date: September, 2025
7 *
8 ******************************************************************************/
9
14
15#include <boost/asio/thread_pool.hpp>
16#include <boost/asio/post.hpp>
17
18#include "utils/raster.h"
19#include "utils/helper.h"
20
21namespace sgs {
22namespace map {
23
110 std::vector<raster::GDALRasterWrapper *> rasters,
111 std::vector<std::vector<int>> bands,
112 std::vector<std::vector<int>> strataCounts,
113 std::string filename,
114 bool largeRaster,
115 int threadCount,
116 std::string tempFolder,
117 std::map<std::string, std::string> driverOptions)
118{
119 GDALAllRegister();
120
121 int height = rasters[0]->getHeight();
122 int width = rasters[0]->getWidth();
123 double *geotransform = rasters[0]->getGeotransform();
124 std::string projection = (rasters[0]->getDataset()->GetProjectionRef());
125 if (projection == "") {
126 throw std::runtime_error("could not get projection from the first raster argument.");
127 }
128
129 OGRSpatialReference srs;
130 srs.importFromWkt(projection.c_str());
131
132 for (size_t i = 1; i < rasters.size(); i++) {
133 if (rasters[i]->getHeight() != height) {
134 std::string err = "raster with index " + std::to_string(i) + " has a different height from the raster at index 0.";
135 throw std::runtime_error(err);
136 }
137
138 if (rasters[i]->getWidth() != width) {
139 std::string err = "raster with index " + std::to_string(i) + " has a different width from the raster at index 0.";
140 throw std::runtime_error(err);
141 }
142
143 double *checkGeotransform = rasters[i]->getGeotransform();
144 for (int j = 0; j < 6; j++) {
145 if (geotransform[i] != checkGeotransform[i]) {
146 std::string err = "raster with index " + std::to_string(i) + " has a different geotransform from the raster at index 0.";
147 throw std::runtime_error(err);
148 }
149 }
150
151 OGRSpatialReference checkSrs;
152 checkSrs.importFromWkt(rasters[i]->getDataset()->GetProjectionRef());
153 if (!srs.IsSame(&checkSrs)) {
154 std::string err = "raster with index " + std::to_string(i) + " has a different projection from the raster at index 0.";
155 throw std::runtime_error(err);
156 }
157 }
158
159 std::vector<helper::RasterBandMetaData> stratBands;
160 std::vector<int> numStrataPerBand;
162 std::vector<helper::VRTBandDatasetInfo> VRTBandInfo(1);
163
164 bool isMEMDataset = !largeRaster && filename == "";
165 bool isVRTDataset = largeRaster && filename == "";
166
167 std::vector<std::mutex> stratDatasetMutexes(rasters.size());
168 std::mutex mapBandMutex;
169
170 std::string driver;
171 GDALDataset *p_dataset = nullptr;
172 if (isMEMDataset || isVRTDataset) {
173 std::string driver = isMEMDataset ? "MEM" : "VRT";
174 p_dataset = helper::createVirtualDataset(driver, width, height, geotransform, projection);
175 }
176 else {
177 std::filesystem::path filepath = filename;
178 std::string extension = filepath.extension().string();
179
180 if (extension == ".tif") {
181 driver = "Gtiff";
182 }
183 else {
184 throw std::runtime_error("sgs only supports .tif files right now");
185 }
186 }
187
188 //step 1 iterate through bands populating rasterBands and bandStratMultiplier objects
189 std::vector<size_t> multipliers(1, 1);
190 for (size_t i = 0; i < rasters.size(); i++) {
191 raster::GDALRasterWrapper *p_raster = rasters[i];
192
193 for (size_t j = 0; j < bands[i].size(); j++) {
194 int band = bands[i][j];
195 int strataCount = strataCounts[i][j];
196 numStrataPerBand.push_back(strataCount);
197
199
200 GDALRasterBand *p_band = p_raster->getRasterBand(band);
201 stratBand.p_band = p_band;
202 stratBand.type = p_raster->getRasterBandType(band);
203 stratBand.size = p_raster->getRasterBandTypeSize(band);
204 stratBand.p_buffer = largeRaster ? nullptr : p_raster->getRasterBandBuffer(band);
205 stratBand.nan = p_band->GetNoDataValue();
206 stratBand.p_mutex = &stratDatasetMutexes[i];
207 p_band->GetBlockSize(&stratBand.xBlockSize, &stratBand.yBlockSize);
208 stratBands.push_back(stratBand);
209
211 multipliers.push_back(multipliers.back() * strataCount);
212 }
213 }
214
215 size_t bandCount = stratBands.size();
216 size_t maxStrata = multipliers.back();
217 multipliers.pop_back();
218 helper::setStratBandTypeAndSize(maxStrata, &mapBand.type, &mapBand.size);
219 mapBand.name = "strat_map";
220 mapBand.xBlockSize = stratBands[0].xBlockSize;
221 mapBand.yBlockSize = stratBands[0].yBlockSize;
222 mapBand.p_mutex = &mapBandMutex;
223
224 if (isMEMDataset) {
225 helper::addBandToMEMDataset(p_dataset, mapBand);
226 }
227 else if (isVRTDataset) {
229 p_dataset,
230 mapBand,
231 tempFolder,
232 "map",
233 VRTBandInfo,
234 driverOptions
235 );
236 }
237 else {
238 bool useTiles = mapBand.xBlockSize != width &&
239 mapBand.yBlockSize != height;
240
241 mapBand.p_buffer = !largeRaster ?
242 VSIMalloc3(height, width, mapBand.size) :
243 nullptr;
244
245 p_dataset = helper::createDataset(
246 filename,
247 driver,
248 width,
249 height,
250 geotransform,
251 projection,
252 &mapBand,
253 1,
254 useTiles,
255 driverOptions
256 );
257 }
258
259 if (largeRaster) {
260 pybind11::gil_scoped_acquire acquire;
261 boost::asio::thread_pool pool(threadCount);
262
263 int xBlockSize = stratBands[0].xBlockSize;
264 int yBlockSize = stratBands[0].yBlockSize;
265
266 int xBlocks = (width + xBlockSize - 1) / xBlockSize;
267 int yBlocks = (height + yBlockSize - 1) / yBlockSize;
268 int chunkSize = yBlocks / threadCount;
269
270 for (int yBlockStart = 0; yBlockStart < yBlocks; yBlockStart += chunkSize) {
271 int yBlockEnd = std::min(yBlockStart + chunkSize, yBlocks);
272
273 boost::asio::post(pool, [
274 bandCount,
275 xBlockSize,
276 yBlockSize,
277 yBlockStart,
278 yBlockEnd,
279 xBlocks,
280 &stratBands,
281 &numStrataPerBand,
282 &mapBand,
283 &multipliers
284 ] {
285 std::vector<void *> stratBuffers(bandCount);
286 for (size_t band = 0; band < bandCount; band++) {
287 stratBuffers[band] = VSIMalloc3(xBlockSize, yBlockSize, stratBands[band].size);
288 }
289 void *p_mapBuffer = VSIMalloc3(xBlockSize, yBlockSize, mapBand.size);
290
291 //cast to int so there is less data type conversion while processign
292 std::vector<int> intNoDataValues(bandCount);
293 for (size_t band = 0; band < bandCount; band++) {
294 intNoDataValues[band] = static_cast<int>(stratBands[band].nan);
295 }
296
297 for (int yBlock = yBlockStart; yBlock < yBlockEnd; yBlock++) {
298 for (int xBlock = 0; xBlock < xBlocks; xBlock++) {
299 int xValid, yValid;
300 stratBands[0].p_mutex->lock();
301 stratBands[0].p_band->GetActualBlockSize(xBlock, yBlock, &xValid, &yValid);
302 stratBands[0].p_mutex->unlock();
303
304 for (size_t band = 0; band < bandCount; band++) {
305 rasterBandIO(
306 stratBands[band],
307 stratBuffers[band],
308 xBlockSize,
309 yBlockSize,
310 xBlock,
311 yBlock,
312 xValid,
313 yValid,
314 true //read = true
315 );
316 }
317
318 for (int y = 0; y < yValid; y++) {
319 for (int x = 0; x < xValid; x++) {
320 size_t index = static_cast<size_t>(x + y * xBlockSize);
321 bool isNan = false;
322 int mappedStrat = 0;
323
324 for (size_t band = 0; band < bandCount; band++) {
325 int strat = helper::getPixelValueDependingOnType<int>(stratBands[band].type, stratBuffers[band], index);
326 isNan = strat == intNoDataValues[band];
327
328 if (!isNan) {
329 if (strat >= numStrataPerBand[band]) {
330 std::string bandName = stratBands[band].p_band->GetDescription();
331 std::string errmsg = "the num_strata indicated for band " + bandName + " is less than or equal to one of the values in that band.";
332 throw std::runtime_error(errmsg);
333 }
334
335 if (strat < 0) {
336 std::string bandName = stratBands[band].p_band->GetDescription();
337 std::string errmsg = "a negative strata value of " + std::to_string(strat) + " was found in band " + bandName + ", and is not marked as a nodata value.";
338 throw std::runtime_error(errmsg);
339 }
340
341 mappedStrat += strat * multipliers[band];
342 }
343 else {
344 break;
345 }
346 }
347
348 helper::setStrataPixelDependingOnType(mapBand.type, p_mapBuffer, index, isNan, mappedStrat);
349 }
350 }
351
353 mapBand,
354 p_mapBuffer,
355 xBlockSize,
356 yBlockSize,
357 xBlock,
358 yBlock,
359 xValid,
360 yValid,
361 false //read = false
362 );
363 }
364 }
365
366 for (size_t band = 0; band < bandCount; band++) {
367 VSIFree(stratBuffers[band]);
368 }
369 VSIFree(p_mapBuffer);
370 });
371 }
372
373 pool.join();
374 pybind11::gil_scoped_release release;
375 }
376 else {
377 std::vector<int> intNoDataValues(bandCount);
378 for (size_t band = 0; band < bandCount; band++) {
379 intNoDataValues[band] = static_cast<int>(stratBands[band].nan);
380 }
381
382 size_t pixelCount = static_cast<size_t>(height) * static_cast<size_t>(width);
383 for (size_t index = 0; index < pixelCount; index++) {
384 bool isNan = false;
385 int mappedStrat = 0;
386
387 for (size_t band = 0; (band < bandCount) && !isNan; band++) {
388 int strat = helper::getPixelValueDependingOnType<int>(stratBands[band].type, stratBands[band].p_buffer, index);
389 isNan = strat == intNoDataValues[band];
390 if (!isNan) {
391 mappedStrat += strat * multipliers[band];
392 }
393 }
394
395 helper::setStrataPixelDependingOnType(mapBand.type, mapBand.p_buffer, index, isNan, mappedStrat);
396 }
397
398 if (!isVRTDataset && !isMEMDataset) {
399 CPLErr err = mapBand.p_band->RasterIO(
400 GF_Write,
401 0,
402 0,
403 width,
404 height,
405 mapBand.p_buffer,
406 width,
407 height,
408 mapBand.type,
409 0,
410 0
411 );
412 if (err) {
413 throw std::runtime_error("error writing band to file.");
414 }
415 }
416 }
417
418 //close and add VRT sub dataset as band
419 if (isVRTDataset) {
420 GDALClose(VRTBandInfo[0].p_dataset);
421 helper::addBandToVRTDataset(p_dataset, mapBand, VRTBandInfo[0]);
422 }
423
424 return largeRaster ?
425 new raster::GDALRasterWrapper(p_dataset) :
426 new raster::GDALRasterWrapper(p_dataset, {mapBand.p_buffer});
427}
428
429} //namespace map
430} //namespace sgs
Definition raster.h:57
void * getRasterBandBuffer(int band)
Definition raster.h:706
size_t getRasterBandTypeSize(int band)
Definition raster.h:735
GDALDataType getRasterBandType(int band)
Definition raster.h:724
GDALRasterBand * getRasterBand(int band)
Definition raster.h:696
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 printTypeWarningsForInt32Conversion(GDALDataType type)
Definition helper.h:251
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
raster::GDALRasterWrapper * map(std::vector< raster::GDALRasterWrapper * > rasters, std::vector< std::vector< int > > bands, std::vector< std::vector< int > > strataCounts, std::string filename, bool largeRaster, int threadCount, std::string tempFolder, std::map< std::string, std::string > driverOptions)
Definition map.h:109
Definition srs.h:28
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