Line data Source code
1 : ///////////////////////////////////////////////////////////////////////////////
2 : //
3 : // Project: C++ Test Suite for GDAL/OGR
4 : // Purpose: Shapefile driver testing. Ported from ogr/ogr_shape.py.
5 : // Author: Mateusz Loskot <mateusz@loskot.net>
6 : //
7 : ///////////////////////////////////////////////////////////////////////////////
8 : // Copyright (c) 2006, Mateusz Loskot <mateusz@loskot.net>
9 : // Copyright (c) 2010, Even Rouault <even dot rouault at spatialys.com>
10 : /*
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "gdal_unit_test.h"
15 :
16 : #include "ogr_api.h"
17 : #include "ogrsf_frmts.h"
18 :
19 : #include <algorithm>
20 : #include <iterator>
21 : #include <string>
22 : #include <vector>
23 :
24 : #include "gtest_include.h"
25 :
26 : namespace
27 : {
28 : using namespace tut; // for CheckEqualGeometries
29 :
30 : // Test data
31 : struct test_ogr_shape : public ::testing::Test
32 : {
33 : OGRSFDriverH drv_ = nullptr;
34 : const char *drv_name_ = "ESRI Shapefile";
35 : std::string data_{};
36 : std::string data_tmp_{};
37 : const char *test_name_ = nullptr;
38 :
39 8 : test_ogr_shape()
40 8 : {
41 8 : drv_ = OGRGetDriverByName(drv_name_);
42 :
43 : // Compose data path for test group
44 8 : data_ = tut::common::data_basedir;
45 8 : data_tmp_ = tut::common::tmp_basedir;
46 8 : }
47 :
48 8 : void TearDown() override
49 : {
50 8 : if (!drv_)
51 0 : return;
52 : OGRDataSourceH ds =
53 8 : OGR_Dr_CreateDataSource(drv_, data_tmp_.c_str(), nullptr);
54 8 : if (ds == nullptr)
55 : {
56 0 : return;
57 : }
58 :
59 8 : if (test_name_)
60 : {
61 8 : const int nlyr = OGR_DS_GetLayerCount(ds);
62 15 : for (int i = 0; i < nlyr; i++)
63 : {
64 7 : OGRLayerH lyr = OGR_DS_GetLayer(ds, i);
65 7 : if (EQUAL(OGR_L_GetName(lyr), test_name_))
66 : {
67 7 : OGR_DS_DeleteLayer(ds, i);
68 : }
69 : }
70 : }
71 : }
72 :
73 8 : void SetUp() override
74 : {
75 8 : if (drv_ == nullptr)
76 : {
77 0 : GTEST_SKIP() << "ESRI Shapefile driver missing";
78 : }
79 : else
80 : {
81 8 : test_name_ =
82 8 : ::testing::UnitTest::GetInstance()->current_test_info()->name();
83 :
84 8 : OGRErr err = OGRERR_NONE;
85 :
86 8 : OGRDataSourceH ds = nullptr;
87 8 : ds = OGR_Dr_CreateDataSource(drv_, data_tmp_.c_str(), nullptr);
88 8 : ASSERT_TRUE(nullptr != ds);
89 :
90 : // Create memory Layer
91 8 : OGRLayerH lyr = nullptr;
92 8 : lyr = OGR_DS_CreateLayer(ds, test_name_, nullptr, wkbPolygon,
93 : nullptr);
94 8 : EXPECT_TRUE(nullptr != lyr);
95 8 : if (lyr == nullptr)
96 : {
97 0 : OGR_DS_Destroy(ds);
98 0 : return;
99 : }
100 :
101 : // Create schema
102 8 : OGRFieldDefnH fld = nullptr;
103 :
104 8 : fld = OGR_Fld_Create("AREA", OFTReal);
105 8 : err = OGR_L_CreateField(lyr, fld, true);
106 8 : OGR_Fld_Destroy(fld);
107 8 : EXPECT_EQ(OGRERR_NONE, err);
108 :
109 8 : fld = OGR_Fld_Create("EAS_ID", OFTInteger);
110 8 : err = OGR_L_CreateField(lyr, fld, true);
111 8 : OGR_Fld_Destroy(fld);
112 8 : EXPECT_EQ(OGRERR_NONE, err);
113 :
114 8 : fld = OGR_Fld_Create("PRFEDEA", OFTString);
115 8 : err = OGR_L_CreateField(lyr, fld, true);
116 8 : OGR_Fld_Destroy(fld);
117 8 : EXPECT_EQ(OGRERR_NONE, err);
118 :
119 : // Check schema
120 8 : OGRFeatureDefnH featDefn = OGR_L_GetLayerDefn(lyr);
121 8 : ASSERT_TRUE(nullptr != featDefn);
122 8 : EXPECT_EQ(3, OGR_FD_GetFieldCount(featDefn));
123 :
124 : // Copy ogr/poly.shp to temporary layer
125 8 : OGRFeatureH featDst = OGR_F_Create(featDefn);
126 8 : EXPECT_TRUE(nullptr != featDst);
127 8 : if (featDst)
128 : {
129 16 : std::string source(data_);
130 8 : source += SEP;
131 8 : source += "poly.shp";
132 8 : OGRDataSourceH dsSrc = OGR_Dr_Open(drv_, source.c_str(), false);
133 8 : EXPECT_TRUE(nullptr != dsSrc);
134 8 : if (dsSrc)
135 : {
136 8 : OGRLayerH lyrSrc = OGR_DS_GetLayer(dsSrc, 0);
137 8 : EXPECT_TRUE(nullptr != lyrSrc);
138 8 : if (lyrSrc)
139 : {
140 8 : OGRFeatureH featSrc = nullptr;
141 80 : while (nullptr !=
142 88 : (featSrc = OGR_L_GetNextFeature(lyrSrc)))
143 : {
144 80 : err = OGR_F_SetFrom(featDst, featSrc, true);
145 80 : EXPECT_EQ(OGRERR_NONE, err);
146 :
147 80 : err = OGR_L_CreateFeature(lyr, featDst);
148 80 : EXPECT_EQ(OGRERR_NONE, err);
149 :
150 80 : OGR_F_Destroy(featSrc);
151 : }
152 : }
153 : // Release and close resources
154 :
155 8 : OGR_DS_Destroy(dsSrc);
156 : }
157 8 : OGR_F_Destroy(featDst);
158 : }
159 8 : OGR_DS_Destroy(ds);
160 : }
161 : }
162 : };
163 :
164 : //
165 : // Template of attribute reading function and its specializations
166 : //
167 : template <typename T> inline void read_feature_attribute(OGRFeatureH, int, T &)
168 : {
169 : assert(!"Can't find read_feature_attribute specialization for given type");
170 : }
171 :
172 : template <>
173 16 : inline void read_feature_attribute(OGRFeatureH feature, int index, int &val)
174 : {
175 16 : val = OGR_F_GetFieldAsInteger(feature, index);
176 16 : }
177 :
178 : #ifdef unused
179 : template <>
180 : inline void read_feature_attribute(OGRFeatureH feature, int index, double &val)
181 : {
182 : val = OGR_F_GetFieldAsDouble(feature, index);
183 : }
184 : #endif
185 :
186 : template <>
187 1 : inline void read_feature_attribute(OGRFeatureH feature, int index,
188 : std::string &val)
189 : {
190 1 : val = OGR_F_GetFieldAsString(feature, index);
191 1 : }
192 :
193 : //
194 : // Test layer attributes from given field against expected list of values
195 : //
196 : template <typename T>
197 : ::testing::AssertionResult
198 4 : CheckEqualAttributes(OGRLayerH layer, std::string const &field, T const &list)
199 : {
200 : // Test raw pointers
201 4 : if (nullptr == layer)
202 : {
203 0 : return ::testing::AssertionFailure() << "Layer is NULL";
204 : }
205 :
206 4 : OGRFeatureDefnH featDefn = OGR_L_GetLayerDefn(layer);
207 4 : if (nullptr == featDefn)
208 : {
209 0 : return ::testing::AssertionFailure() << "Layer schema is NULL";
210 : }
211 :
212 4 : int fldIndex = OGR_FD_GetFieldIndex(featDefn, field.c_str());
213 4 : if (fldIndex < 0)
214 : {
215 0 : return ::testing::AssertionFailure() << "Can't find field " << field;
216 : }
217 :
218 : // Test value in tested field from subsequent features
219 4 : OGRFeatureH feat = nullptr;
220 4 : OGRFieldDefnH fldDefn = nullptr;
221 2 : typename T::value_type attrVal;
222 :
223 21 : for (const auto &attr : list)
224 : {
225 17 : feat = OGR_L_GetNextFeature(layer);
226 :
227 17 : fldDefn = OGR_F_GetFieldDefnRef(feat, fldIndex);
228 17 : if (nullptr == fldDefn)
229 : {
230 0 : return ::testing::AssertionFailure() << "Field schema is NULL";
231 : }
232 :
233 17 : read_feature_attribute(feat, fldIndex, attrVal);
234 :
235 17 : OGR_F_Destroy(feat);
236 :
237 : // Test attribute against expected value
238 17 : if (attr != attrVal)
239 : {
240 : return ::testing::AssertionFailure()
241 0 : << "Attributes not equal. Expected " << attr << ", got "
242 0 : << attrVal;
243 : }
244 : }
245 :
246 : // Check if not too many features filtered
247 4 : feat = OGR_L_GetNextFeature(layer);
248 4 : OGR_F_Destroy(feat);
249 :
250 4 : if (nullptr != feat)
251 : {
252 : return ::testing::AssertionFailure()
253 0 : << "Got more features than expected";
254 : }
255 4 : return ::testing::AssertionSuccess();
256 : }
257 :
258 : // Test Create/Destroy empty directory datasource
259 4 : TEST_F(test_ogr_shape, create)
260 : {
261 : // Try to remove tmp and ignore error code
262 1 : OGR_Dr_DeleteDataSource(drv_, data_tmp_.c_str());
263 :
264 1 : OGRDataSourceH ds = nullptr;
265 1 : ds = OGR_Dr_CreateDataSource(drv_, data_tmp_.c_str(), nullptr);
266 1 : ASSERT_TRUE(nullptr != ds);
267 :
268 1 : OGR_DS_Destroy(ds);
269 : }
270 :
271 : // Test attributes written to new table
272 4 : TEST_F(test_ogr_shape, attributes)
273 : {
274 1 : OGRErr err = OGRERR_NONE;
275 1 : const int size = 5;
276 1 : const int expect[size] = {168, 169, 166, 158, 165};
277 :
278 1 : std::string source(data_tmp_);
279 1 : source += SEP;
280 1 : source += test_name_;
281 1 : source += ".shp";
282 1 : OGRDataSourceH ds = OGR_Dr_Open(drv_, source.c_str(), false);
283 1 : ASSERT_TRUE(nullptr != ds);
284 :
285 1 : OGRLayerH lyr = OGR_DS_GetLayer(ds, 0);
286 1 : EXPECT_TRUE(nullptr != lyr);
287 1 : if (lyr)
288 : {
289 1 : err = OGR_L_SetAttributeFilter(lyr, "eas_id < 170");
290 1 : EXPECT_EQ(OGRERR_NONE, err);
291 :
292 : // Prepare tester collection
293 2 : std::vector<int> list;
294 1 : std::copy(expect, expect + size, std::back_inserter(list));
295 :
296 2 : EXPECT_TRUE(CheckEqualAttributes(lyr, "eas_id", list));
297 : }
298 1 : OGR_DS_Destroy(ds);
299 : }
300 :
301 : // Test geometries written to new shapefile
302 4 : TEST_F(test_ogr_shape, geometries)
303 : {
304 : // Original shapefile
305 1 : std::string orig(data_);
306 1 : orig += SEP;
307 1 : orig += "poly.shp";
308 1 : OGRDataSourceH dsOrig = OGR_Dr_Open(drv_, orig.c_str(), false);
309 1 : ASSERT_TRUE(nullptr != dsOrig);
310 :
311 1 : OGRLayerH lyrOrig = OGR_DS_GetLayer(dsOrig, 0);
312 1 : EXPECT_TRUE(nullptr != lyrOrig);
313 1 : if (lyrOrig)
314 : {
315 : // Copied shapefile
316 2 : std::string tmp(data_tmp_);
317 1 : tmp += SEP;
318 1 : tmp += test_name_;
319 1 : tmp += ".shp";
320 1 : OGRDataSourceH dsTmp = OGR_Dr_Open(drv_, tmp.c_str(), false);
321 1 : EXPECT_TRUE(nullptr != dsTmp);
322 :
323 1 : OGRLayerH lyrTmp = OGR_DS_GetLayer(dsTmp, 0);
324 1 : EXPECT_TRUE(nullptr != lyrTmp);
325 1 : if (lyrTmp)
326 : {
327 : // Iterate through features and compare geometries
328 1 : OGRFeatureH featOrig = OGR_L_GetNextFeature(lyrOrig);
329 1 : OGRFeatureH featTmp = OGR_L_GetNextFeature(lyrTmp);
330 :
331 11 : while (nullptr != featOrig && nullptr != featTmp)
332 : {
333 10 : OGRGeometryH lhs = OGR_F_GetGeometryRef(featOrig);
334 10 : OGRGeometryH rhs = OGR_F_GetGeometryRef(featTmp);
335 :
336 10 : EXPECT_TRUE(CheckEqualGeometries(lhs, rhs, 0.000000001));
337 :
338 : // TODO: add ensure_equal_attributes()
339 :
340 10 : OGR_F_Destroy(featOrig);
341 10 : OGR_F_Destroy(featTmp);
342 :
343 : // Move to next feature
344 10 : featOrig = OGR_L_GetNextFeature(lyrOrig);
345 10 : featTmp = OGR_L_GetNextFeature(lyrTmp);
346 : }
347 : }
348 1 : OGR_DS_Destroy(dsTmp);
349 : }
350 :
351 1 : OGR_DS_Destroy(dsOrig);
352 : }
353 :
354 : // Write a feature without a geometry, then read it back
355 4 : TEST_F(test_ogr_shape, no_geometry)
356 : {
357 : // Create feature without geometry
358 1 : std::string tmp(data_tmp_);
359 1 : tmp += SEP;
360 1 : tmp += test_name_;
361 1 : tmp += ".shp";
362 :
363 : // Write the feature
364 : {
365 1 : OGRDataSourceH ds = OGR_Dr_Open(drv_, tmp.c_str(), true);
366 1 : ASSERT_TRUE(nullptr != ds);
367 :
368 1 : OGRLayerH lyr = OGR_DS_GetLayer(ds, 0);
369 1 : EXPECT_TRUE(nullptr != lyr);
370 1 : if (lyr != nullptr)
371 : {
372 1 : OGRFeatureDefnH featDefn = OGR_L_GetLayerDefn(lyr);
373 1 : EXPECT_TRUE(nullptr != featDefn);
374 :
375 1 : OGRFeatureH featNonSpatial = OGR_F_Create(featDefn);
376 1 : EXPECT_TRUE(nullptr != featNonSpatial);
377 1 : if (featDefn && featNonSpatial)
378 : {
379 1 : int fldIndex = OGR_FD_GetFieldIndex(featDefn, "PRFEDEA");
380 1 : EXPECT_TRUE(fldIndex >= 0);
381 1 : if (fldIndex >= 0)
382 : {
383 1 : OGR_F_SetFieldString(featNonSpatial, fldIndex, "nulled");
384 :
385 1 : OGRErr err = OGR_L_CreateFeature(lyr, featNonSpatial);
386 1 : EXPECT_EQ(OGRERR_NONE, err);
387 : }
388 : }
389 :
390 1 : OGR_F_Destroy(featNonSpatial);
391 : }
392 1 : OGR_DS_Destroy(ds);
393 : }
394 :
395 : // Read back the non-spatial feature and get the geometry
396 : {
397 1 : OGRDataSourceH ds = OGR_Dr_Open(drv_, tmp.c_str(), false);
398 1 : ASSERT_TRUE(nullptr != ds);
399 :
400 1 : OGRLayerH lyr = OGR_DS_GetLayer(ds, 0);
401 1 : EXPECT_TRUE(nullptr != lyr);
402 1 : if (lyr != nullptr)
403 : {
404 1 : OGRErr err = OGR_L_SetAttributeFilter(lyr, "PRFEDEA = 'nulled'");
405 1 : EXPECT_EQ(OGRERR_NONE, err);
406 :
407 : // Fetch feature without geometry
408 1 : OGRFeatureH featNonSpatial = OGR_L_GetNextFeature(lyr);
409 1 : EXPECT_TRUE(nullptr != featNonSpatial);
410 1 : if (featNonSpatial != nullptr)
411 : {
412 : // Null geometry is expected
413 1 : OGRGeometryH nonGeom = OGR_F_GetGeometryRef(featNonSpatial);
414 1 : EXPECT_TRUE(nullptr == nonGeom);
415 :
416 1 : OGR_F_Destroy(featNonSpatial);
417 : }
418 : }
419 1 : OGR_DS_Destroy(ds);
420 : }
421 : }
422 :
423 : // Test ExecuteSQL() results layers without geometry
424 4 : TEST_F(test_ogr_shape, ExecuteSQL_no_geometry)
425 : {
426 1 : const int size = 10;
427 1 : const int expect[size] = {179, 173, 172, 171, 170, 169, 168, 166, 165, 158};
428 :
429 : // Open directory as a datasource
430 1 : OGRDataSourceH ds = OGR_Dr_Open(drv_, data_.c_str(), false);
431 1 : ASSERT_TRUE(nullptr != ds);
432 :
433 2 : std::string sql("select distinct eas_id from poly order by eas_id desc");
434 1 : OGRLayerH lyr = OGR_DS_ExecuteSQL(ds, sql.c_str(), nullptr, nullptr);
435 1 : EXPECT_TRUE(nullptr != lyr);
436 1 : if (lyr)
437 : {
438 : // Prepare tester collection
439 2 : std::vector<int> list;
440 1 : std::copy(expect, expect + size, std::back_inserter(list));
441 :
442 2 : EXPECT_TRUE(CheckEqualAttributes(lyr, "eas_id", list));
443 :
444 1 : OGR_DS_ReleaseResultSet(ds, lyr);
445 : }
446 1 : OGR_DS_Destroy(ds);
447 : }
448 :
449 : // Test ExecuteSQL() results layers with geometry
450 4 : TEST_F(test_ogr_shape, ExecuteSQL_geometry)
451 : {
452 : // Open directory as a datasource
453 1 : OGRDataSourceH ds = OGR_Dr_Open(drv_, data_.c_str(), false);
454 1 : ASSERT_TRUE(nullptr != ds);
455 :
456 2 : std::string sql("select * from poly where prfedea = '35043413'");
457 1 : OGRLayerH lyr = OGR_DS_ExecuteSQL(ds, sql.c_str(), nullptr, nullptr);
458 1 : EXPECT_TRUE(nullptr != lyr);
459 1 : if (lyr)
460 : {
461 : // Prepare tester collection
462 2 : std::vector<std::string> list;
463 1 : list.push_back("35043413");
464 :
465 : // Test attributes
466 2 : EXPECT_TRUE(CheckEqualAttributes(lyr, "prfedea", list));
467 :
468 : // Test geometry
469 1 : const char *wkt =
470 : "POLYGON ((479750.688 4764702.000,479658.594 4764670.000,"
471 : "479640.094 4764721.000,479735.906 4764752.000,"
472 : "479750.688 4764702.000))";
473 :
474 1 : OGRGeometryH testGeom = nullptr;
475 1 : OGRErr err = OGR_G_CreateFromWkt((char **)&wkt, nullptr, &testGeom);
476 1 : EXPECT_EQ(OGRERR_NONE, err);
477 1 : if (testGeom)
478 : {
479 1 : OGR_L_ResetReading(lyr);
480 1 : OGRFeatureH feat = OGR_L_GetNextFeature(lyr);
481 1 : EXPECT_TRUE(nullptr != feat);
482 1 : if (feat)
483 : {
484 1 : EXPECT_TRUE(CheckEqualGeometries(OGR_F_GetGeometryRef(feat),
485 : testGeom, 0.001));
486 1 : OGR_F_Destroy(feat);
487 : }
488 1 : OGR_G_DestroyGeometry(testGeom);
489 : }
490 1 : OGR_DS_ReleaseResultSet(ds, lyr);
491 : }
492 1 : OGR_DS_Destroy(ds);
493 : }
494 :
495 : // Test spatial filtering
496 4 : TEST_F(test_ogr_shape, spatial_filtering)
497 : {
498 1 : OGRErr err = OGRERR_NONE;
499 :
500 : // Read feature without geometry
501 1 : std::string tmp(data_tmp_);
502 1 : tmp += SEP;
503 1 : tmp += "poly.shp";
504 1 : OGRDataSourceH ds = OGR_Dr_Open(drv_, data_.c_str(), false);
505 1 : ASSERT_TRUE(nullptr != ds);
506 :
507 1 : OGRLayerH lyr = OGR_DS_GetLayer(ds, 0);
508 1 : EXPECT_TRUE(nullptr != lyr);
509 1 : if (lyr)
510 : {
511 : // Set empty filter for attributes
512 1 : err = OGR_L_SetAttributeFilter(lyr, nullptr);
513 1 : EXPECT_EQ(OGRERR_NONE, err);
514 :
515 : // Set spatial filter
516 1 : const char *wkt = "LINESTRING(479505 4763195,480526 4762819)";
517 1 : OGRGeometryH filterGeom = nullptr;
518 1 : err = OGR_G_CreateFromWkt((char **)&wkt, nullptr, &filterGeom);
519 1 : ASSERT_EQ(OGRERR_NONE, err);
520 1 : if (filterGeom)
521 : {
522 1 : OGR_L_SetSpatialFilter(lyr, filterGeom);
523 :
524 : // Prepare tester collection
525 2 : std::vector<int> list;
526 1 : list.push_back(158);
527 :
528 : // Test attributes
529 2 : EXPECT_TRUE(CheckEqualAttributes(lyr, "eas_id", list));
530 :
531 1 : OGR_G_DestroyGeometry(filterGeom);
532 : }
533 : }
534 1 : OGR_DS_Destroy(ds);
535 : }
536 :
537 4 : TEST_F(test_ogr_shape, create_gdal)
538 : {
539 : GDALDriver *shpDriver =
540 1 : GetGDALDriverManager()->GetDriverByName("ESRI Shapefile");
541 : GDALDataset *pShpDst =
542 1 : shpDriver->Create("/vsimem/test.shp", 0, 0, 0, GDT_Unknown, nullptr);
543 1 : EXPECT_EQ(pShpDst->GetAccess(), GA_Update);
544 1 : GDALClose(pShpDst);
545 1 : }
546 :
547 : } // namespace
|