sgsPy
structurally guided sampling
Loading...
Searching...
No Matches
access.h
Go to the documentation of this file.
1/******************************************************************************
2 *
3 * Project: sgs
4 * Purpose: generate an access mask using vector and raster datasets
5 * Author: Joseph Meyer
6 * Date: October, 2025
7 *
8 ******************************************************************************/
9
14
15#pragma once
16
17#include "helper.h"
18#include "raster.h"
19#include "vector.h"
20#include "gdal_utils.h"
21
22namespace sgs {
23namespace access {
24
30struct Access {
31 bool used = false;
32 double area = -1;
33 GDALDataset *p_dataset = nullptr;
35
75 std::string layerName,
76 double buffInner,
77 double buffOuter,
78 bool largeRaster,
79 std::string tempFolder,
80 int xBlockSize,
81 int yBlockSize)
82 {
83 if (!p_vector) {
84 return;
85 }
86
87 std::string rastProj = p_raster->getDataset()->GetProjectionRef();
88 OGRSpatialReference rastSRS;
89 rastSRS.importFromWkt(rastProj.c_str());
90 OGRLayer *p_inputLayer = p_vector->getLayer(layerName.c_str());
91 const OGRSpatialReference *p_vectSRS = p_inputLayer->GetSpatialRef();
92 if (!rastSRS.IsSame(p_vectSRS)) {
93 throw std::runtime_error("access vector and raster do not have the same spatial reference system.");
94 }
95
96 //step 1: create multipolygon buffers
97 OGRMultiPolygon *buffInnerPolygons = new OGRMultiPolygon;
98 OGRMultiPolygon *buffOuterPolygons = new OGRMultiPolygon;
99 OGRGeometry *p_polygonMask;
100
101 //occasionally, samples end up being placed just outside the accessible area,
102 //typically when the buffer sizes are multiples of the pixel size.
103 //
104 //Adjusting the buffers minorly fixes this problem.
105 double pixelSize = std::min(p_raster->getPixelHeight(), p_raster->getPixelWidth());
106 buffOuter = buffOuter - pixelSize / 50;
107 buffInner = buffInner == 0 ? 0 : buffInner + pixelSize / 50;
108
109 //step 2: add geometries to access polygon buffers
110 for (const auto& p_feature : *p_vector->getLayer(layerName.c_str())) {
111 OGRGeometry *p_geometry = p_feature->GetGeometryRef();
112 if (!p_geometry) {
113 throw std::runtime_error(std::format("an invalid geometry was encountered in the {} layer.", layerName));
114 }
115
116 OGRwkbGeometryType type = wkbFlatten(p_geometry->getGeometryType());
117
118 switch (type) {
119 case OGRwkbGeometryType::wkbLineString: {
120 OGRGeometry *p_outer = p_geometry->Buffer(buffOuter);
121 buffOuterPolygons->addGeometry(p_outer);
122 OGRGeometryFactory::destroyGeometry(p_outer);
123
124 if (buffInner != 0) {
125 OGRGeometry *p_inner = p_geometry->Buffer(buffInner);
126 buffInnerPolygons->addGeometry(p_inner);
127 OGRGeometryFactory::destroyGeometry(p_inner);
128 }
129 break;
130 }
131 case OGRwkbGeometryType::wkbMultiLineString: {
132 for (const auto& p_lineString : *p_geometry->toMultiLineString()) {
133 OGRGeometry *p_outer = p_lineString->Buffer(buffOuter);
134 buffOuterPolygons->addGeometry(p_outer);
135 OGRGeometryFactory::destroyGeometry(p_outer);
136
137 if (buffInner != 0) {
138 OGRGeometry *p_inner = p_lineString->Buffer(buffInner);
139 buffInnerPolygons->addGeometry(p_inner);
140 OGRGeometryFactory::destroyGeometry(p_inner);
141 }
142 }
143
144 break;
145 }
146 default: {
147 throw std::runtime_error("geometry type must be LineString or MultiLineString");
148 }
149 }
150 }
151
152 //step 3: generate the polygon mask and free no longer used memory
153 if (buffInner == 0) {
154 p_polygonMask = buffOuterPolygons->UnionCascaded();
155 }
156 else {
157 OGRGeometry *buffOuterUnion = buffOuterPolygons->UnionCascaded();
158 OGRGeometry *buffInnerUnion = buffInnerPolygons->UnionCascaded();
159 p_polygonMask = buffOuterUnion->Difference(buffInnerUnion);
160 OGRGeometryFactory::destroyGeometry(buffOuterUnion);
161 OGRGeometryFactory::destroyGeometry(buffInnerUnion);
162 }
163 delete buffOuterPolygons;
164 delete buffInnerPolygons;
165
166 //update area to contain the total accessible are within the raster
167 this->area = 0;
168 OGRPolygon extent(p_raster->getXMin(), p_raster->getYMin(), p_raster->getXMax(), p_raster->getYMax());
169 OGRGeometry *p_polygonWithinExtent;
170 OGRwkbGeometryType geomtype = p_polygonMask->getGeometryType();
171 if (geomtype == wkbPolygon) {
172 p_polygonWithinExtent = p_polygonMask->toPolygon()->Intersection(&extent);
173 }
174 else if (geomtype == wkbMultiPolygon) {
175 p_polygonWithinExtent = p_polygonMask->toMultiPolygon()->Intersection(&extent);
176 }
177 else {
178 throw std::runtime_error("p_polygonMask is not a polygon or a multipolygon!");
179 }
180
181 geomtype = p_polygonWithinExtent->getGeometryType();
182 if (geomtype == wkbPolygon) {
183 this->area += p_polygonWithinExtent->toPolygon()->toUpperClass()->get_Area();
184 }
185 else if (geomtype == wkbMultiPolygon) {
186 for (const auto& p_polygon : *p_polygonWithinExtent->toMultiPolygon()) {
187 this->area += p_polygon->toUpperClass()->get_Area();
188 }
189 }
190 else {
191 throw std::runtime_error("taking the intersection of polygons somehow resulted in something not a polygon! This is a bug.");
192 }
193
194 //invert polygon mask
195 OGRGeometry *p_invertedMask = extent.Difference(p_polygonMask);
196
197 //step 4: create new GDAL dataset to rasterize as access mask
198 GDALDataset *p_accessPolygonDataset = GetGDALDriverManager()->GetDriverByName("MEM")->Create(
199 "",
200 0,
201 0,
202 0,
203 GDT_Unknown,
204 nullptr
205 );
206
207 OGRLayer *p_layer = p_accessPolygonDataset->CreateLayer(
208 "access",
209 p_vector->getLayer(layerName.c_str())->GetSpatialRef(),
210 p_invertedMask->getGeometryType(),
211 nullptr
212 );
213 OGRFieldDefn field("index", OFTInteger);
214 p_layer->CreateField(&field);
215
216 //step 6: add the access polygon to the new layer
217 OGRFeature *p_feature = OGRFeature::CreateFeature(p_layer->GetLayerDefn());
218 p_feature->SetField("index", 0);
219 p_feature->SetGeometry(p_invertedMask);
220 p_layer->CreateFeature(p_feature); //error handling here???
221 OGRFeature::DestroyFeature(p_feature);
222
223 std::filesystem::path path = tempFolder;
224 path = path / "access.tif";
225
226 //step 9: generate options list for rasterization
227 char **argv = nullptr;
228
229 //specify 'all touched'
230 argv = CSLAddString(argv, "-at");
231
232 //specify the burn value for the polygon
233 argv = CSLAddString(argv, "-burn");
234 argv = CSLAddString(argv, std::to_string(1).c_str());
235
236 //specify the layer
237 argv = CSLAddString(argv, "-l");
238 argv = CSLAddString(argv, "access");
239
240 //specify extent
241 argv = CSLAddString(argv, "-te");
242 argv = CSLAddString(argv, std::to_string(p_raster->getXMin()).c_str());
243 argv = CSLAddString(argv, std::to_string(p_raster->getYMin()).c_str());
244 argv = CSLAddString(argv, std::to_string(p_raster->getXMax()).c_str());
245 argv = CSLAddString(argv, std::to_string(p_raster->getYMax()).c_str());
246
247 //specify dimensions
248 argv = CSLAddString(argv, "-ts");
249 argv = CSLAddString(argv, std::to_string(p_raster->getWidth()).c_str());
250 argv = CSLAddString(argv, std::to_string(p_raster->getHeight()).c_str());
251
252 //specify output type
253 argv = CSLAddString(argv, "-ot");
254 argv = CSLAddString(argv, "Int8");
255
256 GDALRasterizeOptions *options = GDALRasterizeOptionsNew(argv, nullptr);
257
258 //step 10: rasterize vector creating in-memory dataset
259 this->p_dataset = GDALDataset::FromHandle(GDALRasterize(
260 path.string().c_str(),
261 nullptr,
262 p_accessPolygonDataset,
263 options,
264 nullptr
265 ));
266
267 this->band.p_band = this->p_dataset->GetRasterBand(1);
268 this->band.type = GDT_Int8;
269 this->band.size = sizeof(int8_t);
270 this->band.p_band->GetBlockSize(&this->band.xBlockSize, &this->band.yBlockSize);
271
272 //step 11: free dynamically allocated data
273 GDALRasterizeOptionsFree(options);
274 CSLDestroy(argv);
275 GDALClose(GDALDataset::ToHandle(p_accessPolygonDataset));
276 OGRGeometryFactory::destroyGeometry(p_polygonMask);
277 OGRGeometryFactory::destroyGeometry(p_polygonWithinExtent);
278 OGRGeometryFactory::destroyGeometry(p_invertedMask);
279
280 this->used = true;
281 }
282
284 GDALClose(GDALDataset::ToHandle(this->p_dataset));
285 }
286};
287
288} //namespace access
289} //namespace sgs
Definition raster.h:57
double getYMax()
Definition raster.h:525
GDALDataset * getDataset()
Definition raster.h:429
int getWidth()
Definition raster.h:467
double getXMax()
Definition raster.h:495
int getHeight()
Definition raster.h:476
double getXMin()
Definition raster.h:510
double getPixelWidth()
Definition raster.h:555
double getYMin()
Definition raster.h:540
double getPixelHeight()
Definition raster.h:565
Definition vector.h:46
OGRLayer * getLayer(std::string layerName)
Definition vector.h:250
Definition access.h:23
Definition pca.h:23
~Access()
Definition access.h:283
helper::RasterBandMetaData band
Definition access.h:34
Access(vector::GDALVectorWrapper *p_vector, raster::GDALRasterWrapper *p_raster, std::string layerName, double buffInner, double buffOuter, bool largeRaster, std::string tempFolder, int xBlockSize, int yBlockSize)
Definition access.h:73
bool used
Definition access.h:31
GDALDataset * p_dataset
Definition access.h:33
double area
Definition access.h:32
Definition helper.h:87
int xBlockSize
Definition helper.h:94
size_t size
Definition helper.h:91
GDALDataType type
Definition helper.h:90
GDALRasterBand * p_band
Definition helper.h:88