sgsPy
structurally guided sampling
Loading...
Searching...
No Matches
vector.h
Go to the documentation of this file.
1/******************************************************************************
2 *
3 * Project: sgs
4 * Purpose: GDALDataset wrapper for vector operations
5 * Author: Joseph Meyer
6 * Date: June, 2025
7 *
8 ******************************************************************************/
9
14
15#pragma once
16
17#include <iostream>
18#include <filesystem>
19
20#include <gdal_priv.h>
21#include <ogrsf_frmts.h>
22#include <ogr_core.h>
23#include <ogr_geometry.h>
24#include <ogr_api.h>
25#include <ogr_recordbatch.h>
26#include <pybind11/pybind11.h>
27#include <pybind11/stl.h>
28
29namespace sgs {
30namespace vector {
31
32namespace py = pybind11;
33using namespace pybind11::literals;
34
47 private:
48 GDALDatasetUniquePtr p_dataset;
49 OGRSpatialReference srs;
50 bool haveSRS = false;
51
52 public:
62 GDALVectorWrapper(std::string filename, std::string projDBPath) {
63 //set proj.db search path to search for the proj.db file which is included in sgs package
64 char **paths = nullptr;
65 paths = CSLAddString(paths, projDBPath.c_str());
66 OSRSetPROJSearchPaths(paths);
67 CSLDestroy(paths);
68
69 //must register drivers before trying to open a dataset
70 GDALAllRegister();
71
72 //dataset
73 this->p_dataset = GDALDatasetUniquePtr(GDALDataset::Open(filename.c_str(), GDAL_OF_VECTOR));
74 if (!this->p_dataset) {
75 throw std::runtime_error("dataset pointer is null after initialization, dataset unable to be initialized.");
76 }
77 }
78
85 GDALVectorWrapper(GDALDataset *p_dataset, std::string projection) {
86 this->p_dataset = GDALDatasetUniquePtr(p_dataset);
87 OGRErr err = this->srs.importFromWkt(projection.c_str());
88 if (err) {
89 throw std::runtime_error("unable to get Spatial Reference System from projection string.");
90 }
91 this->haveSRS = true;
92 }
93
111 GDALVectorWrapper(std::string bytes, std::string projection, std::string name, std::string projDBPath) {
112 //set proj.db search path to search for the proj.db file which is included in sgs package
113
114 std::cout << "in GDALVectorWrapper constructor" << std::endl;
115 char **paths = nullptr;
116 paths = CSLAddString(paths, projDBPath.c_str());
117 OSRSetPROJSearchPaths(paths);
118 CSLDestroy(paths);
119
120 GDALAllRegister();
121
122 //create dataset from geojson string to copy to new layer
123 GDALDataset *p_indataset = GDALDataset::FromHandle(GDALOpenEx(bytes.c_str(), GDAL_OF_VECTOR, nullptr, nullptr, nullptr));
124 OGRLayer *p_inlayer = p_indataset->GetLayerByName("OGRGeoJSON");
125
126 //set spatial reference
127 OGRErr err = this->srs.importFromWkt(projection.c_str());
128 if (err) {
129 throw std::runtime_error("unable to get Spatial Reference System from projection string.");
130 }
131 this->haveSRS = true;
132
133 //create dataset and layer with correct spatial reference and
134 GDALDriver *p_driver = GetGDALDriverManager()->GetDriverByName("MEM");
135 if (!p_driver) {
136 throw std::runtime_error("unable to create dataset driver.");
137 }
138 GDALDataset *p_dataset = p_driver->Create("", 0, 0, 0, GDT_Unknown, nullptr);
139 if (!p_dataset) {
140 throw std::runtime_error("unable to create dataset from driver.");
141 }
142 OGRLayer *p_outlayer = p_dataset->CreateLayer(name.c_str(), &this->srs, wkbUnknown, nullptr);
143 if (!p_outlayer) {
144 throw std::runtime_error("unable to create dataset layer.");
145 }
146 this->p_dataset = GDALDatasetUniquePtr(p_dataset);
147
148 /* copy data over from dataset created using geojson string
149 *
150 * This is done, because when converting a geopandas geodataframe to json, the layer name,
151 * and (more importantly) the spatial reference system may not be transferred correctly.
152 * This way does include A LOT of copying data, however it ensures the srs is correct and
153 * all of the data from the geodataframe is moved over effectively.
154 */
155
156 //copy over field definitions
157 OGRFeatureDefn *p_featdef = p_inlayer->GetLayerDefn();
158 int fcount = p_featdef->GetFieldCount();
159 for (int i = 0; i < fcount; i++) {
160 OGRFieldDefn *p_fielddef = p_featdef->GetFieldDefn(i);
161 OGRErr err = p_outlayer->CreateField(p_fielddef);
162 if (err) {
163 std::cout << "unable to copy field definition." << std::endl;
164 }
165 }
166
167 //copy over features
168 for (auto& p_infeature : p_inlayer) {
169 OGRFeature *p_outfeature = OGRFeature::CreateFeature(p_featdef);
170 for (int i = 0; i < fcount; i++) {
171 p_outfeature->SetField(i, p_infeature->GetRawFieldRef(i));
172 }
173 OGRGeometry *p_geom = p_infeature->GetGeometryRef();
174 if (!p_geom) {
175 throw std::runtime_error("an invalid geometry was encountered in the vector file.");
176 }
177 p_geom->assignSpatialReference(&this->srs);
178 p_outfeature->SetGeometry(p_geom);
179 p_outlayer->CreateFeature(p_outfeature);
180 OGRFeature::DestroyFeature(p_outfeature);
181 }
182 }
183
189 GDALDataset *getDataset() {
190 return this->p_dataset.get();
191 }
192
198 std::vector<std::string> getLayerNames() {
199 std::vector<std::string> retval;
200
201 for (OGRLayer *p_layer : this->p_dataset->GetLayers()) {
202 retval.push_back(std::string(p_layer->GetName()));
203 }
204
205 return retval;
206 }
207
221 std::unordered_map<std::string, std::string> getLayerInfo(std::string layerName) {
222 std::unordered_map<std::string, std::string> retval;
223
224 OGRLayer *p_layer = this->p_dataset->GetLayerByName(layerName.c_str());
225 std::unique_ptr<OGREnvelope> extent = std::unique_ptr<OGREnvelope>(new OGREnvelope);
226 p_layer->GetExtent(extent.get());
227
228 retval.emplace("feature_count", std::to_string(p_layer->GetFeatureCount()));
229 retval.emplace("field_count", std::to_string(p_layer->GetLayerDefn()->GetFieldCount()));
230 retval.emplace("geometry_type", OGRGeometryTypeToName(p_layer->GetGeomType()));
231 retval.emplace("xmin", std::to_string(extent->MinX));
232 retval.emplace("xmax", std::to_string(extent->MaxX));
233 retval.emplace("ymin", std::to_string(extent->MinY));
234 retval.emplace("ymax", std::to_string(extent->MaxY));
235 if (!p_layer->GetSpatialRef()) {
236 std::cout << "WARNING: cannot get spatial reference for layer " << layerName << "." << std::endl;
237 }
238 else {
239 retval.emplace("crs", std::string(p_layer->GetSpatialRef()->GetName()));
240 }
241 return retval;
242 }
243
250 OGRLayer *getLayer(std::string layerName) {
251 return this->p_dataset->GetLayerByName(layerName.c_str());
252 }
253
272 std::vector<std::vector<double>> getPoints(std::string layerName) {
273 OGRLayer *p_layer = this->p_dataset->GetLayerByName(layerName.c_str());
274 std::vector<double> xCoords;
275 std::vector<double> yCoords;
276
277 for (const auto& p_feature : *p_layer) {
278 OGRGeometry *p_geometry = p_feature->GetGeometryRef();
279 if (!p_geometry) {
280 throw std::runtime_error(std::format("an invalid geometry was encountered in the {} layer.", layerName));
281 }
282 switch (wkbFlatten(p_geometry->getGeometryType())) {
283 case OGRwkbGeometryType::wkbPoint: {
284 OGRPoint *p_point = p_geometry->toPoint();
285 xCoords.push_back(p_point->getX());
286 yCoords.push_back(p_point->getY());
287 break;
288 }
289 case OGRwkbGeometryType::wkbMultiPoint: {
290 for (const auto& p_point : *p_geometry->toMultiPoint()) {
291 xCoords.push_back(p_point->getX());
292 yCoords.push_back(p_point->getY());
293 }
294 break;
295 }
296 default:
297 throw std::runtime_error("encountered a geometry which was not of type Point or MultiPoint.");
298 }
299 }
300
301 return {xCoords, yCoords};
302 }
303
313 std::vector<std::string> getPointsAsWkt(std::string layerName){
314 OGRLayer *p_layer = this->p_dataset->GetLayerByName(layerName.c_str());
315 std::vector<std::string> retval;
316
317 for (const auto& p_feature : *p_layer) {
318 OGRGeometry *p_geometry = p_feature->GetGeometryRef();
319 if (!p_geometry) {
320 throw std::runtime_error(std::format("an invalid geometry was encountered in the {} layer.", layerName));
321 }
322 switch (wkbFlatten(p_geometry->getGeometryType())) {
323 case OGRwkbGeometryType::wkbPoint: {
324 OGRPoint *p_point = p_geometry->toPoint();
325 retval.push_back(p_point->exportToWkt());
326 break;
327 }
328 case OGRwkbGeometryType::wkbMultiPoint: {
329 for (const auto& p_point : *p_geometry->toMultiPoint()) {
330 retval.push_back(p_point->exportToWkt());
331 }
332 break;
333 }
334 default:
335 throw std::runtime_error("encountered a geometry which was not of type Point or MultiPoint.");
336 }
337 }
338
339 return retval;
340 }
341
361 std::vector<std::vector<std::vector<double>>> getLineStrings(std::string layerName){
362 OGRLayer *p_layer = this->p_dataset->GetLayerByName(layerName.c_str());
363 std::vector<std::vector<std::vector<double>>> retval;
364
365 for (const auto& p_feature : *p_layer) {
366 OGRGeometry *p_geometry = p_feature->GetGeometryRef();
367 if (!p_geometry) {
368 throw std::runtime_error(std::format("an invalid geometry was encountered in the {} layer.", layerName));
369 }
370 switch (wkbFlatten(p_geometry->getGeometryType())) {
371 case OGRwkbGeometryType::wkbLineString: {
372 std::vector<double> xCoords;
373 std::vector<double> yCoords;
374 for (const auto& p_point : *p_geometry->toLineString()) {
375 xCoords.push_back(p_point.getX());
376 yCoords.push_back(p_point.getY());
377 }
378 retval.push_back({xCoords, yCoords});
379 break;
380 }
381 case OGRwkbGeometryType::wkbMultiLineString: {
382 for (const auto& p_lineString : *p_geometry->toMultiLineString()) {
383 std::vector<double> xCoords;
384 std::vector<double> yCoords;
385 for (const auto& p_point : *p_lineString) {
386 xCoords.push_back(p_point.getX());
387 yCoords.push_back(p_point.getY());
388 }
389 retval.push_back({xCoords, yCoords});
390 }
391 break;
392 }
393 default:
394 throw std::runtime_error("encountered a point which was not of type LineString or MultiLineString");
395 }
396 }
397
398 return retval;
399 }
400
410 void write(std::string filename) {
411 std::filesystem::path filepath = filename;
412 std::string extension = filepath.extension().string();
413
414 GDALAllRegister();
415 GDALDriver *p_driver;
416 if (extension == ".geojson") {
417 p_driver = GetGDALDriverManager()->GetDriverByName("GeoJSON");
418 }
419 else if (extension == ".shp") {
420 p_driver = GetGDALDriverManager()->GetDriverByName("ESRI Shapefile");
421 }
422 else {
423 throw std::runtime_error("file extension must be one of : .geojson, .shp");
424 }
425
426 GDALDataset *datasetCopy = p_driver->CreateCopy(
427 filename.c_str(),
428 this->p_dataset.get(),
429 FALSE,
430 nullptr,
431 nullptr,
432 nullptr
433 );
434
435 if (!datasetCopy) {
436 std::cout << "failed to create dataset with filename " << filename << "." << std::endl;
437 }
438
439 CPLErr err = GDALClose(datasetCopy);
440
441 if (err != CE_None) {
442 std::cout << "failed to close dataset of file " << filename << ". The file output may not be correct." << std::endl;
443 }
444 }
445
451 std::string getFullProjectionInfo() {
452 char *p_proj;
453 if (haveSRS) {
454 srs.exportToPrettyWkt(&p_proj);
455 }
456 else {
457 this->p_dataset->GetLayer(0)->GetSpatialRef()->exportToPrettyWkt(&p_proj);
458 }
459
460 std::string retval = std::string(p_proj);
461 CPLFree(p_proj);
462 return retval;
463 }
464
470 OGRSpatialReference *getSRS(void) {
471 if (!this->haveSRS) {
472 throw std::runtime_error("do not have OGRSpatialReference associated with GDALVectorWrapper.");
473 }
474
475 return &this->srs;
476 }
477};
478
479} //namespace vector
480} //namespace sgs
std::vector< std::string > getPointsAsWkt(std::string layerName)
Definition vector.h:313
OGRSpatialReference * getSRS(void)
Definition vector.h:470
OGRLayer * getLayer(std::string layerName)
Definition vector.h:250
void write(std::string filename)
Definition vector.h:410
std::string getFullProjectionInfo()
Definition vector.h:451
GDALDataset * getDataset()
Definition vector.h:189
std::vector< std::vector< std::vector< double > > > getLineStrings(std::string layerName)
Definition vector.h:361
GDALVectorWrapper(std::string filename, std::string projDBPath)
Definition vector.h:62
GDALVectorWrapper(std::string bytes, std::string projection, std::string name, std::string projDBPath)
Definition vector.h:111
GDALVectorWrapper(GDALDataset *p_dataset, std::string projection)
Definition vector.h:85
std::vector< std::string > getLayerNames()
Definition vector.h:198
std::vector< std::vector< double > > getPoints(std::string layerName)
Definition vector.h:272
std::unordered_map< std::string, std::string > getLayerInfo(std::string layerName)
Definition vector.h:221
Definition vector.h:30
Definition pca.h:23