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