/src/gdal/ogr/ogrsf_frmts/openfilegdb/ogropenfilegdblayer.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: OpenGIS Simple Features Reference Implementation |
4 | | * Purpose: Implements Open FileGDB OGR driver. |
5 | | * Author: Even Rouault, <even dot rouault at spatialys.com> |
6 | | * |
7 | | ****************************************************************************** |
8 | | * Copyright (c) 2014, Even Rouault <even dot rouault at spatialys.com> |
9 | | * |
10 | | * SPDX-License-Identifier: MIT |
11 | | ****************************************************************************/ |
12 | | |
13 | | #include "cpl_port.h" |
14 | | #include "ogr_openfilegdb.h" |
15 | | |
16 | | #include <cmath> |
17 | | #include <cstddef> |
18 | | #include <cstdio> |
19 | | #include <cstdlib> |
20 | | #include <cstring> |
21 | | #include <cwchar> |
22 | | #include <algorithm> |
23 | | #include <limits> |
24 | | #include <string> |
25 | | |
26 | | #include "cpl_conv.h" |
27 | | #include "cpl_error.h" |
28 | | #include "cpl_minixml.h" |
29 | | #include "cpl_quad_tree.h" |
30 | | #include "cpl_string.h" |
31 | | #include "ogr_api.h" |
32 | | #include "ogr_core.h" |
33 | | #include "ogr_feature.h" |
34 | | #include "ogr_geometry.h" |
35 | | #include "ogr_spatialref.h" |
36 | | #include "ogr_srs_api.h" |
37 | | #include "ogrsf_frmts.h" |
38 | | #include "filegdbtable.h" |
39 | | #include "ogr_swq.h" |
40 | | #include "filegdb_coordprec_read.h" |
41 | | |
42 | 10.2k | OGROpenFileGDBGeomFieldDefn::~OGROpenFileGDBGeomFieldDefn() = default; |
43 | | |
44 | 17.0k | OGROpenFileGDBFeatureDefn::~OGROpenFileGDBFeatureDefn() = default; |
45 | | |
46 | | /************************************************************************/ |
47 | | /* OGROpenFileGDBLayer() */ |
48 | | /************************************************************************/ |
49 | | |
50 | | OGROpenFileGDBLayer::OGROpenFileGDBLayer( |
51 | | OGROpenFileGDBDataSource *poDS, const char *pszGDBFilename, |
52 | | const char *pszName, const std::string &osDefinition, |
53 | | const std::string &osDocumentation, bool bEditable, |
54 | | OGRwkbGeometryType eGeomType, const std::string &osParentDefinition) |
55 | 15.6k | : m_poDS(poDS), m_osGDBFilename(pszGDBFilename), m_osName(pszName), |
56 | 15.6k | m_bEditable(bEditable), m_osDefinition(osDefinition), |
57 | 15.6k | m_osDocumentation(osDocumentation) |
58 | 15.6k | { |
59 | | // TODO(rouault): What error on compiler versions? r33032 does not say. |
60 | | |
61 | | // We cannot initialize m_poFeatureDefn in above list. MSVC doesn't like |
62 | | // this to be used in initialization list. |
63 | 15.6k | m_poFeatureDefn = new OGROpenFileGDBFeatureDefn(this, pszName, false); |
64 | 15.6k | SetDescription(m_poFeatureDefn->GetName()); |
65 | 15.6k | m_poFeatureDefn->SetGeomType(wkbNone); |
66 | 15.6k | m_poFeatureDefn->Reference(); |
67 | | |
68 | 15.6k | m_eGeomType = eGeomType; |
69 | | |
70 | 15.6k | if (!m_osDefinition.empty()) |
71 | 4.22k | { |
72 | 4.22k | BuildGeometryColumnGDBv10(osParentDefinition); |
73 | 4.22k | } |
74 | | |
75 | | // bSealFields = false because we do lazy resolution of fields |
76 | 15.6k | m_poFeatureDefn->Seal(/* bSealFields = */ false); |
77 | 15.6k | } |
78 | | |
79 | | /************************************************************************/ |
80 | | /* OGROpenFileGDBLayer() */ |
81 | | /************************************************************************/ |
82 | | |
83 | | OGROpenFileGDBLayer::OGROpenFileGDBLayer(OGROpenFileGDBDataSource *poDS, |
84 | | const char *pszGDBFilename, |
85 | | const char *pszName, |
86 | | OGRwkbGeometryType eType, |
87 | | CSLConstList papszOptions) |
88 | 1.79k | : m_poDS(poDS), m_osGDBFilename(pszGDBFilename), m_osName(pszName), |
89 | 1.79k | m_aosCreationOptions(papszOptions), m_eGeomType(eType), |
90 | | m_bArcGISPro32OrLater( |
91 | 1.79k | EQUAL(CSLFetchNameValueDef(papszOptions, "TARGET_ARCGIS_VERSION", ""), |
92 | | "ARCGIS_PRO_3_2_OR_LATER")) |
93 | 1.79k | { |
94 | 1.79k | } |
95 | | |
96 | | /***********************************************************************/ |
97 | | /* ~OGROpenFileGDBLayer() */ |
98 | | /***********************************************************************/ |
99 | | |
100 | | OGROpenFileGDBLayer::~OGROpenFileGDBLayer() |
101 | 17.4k | { |
102 | 17.4k | OGROpenFileGDBLayer::SyncToDisk(); |
103 | | |
104 | 17.4k | if (m_poFeatureDefn) |
105 | 17.0k | { |
106 | 17.0k | m_poFeatureDefn->UnsetLayer(); |
107 | 17.0k | m_poFeatureDefn->Release(); |
108 | 17.0k | } |
109 | | |
110 | 17.4k | delete m_poLyrTable; |
111 | | |
112 | 17.4k | delete m_poAttributeIterator; |
113 | 17.4k | delete m_poIterMinMax; |
114 | 17.4k | delete m_poSpatialIndexIterator; |
115 | 17.4k | delete m_poCombinedIterator; |
116 | 17.4k | if (m_pQuadTree != nullptr) |
117 | 6.37k | CPLQuadTreeDestroy(m_pQuadTree); |
118 | 17.4k | CPLFree(m_pahFilteredFeatures); |
119 | 17.4k | } |
120 | | |
121 | | /************************************************************************/ |
122 | | /* Close() */ |
123 | | /************************************************************************/ |
124 | | |
125 | | void OGROpenFileGDBLayer::Close() |
126 | 3.83k | { |
127 | 3.83k | delete m_poLyrTable; |
128 | 3.83k | m_poLyrTable = nullptr; |
129 | 3.83k | m_bValidLayerDefn = FALSE; |
130 | 3.83k | } |
131 | | |
132 | | /************************************************************************/ |
133 | | /* BuildGeometryColumnGDBv10() */ |
134 | | /************************************************************************/ |
135 | | |
136 | | int OGROpenFileGDBLayer::BuildGeometryColumnGDBv10( |
137 | | const std::string &osParentDefinition) |
138 | 4.22k | { |
139 | 4.22k | CPLXMLNode *psTree = CPLParseXMLString(m_osDefinition.c_str()); |
140 | 4.22k | if (psTree == nullptr) |
141 | 435 | { |
142 | 435 | m_osDefinition = ""; |
143 | 435 | return FALSE; |
144 | 435 | } |
145 | | |
146 | 3.79k | CPLStripXMLNamespace(psTree, nullptr, TRUE); |
147 | | /* CPLSerializeXMLTreeToFile( psTree, "/dev/stderr" ); */ |
148 | 3.79k | const CPLXMLNode *psInfo = CPLSearchXMLNode(psTree, "=DEFeatureClassInfo"); |
149 | 3.79k | if (psInfo == nullptr) |
150 | 811 | psInfo = CPLSearchXMLNode(psTree, "=DETableInfo"); |
151 | 3.79k | if (psInfo == nullptr) |
152 | 20 | { |
153 | 20 | m_osDefinition = ""; |
154 | 20 | CPLDestroyXMLNode(psTree); |
155 | 20 | return FALSE; |
156 | 20 | } |
157 | | |
158 | 3.77k | const char *pszAliasName = CPLGetXMLValue(psInfo, "AliasName", nullptr); |
159 | 3.77k | if (pszAliasName && strcmp(pszAliasName, GetDescription()) != 0) |
160 | 4 | { |
161 | 4 | SetMetadataItem("ALIAS_NAME", pszAliasName); |
162 | 4 | } |
163 | | |
164 | 3.77k | m_bTimeInUTC = CPLTestBool(CPLGetXMLValue(psInfo, "IsTimeInUTC", "false")); |
165 | | |
166 | | /* We cannot trust the XML definition to build the field definitions. */ |
167 | | /* It sometimes misses a few fields ! */ |
168 | | |
169 | 3.77k | const bool bHasZ = CPLTestBool(CPLGetXMLValue(psInfo, "HasZ", "NO")); |
170 | 3.77k | const bool bHasM = CPLTestBool(CPLGetXMLValue(psInfo, "HasM", "NO")); |
171 | 3.77k | const char *pszShapeType = CPLGetXMLValue(psInfo, "ShapeType", nullptr); |
172 | 3.77k | const char *pszShapeFieldName = |
173 | 3.77k | CPLGetXMLValue(psInfo, "ShapeFieldName", nullptr); |
174 | 3.77k | if (pszShapeType != nullptr && pszShapeFieldName != nullptr) |
175 | 2.97k | { |
176 | 2.97k | m_eGeomType = |
177 | 2.97k | FileGDBOGRGeometryConverter::GetGeometryTypeFromESRI(pszShapeType); |
178 | | |
179 | 2.97k | if (EQUAL(pszShapeType, "esriGeometryMultiPatch")) |
180 | 113 | { |
181 | 113 | if (m_poLyrTable == nullptr) |
182 | 113 | { |
183 | 113 | m_poLyrTable = new FileGDBTable(); |
184 | 113 | if (!(m_poLyrTable->Open(m_osGDBFilename, m_bEditable, |
185 | 113 | GetDescription()))) |
186 | 25 | { |
187 | 25 | Close(); |
188 | 25 | } |
189 | 113 | } |
190 | 113 | if (m_poLyrTable != nullptr) |
191 | 88 | { |
192 | 88 | m_iGeomFieldIdx = m_poLyrTable->GetGeomFieldIdx(); |
193 | 88 | if (m_iGeomFieldIdx >= 0) |
194 | 88 | { |
195 | 88 | FileGDBGeomField *poGDBGeomField = |
196 | 88 | reinterpret_cast<FileGDBGeomField *>( |
197 | 88 | m_poLyrTable->GetField(m_iGeomFieldIdx)); |
198 | 88 | m_poGeomConverter.reset( |
199 | 88 | FileGDBOGRGeometryConverter::BuildConverter( |
200 | 88 | poGDBGeomField)); |
201 | 88 | TryToDetectMultiPatchKind(); |
202 | 88 | } |
203 | 88 | } |
204 | 113 | } |
205 | | |
206 | 2.97k | if (bHasZ) |
207 | 1.38k | m_eGeomType = wkbSetZ(m_eGeomType); |
208 | 2.97k | if (bHasM) |
209 | 1.48k | m_eGeomType = wkbSetM(m_eGeomType); |
210 | | |
211 | 2.97k | auto poGeomFieldDefn = std::make_unique<OGROpenFileGDBGeomFieldDefn>( |
212 | 2.97k | nullptr, pszShapeFieldName, m_eGeomType); |
213 | | |
214 | 2.97k | const CPLXMLNode *psGPFieldInfoExs = |
215 | 2.97k | CPLGetXMLNode(psInfo, "GPFieldInfoExs"); |
216 | 2.97k | if (psGPFieldInfoExs) |
217 | 2.63k | { |
218 | 6.82k | for (const CPLXMLNode *psChild = psGPFieldInfoExs->psChild; psChild; |
219 | 4.19k | psChild = psChild->psNext) |
220 | 6.53k | { |
221 | 6.53k | if (psChild->eType != CXT_Element) |
222 | 2.74k | continue; |
223 | 3.79k | if (EQUAL(psChild->pszValue, "GPFieldInfoEx") && |
224 | 3.79k | EQUAL(CPLGetXMLValue(psChild, "Name", ""), |
225 | 3.79k | pszShapeFieldName)) |
226 | 2.34k | { |
227 | 2.34k | poGeomFieldDefn->SetNullable(CPLTestBool( |
228 | 2.34k | CPLGetXMLValue(psChild, "IsNullable", "TRUE"))); |
229 | 2.34k | break; |
230 | 2.34k | } |
231 | 3.79k | } |
232 | 2.63k | } |
233 | | |
234 | 2.97k | const CPLXMLNode *psSpatialReference = |
235 | 2.97k | CPLGetXMLNode(psInfo, "SpatialReference"); |
236 | 2.97k | if (psSpatialReference) |
237 | 2.97k | { |
238 | 2.97k | poGeomFieldDefn->SetCoordinatePrecision( |
239 | 2.97k | GDBGridSettingsToOGR(psSpatialReference)); |
240 | 2.97k | } |
241 | | |
242 | 2.97k | OGRSpatialReference *poParentSRS = nullptr; |
243 | 2.97k | if (!osParentDefinition.empty()) |
244 | 0 | { |
245 | 0 | CPLXMLNode *psParentTree = |
246 | 0 | CPLParseXMLString(osParentDefinition.c_str()); |
247 | 0 | if (psParentTree != nullptr) |
248 | 0 | { |
249 | 0 | CPLStripXMLNamespace(psParentTree, nullptr, TRUE); |
250 | 0 | CPLXMLNode *psParentInfo = |
251 | 0 | CPLSearchXMLNode(psParentTree, "=DEFeatureDataset"); |
252 | 0 | if (psParentInfo != nullptr) |
253 | 0 | { |
254 | 0 | poParentSRS = m_poDS->BuildSRS(psParentInfo); |
255 | 0 | } |
256 | 0 | CPLDestroyXMLNode(psParentTree); |
257 | 0 | } |
258 | 0 | if (poParentSRS == nullptr) |
259 | 0 | { |
260 | 0 | CPLDebug("OpenFileGDB", "Cannot get SRS from feature dataset"); |
261 | 0 | } |
262 | 0 | } |
263 | | |
264 | 2.97k | auto poSRS = m_poDS->BuildSRS(psInfo); |
265 | 2.97k | if (poParentSRS) |
266 | 0 | { |
267 | 0 | if (poSRS) |
268 | 0 | { |
269 | 0 | if (!poSRS->IsSame(poParentSRS)) |
270 | 0 | { |
271 | | // Not sure this situation is really valid (seems more a |
272 | | // bug of the editing software), but happens with |
273 | | // https://github.com/OSGeo/gdal/issues/5747 |
274 | | // In the situation of |
275 | | // https://github.com/OSGeo/gdal/issues/5747, the SRS inside |
276 | | // the .gdbtable is consistent with the XML definition of |
277 | | // the feature dataset, so it seems that the XML |
278 | | // definition of the feature table lacked an update. |
279 | 0 | CPLDebug( |
280 | 0 | "OpenFileGDB", |
281 | 0 | "Table %s declare a CRS '%s' in its XML definition, " |
282 | 0 | "but its feature dataset declares '%s'. " |
283 | 0 | "Using the later", |
284 | 0 | GetDescription(), poSRS->GetName(), |
285 | 0 | poParentSRS->GetName()); |
286 | 0 | } |
287 | 0 | poSRS->Release(); |
288 | 0 | } |
289 | | // Always use the SRS of the feature dataset |
290 | 0 | poSRS = poParentSRS; |
291 | 0 | } |
292 | 2.97k | if (poSRS != nullptr) |
293 | 2.09k | { |
294 | 2.09k | poGeomFieldDefn->SetSpatialRef(poSRS); |
295 | 2.09k | poSRS->Dereference(); |
296 | 2.09k | } |
297 | 2.97k | m_poFeatureDefn->AddGeomFieldDefn(std::move(poGeomFieldDefn)); |
298 | 2.97k | } |
299 | 799 | else |
300 | 799 | { |
301 | 799 | m_eGeomType = wkbNone; |
302 | 799 | } |
303 | 3.77k | CPLDestroyXMLNode(psTree); |
304 | | |
305 | 3.77k | return TRUE; |
306 | 3.79k | } |
307 | | |
308 | | /************************************************************************/ |
309 | | /* TryToDetectMultiPatchKind() */ |
310 | | /************************************************************************/ |
311 | | |
312 | | // If the first and last feature have the same geometry type, then use |
313 | | // it for the whole layer. |
314 | | void OGROpenFileGDBLayer::TryToDetectMultiPatchKind() |
315 | 1.22k | { |
316 | 1.22k | CPLAssert(m_poLyrTable != nullptr); |
317 | 1.22k | CPLAssert(m_iGeomFieldIdx >= 0); |
318 | | |
319 | 1.22k | if (m_poLyrTable->GetTotalRecordCount() == 0) |
320 | 11 | return; |
321 | 1.21k | const int64_t nFirstIdx = m_poLyrTable->GetAndSelectNextNonEmptyRow(0); |
322 | 1.21k | if (nFirstIdx < 0) |
323 | 7 | return; |
324 | | |
325 | 1.20k | const OGRField *psField = m_poLyrTable->GetFieldValue(m_iGeomFieldIdx); |
326 | 1.20k | if (psField == nullptr) |
327 | 108 | return; |
328 | 1.09k | OGRGeometry *poGeom = m_poGeomConverter->GetAsGeometry(psField); |
329 | 1.09k | if (poGeom == nullptr) |
330 | 199 | return; |
331 | 899 | const OGRwkbGeometryType eType = poGeom->getGeometryType(); |
332 | 899 | delete poGeom; |
333 | | |
334 | 899 | int64_t nLastIdx = m_poLyrTable->GetTotalRecordCount() - 1; |
335 | 899 | const GUInt32 nErrorCount = CPLGetErrorCounter(); |
336 | 8.53k | while (nLastIdx > nFirstIdx && |
337 | 8.53k | m_poLyrTable->GetOffsetInTableForRow(nLastIdx) == 0 && |
338 | 8.53k | nErrorCount == CPLGetErrorCounter()) |
339 | 7.63k | { |
340 | 7.63k | nLastIdx--; |
341 | 7.63k | } |
342 | 899 | if (nLastIdx > nFirstIdx && m_poLyrTable->SelectRow(nLastIdx)) |
343 | 654 | { |
344 | 654 | psField = m_poLyrTable->GetFieldValue(m_iGeomFieldIdx); |
345 | 654 | if (psField == nullptr) |
346 | 12 | { |
347 | 12 | m_eGeomType = eType; |
348 | 12 | return; |
349 | 12 | } |
350 | 642 | poGeom = m_poGeomConverter->GetAsGeometry(psField); |
351 | 642 | if (poGeom == nullptr) |
352 | 99 | { |
353 | 99 | m_eGeomType = eType; |
354 | 99 | return; |
355 | 99 | } |
356 | 543 | if (eType == poGeom->getGeometryType()) |
357 | 351 | m_eGeomType = eType; |
358 | 543 | delete poGeom; |
359 | 543 | } |
360 | 899 | } |
361 | | |
362 | | /************************************************************************/ |
363 | | /* BuildLayerDefinition() */ |
364 | | /************************************************************************/ |
365 | | |
366 | | int OGROpenFileGDBLayer::BuildLayerDefinition() |
367 | 929k | { |
368 | 929k | if (m_bValidLayerDefn >= 0) |
369 | 917k | return m_bValidLayerDefn; |
370 | | |
371 | 12.4k | if (m_poLyrTable == nullptr) |
372 | 12.4k | { |
373 | 12.4k | m_poLyrTable = new FileGDBTable(); |
374 | 12.4k | if (!(m_poLyrTable->Open(m_osGDBFilename, m_bEditable, |
375 | 12.4k | GetDescription()))) |
376 | 3.80k | { |
377 | 3.80k | if (m_bEditable) |
378 | 0 | { |
379 | | // Retry in read-only mode |
380 | 0 | m_bEditable = false; |
381 | 0 | delete m_poLyrTable; |
382 | 0 | m_poLyrTable = new FileGDBTable(); |
383 | 0 | if (!(m_poLyrTable->Open(m_osGDBFilename, m_bEditable, |
384 | 0 | GetDescription()))) |
385 | 0 | { |
386 | 0 | Close(); |
387 | 0 | return FALSE; |
388 | 0 | } |
389 | 0 | else |
390 | 0 | { |
391 | 0 | CPLError( |
392 | 0 | CE_Failure, CPLE_FileIO, |
393 | 0 | "Cannot open %s in update mode, but only in read-only", |
394 | 0 | GetDescription()); |
395 | 0 | } |
396 | 0 | } |
397 | 3.80k | else |
398 | 3.80k | { |
399 | 3.80k | Close(); |
400 | 3.80k | return FALSE; |
401 | 3.80k | } |
402 | 3.80k | } |
403 | 12.4k | } |
404 | | |
405 | 8.65k | m_bValidLayerDefn = TRUE; |
406 | 8.65k | auto oTemporaryUnsealer(m_poFeatureDefn->GetTemporaryUnsealer()); |
407 | | |
408 | 8.65k | m_iGeomFieldIdx = m_poLyrTable->GetGeomFieldIdx(); |
409 | 8.65k | if (m_iGeomFieldIdx >= 0) |
410 | 7.00k | { |
411 | 7.00k | FileGDBGeomField *poGDBGeomField = cpl::down_cast<FileGDBGeomField *>( |
412 | 7.00k | m_poLyrTable->GetField(m_iGeomFieldIdx)); |
413 | 7.00k | m_poGeomConverter.reset( |
414 | 7.00k | FileGDBOGRGeometryConverter::BuildConverter(poGDBGeomField)); |
415 | | |
416 | | #ifdef DEBUG |
417 | | const auto poSRS = GetSpatialRef(); |
418 | | if (poSRS != nullptr && !poGDBGeomField->GetWKT().empty() && |
419 | | poGDBGeomField->GetWKT()[0] != '{') |
420 | | { |
421 | | auto poSRSFromGDBTable = |
422 | | m_poDS->BuildSRS(poGDBGeomField->GetWKT().c_str()); |
423 | | if (poSRSFromGDBTable) |
424 | | { |
425 | | if (!poSRS->IsSame(poSRSFromGDBTable)) |
426 | | { |
427 | | CPLDebug("OpenFileGDB", |
428 | | "Table %s declare a CRS '%s' in its XML " |
429 | | "definition (or in its parent's one), " |
430 | | "but its .gdbtable declares '%s'. " |
431 | | "Using the former", |
432 | | GetDescription(), poSRS->GetName(), |
433 | | poSRSFromGDBTable->GetName()); |
434 | | } |
435 | | poSRSFromGDBTable->Release(); |
436 | | } |
437 | | } |
438 | | #endif |
439 | | |
440 | 7.00k | if (!(m_poLyrTable->CanUseIndices() && |
441 | 7.00k | m_poLyrTable->HasSpatialIndex() && |
442 | 7.00k | CPLTestBool(CPLGetConfigOption("OPENFILEGDB_USE_SPATIAL_INDEX", |
443 | 627 | "YES"))) && |
444 | 7.00k | CPLTestBool(CPLGetConfigOption("OPENFILEGDB_IN_MEMORY_SPI", "YES"))) |
445 | 6.37k | { |
446 | 6.37k | CPLRectObj sGlobalBounds; |
447 | 6.37k | sGlobalBounds.minx = poGDBGeomField->GetXMin(); |
448 | 6.37k | sGlobalBounds.miny = poGDBGeomField->GetYMin(); |
449 | 6.37k | sGlobalBounds.maxx = poGDBGeomField->GetXMax(); |
450 | 6.37k | sGlobalBounds.maxy = poGDBGeomField->GetYMax(); |
451 | 6.37k | m_pQuadTree = CPLQuadTreeCreate(&sGlobalBounds, nullptr); |
452 | 6.37k | CPLQuadTreeSetMaxDepth( |
453 | 6.37k | m_pQuadTree, |
454 | 6.37k | CPLQuadTreeGetAdvisedMaxDepth( |
455 | 6.37k | static_cast<int>(std::min<int64_t>( |
456 | 6.37k | INT_MAX, m_poLyrTable->GetValidRecordCount())))); |
457 | 6.37k | } |
458 | 627 | else |
459 | 627 | { |
460 | 627 | m_eSpatialIndexState = SPI_INVALID; |
461 | 627 | } |
462 | 7.00k | } |
463 | | |
464 | 8.65k | if (m_iGeomFieldIdx >= 0 && |
465 | 8.65k | (m_osDefinition.empty() || |
466 | 7.00k | m_poFeatureDefn->OGRFeatureDefn::GetGeomFieldCount() == 0)) |
467 | 6.62k | { |
468 | | /* FileGDB v9 case */ |
469 | 6.62k | FileGDBGeomField *poGDBGeomField = reinterpret_cast<FileGDBGeomField *>( |
470 | 6.62k | m_poLyrTable->GetField(m_iGeomFieldIdx)); |
471 | 6.62k | const char *pszName = poGDBGeomField->GetName().c_str(); |
472 | 6.62k | const FileGDBTableGeometryType eGDBGeomType = |
473 | 6.62k | m_poLyrTable->GetGeometryType(); |
474 | | |
475 | 6.62k | OGRwkbGeometryType eGeomType = wkbUnknown; |
476 | 6.62k | switch (eGDBGeomType) |
477 | 6.62k | { |
478 | 270 | case FGTGT_NONE: /* doesn't make sense ! */ |
479 | 270 | break; |
480 | 332 | case FGTGT_POINT: |
481 | 332 | eGeomType = wkbPoint; |
482 | 332 | break; |
483 | 263 | case FGTGT_MULTIPOINT: |
484 | 263 | eGeomType = wkbMultiPoint; |
485 | 263 | break; |
486 | 1.10k | case FGTGT_LINE: |
487 | 1.10k | eGeomType = wkbMultiLineString; |
488 | 1.10k | break; |
489 | 3.51k | case FGTGT_POLYGON: |
490 | 3.51k | eGeomType = wkbMultiPolygon; |
491 | 3.51k | break; |
492 | 1.13k | case FGTGT_MULTIPATCH: |
493 | 1.13k | eGeomType = wkbUnknown; |
494 | 1.13k | break; |
495 | 6.62k | } |
496 | | |
497 | 6.62k | if (m_eGeomType != wkbUnknown && |
498 | 6.62k | wkbFlatten(eGeomType) != wkbFlatten(m_eGeomType)) |
499 | 52 | { |
500 | 52 | CPLError(CE_Warning, CPLE_AppDefined, |
501 | 52 | "Inconsistency for layer geometry type"); |
502 | 52 | } |
503 | | |
504 | 6.62k | m_eGeomType = eGeomType; |
505 | | |
506 | 6.62k | if (eGDBGeomType == FGTGT_MULTIPATCH) |
507 | 1.13k | { |
508 | 1.13k | TryToDetectMultiPatchKind(); |
509 | 1.13k | } |
510 | | |
511 | 6.62k | if (m_poLyrTable->GetGeomTypeHasZ()) |
512 | 2.40k | m_eGeomType = wkbSetZ(m_eGeomType); |
513 | | |
514 | 6.62k | if (m_poLyrTable->GetGeomTypeHasM()) |
515 | 1.99k | m_eGeomType = wkbSetM(m_eGeomType); |
516 | | |
517 | 6.62k | { |
518 | 6.62k | auto poGeomFieldDefn = |
519 | 6.62k | std::make_unique<OGROpenFileGDBGeomFieldDefn>(nullptr, pszName, |
520 | 6.62k | m_eGeomType); |
521 | 6.62k | poGeomFieldDefn->SetNullable(poGDBGeomField->IsNullable()); |
522 | | |
523 | 6.62k | m_poFeatureDefn->AddGeomFieldDefn(std::move(poGeomFieldDefn)); |
524 | 6.62k | } |
525 | 6.62k | auto poGeomFieldDefn = m_poFeatureDefn->GetGeomFieldDefn(0); |
526 | | |
527 | 6.62k | OGRSpatialReference *poSRS = nullptr; |
528 | 6.62k | if (!poGDBGeomField->GetWKT().empty() && |
529 | 6.62k | poGDBGeomField->GetWKT()[0] != '{') |
530 | 6.56k | { |
531 | 6.56k | poSRS = m_poDS->BuildSRS(poGDBGeomField->GetWKT().c_str()); |
532 | 6.56k | } |
533 | 6.62k | if (poSRS != nullptr) |
534 | 3.46k | { |
535 | 3.46k | poGeomFieldDefn->SetSpatialRef(poSRS); |
536 | 3.46k | poSRS->Dereference(); |
537 | 3.46k | } |
538 | 6.62k | } |
539 | 2.02k | else if (m_osDefinition.empty() && m_iGeomFieldIdx < 0) |
540 | 1.57k | { |
541 | 1.57k | m_eGeomType = wkbNone; |
542 | 1.57k | } |
543 | | |
544 | 8.65k | CPLXMLTreeCloser oTree(nullptr); |
545 | 8.65k | const CPLXMLNode *psGPFieldInfoExs = nullptr; |
546 | | |
547 | 8.65k | std::string osAreaFieldName; |
548 | 8.65k | std::string osLengthFieldName; |
549 | 8.65k | if (!m_osDefinition.empty()) |
550 | 484 | { |
551 | 484 | oTree.reset(CPLParseXMLString(m_osDefinition.c_str())); |
552 | 484 | if (oTree != nullptr) |
553 | 484 | { |
554 | 484 | CPLStripXMLNamespace(oTree.get(), nullptr, TRUE); |
555 | 484 | CPLXMLNode *psInfo = |
556 | 484 | CPLSearchXMLNode(oTree.get(), "=DEFeatureClassInfo"); |
557 | 484 | if (psInfo == nullptr) |
558 | 72 | psInfo = CPLSearchXMLNode(oTree.get(), "=DETableInfo"); |
559 | 484 | if (psInfo != nullptr) |
560 | 484 | { |
561 | 484 | psGPFieldInfoExs = CPLGetXMLNode(psInfo, "GPFieldInfoExs"); |
562 | 484 | osAreaFieldName = CPLGetXMLValue(psInfo, "AreaFieldName", ""); |
563 | 484 | osLengthFieldName = |
564 | 484 | CPLGetXMLValue(psInfo, "LengthFieldName", ""); |
565 | 484 | m_osPath = CPLGetXMLValue(psInfo, "CatalogPath", ""); |
566 | 484 | } |
567 | 484 | } |
568 | 484 | } |
569 | | |
570 | 64.1k | for (int i = 0; i < m_poLyrTable->GetFieldCount(); i++) |
571 | 55.4k | { |
572 | 55.4k | if (i == m_iGeomFieldIdx) |
573 | 7.00k | continue; |
574 | 48.4k | if (i == m_poLyrTable->GetObjectIdFieldIdx()) |
575 | 8.43k | continue; |
576 | | |
577 | 40.0k | FileGDBField *poGDBField = m_poLyrTable->GetField(i); |
578 | 40.0k | OGRFieldType eType = OFTString; |
579 | 40.0k | OGRFieldSubType eSubType = OFSTNone; |
580 | 40.0k | int nWidth = poGDBField->GetMaxWidth(); |
581 | 40.0k | switch (poGDBField->GetType()) |
582 | 40.0k | { |
583 | 3.51k | case FGFT_INT16: |
584 | 3.51k | eType = OFTInteger; |
585 | 3.51k | eSubType = OFSTInt16; |
586 | 3.51k | break; |
587 | 8.63k | case FGFT_INT32: |
588 | 8.63k | eType = OFTInteger; |
589 | 8.63k | break; |
590 | 2.12k | case FGFT_FLOAT32: |
591 | 2.12k | eType = OFTReal; |
592 | 2.12k | eSubType = OFSTFloat32; |
593 | 2.12k | break; |
594 | 7.03k | case FGFT_FLOAT64: |
595 | 7.03k | eType = OFTReal; |
596 | 7.03k | break; |
597 | 5.38k | case FGFT_STRING: |
598 | | /* nWidth = poGDBField->GetMaxWidth(); */ |
599 | 5.38k | eType = OFTString; |
600 | 5.38k | break; |
601 | 2.42k | case FGFT_GUID: |
602 | 2.62k | case FGFT_GLOBALID: |
603 | 4.99k | case FGFT_XML: |
604 | 4.99k | eType = OFTString; |
605 | 4.99k | break; |
606 | 2.16k | case FGFT_DATETIME: |
607 | 2.16k | eType = OFTDateTime; |
608 | 2.16k | break; |
609 | 0 | case FGFT_UNDEFINED: |
610 | 0 | case FGFT_OBJECTID: |
611 | 0 | case FGFT_GEOMETRY: |
612 | 0 | CPLAssert(false); |
613 | 0 | break; |
614 | 4.74k | case FGFT_BINARY: |
615 | 4.74k | { |
616 | | /* Special case for v9 GDB_UserMetadata table */ |
617 | 4.74k | if (m_iFieldToReadAsBinary < 0 && |
618 | 4.74k | poGDBField->GetName() == "Xml" && |
619 | 4.74k | poGDBField->GetType() == FGFT_BINARY) |
620 | 21 | { |
621 | 21 | m_iFieldToReadAsBinary = i; |
622 | 21 | eType = OFTString; |
623 | 21 | } |
624 | 4.72k | else |
625 | 4.72k | { |
626 | 4.72k | eType = OFTBinary; |
627 | 4.72k | } |
628 | 4.74k | break; |
629 | 0 | } |
630 | 179 | case FGFT_RASTER: |
631 | 179 | { |
632 | 179 | const FileGDBRasterField *rasterField = |
633 | 179 | cpl::down_cast<const FileGDBRasterField *>(poGDBField); |
634 | 179 | if (rasterField->GetRasterType() == |
635 | 179 | FileGDBRasterField::Type::MANAGED) |
636 | 61 | eType = OFTInteger; |
637 | 118 | else if (rasterField->GetRasterType() == |
638 | 118 | FileGDBRasterField::Type::EXTERNAL) |
639 | 105 | eType = OFTString; |
640 | 13 | else |
641 | 13 | eType = OFTBinary; |
642 | 179 | break; |
643 | 0 | } |
644 | 109 | case FGFT_INT64: |
645 | 109 | m_bArcGISPro32OrLater = true; |
646 | 109 | eType = OFTInteger64; |
647 | 109 | break; |
648 | 838 | case FGFT_DATE: |
649 | 838 | m_bArcGISPro32OrLater = true; |
650 | 838 | eType = OFTDate; |
651 | 838 | break; |
652 | 270 | case FGFT_TIME: |
653 | 270 | m_bArcGISPro32OrLater = true; |
654 | 270 | eType = OFTTime; |
655 | 270 | break; |
656 | 40 | case FGFT_DATETIME_WITH_OFFSET: |
657 | 40 | m_bArcGISPro32OrLater = true; |
658 | 40 | eType = OFTDateTime; |
659 | 40 | break; |
660 | 40.0k | } |
661 | 40.0k | OGRFieldDefn oFieldDefn(poGDBField->GetName().c_str(), eType); |
662 | 40.0k | oFieldDefn.SetAlternativeName(poGDBField->GetAlias().c_str()); |
663 | 40.0k | oFieldDefn.SetSubType(eSubType); |
664 | | // On creation in the FileGDB driver (GDBFieldTypeToLengthInBytes) if |
665 | | // string width is 0, we pick up DEFAULT_STRING_WIDTH=65536 by default |
666 | | // to mean unlimited string length, but we do not want to advertise |
667 | | // such a big number. |
668 | 40.0k | if (eType == OFTString && |
669 | 40.0k | (nWidth < DEFAULT_STRING_WIDTH || |
670 | 10.5k | CPLTestBool(CPLGetConfigOption( |
671 | 2.28k | "OPENFILEGDB_REPORT_GENUINE_FIELD_WIDTH", "NO")))) |
672 | 8.22k | { |
673 | 8.22k | oFieldDefn.SetWidth(nWidth); |
674 | 8.22k | } |
675 | 40.0k | oFieldDefn.SetNullable(poGDBField->IsNullable()); |
676 | | |
677 | 40.0k | const CPLXMLNode *psFieldDef = nullptr; |
678 | 40.0k | if (psGPFieldInfoExs != nullptr) |
679 | 4.14k | { |
680 | 4.14k | for (const CPLXMLNode *psChild = psGPFieldInfoExs->psChild; |
681 | 39.3k | psChild != nullptr; psChild = psChild->psNext) |
682 | 38.7k | { |
683 | 38.7k | if (psChild->eType != CXT_Element) |
684 | 4.23k | continue; |
685 | 34.5k | if (EQUAL(psChild->pszValue, "GPFieldInfoEx") && |
686 | 34.5k | EQUAL(CPLGetXMLValue(psChild, "Name", ""), |
687 | 34.5k | poGDBField->GetName().c_str())) |
688 | 3.57k | { |
689 | 3.57k | psFieldDef = psChild; |
690 | 3.57k | break; |
691 | 3.57k | } |
692 | 34.5k | } |
693 | 4.14k | } |
694 | | |
695 | 40.0k | if (psFieldDef && poGDBField->GetType() == FGFT_DATETIME) |
696 | 289 | { |
697 | 289 | if (EQUAL(CPLGetXMLValue(psFieldDef, "HighPrecision", ""), "true")) |
698 | 0 | { |
699 | 0 | poGDBField->SetHighPrecision(); |
700 | 0 | } |
701 | 289 | } |
702 | | |
703 | 40.0k | const OGRField *psDefault = poGDBField->GetDefault(); |
704 | 40.0k | if (!OGR_RawField_IsUnset(psDefault) && !OGR_RawField_IsNull(psDefault)) |
705 | 458 | { |
706 | 458 | if (eType == OFTString) |
707 | 28 | { |
708 | 28 | CPLString osDefault("'"); |
709 | 28 | char *pszTmp = |
710 | 28 | CPLEscapeString(psDefault->String, -1, CPLES_SQL); |
711 | 28 | osDefault += pszTmp; |
712 | 28 | CPLFree(pszTmp); |
713 | 28 | osDefault += "'"; |
714 | 28 | oFieldDefn.SetDefault(osDefault); |
715 | 28 | } |
716 | 430 | else if (eType == OFTInteger || eType == OFTReal || |
717 | 430 | eType == OFTInteger64) |
718 | 427 | { |
719 | | // GDBs and the FileGDB SDK are not always reliable for |
720 | | // numeric values It often occurs that the XML definition in |
721 | | // a00000004.gdbtable does not match the default values (in |
722 | | // binary) found in the field definition section of the |
723 | | // .gdbtable of the layers themselves So check consistency. |
724 | | |
725 | 427 | const char *pszDefaultValue = nullptr; |
726 | 427 | if (psFieldDef) |
727 | 2 | { |
728 | | // From ArcGIS this is called DefaultValueNumeric |
729 | | // for integer and real. |
730 | | // From FileGDB API this is |
731 | | // called DefaultValue xsi:type=xs:int for integer |
732 | | // and DefaultValueNumeric for real ... |
733 | 2 | pszDefaultValue = CPLGetXMLValue( |
734 | 2 | psFieldDef, "DefaultValueNumeric", nullptr); |
735 | 2 | if (pszDefaultValue == nullptr) |
736 | 1 | pszDefaultValue = |
737 | 1 | CPLGetXMLValue(psFieldDef, "DefaultValue", nullptr); |
738 | | // For ArcGIS Pro 3.2 and esriFieldTypeBigInteger, this is |
739 | | // DefaultValueInteger |
740 | 2 | if (pszDefaultValue == nullptr) |
741 | 1 | pszDefaultValue = CPLGetXMLValue( |
742 | 1 | psFieldDef, "DefaultValueInteger", nullptr); |
743 | 2 | } |
744 | 427 | if (pszDefaultValue != nullptr) |
745 | 1 | { |
746 | 1 | if (eType == OFTInteger) |
747 | 1 | { |
748 | 1 | if (atoi(pszDefaultValue) != psDefault->Integer) |
749 | 0 | { |
750 | 0 | CPLDebug( |
751 | 0 | "OpenFileGDB", |
752 | 0 | "For field %s, XML definition mentions %s " |
753 | 0 | "as default value whereas .gdbtable header " |
754 | 0 | "mentions %d. Using %s", |
755 | 0 | poGDBField->GetName().c_str(), pszDefaultValue, |
756 | 0 | psDefault->Integer, pszDefaultValue); |
757 | 0 | } |
758 | 1 | oFieldDefn.SetDefault(pszDefaultValue); |
759 | 1 | } |
760 | 0 | else if (eType == OFTReal) |
761 | 0 | { |
762 | 0 | if (fabs(CPLAtof(pszDefaultValue) - psDefault->Real) > |
763 | 0 | 1e-15) |
764 | 0 | { |
765 | 0 | CPLDebug( |
766 | 0 | "OpenFileGDB", |
767 | 0 | "For field %s, XML definition " |
768 | 0 | "mentions %s as default value whereas " |
769 | 0 | ".gdbtable header mentions %.17g. Using %s", |
770 | 0 | poGDBField->GetName().c_str(), pszDefaultValue, |
771 | 0 | psDefault->Real, pszDefaultValue); |
772 | 0 | } |
773 | 0 | oFieldDefn.SetDefault(pszDefaultValue); |
774 | 0 | } |
775 | 0 | else if (eType == OFTInteger64) |
776 | 0 | { |
777 | 0 | if (CPLAtoGIntBig(pszDefaultValue) != |
778 | 0 | psDefault->Integer64) |
779 | 0 | { |
780 | 0 | CPLDebug( |
781 | 0 | "OpenFileGDB", |
782 | 0 | "For field %s, XML definition mentions %s " |
783 | 0 | "as default value whereas .gdbtable header " |
784 | 0 | "mentions " CPL_FRMT_GIB ". Using %s", |
785 | 0 | poGDBField->GetName().c_str(), pszDefaultValue, |
786 | 0 | psDefault->Integer64, pszDefaultValue); |
787 | 0 | } |
788 | 0 | oFieldDefn.SetDefault(pszDefaultValue); |
789 | 0 | } |
790 | 1 | } |
791 | 427 | } |
792 | 3 | else if (eType == OFTDateTime) |
793 | 2 | { |
794 | 2 | if (poGDBField->GetType() == FGFT_DATETIME_WITH_OFFSET) |
795 | 1 | { |
796 | 1 | oFieldDefn.SetDefault(CPLSPrintf( |
797 | 1 | "'%04d/%02d/%02d %02d:%02d:%06.03f%c%02d:%02d'", |
798 | 1 | psDefault->Date.Year, psDefault->Date.Month, |
799 | 1 | psDefault->Date.Day, psDefault->Date.Hour, |
800 | 1 | psDefault->Date.Minute, psDefault->Date.Second, |
801 | 1 | psDefault->Date.TZFlag >= 100 ? '+' : '-', |
802 | 1 | std::abs(psDefault->Date.TZFlag - 100) / 4, |
803 | 1 | (std::abs(psDefault->Date.TZFlag - 100) % 4) * 15)); |
804 | 1 | } |
805 | 1 | else |
806 | 1 | { |
807 | 1 | oFieldDefn.SetDefault(CPLSPrintf( |
808 | 1 | "'%04d/%02d/%02d %02d:%02d:%02d'", psDefault->Date.Year, |
809 | 1 | psDefault->Date.Month, psDefault->Date.Day, |
810 | 1 | psDefault->Date.Hour, psDefault->Date.Minute, |
811 | 1 | static_cast<int>(psDefault->Date.Second))); |
812 | 1 | } |
813 | 2 | } |
814 | 1 | else if (eType == OFTDate) |
815 | 0 | oFieldDefn.SetDefault( |
816 | 0 | CPLSPrintf("'%04d/%02d/%02d'", psDefault->Date.Year, |
817 | 0 | psDefault->Date.Month, psDefault->Date.Day)); |
818 | 1 | else if (eType == OFTTime) |
819 | 1 | oFieldDefn.SetDefault( |
820 | 1 | CPLSPrintf("'%02d:%02d:%02d'", psDefault->Date.Hour, |
821 | 1 | psDefault->Date.Minute, |
822 | 1 | static_cast<int>(psDefault->Date.Second))); |
823 | 458 | } |
824 | | |
825 | 40.0k | if (psFieldDef) |
826 | 3.57k | { |
827 | 3.57k | const char *pszDomainName = |
828 | 3.57k | CPLGetXMLValue(psFieldDef, "DomainName", nullptr); |
829 | 3.57k | if (pszDomainName) |
830 | 0 | oFieldDefn.SetDomainName(pszDomainName); |
831 | 3.57k | } |
832 | | |
833 | 40.0k | if (osAreaFieldName == poGDBField->GetName() && |
834 | 40.0k | oFieldDefn.GetType() == OFTReal) |
835 | 108 | { |
836 | 108 | m_iAreaField = m_poFeatureDefn->GetFieldCount(); |
837 | 108 | oFieldDefn.SetDefault("FILEGEODATABASE_SHAPE_AREA"); |
838 | 108 | } |
839 | 39.9k | else if (osLengthFieldName == poGDBField->GetName() && |
840 | 39.9k | oFieldDefn.GetType() == OFTReal) |
841 | 1 | { |
842 | 1 | m_iLengthField = m_poFeatureDefn->GetFieldCount(); |
843 | 1 | oFieldDefn.SetDefault("FILEGEODATABASE_SHAPE_LENGTH"); |
844 | 1 | } |
845 | | |
846 | 40.0k | m_poFeatureDefn->AddFieldDefn(&oFieldDefn); |
847 | 40.0k | } |
848 | | |
849 | 8.65k | if (m_poLyrTable->HasDeletedFeaturesListed()) |
850 | 0 | { |
851 | 0 | OGRFieldDefn oFieldDefn("_deleted_", OFTInteger); |
852 | 0 | m_poFeatureDefn->AddFieldDefn(&oFieldDefn); |
853 | 0 | } |
854 | | |
855 | 8.65k | return TRUE; |
856 | 8.65k | } |
857 | | |
858 | | /************************************************************************/ |
859 | | /* GetGeomType() */ |
860 | | /************************************************************************/ |
861 | | |
862 | | OGRwkbGeometryType OGROpenFileGDBLayer::GetGeomType() |
863 | 12.4k | { |
864 | 12.4k | if (m_eGeomType == wkbUnknown || |
865 | 12.4k | m_osDefinition.empty() /* FileGDB v9 case */) |
866 | 10.6k | { |
867 | 10.6k | (void)BuildLayerDefinition(); |
868 | 10.6k | } |
869 | | |
870 | 12.4k | return m_eGeomType; |
871 | 12.4k | } |
872 | | |
873 | | /***********************************************************************/ |
874 | | /* GetLayerDefn() */ |
875 | | /***********************************************************************/ |
876 | | |
877 | | OGRFeatureDefn *OGROpenFileGDBLayer::GetLayerDefn() |
878 | 59.9k | { |
879 | 59.9k | return m_poFeatureDefn; |
880 | 59.9k | } |
881 | | |
882 | | /***********************************************************************/ |
883 | | /* GetFIDColumn() */ |
884 | | /***********************************************************************/ |
885 | | |
886 | | const char *OGROpenFileGDBLayer::GetFIDColumn() |
887 | 37.2k | { |
888 | 37.2k | if (!BuildLayerDefinition()) |
889 | 3.81k | return ""; |
890 | 33.4k | int iIdx = m_poLyrTable->GetObjectIdFieldIdx(); |
891 | 33.4k | if (iIdx < 0) |
892 | 211 | return ""; |
893 | 33.2k | return m_poLyrTable->GetField(iIdx)->GetName().c_str(); |
894 | 33.4k | } |
895 | | |
896 | | /***********************************************************************/ |
897 | | /* ResetReading() */ |
898 | | /***********************************************************************/ |
899 | | |
900 | | void OGROpenFileGDBLayer::ResetReading() |
901 | 0 | { |
902 | 0 | if (m_iCurFeat != 0) |
903 | 0 | { |
904 | 0 | if (m_eSpatialIndexState == SPI_IN_BUILDING) |
905 | 0 | m_eSpatialIndexState = SPI_INVALID; |
906 | 0 | } |
907 | 0 | m_bEOF = FALSE; |
908 | 0 | m_iCurFeat = 0; |
909 | 0 | if (m_poAttributeIterator) |
910 | 0 | m_poAttributeIterator->Reset(); |
911 | 0 | if (m_poSpatialIndexIterator) |
912 | 0 | m_poSpatialIndexIterator->Reset(); |
913 | 0 | if (m_poCombinedIterator) |
914 | 0 | m_poCombinedIterator->Reset(); |
915 | 0 | } |
916 | | |
917 | | /***********************************************************************/ |
918 | | /* ISetSpatialFilter() */ |
919 | | /***********************************************************************/ |
920 | | |
921 | | OGRErr OGROpenFileGDBLayer::ISetSpatialFilter(int iGeomField, |
922 | | const OGRGeometry *poGeom) |
923 | 0 | { |
924 | 0 | if (!BuildLayerDefinition()) |
925 | 0 | return OGRERR_FAILURE; |
926 | | |
927 | 0 | OGRLayer::ISetSpatialFilter(iGeomField, poGeom); |
928 | |
|
929 | 0 | if (m_bFilterIsEnvelope) |
930 | 0 | { |
931 | 0 | OGREnvelope sLayerEnvelope; |
932 | 0 | if (GetExtent(&sLayerEnvelope, FALSE) == OGRERR_NONE) |
933 | 0 | { |
934 | 0 | if (m_sFilterEnvelope.MinX <= sLayerEnvelope.MinX && |
935 | 0 | m_sFilterEnvelope.MinY <= sLayerEnvelope.MinY && |
936 | 0 | m_sFilterEnvelope.MaxX >= sLayerEnvelope.MaxX && |
937 | 0 | m_sFilterEnvelope.MaxY >= sLayerEnvelope.MaxY) |
938 | 0 | { |
939 | | #ifdef DEBUG |
940 | | CPLDebug("OpenFileGDB", "Disabling spatial filter since it " |
941 | | "contains the layer spatial extent"); |
942 | | #endif |
943 | 0 | poGeom = nullptr; |
944 | 0 | OGRLayer::ISetSpatialFilter(iGeomField, poGeom); |
945 | 0 | } |
946 | 0 | } |
947 | 0 | } |
948 | |
|
949 | 0 | if (poGeom != nullptr) |
950 | 0 | { |
951 | 0 | if (m_poSpatialIndexIterator == nullptr && |
952 | 0 | m_poLyrTable->CanUseIndices() && m_poLyrTable->HasSpatialIndex() && |
953 | 0 | CPLTestBool( |
954 | 0 | CPLGetConfigOption("OPENFILEGDB_USE_SPATIAL_INDEX", "YES"))) |
955 | 0 | { |
956 | 0 | m_poSpatialIndexIterator = FileGDBSpatialIndexIterator::Build( |
957 | 0 | m_poLyrTable, m_sFilterEnvelope); |
958 | 0 | } |
959 | 0 | else if (m_poSpatialIndexIterator != nullptr) |
960 | 0 | { |
961 | 0 | if (!m_poSpatialIndexIterator->SetEnvelope(m_sFilterEnvelope)) |
962 | 0 | { |
963 | 0 | delete m_poSpatialIndexIterator; |
964 | 0 | m_poSpatialIndexIterator = nullptr; |
965 | 0 | } |
966 | 0 | } |
967 | 0 | else if (m_eSpatialIndexState == SPI_COMPLETED) |
968 | 0 | { |
969 | 0 | CPLRectObj aoi; |
970 | 0 | aoi.minx = m_sFilterEnvelope.MinX; |
971 | 0 | aoi.miny = m_sFilterEnvelope.MinY; |
972 | 0 | aoi.maxx = m_sFilterEnvelope.MaxX; |
973 | 0 | aoi.maxy = m_sFilterEnvelope.MaxY; |
974 | 0 | CPLFree(m_pahFilteredFeatures); |
975 | 0 | m_nFilteredFeatureCount = -1; |
976 | 0 | m_pahFilteredFeatures = |
977 | 0 | CPLQuadTreeSearch(m_pQuadTree, &aoi, &m_nFilteredFeatureCount); |
978 | 0 | if (m_nFilteredFeatureCount >= 0) |
979 | 0 | { |
980 | 0 | size_t *panStart = |
981 | 0 | reinterpret_cast<size_t *>(m_pahFilteredFeatures); |
982 | 0 | std::sort(panStart, panStart + m_nFilteredFeatureCount); |
983 | 0 | } |
984 | 0 | } |
985 | |
|
986 | 0 | m_poLyrTable->InstallFilterEnvelope(&m_sFilterEnvelope); |
987 | 0 | } |
988 | 0 | else |
989 | 0 | { |
990 | 0 | delete m_poSpatialIndexIterator; |
991 | 0 | m_poSpatialIndexIterator = nullptr; |
992 | 0 | CPLFree(m_pahFilteredFeatures); |
993 | 0 | m_pahFilteredFeatures = nullptr; |
994 | 0 | m_nFilteredFeatureCount = -1; |
995 | 0 | m_poLyrTable->InstallFilterEnvelope(nullptr); |
996 | 0 | } |
997 | |
|
998 | 0 | BuildCombinedIterator(); |
999 | |
|
1000 | 0 | return OGRERR_NONE; |
1001 | 0 | } |
1002 | | |
1003 | | /***********************************************************************/ |
1004 | | /* CompValues() */ |
1005 | | /***********************************************************************/ |
1006 | | |
1007 | | static int CompValues(OGRFieldDefn *poFieldDefn, const swq_expr_node *poValue1, |
1008 | | const swq_expr_node *poValue2) |
1009 | 0 | { |
1010 | 0 | int ret = 0; |
1011 | 0 | switch (poFieldDefn->GetType()) |
1012 | 0 | { |
1013 | 0 | case OFTInteger: |
1014 | 0 | { |
1015 | 0 | int n1, n2; |
1016 | 0 | if (poValue1->field_type == SWQ_FLOAT) |
1017 | 0 | n1 = static_cast<int>(poValue1->float_value); |
1018 | 0 | else |
1019 | 0 | n1 = static_cast<int>(poValue1->int_value); |
1020 | 0 | if (poValue2->field_type == SWQ_FLOAT) |
1021 | 0 | n2 = static_cast<int>(poValue2->float_value); |
1022 | 0 | else |
1023 | 0 | n2 = static_cast<int>(poValue2->int_value); |
1024 | 0 | if (n1 < n2) |
1025 | 0 | ret = -1; |
1026 | 0 | else if (n1 == n2) |
1027 | 0 | ret = 0; |
1028 | 0 | else |
1029 | 0 | ret = 1; |
1030 | 0 | break; |
1031 | 0 | } |
1032 | | |
1033 | 0 | case OFTReal: |
1034 | 0 | if (poValue1->float_value < poValue2->float_value) |
1035 | 0 | ret = -1; |
1036 | 0 | else if (poValue1->float_value == poValue2->float_value) |
1037 | 0 | ret = 0; |
1038 | 0 | else |
1039 | 0 | ret = 1; |
1040 | 0 | break; |
1041 | | |
1042 | 0 | case OFTString: |
1043 | 0 | ret = strcmp(poValue1->string_value, poValue2->string_value); |
1044 | 0 | break; |
1045 | | |
1046 | 0 | case OFTDate: |
1047 | 0 | case OFTTime: |
1048 | 0 | case OFTDateTime: |
1049 | 0 | { |
1050 | 0 | if ((poValue1->field_type == SWQ_TIMESTAMP || |
1051 | 0 | poValue1->field_type == SWQ_DATE || |
1052 | 0 | poValue1->field_type == SWQ_TIME) && |
1053 | 0 | (poValue2->field_type == SWQ_TIMESTAMP || |
1054 | 0 | poValue2->field_type == SWQ_DATE || |
1055 | 0 | poValue2->field_type == SWQ_TIME)) |
1056 | 0 | { |
1057 | 0 | ret = strcmp(poValue1->string_value, poValue2->string_value); |
1058 | 0 | } |
1059 | 0 | break; |
1060 | 0 | } |
1061 | | |
1062 | 0 | default: |
1063 | 0 | break; |
1064 | 0 | } |
1065 | 0 | return ret; |
1066 | 0 | } |
1067 | | |
1068 | | /***********************************************************************/ |
1069 | | /* OGROpenFileGDBIsComparisonOp() */ |
1070 | | /***********************************************************************/ |
1071 | | |
1072 | | int OGROpenFileGDBIsComparisonOp(int op) |
1073 | 0 | { |
1074 | 0 | return (op == SWQ_EQ || op == SWQ_NE || op == SWQ_LT || op == SWQ_LE || |
1075 | 0 | op == SWQ_GT || op == SWQ_GE); |
1076 | 0 | } |
1077 | | |
1078 | | /***********************************************************************/ |
1079 | | /* AreExprExclusive() */ |
1080 | | /***********************************************************************/ |
1081 | | |
1082 | | static const struct |
1083 | | { |
1084 | | swq_op op1; |
1085 | | swq_op op2; |
1086 | | int expected_comp_1; |
1087 | | int expected_comp_2; |
1088 | | } asPairsOfComparisons[] = { |
1089 | | {SWQ_EQ, SWQ_EQ, -1, 1}, {SWQ_LT, SWQ_GT, -1, 0}, |
1090 | | {SWQ_GT, SWQ_LT, 0, 1}, {SWQ_LT, SWQ_GE, -1, 999}, |
1091 | | {SWQ_LE, SWQ_GE, -1, 999}, {SWQ_LE, SWQ_GT, -1, 999}, |
1092 | | {SWQ_GE, SWQ_LE, 1, 999}, {SWQ_GE, SWQ_LT, 1, 999}, |
1093 | | {SWQ_GT, SWQ_LE, 1, 999}}; |
1094 | | |
1095 | | static int AreExprExclusive(OGRFeatureDefn *poFeatureDefn, |
1096 | | const swq_expr_node *poNode1, |
1097 | | const swq_expr_node *poNode2) |
1098 | 0 | { |
1099 | 0 | if (poNode1->eNodeType != SNT_OPERATION) |
1100 | 0 | return FALSE; |
1101 | 0 | if (poNode2->eNodeType != SNT_OPERATION) |
1102 | 0 | return FALSE; |
1103 | | |
1104 | 0 | const size_t nPairs = |
1105 | 0 | sizeof(asPairsOfComparisons) / sizeof(asPairsOfComparisons[0]); |
1106 | 0 | for (size_t i = 0; i < nPairs; i++) |
1107 | 0 | { |
1108 | 0 | if (poNode1->nOperation == asPairsOfComparisons[i].op1 && |
1109 | 0 | poNode2->nOperation == asPairsOfComparisons[i].op2 && |
1110 | 0 | poNode1->nSubExprCount == 2 && poNode2->nSubExprCount == 2) |
1111 | 0 | { |
1112 | 0 | swq_expr_node *poColumn1 = poNode1->papoSubExpr[0]; |
1113 | 0 | swq_expr_node *poValue1 = poNode1->papoSubExpr[1]; |
1114 | 0 | swq_expr_node *poColumn2 = poNode2->papoSubExpr[0]; |
1115 | 0 | swq_expr_node *poValue2 = poNode2->papoSubExpr[1]; |
1116 | 0 | if (poColumn1->eNodeType == SNT_COLUMN && |
1117 | 0 | poValue1->eNodeType == SNT_CONSTANT && |
1118 | 0 | poColumn2->eNodeType == SNT_COLUMN && |
1119 | 0 | poValue2->eNodeType == SNT_CONSTANT && |
1120 | 0 | poColumn1->field_index == poColumn2->field_index && |
1121 | 0 | poColumn1->field_index < poFeatureDefn->GetFieldCount()) |
1122 | 0 | { |
1123 | 0 | OGRFieldDefn *poFieldDefn = |
1124 | 0 | poFeatureDefn->GetFieldDefn(poColumn1->field_index); |
1125 | |
|
1126 | 0 | const int nComp = CompValues(poFieldDefn, poValue1, poValue2); |
1127 | 0 | return nComp == asPairsOfComparisons[i].expected_comp_1 || |
1128 | 0 | nComp == asPairsOfComparisons[i].expected_comp_2; |
1129 | 0 | } |
1130 | 0 | return FALSE; |
1131 | 0 | } |
1132 | 0 | } |
1133 | | |
1134 | 0 | if ((poNode2->nOperation == SWQ_ISNULL && |
1135 | 0 | OGROpenFileGDBIsComparisonOp(poNode1->nOperation) && |
1136 | 0 | poNode1->nSubExprCount == 2 && poNode2->nSubExprCount == 1) || |
1137 | 0 | (poNode1->nOperation == SWQ_ISNULL && |
1138 | 0 | OGROpenFileGDBIsComparisonOp(poNode2->nOperation) && |
1139 | 0 | poNode2->nSubExprCount == 2 && poNode1->nSubExprCount == 1)) |
1140 | 0 | { |
1141 | 0 | swq_expr_node *poColumn1 = poNode1->papoSubExpr[0]; |
1142 | 0 | swq_expr_node *poColumn2 = poNode2->papoSubExpr[0]; |
1143 | 0 | if (poColumn1->eNodeType == SNT_COLUMN && |
1144 | 0 | poColumn2->eNodeType == SNT_COLUMN && |
1145 | 0 | poColumn1->field_index == poColumn2->field_index && |
1146 | 0 | poColumn1->field_index < poFeatureDefn->GetFieldCount()) |
1147 | 0 | { |
1148 | 0 | return TRUE; |
1149 | 0 | } |
1150 | 0 | } |
1151 | | |
1152 | | /* In doubt: return FALSE */ |
1153 | 0 | return FALSE; |
1154 | 0 | } |
1155 | | |
1156 | | /***********************************************************************/ |
1157 | | /* FillTargetValueFromSrcExpr() */ |
1158 | | /***********************************************************************/ |
1159 | | |
1160 | | static int FillTargetValueFromSrcExpr(OGRFieldDefn *poFieldDefn, |
1161 | | OGRField *poTargetValue, |
1162 | | const swq_expr_node *poSrcValue) |
1163 | 0 | { |
1164 | 0 | switch (poFieldDefn->GetType()) |
1165 | 0 | { |
1166 | 0 | case OFTInteger: |
1167 | 0 | if (poSrcValue->field_type == SWQ_FLOAT) |
1168 | 0 | poTargetValue->Integer = |
1169 | 0 | static_cast<int>(poSrcValue->float_value); |
1170 | 0 | else |
1171 | 0 | poTargetValue->Integer = |
1172 | 0 | static_cast<int>(poSrcValue->int_value); |
1173 | 0 | break; |
1174 | | |
1175 | 0 | case OFTInteger64: |
1176 | 0 | if (poSrcValue->field_type == SWQ_FLOAT) |
1177 | 0 | poTargetValue->Integer64 = |
1178 | 0 | static_cast<GIntBig>(poSrcValue->float_value); |
1179 | 0 | else |
1180 | 0 | poTargetValue->Integer64 = poSrcValue->int_value; |
1181 | 0 | break; |
1182 | | |
1183 | 0 | case OFTReal: |
1184 | 0 | poTargetValue->Real = poSrcValue->float_value; |
1185 | 0 | break; |
1186 | | |
1187 | 0 | case OFTString: |
1188 | 0 | poTargetValue->String = poSrcValue->string_value; |
1189 | 0 | break; |
1190 | | |
1191 | 0 | case OFTDate: |
1192 | 0 | case OFTTime: |
1193 | 0 | case OFTDateTime: |
1194 | 0 | if (poSrcValue->field_type == SWQ_TIMESTAMP || |
1195 | 0 | poSrcValue->field_type == SWQ_DATE || |
1196 | 0 | poSrcValue->field_type == SWQ_TIME) |
1197 | 0 | { |
1198 | 0 | int nYear = 0, nMonth = 0, nDay = 0, nHour = 0, nMin = 0, |
1199 | 0 | nSec = 0; |
1200 | 0 | if (sscanf(poSrcValue->string_value, |
1201 | 0 | "%04d/%02d/%02d %02d:%02d:%02d", &nYear, &nMonth, |
1202 | 0 | &nDay, &nHour, &nMin, &nSec) == 6 || |
1203 | 0 | sscanf(poSrcValue->string_value, "%04d/%02d/%02d", &nYear, |
1204 | 0 | &nMonth, &nDay) == 3 || |
1205 | 0 | sscanf(poSrcValue->string_value, "%02d:%02d:%02d", &nHour, |
1206 | 0 | &nMin, &nSec) == 3) |
1207 | 0 | { |
1208 | 0 | poTargetValue->Date.Year = static_cast<GInt16>(nYear); |
1209 | 0 | poTargetValue->Date.Month = static_cast<GByte>(nMonth); |
1210 | 0 | poTargetValue->Date.Day = static_cast<GByte>(nDay); |
1211 | 0 | poTargetValue->Date.Hour = static_cast<GByte>(nHour); |
1212 | 0 | poTargetValue->Date.Minute = static_cast<GByte>(nMin); |
1213 | 0 | poTargetValue->Date.Second = static_cast<GByte>(nSec); |
1214 | 0 | poTargetValue->Date.TZFlag = 0; |
1215 | 0 | poTargetValue->Date.Reserved = 0; |
1216 | 0 | } |
1217 | 0 | else |
1218 | 0 | return FALSE; |
1219 | 0 | } |
1220 | 0 | else |
1221 | 0 | return FALSE; |
1222 | 0 | break; |
1223 | | |
1224 | 0 | default: |
1225 | 0 | return FALSE; |
1226 | 0 | } |
1227 | 0 | return TRUE; |
1228 | 0 | } |
1229 | | |
1230 | | /***********************************************************************/ |
1231 | | /* GetColumnSubNode() */ |
1232 | | /***********************************************************************/ |
1233 | | |
1234 | | static swq_expr_node *GetColumnSubNode(swq_expr_node *poNode) |
1235 | 0 | { |
1236 | 0 | if (poNode->eNodeType == SNT_OPERATION && poNode->nSubExprCount == 2) |
1237 | 0 | { |
1238 | 0 | if (poNode->papoSubExpr[0]->eNodeType == SNT_COLUMN) |
1239 | 0 | return poNode->papoSubExpr[0]; |
1240 | 0 | if (poNode->papoSubExpr[1]->eNodeType == SNT_COLUMN) |
1241 | 0 | return poNode->papoSubExpr[1]; |
1242 | 0 | } |
1243 | 0 | return nullptr; |
1244 | 0 | } |
1245 | | |
1246 | | /***********************************************************************/ |
1247 | | /* GetConstantSubNode() */ |
1248 | | /***********************************************************************/ |
1249 | | |
1250 | | static swq_expr_node *GetConstantSubNode(swq_expr_node *poNode) |
1251 | 0 | { |
1252 | 0 | if (poNode->eNodeType == SNT_OPERATION && poNode->nSubExprCount == 2) |
1253 | 0 | { |
1254 | 0 | if (poNode->papoSubExpr[1]->eNodeType == SNT_CONSTANT) |
1255 | 0 | return poNode->papoSubExpr[1]; |
1256 | 0 | if (poNode->papoSubExpr[0]->eNodeType == SNT_CONSTANT) |
1257 | 0 | return poNode->papoSubExpr[0]; |
1258 | 0 | } |
1259 | 0 | return nullptr; |
1260 | 0 | } |
1261 | | |
1262 | | /***********************************************************************/ |
1263 | | /* BuildIteratorFromExprNode() */ |
1264 | | /***********************************************************************/ |
1265 | | |
1266 | | FileGDBIterator * |
1267 | | OGROpenFileGDBLayer::BuildIteratorFromExprNode(swq_expr_node *poNode) |
1268 | 0 | { |
1269 | 0 | if (m_bIteratorSufficientToEvaluateFilter == FALSE) |
1270 | 0 | return nullptr; |
1271 | | |
1272 | 0 | if (poNode->eNodeType == SNT_OPERATION && poNode->nOperation == SWQ_AND && |
1273 | 0 | poNode->nSubExprCount == 2) |
1274 | 0 | { |
1275 | | // Even if there is only one branch of the 2 that results to an |
1276 | | // iterator, it is useful. Of course, the iterator will not be |
1277 | | // sufficient to evaluatethe filter, but it will be a super-set of the |
1278 | | // features |
1279 | 0 | FileGDBIterator *poIter1 = |
1280 | 0 | BuildIteratorFromExprNode(poNode->papoSubExpr[0]); |
1281 | | |
1282 | | /* In case the first branch didn't result to an iterator, temporarily */ |
1283 | | /* restore the flag */ |
1284 | 0 | const bool bSaveIteratorSufficientToEvaluateFilter = |
1285 | 0 | CPL_TO_BOOL(m_bIteratorSufficientToEvaluateFilter); |
1286 | 0 | m_bIteratorSufficientToEvaluateFilter = -1; |
1287 | 0 | FileGDBIterator *poIter2 = |
1288 | 0 | BuildIteratorFromExprNode(poNode->papoSubExpr[1]); |
1289 | 0 | m_bIteratorSufficientToEvaluateFilter = |
1290 | 0 | bSaveIteratorSufficientToEvaluateFilter; |
1291 | |
|
1292 | 0 | if (poIter1 != nullptr && poIter2 != nullptr) |
1293 | 0 | return FileGDBIterator::BuildAnd(poIter1, poIter2, true); |
1294 | 0 | m_bIteratorSufficientToEvaluateFilter = FALSE; |
1295 | 0 | if (poIter1 != nullptr) |
1296 | 0 | return poIter1; |
1297 | 0 | if (poIter2 != nullptr) |
1298 | 0 | return poIter2; |
1299 | 0 | } |
1300 | | |
1301 | 0 | else if (poNode->eNodeType == SNT_OPERATION && |
1302 | 0 | poNode->nOperation == SWQ_OR && poNode->nSubExprCount == 2) |
1303 | 0 | { |
1304 | | /* For a OR, we need an iterator for the 2 branches */ |
1305 | 0 | FileGDBIterator *poIter1 = |
1306 | 0 | BuildIteratorFromExprNode(poNode->papoSubExpr[0]); |
1307 | 0 | if (poIter1 != nullptr) |
1308 | 0 | { |
1309 | 0 | FileGDBIterator *poIter2 = |
1310 | 0 | BuildIteratorFromExprNode(poNode->papoSubExpr[1]); |
1311 | 0 | if (poIter2 == nullptr) |
1312 | 0 | { |
1313 | 0 | delete poIter1; |
1314 | 0 | } |
1315 | 0 | else |
1316 | 0 | { |
1317 | 0 | return FileGDBIterator::BuildOr( |
1318 | 0 | poIter1, poIter2, |
1319 | 0 | AreExprExclusive(GetLayerDefn(), poNode->papoSubExpr[0], |
1320 | 0 | poNode->papoSubExpr[1])); |
1321 | 0 | } |
1322 | 0 | } |
1323 | 0 | } |
1324 | | |
1325 | 0 | else if (poNode->eNodeType == SNT_OPERATION && |
1326 | 0 | (OGROpenFileGDBIsComparisonOp(poNode->nOperation) || |
1327 | 0 | poNode->nOperation == SWQ_ILIKE) && |
1328 | 0 | poNode->nSubExprCount == 2) |
1329 | 0 | { |
1330 | 0 | swq_expr_node *poColumn = GetColumnSubNode(poNode); |
1331 | 0 | swq_expr_node *poValue = GetConstantSubNode(poNode); |
1332 | 0 | if (poColumn != nullptr && poValue != nullptr && |
1333 | 0 | poColumn->field_index < GetLayerDefn()->GetFieldCount()) |
1334 | 0 | { |
1335 | 0 | OGRFieldDefn *poFieldDefn = |
1336 | 0 | GetLayerDefn()->GetFieldDefn(poColumn->field_index); |
1337 | |
|
1338 | 0 | int nTableColIdx = |
1339 | 0 | m_poLyrTable->GetFieldIdx(poFieldDefn->GetNameRef()); |
1340 | 0 | if (nTableColIdx >= 0 && |
1341 | 0 | m_poLyrTable->GetField(nTableColIdx)->HasIndex()) |
1342 | 0 | { |
1343 | 0 | OGRField sValue; |
1344 | |
|
1345 | 0 | if (FillTargetValueFromSrcExpr(poFieldDefn, &sValue, poValue)) |
1346 | 0 | { |
1347 | 0 | FileGDBSQLOp eOp = FGSO_EQ; |
1348 | 0 | CPL_IGNORE_RET_VAL(eOp); |
1349 | 0 | if (poColumn == poNode->papoSubExpr[0]) |
1350 | 0 | { |
1351 | 0 | switch (poNode->nOperation) |
1352 | 0 | { |
1353 | 0 | case SWQ_LE: |
1354 | 0 | eOp = FGSO_LE; |
1355 | 0 | break; |
1356 | 0 | case SWQ_LT: |
1357 | 0 | eOp = FGSO_LT; |
1358 | 0 | break; |
1359 | 0 | case SWQ_NE: |
1360 | 0 | eOp = FGSO_EQ; /* yes : EQ */ |
1361 | 0 | break; |
1362 | 0 | case SWQ_EQ: |
1363 | 0 | eOp = FGSO_EQ; |
1364 | 0 | break; |
1365 | 0 | case SWQ_GE: |
1366 | 0 | eOp = FGSO_GE; |
1367 | 0 | break; |
1368 | 0 | case SWQ_GT: |
1369 | 0 | eOp = FGSO_GT; |
1370 | 0 | break; |
1371 | 0 | case SWQ_ILIKE: |
1372 | 0 | eOp = FGSO_ILIKE; |
1373 | 0 | break; |
1374 | 0 | default: |
1375 | 0 | CPLAssert(false); |
1376 | 0 | break; |
1377 | 0 | } |
1378 | 0 | } |
1379 | 0 | else |
1380 | 0 | { |
1381 | | /* If "constant op column", then we must reverse */ |
1382 | | /* the operator */ |
1383 | 0 | switch (poNode->nOperation) |
1384 | 0 | { |
1385 | 0 | case SWQ_LE: |
1386 | 0 | eOp = FGSO_GE; |
1387 | 0 | break; |
1388 | 0 | case SWQ_LT: |
1389 | 0 | eOp = FGSO_GT; |
1390 | 0 | break; |
1391 | 0 | case SWQ_NE: |
1392 | 0 | eOp = FGSO_EQ; /* yes : EQ */ |
1393 | 0 | break; |
1394 | 0 | case SWQ_EQ: |
1395 | 0 | eOp = FGSO_EQ; |
1396 | 0 | break; |
1397 | 0 | case SWQ_GE: |
1398 | 0 | eOp = FGSO_LE; |
1399 | 0 | break; |
1400 | 0 | case SWQ_GT: |
1401 | 0 | eOp = FGSO_LT; |
1402 | 0 | break; |
1403 | 0 | case SWQ_ILIKE: |
1404 | 0 | eOp = FGSO_ILIKE; |
1405 | 0 | break; |
1406 | 0 | default: |
1407 | 0 | CPLAssert(false); |
1408 | 0 | break; |
1409 | 0 | } |
1410 | 0 | } |
1411 | | |
1412 | 0 | bool bIteratorSufficient = true; |
1413 | 0 | auto poField = m_poLyrTable->GetField(nTableColIdx); |
1414 | 0 | std::string osTruncatedStr; // keep it in this scope ! |
1415 | 0 | if (poField->GetType() == FGFT_STRING && |
1416 | 0 | poFieldDefn->GetType() == OFTString) |
1417 | 0 | { |
1418 | | // If we have an equality comparison, but the index |
1419 | | // uses LOWER(), transform it to a ILIKE comparison |
1420 | 0 | if (eOp == FGSO_EQ && poField->HasIndex() && |
1421 | 0 | STARTS_WITH_CI( |
1422 | 0 | poField->GetIndex()->GetExpression().c_str(), |
1423 | 0 | "LOWER(")) |
1424 | 0 | { |
1425 | | // Note: FileGDBIndexIterator::SetConstraint() |
1426 | | // checks that the string to compare with has no |
1427 | | // wildcard |
1428 | 0 | eOp = FGSO_ILIKE; |
1429 | | |
1430 | | // In theory, a ILIKE is not sufficient as it is |
1431 | | // case insensitive, whereas one could expect |
1432 | | // equality testing to be case sensitive... but |
1433 | | // it is not in OGR SQL... |
1434 | | // So we can comment the below line |
1435 | | // bIteratorSufficient = false; |
1436 | 0 | } |
1437 | | |
1438 | | // As the index use ' ' as padding value, we cannot |
1439 | | // fully trust the index. |
1440 | 0 | else if ((eOp == FGSO_EQ && |
1441 | 0 | poNode->nOperation != SWQ_NE) || |
1442 | 0 | eOp == FGSO_GE) |
1443 | 0 | bIteratorSufficient = false; |
1444 | 0 | else |
1445 | 0 | return nullptr; |
1446 | | |
1447 | 0 | const int nMaxWidthIndexedStr = |
1448 | 0 | poField->GetIndex()->GetMaxWidthInBytes( |
1449 | 0 | m_poLyrTable); |
1450 | 0 | if (nMaxWidthIndexedStr > 0) |
1451 | 0 | { |
1452 | 0 | wchar_t *pWide = CPLRecodeToWChar( |
1453 | 0 | sValue.String, CPL_ENC_UTF8, CPL_ENC_UCS2); |
1454 | 0 | if (pWide) |
1455 | 0 | { |
1456 | 0 | const size_t nUCS2Len = wcslen(pWide); |
1457 | 0 | if (nUCS2Len * sizeof(uint16_t) > |
1458 | 0 | static_cast<size_t>(nMaxWidthIndexedStr)) |
1459 | 0 | { |
1460 | 0 | pWide[nMaxWidthIndexedStr / |
1461 | 0 | sizeof(uint16_t)] = 0; |
1462 | 0 | char *pszTruncated = CPLRecodeFromWChar( |
1463 | 0 | pWide, CPL_ENC_UCS2, CPL_ENC_UTF8); |
1464 | 0 | if (pszTruncated) |
1465 | 0 | { |
1466 | 0 | osTruncatedStr = pszTruncated; |
1467 | 0 | sValue.String = &osTruncatedStr[0]; |
1468 | 0 | CPLFree(pszTruncated); |
1469 | 0 | } |
1470 | 0 | } |
1471 | 0 | CPLFree(pWide); |
1472 | 0 | } |
1473 | 0 | } |
1474 | 0 | } |
1475 | 0 | else if (eOp == FGSO_ILIKE) |
1476 | 0 | return nullptr; |
1477 | | |
1478 | 0 | FileGDBIterator *poIter = FileGDBIterator::Build( |
1479 | 0 | m_poLyrTable, nTableColIdx, TRUE, eOp, |
1480 | 0 | poFieldDefn->GetType(), &sValue); |
1481 | 0 | if (poIter != nullptr) |
1482 | 0 | m_bIteratorSufficientToEvaluateFilter = |
1483 | 0 | bIteratorSufficient; |
1484 | 0 | if (poIter && poNode->nOperation == SWQ_NE) |
1485 | 0 | return FileGDBIterator::BuildNot(poIter); |
1486 | 0 | else |
1487 | 0 | return poIter; |
1488 | 0 | } |
1489 | 0 | } |
1490 | 0 | } |
1491 | 0 | } |
1492 | 0 | else if (poNode->eNodeType == SNT_OPERATION && |
1493 | 0 | poNode->nOperation == SWQ_ISNULL && poNode->nSubExprCount == 1) |
1494 | 0 | { |
1495 | 0 | swq_expr_node *poColumn = poNode->papoSubExpr[0]; |
1496 | 0 | if (poColumn->eNodeType == SNT_COLUMN && |
1497 | 0 | poColumn->field_index < GetLayerDefn()->GetFieldCount()) |
1498 | 0 | { |
1499 | 0 | OGRFieldDefn *poFieldDefn = |
1500 | 0 | GetLayerDefn()->GetFieldDefn(poColumn->field_index); |
1501 | |
|
1502 | 0 | int nTableColIdx = |
1503 | 0 | m_poLyrTable->GetFieldIdx(poFieldDefn->GetNameRef()); |
1504 | 0 | if (nTableColIdx >= 0 && |
1505 | 0 | m_poLyrTable->GetField(nTableColIdx)->HasIndex()) |
1506 | 0 | { |
1507 | 0 | FileGDBIterator *poIter = FileGDBIterator::BuildIsNotNull( |
1508 | 0 | m_poLyrTable, nTableColIdx, TRUE); |
1509 | 0 | if (poIter) |
1510 | 0 | { |
1511 | 0 | m_bIteratorSufficientToEvaluateFilter = TRUE; |
1512 | 0 | poIter = FileGDBIterator::BuildNot(poIter); |
1513 | 0 | } |
1514 | 0 | return poIter; |
1515 | 0 | } |
1516 | 0 | } |
1517 | 0 | } |
1518 | 0 | else if (poNode->eNodeType == SNT_OPERATION && |
1519 | 0 | poNode->nOperation == SWQ_NOT && poNode->nSubExprCount == 1 && |
1520 | 0 | poNode->papoSubExpr[0]->eNodeType == SNT_OPERATION && |
1521 | 0 | poNode->papoSubExpr[0]->nOperation == SWQ_ISNULL && |
1522 | 0 | poNode->papoSubExpr[0]->nSubExprCount == 1) |
1523 | 0 | { |
1524 | 0 | swq_expr_node *poColumn = poNode->papoSubExpr[0]->papoSubExpr[0]; |
1525 | 0 | if (poColumn->eNodeType == SNT_COLUMN && |
1526 | 0 | poColumn->field_index < GetLayerDefn()->GetFieldCount()) |
1527 | 0 | { |
1528 | 0 | OGRFieldDefn *poFieldDefn = |
1529 | 0 | GetLayerDefn()->GetFieldDefn(poColumn->field_index); |
1530 | |
|
1531 | 0 | int nTableColIdx = |
1532 | 0 | m_poLyrTable->GetFieldIdx(poFieldDefn->GetNameRef()); |
1533 | 0 | if (nTableColIdx >= 0 && |
1534 | 0 | m_poLyrTable->GetField(nTableColIdx)->HasIndex()) |
1535 | 0 | { |
1536 | 0 | FileGDBIterator *poIter = FileGDBIterator::BuildIsNotNull( |
1537 | 0 | m_poLyrTable, nTableColIdx, TRUE); |
1538 | 0 | if (poIter) |
1539 | 0 | m_bIteratorSufficientToEvaluateFilter = TRUE; |
1540 | 0 | return poIter; |
1541 | 0 | } |
1542 | 0 | } |
1543 | 0 | } |
1544 | 0 | else if (poNode->eNodeType == SNT_OPERATION && |
1545 | 0 | poNode->nOperation == SWQ_IN && poNode->nSubExprCount >= 2) |
1546 | 0 | { |
1547 | 0 | swq_expr_node *poColumn = poNode->papoSubExpr[0]; |
1548 | 0 | if (poColumn->eNodeType == SNT_COLUMN && |
1549 | 0 | poColumn->field_index < GetLayerDefn()->GetFieldCount()) |
1550 | 0 | { |
1551 | 0 | bool bAllConstants = true; |
1552 | 0 | for (int i = 1; i < poNode->nSubExprCount; i++) |
1553 | 0 | { |
1554 | 0 | if (poNode->papoSubExpr[i]->eNodeType != SNT_CONSTANT) |
1555 | 0 | bAllConstants = false; |
1556 | 0 | } |
1557 | 0 | OGRFieldDefn *poFieldDefn = |
1558 | 0 | GetLayerDefn()->GetFieldDefn(poColumn->field_index); |
1559 | |
|
1560 | 0 | int nTableColIdx = |
1561 | 0 | m_poLyrTable->GetFieldIdx(poFieldDefn->GetNameRef()); |
1562 | 0 | if (bAllConstants && nTableColIdx >= 0 && |
1563 | 0 | m_poLyrTable->GetField(nTableColIdx)->HasIndex()) |
1564 | 0 | { |
1565 | 0 | FileGDBIterator *poRet = nullptr; |
1566 | |
|
1567 | 0 | bool bIteratorSufficient = true; |
1568 | 0 | auto poField = m_poLyrTable->GetField(nTableColIdx); |
1569 | |
|
1570 | 0 | for (int i = 1; i < poNode->nSubExprCount; i++) |
1571 | 0 | { |
1572 | 0 | OGRField sValue; |
1573 | 0 | if (!FillTargetValueFromSrcExpr(poFieldDefn, &sValue, |
1574 | 0 | poNode->papoSubExpr[i])) |
1575 | 0 | { |
1576 | 0 | delete poRet; |
1577 | 0 | poRet = nullptr; |
1578 | 0 | break; |
1579 | 0 | } |
1580 | | |
1581 | 0 | std::string osTruncatedStr; // keep it in this scope ! |
1582 | 0 | if (poField->GetType() == FGFT_STRING && |
1583 | 0 | poFieldDefn->GetType() == OFTString) |
1584 | 0 | { |
1585 | 0 | const int nMaxWidthIndexedStr = |
1586 | 0 | poField->GetIndex()->GetMaxWidthInBytes( |
1587 | 0 | m_poLyrTable); |
1588 | 0 | if (nMaxWidthIndexedStr > 0) |
1589 | 0 | { |
1590 | 0 | wchar_t *pWide = CPLRecodeToWChar( |
1591 | 0 | sValue.String, CPL_ENC_UTF8, CPL_ENC_UCS2); |
1592 | 0 | if (pWide) |
1593 | 0 | { |
1594 | 0 | const size_t nUCS2Len = wcslen(pWide); |
1595 | 0 | if (nUCS2Len * sizeof(uint16_t) > |
1596 | 0 | static_cast<size_t>(nMaxWidthIndexedStr)) |
1597 | 0 | { |
1598 | 0 | pWide[nMaxWidthIndexedStr / |
1599 | 0 | sizeof(uint16_t)] = 0; |
1600 | 0 | char *pszTruncated = CPLRecodeFromWChar( |
1601 | 0 | pWide, CPL_ENC_UCS2, CPL_ENC_UTF8); |
1602 | 0 | if (pszTruncated) |
1603 | 0 | { |
1604 | 0 | osTruncatedStr = pszTruncated; |
1605 | 0 | sValue.String = &osTruncatedStr[0]; |
1606 | 0 | CPLFree(pszTruncated); |
1607 | 0 | } |
1608 | 0 | } |
1609 | 0 | CPLFree(pWide); |
1610 | 0 | } |
1611 | 0 | } |
1612 | | |
1613 | | // As the index use ' ' as padding value, we cannot |
1614 | | // fully trust the index. |
1615 | 0 | bIteratorSufficient = false; |
1616 | 0 | } |
1617 | |
|
1618 | 0 | FileGDBIterator *poIter = FileGDBIterator::Build( |
1619 | 0 | m_poLyrTable, nTableColIdx, TRUE, FGSO_EQ, |
1620 | 0 | poFieldDefn->GetType(), &sValue); |
1621 | 0 | if (poIter == nullptr) |
1622 | 0 | { |
1623 | 0 | delete poRet; |
1624 | 0 | poRet = nullptr; |
1625 | 0 | break; |
1626 | 0 | } |
1627 | 0 | if (poRet == nullptr) |
1628 | 0 | poRet = poIter; |
1629 | 0 | else |
1630 | 0 | poRet = FileGDBIterator::BuildOr(poRet, poIter); |
1631 | 0 | } |
1632 | 0 | if (poRet != nullptr) |
1633 | 0 | { |
1634 | 0 | m_bIteratorSufficientToEvaluateFilter = bIteratorSufficient; |
1635 | 0 | return poRet; |
1636 | 0 | } |
1637 | 0 | } |
1638 | 0 | } |
1639 | 0 | } |
1640 | 0 | else if (poNode->eNodeType == SNT_OPERATION && |
1641 | 0 | poNode->nOperation == SWQ_NOT && poNode->nSubExprCount == 1) |
1642 | 0 | { |
1643 | 0 | FileGDBIterator *poIter = |
1644 | 0 | BuildIteratorFromExprNode(poNode->papoSubExpr[0]); |
1645 | | /* If we have an iterator that is only partial w.r.t the full clause */ |
1646 | | /* then we cannot do anything with it unfortunately */ |
1647 | 0 | if (m_bIteratorSufficientToEvaluateFilter == FALSE) |
1648 | 0 | { |
1649 | 0 | if (poIter != nullptr) |
1650 | 0 | CPLDebug("OpenFileGDB", "Disabling use of indexes"); |
1651 | 0 | delete poIter; |
1652 | 0 | } |
1653 | 0 | else if (poIter != nullptr) |
1654 | 0 | { |
1655 | 0 | return FileGDBIterator::BuildNot(poIter); |
1656 | 0 | } |
1657 | 0 | } |
1658 | | |
1659 | 0 | if (m_bIteratorSufficientToEvaluateFilter == TRUE) |
1660 | 0 | CPLDebug("OpenFileGDB", "Disabling use of indexes"); |
1661 | 0 | m_bIteratorSufficientToEvaluateFilter = FALSE; |
1662 | 0 | return nullptr; |
1663 | 0 | } |
1664 | | |
1665 | | /***********************************************************************/ |
1666 | | /* SetAttributeFilter() */ |
1667 | | /***********************************************************************/ |
1668 | | |
1669 | | OGRErr OGROpenFileGDBLayer::SetAttributeFilter(const char *pszFilter) |
1670 | 0 | { |
1671 | 0 | if (!BuildLayerDefinition()) |
1672 | 0 | return OGRERR_FAILURE; |
1673 | | |
1674 | 0 | delete m_poAttributeIterator; |
1675 | 0 | m_poAttributeIterator = nullptr; |
1676 | 0 | delete m_poCombinedIterator; |
1677 | 0 | m_poCombinedIterator = nullptr; |
1678 | 0 | m_bIteratorSufficientToEvaluateFilter = FALSE; |
1679 | |
|
1680 | 0 | OGRErr eErr = OGRLayer::SetAttributeFilter(pszFilter); |
1681 | 0 | if (eErr != OGRERR_NONE || |
1682 | 0 | !CPLTestBool(CPLGetConfigOption("OPENFILEGDB_USE_INDEX", "YES"))) |
1683 | 0 | return eErr; |
1684 | | |
1685 | 0 | if (m_poAttrQuery != nullptr && m_nFilteredFeatureCount < 0) |
1686 | 0 | { |
1687 | 0 | swq_expr_node *poNode = |
1688 | 0 | static_cast<swq_expr_node *>(m_poAttrQuery->GetSWQExpr()); |
1689 | 0 | poNode->ReplaceBetweenByGEAndLERecurse(); |
1690 | 0 | m_bIteratorSufficientToEvaluateFilter = -1; |
1691 | 0 | m_poAttributeIterator = BuildIteratorFromExprNode(poNode); |
1692 | 0 | if (m_poAttributeIterator != nullptr && |
1693 | 0 | m_eSpatialIndexState == SPI_IN_BUILDING) |
1694 | 0 | m_eSpatialIndexState = SPI_INVALID; |
1695 | 0 | if (m_bIteratorSufficientToEvaluateFilter < 0) |
1696 | 0 | m_bIteratorSufficientToEvaluateFilter = FALSE; |
1697 | 0 | } |
1698 | |
|
1699 | 0 | BuildCombinedIterator(); |
1700 | |
|
1701 | 0 | return eErr; |
1702 | 0 | } |
1703 | | |
1704 | | /***********************************************************************/ |
1705 | | /* BuildCombinedIterator() */ |
1706 | | /***********************************************************************/ |
1707 | | |
1708 | | void OGROpenFileGDBLayer::BuildCombinedIterator() |
1709 | 0 | { |
1710 | 0 | delete m_poCombinedIterator; |
1711 | 0 | if (m_poAttributeIterator && m_poSpatialIndexIterator) |
1712 | 0 | { |
1713 | 0 | m_poCombinedIterator = FileGDBIterator::BuildAnd( |
1714 | 0 | m_poAttributeIterator, m_poSpatialIndexIterator, false); |
1715 | 0 | } |
1716 | 0 | else |
1717 | 0 | { |
1718 | 0 | m_poCombinedIterator = nullptr; |
1719 | 0 | } |
1720 | 0 | } |
1721 | | |
1722 | | /***********************************************************************/ |
1723 | | /* GetCurrentFeature() */ |
1724 | | /***********************************************************************/ |
1725 | | |
1726 | | OGRFeature *OGROpenFileGDBLayer::GetCurrentFeature() |
1727 | 196k | { |
1728 | 196k | OGRFeature *poFeature = nullptr; |
1729 | 196k | int iOGRIdx = 0; |
1730 | 196k | int64_t iRow = m_poLyrTable->GetCurRow(); |
1731 | 1.26M | for (int iGDBIdx = 0; iGDBIdx < m_poLyrTable->GetFieldCount(); iGDBIdx++) |
1732 | 1.06M | { |
1733 | 1.06M | if (iOGRIdx == m_iFIDAsRegularColumnIndex) |
1734 | 0 | iOGRIdx++; |
1735 | | |
1736 | 1.06M | if (iGDBIdx == m_iGeomFieldIdx) |
1737 | 153k | { |
1738 | 153k | if (m_poFeatureDefn->GetGeomFieldDefn(0)->IsIgnored()) |
1739 | 0 | { |
1740 | 0 | if (m_eSpatialIndexState == SPI_IN_BUILDING) |
1741 | 0 | m_eSpatialIndexState = SPI_INVALID; |
1742 | 0 | continue; |
1743 | 0 | } |
1744 | | |
1745 | 153k | const OGRField *psField = m_poLyrTable->GetFieldValue(iGDBIdx); |
1746 | 153k | if (psField != nullptr) |
1747 | 147k | { |
1748 | 147k | if (m_eSpatialIndexState == SPI_IN_BUILDING) |
1749 | 145k | { |
1750 | 145k | OGREnvelope sFeatureEnvelope; |
1751 | 145k | if (m_poLyrTable->GetFeatureExtent(psField, |
1752 | 145k | &sFeatureEnvelope)) |
1753 | 134k | { |
1754 | | #if SIZEOF_VOIDP < 8 |
1755 | | if (iRow > INT32_MAX) |
1756 | | { |
1757 | | // m_pQuadTree stores iRow values as void* |
1758 | | // This would overflow here. |
1759 | | m_eSpatialIndexState = SPI_INVALID; |
1760 | | } |
1761 | | else |
1762 | | #endif |
1763 | 134k | { |
1764 | 134k | CPLRectObj sBounds; |
1765 | 134k | sBounds.minx = sFeatureEnvelope.MinX; |
1766 | 134k | sBounds.miny = sFeatureEnvelope.MinY; |
1767 | 134k | sBounds.maxx = sFeatureEnvelope.MaxX; |
1768 | 134k | sBounds.maxy = sFeatureEnvelope.MaxY; |
1769 | 134k | CPLQuadTreeInsertWithBounds( |
1770 | 134k | m_pQuadTree, |
1771 | 134k | reinterpret_cast<void *>( |
1772 | 134k | static_cast<uintptr_t>(iRow)), |
1773 | 134k | &sBounds); |
1774 | 134k | } |
1775 | 134k | } |
1776 | 145k | } |
1777 | | |
1778 | 147k | if (m_poFilterGeom != nullptr && |
1779 | 147k | m_eSpatialIndexState != SPI_COMPLETED && |
1780 | 147k | !m_poLyrTable->DoesGeometryIntersectsFilterEnvelope( |
1781 | 0 | psField)) |
1782 | 0 | { |
1783 | 0 | delete poFeature; |
1784 | 0 | return nullptr; |
1785 | 0 | } |
1786 | | |
1787 | 147k | OGRGeometry *poGeom = m_poGeomConverter->GetAsGeometry(psField); |
1788 | 147k | if (poGeom != nullptr) |
1789 | 110k | { |
1790 | 110k | OGRwkbGeometryType eFlattenType = |
1791 | 110k | wkbFlatten(poGeom->getGeometryType()); |
1792 | 110k | if (eFlattenType == wkbPolygon) |
1793 | 18.5k | poGeom = |
1794 | 18.5k | OGRGeometryFactory::forceToMultiPolygon(poGeom); |
1795 | 91.5k | else if (eFlattenType == wkbCurvePolygon) |
1796 | 8.61k | { |
1797 | 8.61k | OGRMultiSurface *poMS = new OGRMultiSurface(); |
1798 | 8.61k | poMS->addGeometryDirectly(poGeom); |
1799 | 8.61k | poGeom = poMS; |
1800 | 8.61k | } |
1801 | 82.9k | else if (eFlattenType == wkbLineString) |
1802 | 13.4k | poGeom = |
1803 | 13.4k | OGRGeometryFactory::forceToMultiLineString(poGeom); |
1804 | 69.4k | else if (eFlattenType == wkbCompoundCurve) |
1805 | 721 | { |
1806 | 721 | OGRMultiCurve *poMC = new OGRMultiCurve(); |
1807 | 721 | poMC->addGeometryDirectly(poGeom); |
1808 | 721 | poGeom = poMC; |
1809 | 721 | } |
1810 | | |
1811 | 110k | poGeom->assignSpatialReference( |
1812 | 110k | m_poFeatureDefn->GetGeomFieldDefn(0)->GetSpatialRef()); |
1813 | | |
1814 | 110k | if (poFeature == nullptr) |
1815 | 109k | poFeature = new OGRFeature(m_poFeatureDefn); |
1816 | 110k | poFeature->SetGeometryDirectly(poGeom); |
1817 | 110k | } |
1818 | 147k | } |
1819 | 153k | } |
1820 | 912k | else if (iGDBIdx != m_poLyrTable->GetObjectIdFieldIdx()) |
1821 | 719k | { |
1822 | 719k | const OGRFieldDefn *poFieldDefn = |
1823 | 719k | m_poFeatureDefn->GetFieldDefn(iOGRIdx); |
1824 | 719k | if (!poFieldDefn->IsIgnored()) |
1825 | 719k | { |
1826 | 719k | const OGRField *psField = m_poLyrTable->GetFieldValue(iGDBIdx); |
1827 | 719k | if (poFeature == nullptr) |
1828 | 72.3k | poFeature = new OGRFeature(m_poFeatureDefn); |
1829 | 719k | if (psField == nullptr) |
1830 | 188k | { |
1831 | 188k | poFeature->SetFieldNull(iOGRIdx); |
1832 | 188k | } |
1833 | 530k | else |
1834 | 530k | { |
1835 | | |
1836 | 530k | if (iGDBIdx == m_iFieldToReadAsBinary) |
1837 | 466 | poFeature->SetField(iOGRIdx, |
1838 | 466 | reinterpret_cast<const char *>( |
1839 | 466 | psField->Binary.paData)); |
1840 | 530k | else if (poFieldDefn->GetType() == OFTDateTime) |
1841 | 27.2k | { |
1842 | 27.2k | OGRField sField = *psField; |
1843 | 27.2k | if (m_poLyrTable->GetField(iGDBIdx)->GetType() == |
1844 | 27.2k | FGFT_DATETIME) |
1845 | 25.6k | { |
1846 | 25.6k | sField.Date.TZFlag = m_bTimeInUTC ? 100 : 0; |
1847 | 25.6k | } |
1848 | 27.2k | poFeature->SetField(iOGRIdx, &sField); |
1849 | 27.2k | } |
1850 | 503k | else |
1851 | 503k | poFeature->SetField(iOGRIdx, psField); |
1852 | 530k | } |
1853 | 719k | } |
1854 | 719k | iOGRIdx++; |
1855 | 719k | } |
1856 | 1.06M | } |
1857 | | |
1858 | 196k | if (poFeature == nullptr) |
1859 | 14.9k | poFeature = new OGRFeature(m_poFeatureDefn); |
1860 | | |
1861 | 196k | if (m_poLyrTable->HasDeletedFeaturesListed()) |
1862 | 0 | { |
1863 | 0 | poFeature->SetField(poFeature->GetFieldCount() - 1, |
1864 | 0 | m_poLyrTable->IsCurRowDeleted()); |
1865 | 0 | } |
1866 | | |
1867 | 196k | poFeature->SetFID(iRow + 1); |
1868 | | |
1869 | 196k | if (m_iFIDAsRegularColumnIndex >= 0) |
1870 | 0 | poFeature->SetField(m_iFIDAsRegularColumnIndex, poFeature->GetFID()); |
1871 | | |
1872 | 196k | return poFeature; |
1873 | 196k | } |
1874 | | |
1875 | | /***********************************************************************/ |
1876 | | /* GetNextFeature() */ |
1877 | | /***********************************************************************/ |
1878 | | |
1879 | | OGRFeature *OGROpenFileGDBLayer::GetNextFeature() |
1880 | 208k | { |
1881 | 208k | if (!BuildLayerDefinition() || m_bEOF) |
1882 | 3.81k | return nullptr; |
1883 | | |
1884 | 205k | FileGDBIterator *poIterator = m_poCombinedIterator ? m_poCombinedIterator |
1885 | 205k | : m_poSpatialIndexIterator |
1886 | 205k | ? m_poSpatialIndexIterator |
1887 | 205k | : m_poAttributeIterator; |
1888 | | |
1889 | 205k | while (true) |
1890 | 205k | { |
1891 | 205k | OGRFeature *poFeature = nullptr; |
1892 | | |
1893 | 205k | if (m_nFilteredFeatureCount >= 0) |
1894 | 0 | { |
1895 | 0 | while (true) |
1896 | 0 | { |
1897 | 0 | if (m_iCurFeat >= m_nFilteredFeatureCount) |
1898 | 0 | { |
1899 | 0 | return nullptr; |
1900 | 0 | } |
1901 | 0 | const auto iRow = |
1902 | 0 | static_cast<int64_t>(reinterpret_cast<GUIntptr_t>( |
1903 | 0 | m_pahFilteredFeatures[m_iCurFeat++])); |
1904 | 0 | if (m_poLyrTable->SelectRow(iRow)) |
1905 | 0 | { |
1906 | 0 | poFeature = GetCurrentFeature(); |
1907 | 0 | if (poFeature) |
1908 | 0 | break; |
1909 | 0 | } |
1910 | 0 | else if (m_poLyrTable->HasGotError()) |
1911 | 0 | { |
1912 | 0 | m_bEOF = TRUE; |
1913 | 0 | return nullptr; |
1914 | 0 | } |
1915 | 0 | } |
1916 | 0 | } |
1917 | 205k | else if (poIterator != nullptr) |
1918 | 0 | { |
1919 | 0 | while (true) |
1920 | 0 | { |
1921 | 0 | const auto iRow = poIterator->GetNextRowSortedByFID(); |
1922 | 0 | if (iRow < 0) |
1923 | 0 | return nullptr; |
1924 | 0 | if (m_poLyrTable->SelectRow(iRow)) |
1925 | 0 | { |
1926 | 0 | poFeature = GetCurrentFeature(); |
1927 | 0 | if (poFeature) |
1928 | 0 | break; |
1929 | 0 | } |
1930 | 0 | else if (m_poLyrTable->HasGotError()) |
1931 | 0 | { |
1932 | 0 | m_bEOF = TRUE; |
1933 | 0 | return nullptr; |
1934 | 0 | } |
1935 | 0 | } |
1936 | 0 | } |
1937 | 205k | else |
1938 | 205k | { |
1939 | 205k | while (true) |
1940 | 205k | { |
1941 | 205k | if (m_iCurFeat == m_poLyrTable->GetTotalRecordCount()) |
1942 | 7.77k | { |
1943 | 7.77k | return nullptr; |
1944 | 7.77k | } |
1945 | 197k | m_iCurFeat = |
1946 | 197k | m_poLyrTable->GetAndSelectNextNonEmptyRow(m_iCurFeat); |
1947 | 197k | if (m_iCurFeat < 0) |
1948 | 851 | { |
1949 | 851 | m_bEOF = TRUE; |
1950 | 851 | return nullptr; |
1951 | 851 | } |
1952 | 196k | else |
1953 | 196k | { |
1954 | 196k | m_iCurFeat++; |
1955 | 196k | poFeature = GetCurrentFeature(); |
1956 | 196k | if (m_eSpatialIndexState == SPI_IN_BUILDING && |
1957 | 196k | m_iCurFeat == m_poLyrTable->GetTotalRecordCount()) |
1958 | 7.16k | { |
1959 | 7.16k | CPLDebug("OpenFileGDB", "SPI_COMPLETED"); |
1960 | 7.16k | m_eSpatialIndexState = SPI_COMPLETED; |
1961 | 7.16k | } |
1962 | 196k | if (poFeature) |
1963 | 196k | break; |
1964 | 196k | } |
1965 | 197k | } |
1966 | 205k | } |
1967 | | |
1968 | 196k | if ((m_poFilterGeom == nullptr || |
1969 | 196k | FilterGeometry(poFeature->GetGeometryRef())) && |
1970 | 196k | (m_poAttrQuery == nullptr || |
1971 | 196k | (m_poAttributeIterator != nullptr && |
1972 | 0 | m_bIteratorSufficientToEvaluateFilter) || |
1973 | 196k | m_poAttrQuery->Evaluate(poFeature))) |
1974 | 196k | { |
1975 | 196k | return poFeature; |
1976 | 196k | } |
1977 | | |
1978 | 0 | delete poFeature; |
1979 | 0 | } |
1980 | 205k | } |
1981 | | |
1982 | | /***********************************************************************/ |
1983 | | /* GetFeature() */ |
1984 | | /***********************************************************************/ |
1985 | | |
1986 | | OGRFeature *OGROpenFileGDBLayer::GetFeature(GIntBig nFeatureId) |
1987 | 0 | { |
1988 | 0 | if (!BuildLayerDefinition()) |
1989 | 0 | return nullptr; |
1990 | | |
1991 | 0 | if (nFeatureId < 1 || nFeatureId > m_poLyrTable->GetTotalRecordCount()) |
1992 | 0 | return nullptr; |
1993 | 0 | if (!m_poLyrTable->SelectRow(nFeatureId - 1)) |
1994 | 0 | return nullptr; |
1995 | | |
1996 | | /* Temporarily disable spatial filter */ |
1997 | 0 | OGRGeometry *poOldSpatialFilter = m_poFilterGeom; |
1998 | 0 | m_poFilterGeom = nullptr; |
1999 | | /* and also spatial index state to avoid features to be inserted */ |
2000 | | /* multiple times in spatial index */ |
2001 | 0 | SPIState eOldState = m_eSpatialIndexState; |
2002 | 0 | m_eSpatialIndexState = SPI_INVALID; |
2003 | |
|
2004 | 0 | OGRFeature *poFeature = GetCurrentFeature(); |
2005 | | |
2006 | | /* Set it back */ |
2007 | 0 | m_poFilterGeom = poOldSpatialFilter; |
2008 | 0 | m_eSpatialIndexState = eOldState; |
2009 | |
|
2010 | 0 | return poFeature; |
2011 | 0 | } |
2012 | | |
2013 | | /***********************************************************************/ |
2014 | | /* SetNextByIndex() */ |
2015 | | /***********************************************************************/ |
2016 | | |
2017 | | OGRErr OGROpenFileGDBLayer::SetNextByIndex(GIntBig nIndex) |
2018 | 0 | { |
2019 | 0 | if (m_poAttributeIterator != nullptr || m_poSpatialIndexIterator != nullptr) |
2020 | 0 | return OGRLayer::SetNextByIndex(nIndex); |
2021 | | |
2022 | 0 | if (!BuildLayerDefinition()) |
2023 | 0 | return OGRERR_FAILURE; |
2024 | | |
2025 | 0 | if (m_eSpatialIndexState == SPI_IN_BUILDING) |
2026 | 0 | m_eSpatialIndexState = SPI_INVALID; |
2027 | |
|
2028 | 0 | if (m_nFilteredFeatureCount >= 0) |
2029 | 0 | { |
2030 | 0 | if (nIndex < 0 || nIndex >= m_nFilteredFeatureCount) |
2031 | 0 | return OGRERR_FAILURE; |
2032 | 0 | m_iCurFeat = nIndex; |
2033 | 0 | return OGRERR_NONE; |
2034 | 0 | } |
2035 | 0 | else if (m_poLyrTable->GetValidRecordCount() == |
2036 | 0 | m_poLyrTable->GetTotalRecordCount()) |
2037 | 0 | { |
2038 | 0 | if (nIndex < 0 || nIndex >= m_poLyrTable->GetValidRecordCount()) |
2039 | 0 | return OGRERR_FAILURE; |
2040 | 0 | m_iCurFeat = nIndex; |
2041 | 0 | return OGRERR_NONE; |
2042 | 0 | } |
2043 | 0 | else |
2044 | 0 | return OGRLayer::SetNextByIndex(nIndex); |
2045 | 0 | } |
2046 | | |
2047 | | /***********************************************************************/ |
2048 | | /* IGetExtent() */ |
2049 | | /***********************************************************************/ |
2050 | | |
2051 | | OGRErr OGROpenFileGDBLayer::IGetExtent(int /* iGeomField */, |
2052 | | OGREnvelope *psExtent, bool /* bForce */) |
2053 | 0 | { |
2054 | 0 | if (!BuildLayerDefinition()) |
2055 | 0 | return OGRERR_FAILURE; |
2056 | | |
2057 | 0 | if (m_iGeomFieldIdx >= 0 && m_poLyrTable->GetValidRecordCount() > 0) |
2058 | 0 | { |
2059 | 0 | FileGDBGeomField *poGDBGeomField = reinterpret_cast<FileGDBGeomField *>( |
2060 | 0 | m_poLyrTable->GetField(m_iGeomFieldIdx)); |
2061 | 0 | if (!std::isnan(poGDBGeomField->GetXMin())) |
2062 | 0 | { |
2063 | 0 | psExtent->MinX = poGDBGeomField->GetXMin(); |
2064 | 0 | psExtent->MinY = poGDBGeomField->GetYMin(); |
2065 | 0 | psExtent->MaxX = poGDBGeomField->GetXMax(); |
2066 | 0 | psExtent->MaxY = poGDBGeomField->GetYMax(); |
2067 | 0 | return OGRERR_NONE; |
2068 | 0 | } |
2069 | 0 | } |
2070 | | |
2071 | 0 | return OGRERR_FAILURE; |
2072 | 0 | } |
2073 | | |
2074 | | /***********************************************************************/ |
2075 | | /* IGetExtent3D() */ |
2076 | | /***********************************************************************/ |
2077 | | |
2078 | | OGRErr OGROpenFileGDBLayer::IGetExtent3D(int iGeomField, |
2079 | | OGREnvelope3D *psExtent, bool bForce) |
2080 | 0 | { |
2081 | 0 | if (!BuildLayerDefinition()) |
2082 | 0 | return OGRERR_FAILURE; |
2083 | | |
2084 | 0 | if (m_poFilterGeom == nullptr && m_poAttrQuery == nullptr && |
2085 | 0 | m_iGeomFieldIdx >= 0 && m_poLyrTable->GetValidRecordCount() > 0) |
2086 | 0 | { |
2087 | 0 | FileGDBGeomField *poGDBGeomField = reinterpret_cast<FileGDBGeomField *>( |
2088 | 0 | m_poLyrTable->GetField(m_iGeomFieldIdx)); |
2089 | 0 | if (!std::isnan(poGDBGeomField->GetXMin())) |
2090 | 0 | { |
2091 | 0 | psExtent->MinX = poGDBGeomField->GetXMin(); |
2092 | 0 | psExtent->MinY = poGDBGeomField->GetYMin(); |
2093 | 0 | psExtent->MaxX = poGDBGeomField->GetXMax(); |
2094 | 0 | psExtent->MaxY = poGDBGeomField->GetYMax(); |
2095 | 0 | if (!std::isnan(poGDBGeomField->GetZMin())) |
2096 | 0 | { |
2097 | 0 | psExtent->MinZ = poGDBGeomField->GetZMin(); |
2098 | 0 | psExtent->MaxZ = poGDBGeomField->GetZMax(); |
2099 | 0 | } |
2100 | 0 | else |
2101 | 0 | { |
2102 | 0 | if (OGR_GT_HasZ(m_eGeomType)) |
2103 | 0 | { |
2104 | 0 | return OGRLayer::IGetExtent3D(iGeomField, psExtent, bForce); |
2105 | 0 | } |
2106 | 0 | psExtent->MinZ = std::numeric_limits<double>::infinity(); |
2107 | 0 | psExtent->MaxZ = -std::numeric_limits<double>::infinity(); |
2108 | 0 | } |
2109 | 0 | return OGRERR_NONE; |
2110 | 0 | } |
2111 | 0 | } |
2112 | | |
2113 | 0 | return OGRLayer::IGetExtent3D(iGeomField, psExtent, bForce); |
2114 | 0 | } |
2115 | | |
2116 | | /***********************************************************************/ |
2117 | | /* GetFeatureCount() */ |
2118 | | /***********************************************************************/ |
2119 | | |
2120 | | GIntBig OGROpenFileGDBLayer::GetFeatureCount(int bForce) |
2121 | 0 | { |
2122 | 0 | if (!BuildLayerDefinition()) |
2123 | 0 | return 0; |
2124 | | |
2125 | | /* No filter */ |
2126 | 0 | if ((m_poFilterGeom == nullptr || m_iGeomFieldIdx < 0) && |
2127 | 0 | m_poAttrQuery == nullptr) |
2128 | 0 | { |
2129 | 0 | return m_poLyrTable->GetValidRecordCount(); |
2130 | 0 | } |
2131 | 0 | else if (m_nFilteredFeatureCount >= 0 && m_poAttrQuery == nullptr) |
2132 | 0 | { |
2133 | 0 | return m_nFilteredFeatureCount; |
2134 | 0 | } |
2135 | | |
2136 | | /* Only geometry filter ? */ |
2137 | 0 | if (m_poAttrQuery == nullptr && m_bFilterIsEnvelope) |
2138 | 0 | { |
2139 | 0 | if (m_poSpatialIndexIterator) |
2140 | 0 | { |
2141 | 0 | m_poSpatialIndexIterator->Reset(); |
2142 | 0 | int nCount = 0; |
2143 | 0 | while (true) |
2144 | 0 | { |
2145 | 0 | const auto nRowIdx = |
2146 | 0 | m_poSpatialIndexIterator->GetNextRowSortedByFID(); |
2147 | 0 | if (nRowIdx < 0) |
2148 | 0 | break; |
2149 | 0 | if (!m_poLyrTable->SelectRow(nRowIdx)) |
2150 | 0 | { |
2151 | 0 | if (m_poLyrTable->HasGotError()) |
2152 | 0 | break; |
2153 | 0 | else |
2154 | 0 | continue; |
2155 | 0 | } |
2156 | | |
2157 | 0 | const OGRField *psField = |
2158 | 0 | m_poLyrTable->GetFieldValue(m_iGeomFieldIdx); |
2159 | 0 | if (psField != nullptr) |
2160 | 0 | { |
2161 | 0 | if (m_poLyrTable->DoesGeometryIntersectsFilterEnvelope( |
2162 | 0 | psField)) |
2163 | 0 | { |
2164 | 0 | OGRGeometry *poGeom = |
2165 | 0 | m_poGeomConverter->GetAsGeometry(psField); |
2166 | 0 | if (poGeom != nullptr && FilterGeometry(poGeom)) |
2167 | 0 | { |
2168 | 0 | nCount++; |
2169 | 0 | } |
2170 | 0 | delete poGeom; |
2171 | 0 | } |
2172 | 0 | } |
2173 | 0 | } |
2174 | 0 | return nCount; |
2175 | 0 | } |
2176 | | |
2177 | 0 | int nCount = 0; |
2178 | 0 | if (m_eSpatialIndexState == SPI_IN_BUILDING && m_iCurFeat != 0) |
2179 | 0 | m_eSpatialIndexState = SPI_INVALID; |
2180 | |
|
2181 | 0 | int nFilteredFeatureCountAlloc = 0; |
2182 | 0 | if (m_eSpatialIndexState == SPI_IN_BUILDING) |
2183 | 0 | { |
2184 | 0 | CPLFree(m_pahFilteredFeatures); |
2185 | 0 | m_pahFilteredFeatures = nullptr; |
2186 | 0 | m_nFilteredFeatureCount = 0; |
2187 | 0 | } |
2188 | |
|
2189 | 0 | for (int64_t i = 0; i < m_poLyrTable->GetTotalRecordCount(); i++) |
2190 | 0 | { |
2191 | 0 | if (!m_poLyrTable->SelectRow(i)) |
2192 | 0 | { |
2193 | 0 | if (m_poLyrTable->HasGotError()) |
2194 | 0 | break; |
2195 | 0 | else |
2196 | 0 | continue; |
2197 | 0 | } |
2198 | | #if SIZEOF_VOIDP < 8 |
2199 | | if (i > INT32_MAX) |
2200 | | { |
2201 | | // CPLQuadTreeInsertWithBounds stores row index values as void* |
2202 | | // This would overflow here. |
2203 | | m_eSpatialIndexState = SPI_INVALID; |
2204 | | break; |
2205 | | } |
2206 | | #endif |
2207 | | |
2208 | 0 | const OGRField *psField = |
2209 | 0 | m_poLyrTable->GetFieldValue(m_iGeomFieldIdx); |
2210 | 0 | if (psField != nullptr) |
2211 | 0 | { |
2212 | 0 | if (m_eSpatialIndexState == SPI_IN_BUILDING) |
2213 | 0 | { |
2214 | 0 | OGREnvelope sFeatureEnvelope; |
2215 | 0 | if (m_poLyrTable->GetFeatureExtent(psField, |
2216 | 0 | &sFeatureEnvelope)) |
2217 | 0 | { |
2218 | 0 | CPLRectObj sBounds; |
2219 | 0 | sBounds.minx = sFeatureEnvelope.MinX; |
2220 | 0 | sBounds.miny = sFeatureEnvelope.MinY; |
2221 | 0 | sBounds.maxx = sFeatureEnvelope.MaxX; |
2222 | 0 | sBounds.maxy = sFeatureEnvelope.MaxY; |
2223 | 0 | CPLQuadTreeInsertWithBounds( |
2224 | 0 | m_pQuadTree, |
2225 | 0 | reinterpret_cast<void *>(static_cast<uintptr_t>(i)), |
2226 | 0 | &sBounds); |
2227 | 0 | } |
2228 | 0 | } |
2229 | |
|
2230 | 0 | if (m_poLyrTable->DoesGeometryIntersectsFilterEnvelope(psField)) |
2231 | 0 | { |
2232 | 0 | OGRGeometry *poGeom = |
2233 | 0 | m_poGeomConverter->GetAsGeometry(psField); |
2234 | 0 | if (poGeom != nullptr && FilterGeometry(poGeom)) |
2235 | 0 | { |
2236 | 0 | if (m_eSpatialIndexState == SPI_IN_BUILDING) |
2237 | 0 | { |
2238 | 0 | if (nCount == nFilteredFeatureCountAlloc) |
2239 | 0 | { |
2240 | 0 | nFilteredFeatureCountAlloc = |
2241 | 0 | 4 * nFilteredFeatureCountAlloc / 3 + 1024; |
2242 | 0 | m_pahFilteredFeatures = static_cast<void **>( |
2243 | 0 | CPLRealloc(m_pahFilteredFeatures, |
2244 | 0 | sizeof(void *) * |
2245 | 0 | nFilteredFeatureCountAlloc)); |
2246 | 0 | } |
2247 | 0 | m_pahFilteredFeatures[nCount] = |
2248 | 0 | reinterpret_cast<void *>( |
2249 | 0 | static_cast<uintptr_t>(i)); |
2250 | 0 | } |
2251 | 0 | nCount++; |
2252 | 0 | } |
2253 | 0 | delete poGeom; |
2254 | 0 | } |
2255 | 0 | } |
2256 | 0 | } |
2257 | 0 | if (m_eSpatialIndexState == SPI_IN_BUILDING) |
2258 | 0 | { |
2259 | 0 | m_nFilteredFeatureCount = nCount; |
2260 | 0 | m_eSpatialIndexState = SPI_COMPLETED; |
2261 | 0 | } |
2262 | |
|
2263 | 0 | return nCount; |
2264 | 0 | } |
2265 | | /* Only simple attribute filter ? */ |
2266 | 0 | else if (m_poFilterGeom == nullptr && m_poAttributeIterator != nullptr && |
2267 | 0 | m_bIteratorSufficientToEvaluateFilter) |
2268 | 0 | { |
2269 | 0 | return m_poAttributeIterator->GetRowCount(); |
2270 | 0 | } |
2271 | | |
2272 | 0 | return OGRLayer::GetFeatureCount(bForce); |
2273 | 0 | } |
2274 | | |
2275 | | /***********************************************************************/ |
2276 | | /* TestCapability() */ |
2277 | | /***********************************************************************/ |
2278 | | |
2279 | | int OGROpenFileGDBLayer::TestCapability(const char *pszCap) |
2280 | 3.89k | { |
2281 | 3.89k | if (!BuildLayerDefinition()) |
2282 | 0 | return FALSE; |
2283 | | |
2284 | 3.89k | if (EQUAL(pszCap, OLCCreateField) || EQUAL(pszCap, OLCDeleteField) || |
2285 | 3.89k | EQUAL(pszCap, OLCAlterFieldDefn) || |
2286 | 3.89k | EQUAL(pszCap, OLCAlterGeomFieldDefn) || |
2287 | 3.89k | EQUAL(pszCap, OLCSequentialWrite) || EQUAL(pszCap, OLCRandomWrite) || |
2288 | 3.89k | EQUAL(pszCap, OLCDeleteFeature) || EQUAL(pszCap, OLCRename)) |
2289 | 0 | { |
2290 | 0 | return m_bEditable; |
2291 | 0 | } |
2292 | | |
2293 | 3.89k | if (EQUAL(pszCap, OLCFastFeatureCount)) |
2294 | 0 | { |
2295 | 0 | return ((m_poFilterGeom == nullptr || m_iGeomFieldIdx < 0) && |
2296 | 0 | m_poAttrQuery == nullptr); |
2297 | 0 | } |
2298 | 3.89k | else if (EQUAL(pszCap, OLCFastSetNextByIndex)) |
2299 | 0 | { |
2300 | 0 | return (m_poLyrTable->GetValidRecordCount() == |
2301 | 0 | m_poLyrTable->GetTotalRecordCount() && |
2302 | 0 | m_poAttributeIterator == nullptr && |
2303 | 0 | m_poSpatialIndexIterator == nullptr); |
2304 | 0 | } |
2305 | 3.89k | else if (EQUAL(pszCap, OLCRandomRead)) |
2306 | 0 | { |
2307 | 0 | return TRUE; |
2308 | 0 | } |
2309 | 3.89k | else if (EQUAL(pszCap, OLCFastGetExtent)) |
2310 | 0 | { |
2311 | 0 | return TRUE; |
2312 | 0 | } |
2313 | 3.89k | else if (EQUAL(pszCap, OLCFastGetExtent3D)) |
2314 | 0 | { |
2315 | 0 | if (m_poFilterGeom == nullptr && m_poAttrQuery == nullptr && |
2316 | 0 | m_iGeomFieldIdx >= 0 && m_poLyrTable->GetValidRecordCount() > 0) |
2317 | 0 | { |
2318 | 0 | FileGDBGeomField *poGDBGeomField = |
2319 | 0 | reinterpret_cast<FileGDBGeomField *>( |
2320 | 0 | m_poLyrTable->GetField(m_iGeomFieldIdx)); |
2321 | 0 | if (!std::isnan(poGDBGeomField->GetXMin())) |
2322 | 0 | { |
2323 | 0 | if (!std::isnan(poGDBGeomField->GetZMin())) |
2324 | 0 | { |
2325 | 0 | return TRUE; |
2326 | 0 | } |
2327 | 0 | else |
2328 | 0 | { |
2329 | 0 | return !OGR_GT_HasZ(m_eGeomType); |
2330 | 0 | } |
2331 | 0 | } |
2332 | 0 | } |
2333 | 0 | return FALSE; |
2334 | 0 | } |
2335 | 3.89k | else if (EQUAL(pszCap, OLCIgnoreFields)) |
2336 | 0 | { |
2337 | 0 | return TRUE; |
2338 | 0 | } |
2339 | 3.89k | else if (EQUAL(pszCap, OLCStringsAsUTF8)) |
2340 | 0 | { |
2341 | 0 | return TRUE; /* ? */ |
2342 | 0 | } |
2343 | | |
2344 | 3.89k | else if (EQUAL(pszCap, OLCMeasuredGeometries)) |
2345 | 1.19k | return TRUE; |
2346 | | |
2347 | 2.69k | else if (EQUAL(pszCap, OLCCurveGeometries)) |
2348 | 2.69k | return TRUE; |
2349 | | |
2350 | 0 | else if (EQUAL(pszCap, OLCZGeometries)) |
2351 | 0 | return TRUE; |
2352 | | |
2353 | 0 | else if (EQUAL(pszCap, OLCFastSpatialFilter)) |
2354 | 0 | { |
2355 | 0 | return m_eSpatialIndexState == SPI_COMPLETED || |
2356 | 0 | (m_poLyrTable->CanUseIndices() && |
2357 | 0 | m_poLyrTable->HasSpatialIndex()); |
2358 | 0 | } |
2359 | | |
2360 | 0 | return FALSE; |
2361 | 3.89k | } |
2362 | | |
2363 | | /***********************************************************************/ |
2364 | | /* HasIndexForField() */ |
2365 | | /***********************************************************************/ |
2366 | | |
2367 | | bool OGROpenFileGDBLayer::HasIndexForField(const char *pszFieldName) |
2368 | 0 | { |
2369 | 0 | if (!BuildLayerDefinition()) |
2370 | 0 | return false; |
2371 | 0 | if (!m_poLyrTable->CanUseIndices()) |
2372 | 0 | return false; |
2373 | 0 | int nTableColIdx = m_poLyrTable->GetFieldIdx(pszFieldName); |
2374 | 0 | return (nTableColIdx >= 0 && |
2375 | 0 | m_poLyrTable->GetField(nTableColIdx)->HasIndex()); |
2376 | 0 | } |
2377 | | |
2378 | | /***********************************************************************/ |
2379 | | /* BuildIndex() */ |
2380 | | /***********************************************************************/ |
2381 | | |
2382 | | FileGDBIterator *OGROpenFileGDBLayer::BuildIndex(const char *pszFieldName, |
2383 | | int bAscending, int op, |
2384 | | swq_expr_node *poValue) |
2385 | 0 | { |
2386 | 0 | if (!BuildLayerDefinition()) |
2387 | 0 | return nullptr; |
2388 | | |
2389 | 0 | int idx = GetLayerDefn()->GetFieldIndex(pszFieldName); |
2390 | 0 | if (idx < 0) |
2391 | 0 | return nullptr; |
2392 | 0 | OGRFieldDefn *poFieldDefn = GetLayerDefn()->GetFieldDefn(idx); |
2393 | |
|
2394 | 0 | int nTableColIdx = m_poLyrTable->GetFieldIdx(pszFieldName); |
2395 | 0 | if (nTableColIdx >= 0 && m_poLyrTable->GetField(nTableColIdx)->HasIndex()) |
2396 | 0 | { |
2397 | 0 | if (op < 0) |
2398 | 0 | return FileGDBIterator::BuildIsNotNull(m_poLyrTable, nTableColIdx, |
2399 | 0 | bAscending); |
2400 | | |
2401 | 0 | OGRField sValue; |
2402 | 0 | if (FillTargetValueFromSrcExpr(poFieldDefn, &sValue, poValue)) |
2403 | 0 | { |
2404 | 0 | FileGDBSQLOp eOp; |
2405 | 0 | switch (op) |
2406 | 0 | { |
2407 | 0 | case SWQ_LE: |
2408 | 0 | eOp = FGSO_LE; |
2409 | 0 | break; |
2410 | 0 | case SWQ_LT: |
2411 | 0 | eOp = FGSO_LT; |
2412 | 0 | break; |
2413 | 0 | case SWQ_EQ: |
2414 | 0 | eOp = FGSO_EQ; |
2415 | 0 | break; |
2416 | 0 | case SWQ_GE: |
2417 | 0 | eOp = FGSO_GE; |
2418 | 0 | break; |
2419 | 0 | case SWQ_GT: |
2420 | 0 | eOp = FGSO_GT; |
2421 | 0 | break; |
2422 | 0 | default: |
2423 | 0 | return nullptr; |
2424 | 0 | } |
2425 | | |
2426 | 0 | return FileGDBIterator::Build(m_poLyrTable, nTableColIdx, |
2427 | 0 | bAscending, eOp, |
2428 | 0 | poFieldDefn->GetType(), &sValue); |
2429 | 0 | } |
2430 | 0 | } |
2431 | 0 | return nullptr; |
2432 | 0 | } |
2433 | | |
2434 | | /***********************************************************************/ |
2435 | | /* GetMinMaxValue() */ |
2436 | | /***********************************************************************/ |
2437 | | |
2438 | | const OGRField *OGROpenFileGDBLayer::GetMinMaxValue(OGRFieldDefn *poFieldDefn, |
2439 | | int bIsMin, int &eOutType) |
2440 | 0 | { |
2441 | 0 | eOutType = -1; |
2442 | 0 | if (!BuildLayerDefinition()) |
2443 | 0 | return nullptr; |
2444 | 0 | if (!m_poLyrTable->CanUseIndices()) |
2445 | 0 | return nullptr; |
2446 | | |
2447 | 0 | const int nTableColIdx = |
2448 | 0 | m_poLyrTable->GetFieldIdx(poFieldDefn->GetNameRef()); |
2449 | 0 | if (nTableColIdx >= 0 && m_poLyrTable->GetField(nTableColIdx)->HasIndex()) |
2450 | 0 | { |
2451 | 0 | delete m_poIterMinMax; |
2452 | 0 | m_poIterMinMax = |
2453 | 0 | FileGDBIterator::BuildIsNotNull(m_poLyrTable, nTableColIdx, TRUE); |
2454 | 0 | if (m_poIterMinMax != nullptr) |
2455 | 0 | { |
2456 | 0 | const OGRField *poRet = (bIsMin) |
2457 | 0 | ? m_poIterMinMax->GetMinValue(eOutType) |
2458 | 0 | : m_poIterMinMax->GetMaxValue(eOutType); |
2459 | 0 | if (poRet == nullptr) |
2460 | 0 | eOutType = poFieldDefn->GetType(); |
2461 | 0 | return poRet; |
2462 | 0 | } |
2463 | 0 | } |
2464 | 0 | return nullptr; |
2465 | 0 | } |
2466 | | |
2467 | | /***********************************************************************/ |
2468 | | /* GetMinMaxSumCount() */ |
2469 | | /***********************************************************************/ |
2470 | | |
2471 | | int OGROpenFileGDBLayer::GetMinMaxSumCount(OGRFieldDefn *poFieldDefn, |
2472 | | double &dfMin, double &dfMax, |
2473 | | double &dfSum, int &nCount) |
2474 | 0 | { |
2475 | 0 | dfMin = 0.0; |
2476 | 0 | dfMax = 0.0; |
2477 | 0 | dfSum = 0.0; |
2478 | 0 | nCount = 0; |
2479 | 0 | if (!BuildLayerDefinition()) |
2480 | 0 | return false; |
2481 | 0 | if (!m_poLyrTable->CanUseIndices()) |
2482 | 0 | return false; |
2483 | | |
2484 | 0 | int nTableColIdx = m_poLyrTable->GetFieldIdx(poFieldDefn->GetNameRef()); |
2485 | 0 | if (nTableColIdx >= 0 && m_poLyrTable->GetField(nTableColIdx)->HasIndex()) |
2486 | 0 | { |
2487 | 0 | auto poIter = std::unique_ptr<FileGDBIterator>( |
2488 | 0 | FileGDBIterator::BuildIsNotNull(m_poLyrTable, nTableColIdx, TRUE)); |
2489 | 0 | if (poIter) |
2490 | 0 | { |
2491 | 0 | return poIter->GetMinMaxSumCount(dfMin, dfMax, dfSum, nCount); |
2492 | 0 | } |
2493 | 0 | } |
2494 | 0 | return false; |
2495 | 0 | } |
2496 | | |
2497 | | /************************************************************************/ |
2498 | | /* GetDataset() */ |
2499 | | /************************************************************************/ |
2500 | | |
2501 | | GDALDataset *OGROpenFileGDBLayer::GetDataset() |
2502 | 0 | { |
2503 | 0 | return m_poDS; |
2504 | 0 | } |