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 OGRwkbGeometryType type = wkbFlatten(p_geometry->getGeometryType());
113
114 switch (type) {
115 case OGRwkbGeometryType::wkbLineString: {
116 OGRGeometry *p_outer = p_geometry->Buffer(buffOuter);
117 buffOuterPolygons->addGeometry(p_outer);
118 OGRGeometryFactory::destroyGeometry(p_outer);
119
120 if (buffInner != 0) {
121 OGRGeometry *p_inner = p_geometry->Buffer(buffInner);
122 buffInnerPolygons->addGeometry(p_inner);
123 OGRGeometryFactory::destroyGeometry(p_inner);
124 }
125 break;
126 }
127 case OGRwkbGeometryType::wkbMultiLineString: {
128 for (const auto& p_lineString : *p_geometry->toMultiLineString()) {
129 OGRGeometry *p_outer = p_lineString->Buffer(buffOuter);
130 buffOuterPolygons->addGeometry(p_outer);
131 OGRGeometryFactory::destroyGeometry(p_outer);
132
133 if (buffInner != 0) {
134 OGRGeometry *p_inner = p_lineString->Buffer(buffInner);
135 buffInnerPolygons->addGeometry(p_inner);
136 OGRGeometryFactory::destroyGeometry(p_inner);
137 }
138 }
139
140 break;
141 }
142 default: {
143 throw std::runtime_error("geometry type must be LineString or MultiLineString");
144 }
145 }
146 }
147
148 //step 3: generate the polygon mask and free no longer used memory
149 if (buffInner == 0) {
150 p_polygonMask = buffOuterPolygons->UnionCascaded();
151 }
152 else {
153 OGRGeometry *buffOuterUnion = buffOuterPolygons->UnionCascaded();
154 OGRGeometry *buffInnerUnion = buffInnerPolygons->UnionCascaded();
155 p_polygonMask = buffOuterUnion->Difference(buffInnerUnion);
156 OGRGeometryFactory::destroyGeometry(buffOuterUnion);
157 OGRGeometryFactory::destroyGeometry(buffInnerUnion);
158 }
159 delete buffOuterPolygons;
160 delete buffInnerPolygons;
161
162 //update area to contain the total accessible are within the raster
163 this->area = 0;
164 OGRPolygon extent(p_raster->getXMin(), p_raster->getYMin(), p_raster->getXMax(), p_raster->getYMax());
165 OGRGeometry *p_polygonWithinExtent;
166 OGRwkbGeometryType geomtype = p_polygonMask->getGeometryType();
167 if (geomtype == wkbPolygon) {
168 p_polygonWithinExtent = p_polygonMask->toPolygon()->Intersection(&extent);
169 }
170 else if (geomtype == wkbMultiPolygon) {
171 p_polygonWithinExtent = p_polygonMask->toMultiPolygon()->Intersection(&extent);
172 }
173 else {
174 throw std::runtime_error("p_polygonMask is not a polygon or a multipolygon!");
175 }
176
177 geomtype = p_polygonWithinExtent->getGeometryType();
178 if (geomtype == wkbPolygon) {
179 this->area += p_polygonWithinExtent->toPolygon()->toUpperClass()->get_Area();
180 }
181 else if (geomtype == wkbMultiPolygon) {
182 for (const auto& p_polygon : *p_polygonWithinExtent->toMultiPolygon()) {
183 this->area += p_polygon->toUpperClass()->get_Area();
184 }
185 }
186 else {
187 throw std::runtime_error("taking the intersection of polygons somehow resulted in something not a polygon! This is a bug.");
188 }
189
190 //invert polygon mask
191 OGRGeometry *p_invertedMask = extent.Difference(p_polygonMask);
192
193 //step 4: create new GDAL dataset to rasterize as access mask
194 GDALDataset *p_accessPolygonDataset = GetGDALDriverManager()->GetDriverByName("MEM")->Create(
195 "",
196 0,
197 0,
198 0,
199 GDT_Unknown,
200 nullptr
201 );
202
203 OGRLayer *p_layer = p_accessPolygonDataset->CreateLayer(
204 "access",
205 p_vector->getLayer(layerName.c_str())->GetSpatialRef(),
206 p_invertedMask->getGeometryType(),
207 nullptr
208 );
209 OGRFieldDefn field("index", OFTInteger);
210 p_layer->CreateField(&field);
211
212 //step 6: add the access polygon to the new layer
213 OGRFeature *p_feature = OGRFeature::CreateFeature(p_layer->GetLayerDefn());
214 p_feature->SetField("index", 0);
215 p_feature->SetGeometry(p_invertedMask);
216 p_layer->CreateFeature(p_feature); //error handling here???
217 OGRFeature::DestroyFeature(p_feature);
218
219 std::filesystem::path path = tempFolder;
220 path = path / "access.tif";
221
222 //step 9: generate options list for rasterization
223 char **argv = nullptr;
224
225 //specify 'all touched'
226 argv = CSLAddString(argv, "-at");
227
228 //specify the burn value for the polygon
229 argv = CSLAddString(argv, "-burn");
230 argv = CSLAddString(argv, std::to_string(1).c_str());
231
232 //specify the layer
233 argv = CSLAddString(argv, "-l");
234 argv = CSLAddString(argv, "access");
235
236 //specify extent
237 argv = CSLAddString(argv, "-te");
238 argv = CSLAddString(argv, std::to_string(p_raster->getXMin()).c_str());
239 argv = CSLAddString(argv, std::to_string(p_raster->getYMin()).c_str());
240 argv = CSLAddString(argv, std::to_string(p_raster->getXMax()).c_str());
241 argv = CSLAddString(argv, std::to_string(p_raster->getYMax()).c_str());
242
243 //specify dimensions
244 argv = CSLAddString(argv, "-ts");
245 argv = CSLAddString(argv, std::to_string(p_raster->getWidth()).c_str());
246 argv = CSLAddString(argv, std::to_string(p_raster->getHeight()).c_str());
247
248 //specify output type
249 argv = CSLAddString(argv, "-ot");
250 argv = CSLAddString(argv, "Int8");
251
252 GDALRasterizeOptions *options = GDALRasterizeOptionsNew(argv, nullptr);
253
254 //step 10: rasterize vector creating in-memory dataset
255 this->p_dataset = GDALDataset::FromHandle(GDALRasterize(
256 path.string().c_str(),
257 nullptr,
258 p_accessPolygonDataset,
259 options,
260 nullptr
261 ));
262
263 this->band.p_band = this->p_dataset->GetRasterBand(1);
264 this->band.type = GDT_Int8;
265 this->band.size = sizeof(int8_t);
266 this->band.p_band->GetBlockSize(&this->band.xBlockSize, &this->band.yBlockSize);
267
268 //step 11: free dynamically allocated data
269 GDALRasterizeOptionsFree(options);
270 CSLDestroy(argv);
271 GDALClose(GDALDataset::ToHandle(p_accessPolygonDataset));
272 OGRGeometryFactory::destroyGeometry(p_polygonMask);
273 OGRGeometryFactory::destroyGeometry(p_polygonWithinExtent);
274 OGRGeometryFactory::destroyGeometry(p_invertedMask);
275
276 this->used = true;
277 }
278
280 GDALClose(GDALDataset::ToHandle(this->p_dataset));
281 }
282};
283
284} //namespace access
285} //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:246
Definition access.h:23
Definition pca.h:23
~Access()
Definition access.h:279
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