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