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 char **paths = nullptr;
114 paths = CSLAddString(paths, projDBPath.c_str());
115 OSRSetPROJSearchPaths(paths);
116 CSLDestroy(paths);
117
118 GDALAllRegister();
119
120 //create dataset from geojson string to copy to new layer
121 GDALDataset *p_indataset = GDALDataset::FromHandle(GDALOpenEx(bytes.c_str(), GDAL_OF_VECTOR, nullptr, nullptr, nullptr));
122 OGRLayer *p_inlayer = p_indataset->GetLayerByName("OGRGeoJSON");
123
124 //set spatial reference
125 OGRErr err = this->srs.importFromWkt(projection.c_str());
126 if (err) {
127 throw std::runtime_error("unable to get Spatial Reference System from projection string.");
128 }
129 this->haveSRS = true;
130
131 //create dataset and layer with correct spatial reference and
132 GDALDriver *p_driver = GetGDALDriverManager()->GetDriverByName("MEM");
133 if (!p_driver) {
134 throw std::runtime_error("unable to create dataset driver.");
135 }
136 GDALDataset *p_dataset = p_driver->Create("", 0, 0, 0, GDT_Unknown, nullptr);
137 if (!p_dataset) {
138 throw std::runtime_error("unable to create dataset from driver.");
139 }
140 OGRLayer *p_outlayer = p_dataset->CreateLayer(name.c_str(), &this->srs, wkbUnknown, nullptr);
141 if (!p_outlayer) {
142 throw std::runtime_error("unable to create dataset layer.");
143 }
144 this->p_dataset = GDALDatasetUniquePtr(p_dataset);
145
146
147 /* copy data over from dataset created using geojson string
148 *
149 * This is done, because when converting a geopandas geodataframe to json, the layer name,
150 * and (more importantly) the spatial reference system may not be transferred correctly.
151 * This way does include A LOT of copying data, however it ensures the srs is correct and
152 * all of the data from the geodataframe is moved over effectively.
153 */
154
155 //copy over field definitions
156 OGRFeatureDefn *p_featdef = p_inlayer->GetLayerDefn();
157 int fcount = p_featdef->GetFieldCount();
158 for (int i = 0; i < fcount; i++) {
159 OGRFieldDefn *p_fielddef = p_featdef->GetFieldDefn(i);
160 OGRErr err = p_outlayer->CreateField(p_fielddef);
161 if (err) {
162 std::cout << "unable to copy field definition." << std::endl;
163 }
164 }
165
166 //copy over features
167 for (auto& p_infeature : p_inlayer) {
168 OGRFeature *p_outfeature = OGRFeature::CreateFeature(p_featdef);
169 for (int i = 0; i < fcount; i++) {
170 p_outfeature->SetField(i, p_infeature->GetRawFieldRef(i));
171 }
172 OGRGeometry *p_geom = p_infeature->GetGeometryRef();
173 p_geom->assignSpatialReference(&this->srs);
174 p_outfeature->SetGeometry(p_geom);
175 p_outlayer->CreateFeature(p_outfeature);
176 OGRFeature::DestroyFeature(p_outfeature);
177 }
178 }
179
185 GDALDataset *getDataset() {
186 return this->p_dataset.get();
187 }
188
194 std::vector<std::string> getLayerNames() {
195 std::vector<std::string> retval;
196
197 for (OGRLayer *p_layer : this->p_dataset->GetLayers()) {
198 retval.push_back(std::string(p_layer->GetName()));
199 }
200
201 return retval;
202 }
203
217 std::unordered_map<std::string, std::string> getLayerInfo(std::string layerName) {
218 std::unordered_map<std::string, std::string> retval;
219
220 OGRLayer *p_layer = this->p_dataset->GetLayerByName(layerName.c_str());
221 std::unique_ptr<OGREnvelope> extent = std::unique_ptr<OGREnvelope>(new OGREnvelope);
222 p_layer->GetExtent(extent.get());
223
224 retval.emplace("feature_count", std::to_string(p_layer->GetFeatureCount()));
225 retval.emplace("field_count", std::to_string(p_layer->GetLayerDefn()->GetFieldCount()));
226 retval.emplace("geometry_type", OGRGeometryTypeToName(p_layer->GetGeomType()));
227 retval.emplace("xmin", std::to_string(extent->MinX));
228 retval.emplace("xmax", std::to_string(extent->MaxX));
229 retval.emplace("ymin", std::to_string(extent->MinY));
230 retval.emplace("ymax", std::to_string(extent->MaxY));
231 if (!p_layer->GetSpatialRef()) {
232 std::cout << "WARNING: cannot get spatial reference for layer " << layerName << "." << std::endl;
233 }
234 else {
235 retval.emplace("crs", std::string(p_layer->GetSpatialRef()->GetName()));
236 }
237 return retval;
238 }
239
246 OGRLayer *getLayer(std::string layerName) {
247 return this->p_dataset->GetLayerByName(layerName.c_str());
248 }
249
268 std::vector<std::vector<double>> getPoints(std::string layerName) {
269 OGRLayer *p_layer = this->p_dataset->GetLayerByName(layerName.c_str());
270 std::vector<double> xCoords;
271 std::vector<double> yCoords;
272
273 for (const auto& p_feature : *p_layer) {
274 OGRGeometry *p_geometry = p_feature->GetGeometryRef();
275 switch (wkbFlatten(p_geometry->getGeometryType())) {
276 case OGRwkbGeometryType::wkbPoint: {
277 OGRPoint *p_point = p_geometry->toPoint();
278 xCoords.push_back(p_point->getX());
279 yCoords.push_back(p_point->getY());
280 break;
281 }
282 case OGRwkbGeometryType::wkbMultiPoint: {
283 for (const auto& p_point : *p_geometry->toMultiPoint()) {
284 xCoords.push_back(p_point->getX());
285 yCoords.push_back(p_point->getY());
286 }
287 break;
288 }
289 default:
290 throw std::runtime_error("encountered a geometry which was not of type Point or MultiPoint.");
291 }
292 }
293
294 return {xCoords, yCoords};
295 }
296
306 std::vector<std::string> getPointsAsWkt(std::string layerName){
307 OGRLayer *p_layer = this->p_dataset->GetLayerByName(layerName.c_str());
308 std::vector<std::string> retval;
309
310 for (const auto& p_feature : *p_layer) {
311 OGRGeometry *p_geometry = p_feature->GetGeometryRef();
312 switch (wkbFlatten(p_geometry->getGeometryType())) {
313 case OGRwkbGeometryType::wkbPoint: {
314 OGRPoint *p_point = p_geometry->toPoint();
315 retval.push_back(p_point->exportToWkt());
316 break;
317 }
318 case OGRwkbGeometryType::wkbMultiPoint: {
319 for (const auto& p_point : *p_geometry->toMultiPoint()) {
320 retval.push_back(p_point->exportToWkt());
321 }
322 break;
323 }
324 default:
325 throw std::runtime_error("encountered a geometry which was not of type Point or MultiPoint.");
326 }
327 }
328
329 return retval;
330 }
331
351 std::vector<std::vector<std::vector<double>>> getLineStrings(std::string layerName){
352 OGRLayer *p_layer = this->p_dataset->GetLayerByName(layerName.c_str());
353 std::vector<std::vector<std::vector<double>>> retval;
354
355 for (const auto& p_feature : *p_layer) {
356 OGRGeometry *p_geometry = p_feature->GetGeometryRef();
357 switch (wkbFlatten(p_geometry->getGeometryType())) {
358 case OGRwkbGeometryType::wkbLineString: {
359 std::vector<double> xCoords;
360 std::vector<double> yCoords;
361 for (const auto& p_point : *p_geometry->toLineString()) {
362 xCoords.push_back(p_point.getX());
363 yCoords.push_back(p_point.getY());
364 }
365 retval.push_back({xCoords, yCoords});
366 break;
367 }
368 case OGRwkbGeometryType::wkbMultiLineString: {
369 for (const auto& p_lineString : *p_geometry->toMultiLineString()) {
370 std::vector<double> xCoords;
371 std::vector<double> yCoords;
372 for (const auto& p_point : *p_lineString) {
373 xCoords.push_back(p_point.getX());
374 yCoords.push_back(p_point.getY());
375 }
376 retval.push_back({xCoords, yCoords});
377 }
378 break;
379 }
380 default:
381 throw std::runtime_error("encountered a point which was not of type LineString or MultiLineString");
382 }
383 }
384
385 return retval;
386 }
387
397 void write(std::string filename) {
398 std::filesystem::path filepath = filename;
399 std::string extension = filepath.extension().string();
400
401 GDALAllRegister();
402 GDALDriver *p_driver;
403 if (extension == ".geojson") {
404 p_driver = GetGDALDriverManager()->GetDriverByName("GeoJSON");
405 }
406 else if (extension == ".shp") {
407 p_driver = GetGDALDriverManager()->GetDriverByName("ESRI Shapefile");
408 }
409 else {
410 throw std::runtime_error("file extension must be one of : .geojson, .shp");
411 }
412
413 GDALDataset *datasetCopy = p_driver->CreateCopy(
414 filename.c_str(),
415 this->p_dataset.get(),
416 FALSE,
417 nullptr,
418 nullptr,
419 nullptr
420 );
421
422 if (!datasetCopy) {
423 std::cout << "failed to create dataset with filename " << filename << "." << std::endl;
424 }
425
426 CPLErr err = GDALClose(datasetCopy);
427
428 if (err != CE_None) {
429 std::cout << "failed to close dataset of file " << filename << ". The file output may not be correct." << std::endl;
430 }
431 }
432
438 std::string getFullProjectionInfo() {
439 char *p_proj;
440 if (haveSRS) {
441 srs.exportToPrettyWkt(&p_proj);
442 }
443 else {
444 this->p_dataset->GetLayer(0)->GetSpatialRef()->exportToPrettyWkt(&p_proj);
445 }
446
447 std::string retval = std::string(p_proj);
448 CPLFree(p_proj);
449 return retval;
450 }
451
457 OGRSpatialReference *getSRS(void) {
458 if (!this->haveSRS) {
459 throw std::runtime_error("do not have OGRSpatialReference associated with GDALVectorWrapper.");
460 }
461
462 return &this->srs;
463 }
464};
465
466} //namespace vector
467} //namespace sgs
std::vector< std::string > getPointsAsWkt(std::string layerName)
Definition vector.h:306
OGRSpatialReference * getSRS(void)
Definition vector.h:457
OGRLayer * getLayer(std::string layerName)
Definition vector.h:246
void write(std::string filename)
Definition vector.h:397
std::string getFullProjectionInfo()
Definition vector.h:438
GDALDataset * getDataset()
Definition vector.h:185
std::vector< std::vector< std::vector< double > > > getLineStrings(std::string layerName)
Definition vector.h:351
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:194
std::vector< std::vector< double > > getPoints(std::string layerName)
Definition vector.h:268
std::unordered_map< std::string, std::string > getLayerInfo(std::string layerName)
Definition vector.h:217
Definition vector.h:30
Definition pca.h:23