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 * Permission is hereby granted, free of charge, to any person obtaining a
11 * copy of this software and associated documentation files (the "Software"),
12 * to deal in the Software without restriction, including without limitation
13 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14 * and/or sell copies of the Software, and to permit persons to whom the
15 * Software is furnished to do so, subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be included
18 * in all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 * DEALINGS IN THE SOFTWARE.
27 ****************************************************************************/
28
29 #include "cpl_port.h"
30 #include "ogr_openfilegdb.h"
31
32 #include <cmath>
33 #include <cstddef>
34 #include <cstdio>
35 #include <cstdlib>
36 #include <cstring>
37 #include <algorithm>
38 #include <string>
39
40 #include "cpl_conv.h"
41 #include "cpl_error.h"
42 #include "cpl_minixml.h"
43 #include "cpl_quad_tree.h"
44 #include "cpl_string.h"
45 #include "ogr_api.h"
46 #include "ogr_core.h"
47 #include "ogr_feature.h"
48 #include "ogr_geometry.h"
49 #include "ogr_spatialref.h"
50 #include "ogr_srs_api.h"
51 #include "ogrsf_frmts.h"
52 #include "filegdbtable.h"
53 #include "ogr_swq.h"
54
55 CPL_CVSID("$Id: ogropenfilegdblayer.cpp 6b84484fd22d27a4611ab64da86ba221156293f8 2021-08-25 10:30:20 +0200 Even Rouault $")
56
57 /************************************************************************/
58 /* OGROpenFileGDBGeomFieldDefn */
59 /************************************************************************/
60 class OGROpenFileGDBGeomFieldDefn: public OGRGeomFieldDefn
61 {
62 OGROpenFileGDBLayer* m_poLayer;
63
64 public:
OGROpenFileGDBGeomFieldDefn(OGROpenFileGDBLayer * poLayer,const char * pszNameIn,OGRwkbGeometryType eGeomTypeIn)65 OGROpenFileGDBGeomFieldDefn(OGROpenFileGDBLayer* poLayer,
66 const char *pszNameIn,
67 OGRwkbGeometryType eGeomTypeIn) :
68 OGRGeomFieldDefn(pszNameIn, eGeomTypeIn),
69 m_poLayer(poLayer)
70 {}
71
~OGROpenFileGDBGeomFieldDefn()72 ~OGROpenFileGDBGeomFieldDefn() {}
73
UnsetLayer()74 void UnsetLayer() { m_poLayer = nullptr; }
75
GetSpatialRef() const76 virtual OGRSpatialReference* GetSpatialRef() const override
77 {
78 if( poSRS )
79 return poSRS;
80 if( m_poLayer != nullptr )
81 (void) m_poLayer->BuildLayerDefinition();
82 return poSRS;
83 }
84 };
85
86 /************************************************************************/
87 /* OGROpenFileGDBFeatureDefn */
88 /************************************************************************/
89 class OGROpenFileGDBFeatureDefn: public OGRFeatureDefn
90 {
91 OGROpenFileGDBLayer* m_poLayer;
92 mutable int m_bHasBuildFieldDefn;
93
LazyGeomInit() const94 void LazyGeomInit() const
95 {
96 /* FileGDB v9 case */
97 if( !m_bHasBuildFieldDefn &&
98 m_poLayer != nullptr && m_poLayer->m_eGeomType != wkbNone &&
99 m_poLayer->m_osDefinition.empty() )
100 {
101 m_bHasBuildFieldDefn = TRUE;
102 (void) m_poLayer->BuildLayerDefinition();
103 }
104 }
105
106 public:
OGROpenFileGDBFeatureDefn(OGROpenFileGDBLayer * poLayer,const char * pszName)107 OGROpenFileGDBFeatureDefn( OGROpenFileGDBLayer* poLayer,
108 const char * pszName ) :
109 OGRFeatureDefn(pszName), m_poLayer(poLayer)
110 {
111 m_bHasBuildFieldDefn = FALSE;
112 }
113
~OGROpenFileGDBFeatureDefn()114 ~OGROpenFileGDBFeatureDefn() {}
115
UnsetLayer()116 void UnsetLayer()
117 {
118 if( nGeomFieldCount )
119 reinterpret_cast<OGROpenFileGDBGeomFieldDefn *>(
120 papoGeomFieldDefn[0])->UnsetLayer();
121 m_poLayer = nullptr;
122 }
123
GetFieldCount() const124 virtual int GetFieldCount() const override
125 {
126 if( nFieldCount )
127 return nFieldCount;
128 if( !m_bHasBuildFieldDefn && m_poLayer != nullptr )
129 {
130 m_bHasBuildFieldDefn = TRUE;
131 (void) m_poLayer->BuildLayerDefinition();
132 }
133 return nFieldCount;
134 }
135
GetGeomFieldCount() const136 virtual int GetGeomFieldCount() const override
137 {
138 LazyGeomInit();
139 return nGeomFieldCount;
140 }
141
GetGeomFieldDefn(int i)142 virtual OGRGeomFieldDefn* GetGeomFieldDefn( int i ) override
143 {
144 LazyGeomInit();
145 return OGRFeatureDefn::GetGeomFieldDefn(i);
146 }
147
GetGeomFieldDefn(int i) const148 virtual const OGRGeomFieldDefn* GetGeomFieldDefn( int i ) const override
149 {
150 LazyGeomInit();
151 return OGRFeatureDefn::GetGeomFieldDefn(i);
152 }
153 };
154
155 /************************************************************************/
156 /* OGROpenFileGDBLayer() */
157 /************************************************************************/
158
OGROpenFileGDBLayer(const char * pszGDBFilename,const char * pszName,const std::string & osDefinition,const std::string & osDocumentation,const char *,OGRwkbGeometryType eGeomType)159 OGROpenFileGDBLayer::OGROpenFileGDBLayer( const char* pszGDBFilename,
160 const char* pszName,
161 const std::string& osDefinition,
162 const std::string& osDocumentation,
163 const char* /* pszGeomName */,
164 OGRwkbGeometryType eGeomType ) :
165 m_osGDBFilename(pszGDBFilename),
166 m_osName(pszName),
167 m_poLyrTable(nullptr),
168 m_poFeatureDefn(nullptr),
169 m_iGeomFieldIdx(-1),
170 m_iCurFeat(0),
171 m_osDefinition(osDefinition),
172 m_osDocumentation(osDocumentation),
173 m_eGeomType(wkbNone),
174 m_bValidLayerDefn(-1),
175 m_bEOF(FALSE),
176 m_poGeomConverter(nullptr),
177 m_iFieldToReadAsBinary(-1),
178 m_poAttributeIterator(nullptr),
179 m_bIteratorSufficientToEvaluateFilter(FALSE),
180 m_poIterMinMax(nullptr),
181 m_eSpatialIndexState(SPI_IN_BUILDING),
182 m_pQuadTree(nullptr),
183 m_pahFilteredFeatures(nullptr),
184 m_nFilteredFeatureCount(-1)
185 {
186 // TODO(rouault): What error on compiler versions? r33032 does not say.
187
188 // We cannot initialize m_poFeatureDefn in above list. MSVC doesn't like
189 // this to be used in initialization list.
190 m_poFeatureDefn = new OGROpenFileGDBFeatureDefn(this, pszName);
191 SetDescription( m_poFeatureDefn->GetName() );
192 m_poFeatureDefn->SetGeomType(wkbNone);
193 m_poFeatureDefn->Reference();
194
195 m_eGeomType = eGeomType;
196
197 if( !m_osDefinition.empty() )
198 {
199 BuildGeometryColumnGDBv10();
200 }
201 }
202
203 /***********************************************************************/
204 /* ~OGROpenFileGDBLayer() */
205 /***********************************************************************/
206
~OGROpenFileGDBLayer()207 OGROpenFileGDBLayer::~OGROpenFileGDBLayer()
208 {
209 delete m_poLyrTable;
210 if( m_poFeatureDefn )
211 {
212 m_poFeatureDefn->UnsetLayer();
213 m_poFeatureDefn->Release();
214 }
215 delete m_poAttributeIterator;
216 delete m_poIterMinMax;
217 delete m_poGeomConverter;
218 delete m_poSpatialIndexIterator;
219 delete m_poCombinedIterator;
220 if( m_pQuadTree != nullptr )
221 CPLQuadTreeDestroy(m_pQuadTree);
222 CPLFree(m_pahFilteredFeatures);
223 }
224
225 /************************************************************************/
226 /* BuildSRS() */
227 /************************************************************************/
228
BuildSRS(const char * pszWKT)229 static OGRSpatialReference* BuildSRS(const char* pszWKT)
230 {
231 OGRSpatialReference* poSRS = new OGRSpatialReference();
232 poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
233 if( poSRS->importFromWkt(pszWKT) != OGRERR_NONE )
234 {
235 delete poSRS;
236 poSRS = nullptr;
237 }
238 if( poSRS != nullptr )
239 {
240 if( CPLTestBool(CPLGetConfigOption("USE_OSR_FIND_MATCHES", "YES")) )
241 {
242 int nEntries = 0;
243 int* panConfidence = nullptr;
244 OGRSpatialReferenceH* pahSRS =
245 poSRS->FindMatches(nullptr, &nEntries, &panConfidence);
246 if( nEntries == 1 && panConfidence[0] == 100 )
247 {
248 poSRS->Release();
249 poSRS = reinterpret_cast<OGRSpatialReference*>(pahSRS[0]);
250 poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
251 CPLFree(pahSRS);
252 }
253 else
254 {
255 OSRFreeSRSArray(pahSRS);
256 }
257 CPLFree(panConfidence);
258 }
259 else
260 {
261 poSRS->AutoIdentifyEPSG();
262 }
263 }
264 return poSRS;
265 }
266
267 /************************************************************************/
268 /* BuildGeometryColumnGDBv10() */
269 /************************************************************************/
270
BuildGeometryColumnGDBv10()271 int OGROpenFileGDBLayer::BuildGeometryColumnGDBv10()
272 {
273 CPLXMLNode* psTree = CPLParseXMLString(m_osDefinition.c_str());
274 if( psTree == nullptr )
275 {
276 m_osDefinition = "";
277 return FALSE;
278 }
279
280 CPLStripXMLNamespace( psTree, nullptr, TRUE );
281 /* CPLSerializeXMLTreeToFile( psTree, "/dev/stderr" ); */
282 CPLXMLNode* psInfo = CPLSearchXMLNode( psTree, "=DEFeatureClassInfo" );
283 if( psInfo == nullptr )
284 psInfo = CPLSearchXMLNode( psTree, "=DETableInfo" );
285 if( psInfo == nullptr )
286 {
287 m_osDefinition = "";
288 CPLDestroyXMLNode(psTree);
289 return FALSE;
290 }
291
292 m_bTimeInUTC = CPLTestBool(CPLGetXMLValue(psInfo, "IsTimeInUTC", "false"));
293
294 /* We cannot trust the XML definition to build the field definitions. */
295 /* It sometimes misses a few fields ! */
296
297 const bool bHasZ = CPLTestBool(CPLGetXMLValue( psInfo, "HasZ", "NO" ));
298 const bool bHasM = CPLTestBool(CPLGetXMLValue( psInfo, "HasM", "NO" ));
299 const char* pszShapeType = CPLGetXMLValue(psInfo, "ShapeType", nullptr);
300 const char* pszShapeFieldName =
301 CPLGetXMLValue(psInfo, "ShapeFieldName", nullptr);
302 if( pszShapeType != nullptr && pszShapeFieldName != nullptr )
303 {
304 m_eGeomType =
305 FileGDBOGRGeometryConverter::GetGeometryTypeFromESRI(pszShapeType);
306
307 if( EQUAL(pszShapeType, "esriGeometryMultiPatch") )
308 {
309 if( m_poLyrTable == nullptr )
310 {
311 m_poLyrTable = new FileGDBTable();
312 if( !(m_poLyrTable->Open(m_osGDBFilename, GetDescription())) )
313 {
314 delete m_poLyrTable;
315 m_poLyrTable = nullptr;
316 m_bValidLayerDefn = FALSE;
317 }
318 }
319 if( m_poLyrTable != nullptr )
320 {
321 m_iGeomFieldIdx = m_poLyrTable->GetGeomFieldIdx();
322 if( m_iGeomFieldIdx >= 0 )
323 {
324 FileGDBGeomField* poGDBGeomField =
325 reinterpret_cast<FileGDBGeomField *>(
326 m_poLyrTable->GetField(m_iGeomFieldIdx));
327 if( m_poGeomConverter == nullptr )
328 {
329 m_poGeomConverter =
330 FileGDBOGRGeometryConverter::BuildConverter(poGDBGeomField);
331 }
332 TryToDetectMultiPatchKind();
333 }
334 }
335 }
336
337 if( bHasZ )
338 m_eGeomType = wkbSetZ( m_eGeomType );
339 if( bHasM )
340 m_eGeomType = wkbSetM( m_eGeomType );
341
342 const char* pszWKT =
343 CPLGetXMLValue( psInfo, "SpatialReference.WKT", nullptr );
344 const int nWKID =
345 atoi(CPLGetXMLValue( psInfo, "SpatialReference.WKID", "0" ));
346 // The concept of LatestWKID is explained in
347 // http://resources.arcgis.com/en/help/arcgis-rest-api/index.html#//02r3000000n1000000
348 int nLatestWKID = atoi(
349 CPLGetXMLValue( psInfo, "SpatialReference.LatestWKID", "0" ));
350
351 OGROpenFileGDBGeomFieldDefn* poGeomFieldDefn =
352 new OGROpenFileGDBGeomFieldDefn(nullptr, pszShapeFieldName, m_eGeomType);
353
354 CPLXMLNode* psGPFieldInfoExs = CPLGetXMLNode(psInfo, "GPFieldInfoExs");
355 if( psGPFieldInfoExs )
356 {
357 for(CPLXMLNode* psChild = psGPFieldInfoExs->psChild;
358 psChild != nullptr;
359 psChild = psChild->psNext )
360 {
361 if( psChild->eType != CXT_Element )
362 continue;
363 if( EQUAL( psChild->pszValue, "GPFieldInfoEx" ) &&
364 EQUAL( CPLGetXMLValue(psChild, "Name", "" ),
365 pszShapeFieldName ) )
366 {
367 poGeomFieldDefn->SetNullable(
368 CPLTestBool(CPLGetXMLValue(
369 psChild, "IsNullable", "TRUE" )) );
370 break;
371 }
372 }
373 }
374
375 OGRSpatialReference* poSRS = nullptr;
376 if( nWKID > 0 || nLatestWKID > 0 )
377 {
378 int bSuccess = FALSE;
379 poSRS = new OGRSpatialReference();
380 poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
381 CPLPushErrorHandler(CPLQuietErrorHandler);
382 // Try first with nLatestWKID as there is a higher chance it is a
383 // EPSG code and not an ESRI one.
384 if( nLatestWKID > 0 )
385 {
386 if( poSRS->importFromEPSG(nLatestWKID) == OGRERR_NONE )
387 {
388 bSuccess = TRUE;
389 }
390 else
391 {
392 CPLDebug( "OpenFileGDB", "Cannot import SRID %d",
393 nLatestWKID);
394 }
395 }
396 if( !bSuccess && nWKID > 0 )
397 {
398 if( poSRS->importFromEPSG(nWKID) == OGRERR_NONE )
399 {
400 bSuccess = TRUE;
401 }
402 else
403 {
404 CPLDebug("OpenFileGDB", "Cannot import SRID %d", nWKID);
405 }
406 }
407 if( !bSuccess )
408 {
409 delete poSRS;
410 poSRS = nullptr;
411 }
412 CPLPopErrorHandler();
413 CPLErrorReset();
414 }
415 if( poSRS == nullptr && pszWKT != nullptr && pszWKT[0] != '{' )
416 {
417 poSRS = BuildSRS(pszWKT);
418 }
419 if( poSRS != nullptr )
420 {
421 poGeomFieldDefn->SetSpatialRef(poSRS);
422 poSRS->Dereference();
423 }
424 m_poFeatureDefn->AddGeomFieldDefn(poGeomFieldDefn, FALSE);
425 }
426 else
427 {
428 m_eGeomType = wkbNone;
429 }
430 CPLDestroyXMLNode(psTree);
431
432 return TRUE;
433 }
434
435 /************************************************************************/
436 /* TryToDetectMultiPatchKind() */
437 /************************************************************************/
438
439 // If the first and last feature have the same geometry type, then use
440 // it for the whole layer.
TryToDetectMultiPatchKind()441 void OGROpenFileGDBLayer::TryToDetectMultiPatchKind()
442 {
443 CPLAssert( m_poLyrTable != nullptr );
444 CPLAssert( m_iGeomFieldIdx >= 0 );
445
446 if( m_poLyrTable->GetTotalRecordCount() == 0 )
447 return;
448 int nFirstIdx = m_poLyrTable->GetAndSelectNextNonEmptyRow(0);
449 if( nFirstIdx < 0 )
450 return;
451
452 const OGRField* psField = m_poLyrTable->GetFieldValue(m_iGeomFieldIdx);
453 if( psField == nullptr )
454 return;
455 OGRGeometry* poGeom = m_poGeomConverter->GetAsGeometry(psField);
456 if( poGeom == nullptr )
457 return;
458 const OGRwkbGeometryType eType = poGeom->getGeometryType();
459 delete poGeom;
460
461 int nLastIdx = m_poLyrTable->GetTotalRecordCount()-1;
462 const GUInt32 nErrorCount = CPLGetErrorCounter();
463 while( nLastIdx > nFirstIdx &&
464 m_poLyrTable->GetOffsetInTableForRow(nLastIdx) == 0 &&
465 nErrorCount == CPLGetErrorCounter() )
466 {
467 nLastIdx --;
468 }
469 if( nLastIdx > nFirstIdx && m_poLyrTable->SelectRow(nLastIdx) )
470 {
471 psField = m_poLyrTable->GetFieldValue(m_iGeomFieldIdx);
472 if( psField == nullptr )
473 {
474 m_eGeomType = eType;
475 return;
476 }
477 poGeom = m_poGeomConverter->GetAsGeometry(psField);
478 if( poGeom == nullptr )
479 {
480 m_eGeomType = eType;
481 return;
482 }
483 if( eType == poGeom->getGeometryType() )
484 m_eGeomType = eType;
485 delete poGeom;
486 }
487 }
488
489 /************************************************************************/
490 /* BuildLayerDefinition() */
491 /************************************************************************/
492
BuildLayerDefinition()493 int OGROpenFileGDBLayer::BuildLayerDefinition()
494 {
495 if( m_bValidLayerDefn >= 0 )
496 return m_bValidLayerDefn;
497
498 if( m_poLyrTable == nullptr )
499 {
500 m_poLyrTable = new FileGDBTable();
501 if( !(m_poLyrTable->Open(m_osGDBFilename, GetDescription())) )
502 {
503 delete m_poLyrTable;
504 m_poLyrTable = nullptr;
505 m_bValidLayerDefn = FALSE;
506 return FALSE;
507 }
508 }
509
510 m_bValidLayerDefn = TRUE;
511
512 m_iGeomFieldIdx = m_poLyrTable->GetGeomFieldIdx();
513 if( m_iGeomFieldIdx >= 0 )
514 {
515 FileGDBGeomField* poGDBGeomField =
516 reinterpret_cast<FileGDBGeomField *>(
517 m_poLyrTable->GetField(m_iGeomFieldIdx));
518 if( m_poGeomConverter == nullptr )
519 {
520 m_poGeomConverter =
521 FileGDBOGRGeometryConverter::BuildConverter(poGDBGeomField);
522 }
523
524 if( !(m_poLyrTable->HasSpatialIndex() &&
525 CPLTestBool(
526 CPLGetConfigOption("OPENFILEGDB_USE_SPATIAL_INDEX", "YES"))) &&
527 CPLTestBool(
528 CPLGetConfigOption("OPENFILEGDB_IN_MEMORY_SPI", "YES")) )
529 {
530 CPLRectObj sGlobalBounds;
531 sGlobalBounds.minx = poGDBGeomField->GetXMin();
532 sGlobalBounds.miny = poGDBGeomField->GetYMin();
533 sGlobalBounds.maxx = poGDBGeomField->GetXMax();
534 sGlobalBounds.maxy = poGDBGeomField->GetYMax();
535 m_pQuadTree = CPLQuadTreeCreate(&sGlobalBounds,
536 nullptr);
537 CPLQuadTreeSetMaxDepth(m_pQuadTree,
538 CPLQuadTreeGetAdvisedMaxDepth(
539 m_poLyrTable->GetValidRecordCount()));
540 }
541 else
542 {
543 m_eSpatialIndexState = SPI_INVALID;
544 }
545 }
546
547 if( m_iGeomFieldIdx >= 0 &&
548 (m_osDefinition.empty() ||
549 m_poFeatureDefn->OGRFeatureDefn::GetGeomFieldCount() == 0) )
550 {
551 /* FileGDB v9 case */
552 FileGDBGeomField* poGDBGeomField = reinterpret_cast<FileGDBGeomField *>(
553 m_poLyrTable->GetField(m_iGeomFieldIdx) );
554 const char* pszName = poGDBGeomField->GetName().c_str();
555 const FileGDBTableGeometryType eGDBGeomType =
556 m_poLyrTable->GetGeometryType();
557
558 OGRwkbGeometryType eGeomType = wkbUnknown;
559 switch( eGDBGeomType )
560 {
561 case FGTGT_NONE: /* doesn't make sense ! */ break;
562 case FGTGT_POINT: eGeomType = wkbPoint; break;
563 case FGTGT_MULTIPOINT: eGeomType = wkbMultiPoint; break;
564 case FGTGT_LINE: eGeomType = wkbMultiLineString; break;
565 case FGTGT_POLYGON: eGeomType = wkbMultiPolygon; break;
566 case FGTGT_MULTIPATCH: eGeomType = wkbUnknown; break;
567 }
568
569 if( m_eGeomType != wkbUnknown && wkbFlatten(eGeomType) != wkbFlatten(m_eGeomType) )
570 {
571 CPLError(CE_Warning, CPLE_AppDefined,
572 "Inconsistency for layer geometry type");
573 }
574
575 m_eGeomType = eGeomType;
576
577 if( eGDBGeomType == FGTGT_MULTIPATCH )
578 {
579 TryToDetectMultiPatchKind();
580 }
581
582 if( m_poLyrTable->GetGeomTypeHasZ() )
583 m_eGeomType = wkbSetZ(m_eGeomType);
584
585 if( m_poLyrTable->GetGeomTypeHasM() )
586 m_eGeomType = wkbSetM(m_eGeomType);
587
588 OGROpenFileGDBGeomFieldDefn* poGeomFieldDefn =
589 new OGROpenFileGDBGeomFieldDefn(nullptr, pszName, m_eGeomType);
590 poGeomFieldDefn->SetNullable(poGDBGeomField->IsNullable());
591
592 m_poFeatureDefn->AddGeomFieldDefn(poGeomFieldDefn, FALSE);
593
594 OGRSpatialReference* poSRS = nullptr;
595 if( !poGDBGeomField->GetWKT().empty() &&
596 poGDBGeomField->GetWKT()[0] != '{' )
597 {
598 poSRS = BuildSRS( poGDBGeomField->GetWKT().c_str() );
599 }
600 if( poSRS != nullptr )
601 {
602 poGeomFieldDefn->SetSpatialRef(poSRS);
603 poSRS->Dereference();
604 }
605 }
606 else if( m_osDefinition.empty() && m_iGeomFieldIdx < 0 )
607 {
608 m_eGeomType = wkbNone;
609 }
610
611 CPLXMLTreeCloser oTree(nullptr);
612 const CPLXMLNode* psGPFieldInfoExs = nullptr;
613
614 if( !m_osDefinition.empty() )
615 {
616 oTree.reset(CPLParseXMLString(m_osDefinition.c_str()));
617 if( oTree != nullptr )
618 {
619 CPLStripXMLNamespace( oTree.get(), nullptr, TRUE );
620 CPLXMLNode* psInfo =
621 CPLSearchXMLNode( oTree.get(), "=DEFeatureClassInfo" );
622 if( psInfo == nullptr )
623 psInfo = CPLSearchXMLNode( oTree.get(), "=DETableInfo" );
624 if( psInfo != nullptr )
625 psGPFieldInfoExs =
626 CPLGetXMLNode(psInfo, "GPFieldInfoExs");
627 }
628 }
629
630 for(int i=0;i<m_poLyrTable->GetFieldCount();i++)
631 {
632 if( i == m_iGeomFieldIdx )
633 continue;
634
635 const FileGDBField* poGDBField = m_poLyrTable->GetField(i);
636 OGRFieldType eType = OFTString;
637 OGRFieldSubType eSubType = OFSTNone;
638 int nWidth = poGDBField->GetMaxWidth();
639 switch( poGDBField->GetType() )
640 {
641 case FGFT_INT16:
642 eType = OFTInteger;
643 eSubType = OFSTInt16;
644 break;
645 case FGFT_INT32:
646 eType = OFTInteger;
647 break;
648 case FGFT_FLOAT32:
649 eType = OFTReal;
650 eSubType = OFSTFloat32;
651 break;
652 case FGFT_FLOAT64:
653 eType = OFTReal;
654 break;
655 case FGFT_STRING:
656 /* nWidth = poGDBField->GetMaxWidth(); */
657 eType = OFTString;
658 break;
659 case FGFT_UUID_1:
660 case FGFT_UUID_2:
661 case FGFT_XML:
662 eType = OFTString;
663 break;
664 case FGFT_DATETIME:
665 eType = OFTDateTime;
666 break;
667 case FGFT_UNDEFINED:
668 case FGFT_OBJECTID:
669 case FGFT_GEOMETRY:
670 CPLAssert(false);
671 break;
672 case FGFT_BINARY:
673 {
674 /* Special case for v9 GDB_UserMetadata table */
675 if( m_iFieldToReadAsBinary < 0 &&
676 poGDBField->GetName() == "Xml" &&
677 poGDBField->GetType() == FGFT_BINARY )
678 {
679 m_iFieldToReadAsBinary = i;
680 eType = OFTString;
681 }
682 else
683 {
684 eType = OFTBinary;
685 }
686 break;
687 }
688 case FGFT_RASTER:
689 {
690 const FileGDBRasterField* rasterField = cpl::down_cast<const FileGDBRasterField*>(poGDBField);
691 if( rasterField->IsManaged() )
692 eType = OFTInteger;
693 else
694 eType = OFTString;
695 break;
696 }
697 }
698 OGRFieldDefn oFieldDefn(poGDBField->GetName().c_str(), eType);
699 oFieldDefn.SetAlternativeName(poGDBField->GetAlias().c_str());
700 oFieldDefn.SetSubType(eSubType);
701 // On creation in the FileGDB driver (GDBFieldTypeToWidthPrecision) if
702 // string width is 0, we pick up 65535 by default to mean unlimited
703 // string length, but we do not want to advertise such a big number.
704 if( eType == OFTString && nWidth < 65535 )
705 oFieldDefn.SetWidth(nWidth);
706 oFieldDefn.SetNullable(poGDBField->IsNullable());
707
708 const CPLXMLNode* psFieldDef = nullptr;
709 if( psGPFieldInfoExs != nullptr )
710 {
711 for(const CPLXMLNode* psChild = psGPFieldInfoExs->psChild;
712 psChild != nullptr;
713 psChild = psChild->psNext )
714 {
715 if( psChild->eType != CXT_Element )
716 continue;
717 if( EQUAL( psChild->pszValue, "GPFieldInfoEx") &&
718 EQUAL( CPLGetXMLValue(psChild, "Name", ""),
719 poGDBField->GetName().c_str()) )
720 {
721 psFieldDef = psChild;
722 break;
723 }
724 }
725 }
726
727 const OGRField* psDefault = poGDBField->GetDefault();
728 if( !OGR_RawField_IsUnset(psDefault) && !OGR_RawField_IsNull(psDefault) )
729 {
730 if( eType == OFTString )
731 {
732 CPLString osDefault("'");
733 char* pszTmp = CPLEscapeString(psDefault->String, -1, CPLES_SQL);
734 osDefault += pszTmp;
735 CPLFree(pszTmp);
736 osDefault += "'";
737 oFieldDefn.SetDefault(osDefault);
738 }
739 else if( eType == OFTInteger || eType == OFTReal )
740 {
741 // GDBs and the FileGDB SDK are not always reliable for
742 // numeric values It often occurs that the XML definition in
743 // a00000004.gdbtable does not match the default values (in
744 // binary) found in the field definition section of the
745 // .gdbtable of the layers themselves So check consistency.
746
747 const char* pszDefaultValue = nullptr;
748 if( psFieldDef )
749 {
750 // From ArcGIS this is called DefaultValueNumeric
751 // for integer and real.
752 // From FileGDB API this is
753 // called DefaultValue xsi:type=xs:int for integer
754 // and DefaultValueNumeric for real ...
755 pszDefaultValue =
756 CPLGetXMLValue( psFieldDef, "DefaultValueNumeric",
757 nullptr );
758 if( pszDefaultValue == nullptr )
759 pszDefaultValue =
760 CPLGetXMLValue( psFieldDef, "DefaultValue",
761 nullptr );
762 }
763 if( pszDefaultValue != nullptr )
764 {
765 if( eType == OFTInteger )
766 {
767 if ( atoi(pszDefaultValue) != psDefault->Integer)
768 {
769 CPLDebug(
770 "OpenFileGDB",
771 "For field %s, XML definition mentions %s "
772 "as default value whereas .gdbtable header "
773 "mentions %d. Using %s",
774 poGDBField->GetName().c_str(),
775 pszDefaultValue,
776 psDefault->Integer,
777 pszDefaultValue );
778 }
779 oFieldDefn.SetDefault(pszDefaultValue);
780 }
781 else if( eType == OFTReal )
782 {
783 if( fabs(CPLAtof(pszDefaultValue) - psDefault->Real) >
784 1e-15 )
785 {
786 CPLDebug(
787 "OpenFileGDB", "For field %s, XML definition "
788 "mentions %s as default value whereas "
789 ".gdbtable header mentions %.18g. Using %s",
790 poGDBField->GetName().c_str(),
791 pszDefaultValue,
792 psDefault->Real,
793 pszDefaultValue );
794 }
795 oFieldDefn.SetDefault(pszDefaultValue);
796 }
797 }
798 }
799 else if( eType == OFTDateTime )
800 oFieldDefn.SetDefault(
801 CPLSPrintf( "'%04d/%02d/%02d %02d:%02d:%02d'",
802 psDefault->Date.Year,
803 psDefault->Date.Month,
804 psDefault->Date.Day,
805 psDefault->Date.Hour,
806 psDefault->Date.Minute,
807 static_cast<int>(psDefault->Date.Second) ));
808 }
809
810 if( psFieldDef )
811 {
812 const char* pszDomainName = CPLGetXMLValue(psFieldDef, "DomainName", nullptr);
813 if( pszDomainName )
814 oFieldDefn.SetDomainName(pszDomainName);
815 }
816
817 m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
818 }
819
820 if( m_poLyrTable->HasDeletedFeaturesListed() )
821 {
822 OGRFieldDefn oFieldDefn("_deleted_", OFTInteger);
823 m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
824 }
825
826 return TRUE;
827 }
828
829 /************************************************************************/
830 /* GetGeomType() */
831 /************************************************************************/
832
GetGeomType()833 OGRwkbGeometryType OGROpenFileGDBLayer::GetGeomType()
834 {
835 if( m_eGeomType == wkbUnknown ||
836 m_osDefinition.empty() /* FileGDB v9 case */ )
837 {
838 (void) BuildLayerDefinition();
839 }
840
841 return m_eGeomType;
842 }
843
844 /***********************************************************************/
845 /* GetLayerDefn() */
846 /***********************************************************************/
847
GetLayerDefn()848 OGRFeatureDefn* OGROpenFileGDBLayer::GetLayerDefn()
849 {
850 return m_poFeatureDefn;
851 }
852
853 /***********************************************************************/
854 /* GetFIDColumn() */
855 /***********************************************************************/
856
GetFIDColumn()857 const char* OGROpenFileGDBLayer::GetFIDColumn()
858 {
859 if( !BuildLayerDefinition() )
860 return "";
861 return m_poLyrTable->GetObjectIdColName().c_str();
862 }
863
864 /***********************************************************************/
865 /* ResetReading() */
866 /***********************************************************************/
867
ResetReading()868 void OGROpenFileGDBLayer::ResetReading()
869 {
870 if( m_iCurFeat != 0 )
871 {
872 if( m_eSpatialIndexState == SPI_IN_BUILDING )
873 m_eSpatialIndexState = SPI_INVALID;
874 }
875 m_bEOF = FALSE;
876 m_iCurFeat = 0;
877 if( m_poAttributeIterator )
878 m_poAttributeIterator->Reset();
879 if( m_poSpatialIndexIterator )
880 m_poSpatialIndexIterator->Reset();
881 if( m_poCombinedIterator )
882 m_poCombinedIterator->Reset();
883 }
884
885 /***********************************************************************/
886 /* SetSpatialFilter() */
887 /***********************************************************************/
888
SetSpatialFilter(OGRGeometry * poGeom)889 void OGROpenFileGDBLayer::SetSpatialFilter( OGRGeometry *poGeom )
890 {
891 if( !BuildLayerDefinition() )
892 return;
893
894 OGRLayer::SetSpatialFilter(poGeom);
895
896 if( m_bFilterIsEnvelope )
897 {
898 OGREnvelope sLayerEnvelope;
899 if( GetExtent(&sLayerEnvelope, FALSE) == OGRERR_NONE )
900 {
901 if( m_sFilterEnvelope.MinX <= sLayerEnvelope.MinX &&
902 m_sFilterEnvelope.MinY <= sLayerEnvelope.MinY &&
903 m_sFilterEnvelope.MaxX >= sLayerEnvelope.MaxX &&
904 m_sFilterEnvelope.MaxY >= sLayerEnvelope.MaxY )
905 {
906 #ifdef DEBUG
907 CPLDebug("OpenFileGDB", "Disabling spatial filter since it "
908 "contains the layer spatial extent");
909 #endif
910 poGeom = nullptr;
911 OGRLayer::SetSpatialFilter(poGeom);
912 }
913 }
914 }
915
916 if( poGeom != nullptr )
917 {
918 if( m_poSpatialIndexIterator == nullptr &&
919 m_poLyrTable->HasSpatialIndex() &&
920 CPLTestBool(
921 CPLGetConfigOption("OPENFILEGDB_USE_SPATIAL_INDEX", "YES")) )
922 {
923 m_poSpatialIndexIterator = FileGDBSpatialIndexIterator::Build(
924 m_poLyrTable, m_sFilterEnvelope);
925 }
926 else if( m_poSpatialIndexIterator != nullptr )
927 {
928 if( !m_poSpatialIndexIterator->SetEnvelope(m_sFilterEnvelope) )
929 {
930 delete m_poSpatialIndexIterator;
931 m_poSpatialIndexIterator = nullptr;
932 }
933 }
934 else if( m_eSpatialIndexState == SPI_COMPLETED )
935 {
936 CPLRectObj aoi;
937 aoi.minx = m_sFilterEnvelope.MinX;
938 aoi.miny = m_sFilterEnvelope.MinY;
939 aoi.maxx = m_sFilterEnvelope.MaxX;
940 aoi.maxy = m_sFilterEnvelope.MaxY;
941 CPLFree(m_pahFilteredFeatures);
942 m_nFilteredFeatureCount = -1;
943 m_pahFilteredFeatures = CPLQuadTreeSearch(m_pQuadTree,
944 &aoi,
945 &m_nFilteredFeatureCount);
946 if( m_nFilteredFeatureCount >= 0 )
947 {
948 size_t* panStart = (size_t*)m_pahFilteredFeatures;
949 std::sort(panStart, panStart + m_nFilteredFeatureCount);
950 }
951 }
952
953 m_poLyrTable->InstallFilterEnvelope(&m_sFilterEnvelope);
954 }
955 else
956 {
957 delete m_poSpatialIndexIterator;
958 m_poSpatialIndexIterator = nullptr;
959 CPLFree(m_pahFilteredFeatures);
960 m_pahFilteredFeatures = nullptr;
961 m_nFilteredFeatureCount = -1;
962 m_poLyrTable->InstallFilterEnvelope(nullptr);
963 }
964
965 BuildCombinedIterator();
966 }
967
968 /***********************************************************************/
969 /* CompValues() */
970 /***********************************************************************/
971
CompValues(OGRFieldDefn * poFieldDefn,const swq_expr_node * poValue1,const swq_expr_node * poValue2)972 static int CompValues(OGRFieldDefn* poFieldDefn,
973 const swq_expr_node* poValue1,
974 const swq_expr_node* poValue2)
975 {
976 int ret = 0;
977 switch( poFieldDefn->GetType() )
978 {
979 case OFTInteger:
980 {
981 int n1, n2;
982 if (poValue1->field_type == SWQ_FLOAT)
983 n1 = (int) poValue1->float_value;
984 else
985 n1 = (int) poValue1->int_value;
986 if (poValue2->field_type == SWQ_FLOAT)
987 n2 = (int) poValue2->float_value;
988 else
989 n2 = (int) poValue2->int_value;
990 if( n1 < n2 )
991 ret = -1;
992 else if( n1 == n2 )
993 ret = 0;
994 else
995 ret = 1;
996 break;
997 }
998
999 case OFTReal:
1000 if( poValue1->float_value < poValue2->float_value )
1001 ret = -1;
1002 else if( poValue1->float_value == poValue2->float_value )
1003 ret = 0;
1004 else
1005 ret = 1;
1006 break;
1007
1008 case OFTString:
1009 ret = strcmp(poValue1->string_value, poValue2->string_value);
1010 break;
1011
1012 case OFTDate:
1013 case OFTTime:
1014 case OFTDateTime:
1015 {
1016 if ((poValue1->field_type == SWQ_TIMESTAMP ||
1017 poValue1->field_type == SWQ_DATE ||
1018 poValue1->field_type == SWQ_TIME) &&
1019 (poValue2->field_type == SWQ_TIMESTAMP ||
1020 poValue2->field_type == SWQ_DATE ||
1021 poValue2->field_type == SWQ_TIME))
1022 {
1023 ret = strcmp(poValue1->string_value, poValue2->string_value);
1024 }
1025 break;
1026 }
1027
1028 default:
1029 break;
1030 }
1031 return ret;
1032 }
1033
1034 /***********************************************************************/
1035 /* OGROpenFileGDBIsComparisonOp() */
1036 /***********************************************************************/
1037
OGROpenFileGDBIsComparisonOp(int op)1038 int OGROpenFileGDBIsComparisonOp(int op)
1039 {
1040 return (op == SWQ_EQ || op == SWQ_NE || op == SWQ_LT ||
1041 op == SWQ_LE || op == SWQ_GT || op == SWQ_GE);
1042 }
1043
1044 /***********************************************************************/
1045 /* AreExprExclusive() */
1046 /***********************************************************************/
1047
1048 static const struct
1049 {
1050 swq_op op1;
1051 swq_op op2;
1052 int expected_comp_1;
1053 int expected_comp_2;
1054 }
1055 asPairsOfComparisons[] =
1056 {
1057 { SWQ_EQ, SWQ_EQ, -1, 1 },
1058 { SWQ_LT, SWQ_GT, -1, 0 },
1059 { SWQ_GT, SWQ_LT, 0, 1 },
1060 { SWQ_LT, SWQ_GE, -1, 999 },
1061 { SWQ_LE, SWQ_GE, -1, 999 },
1062 { SWQ_LE, SWQ_GT, -1, 999 },
1063 { SWQ_GE, SWQ_LE, 1, 999 },
1064 { SWQ_GE, SWQ_LT, 1, 999 },
1065 { SWQ_GT, SWQ_LE, 1, 999 }
1066 };
1067
AreExprExclusive(OGRFeatureDefn * poFeatureDefn,const swq_expr_node * poNode1,const swq_expr_node * poNode2)1068 static int AreExprExclusive(OGRFeatureDefn* poFeatureDefn,
1069 const swq_expr_node* poNode1,
1070 const swq_expr_node* poNode2)
1071 {
1072 if( poNode1->eNodeType != SNT_OPERATION )
1073 return FALSE;
1074 if( poNode2->eNodeType != SNT_OPERATION )
1075 return FALSE;
1076
1077 const size_t nPairs = sizeof(asPairsOfComparisons) /
1078 sizeof(asPairsOfComparisons[0]);
1079 for(size_t i = 0; i < nPairs; i++ )
1080 {
1081 if( poNode1->nOperation == asPairsOfComparisons[i].op1 &&
1082 poNode2->nOperation == asPairsOfComparisons[i].op2 &&
1083 poNode1->nSubExprCount == 2 &&
1084 poNode2->nSubExprCount == 2 )
1085 {
1086 swq_expr_node *poColumn1 = poNode1->papoSubExpr[0];
1087 swq_expr_node *poValue1 = poNode1->papoSubExpr[1];
1088 swq_expr_node *poColumn2 = poNode2->papoSubExpr[0];
1089 swq_expr_node *poValue2 = poNode2->papoSubExpr[1];
1090 if( poColumn1->eNodeType == SNT_COLUMN &&
1091 poValue1->eNodeType == SNT_CONSTANT &&
1092 poColumn2->eNodeType == SNT_COLUMN &&
1093 poValue2->eNodeType == SNT_CONSTANT &&
1094 poColumn1->field_index == poColumn2->field_index &&
1095 poColumn1->field_index < poFeatureDefn->GetFieldCount() )
1096 {
1097 OGRFieldDefn *poFieldDefn =
1098 poFeatureDefn->GetFieldDefn(poColumn1->field_index);
1099
1100 const int nComp = CompValues(poFieldDefn, poValue1, poValue2);
1101 return nComp == asPairsOfComparisons[i].expected_comp_1 ||
1102 nComp == asPairsOfComparisons[i].expected_comp_2;
1103 }
1104 return FALSE;
1105 }
1106 }
1107
1108 if( (poNode2->nOperation == SWQ_ISNULL &&
1109 OGROpenFileGDBIsComparisonOp(poNode1->nOperation) &&
1110 poNode1->nSubExprCount == 2 &&
1111 poNode2->nSubExprCount == 1) ||
1112 (poNode1->nOperation == SWQ_ISNULL &&
1113 OGROpenFileGDBIsComparisonOp(poNode2->nOperation) &&
1114 poNode2->nSubExprCount == 2 &&
1115 poNode1->nSubExprCount == 1))
1116 {
1117 swq_expr_node *poColumn1 = poNode1->papoSubExpr[0];
1118 swq_expr_node *poColumn2 = poNode2->papoSubExpr[0];
1119 if( poColumn1->eNodeType == SNT_COLUMN &&
1120 poColumn2->eNodeType == SNT_COLUMN &&
1121 poColumn1->field_index == poColumn2->field_index &&
1122 poColumn1->field_index < poFeatureDefn->GetFieldCount() )
1123 {
1124 return TRUE;
1125 }
1126 }
1127
1128 /* In doubt: return FALSE */
1129 return FALSE;
1130 }
1131
1132 /***********************************************************************/
1133 /* FillTargetValueFromSrcExpr() */
1134 /***********************************************************************/
1135
1136 static
FillTargetValueFromSrcExpr(OGRFieldDefn * poFieldDefn,OGRField * poTargetValue,const swq_expr_node * poSrcValue)1137 int FillTargetValueFromSrcExpr( OGRFieldDefn* poFieldDefn,
1138 OGRField* poTargetValue,
1139 const swq_expr_node* poSrcValue )
1140 {
1141 switch( poFieldDefn->GetType() )
1142 {
1143 case OFTInteger:
1144 if (poSrcValue->field_type == SWQ_FLOAT)
1145 poTargetValue->Integer = (int) poSrcValue->float_value;
1146 else
1147 poTargetValue->Integer = (int) poSrcValue->int_value;
1148 break;
1149
1150 case OFTReal:
1151 poTargetValue->Real = poSrcValue->float_value;
1152 break;
1153
1154 case OFTString:
1155 poTargetValue->String = poSrcValue->string_value;
1156 break;
1157
1158 case OFTDate:
1159 case OFTTime:
1160 case OFTDateTime:
1161 if (poSrcValue->field_type == SWQ_TIMESTAMP ||
1162 poSrcValue->field_type == SWQ_DATE ||
1163 poSrcValue->field_type == SWQ_TIME)
1164 {
1165 int nYear = 0, nMonth = 0, nDay = 0, nHour = 0, nMin = 0, nSec = 0;
1166 if( sscanf(poSrcValue->string_value, "%04d/%02d/%02d %02d:%02d:%02d",
1167 &nYear, &nMonth, &nDay, &nHour, &nMin, &nSec) == 6 ||
1168 sscanf(poSrcValue->string_value, "%04d/%02d/%02d",
1169 &nYear, &nMonth, &nDay) == 3 ||
1170 sscanf(poSrcValue->string_value, "%02d:%02d:%02d",
1171 &nHour, &nMin, &nSec) == 3 )
1172 {
1173 poTargetValue->Date.Year = (GInt16)nYear;
1174 poTargetValue->Date.Month = (GByte)nMonth;
1175 poTargetValue->Date.Day = (GByte)nDay;
1176 poTargetValue->Date.Hour = (GByte)nHour;
1177 poTargetValue->Date.Minute = (GByte)nMin;
1178 poTargetValue->Date.Second = (GByte)nSec;
1179 poTargetValue->Date.TZFlag = 0;
1180 poTargetValue->Date.Reserved = 0;
1181 }
1182 else
1183 return FALSE;
1184 }
1185 else
1186 return FALSE;
1187 break;
1188
1189 default:
1190 return FALSE;
1191 }
1192 return TRUE;
1193 }
1194
1195 /***********************************************************************/
1196 /* GetColumnSubNode() */
1197 /***********************************************************************/
1198
GetColumnSubNode(swq_expr_node * poNode)1199 static swq_expr_node* GetColumnSubNode(swq_expr_node* poNode)
1200 {
1201 if( poNode->eNodeType == SNT_OPERATION &&
1202 poNode->nSubExprCount == 2 )
1203 {
1204 if( poNode->papoSubExpr[0]->eNodeType == SNT_COLUMN )
1205 return poNode->papoSubExpr[0];
1206 if( poNode->papoSubExpr[1]->eNodeType == SNT_COLUMN )
1207 return poNode->papoSubExpr[1];
1208 }
1209 return nullptr;
1210 }
1211
1212 /***********************************************************************/
1213 /* GetConstantSubNode() */
1214 /***********************************************************************/
1215
GetConstantSubNode(swq_expr_node * poNode)1216 static swq_expr_node* GetConstantSubNode(swq_expr_node* poNode)
1217 {
1218 if( poNode->eNodeType == SNT_OPERATION &&
1219 poNode->nSubExprCount == 2 )
1220 {
1221 if( poNode->papoSubExpr[1]->eNodeType == SNT_CONSTANT )
1222 return poNode->papoSubExpr[1];
1223 if( poNode->papoSubExpr[0]->eNodeType == SNT_CONSTANT )
1224 return poNode->papoSubExpr[0];
1225 }
1226 return nullptr;
1227 }
1228 /***********************************************************************/
1229 /* BuildIteratorFromExprNode() */
1230 /***********************************************************************/
1231
BuildIteratorFromExprNode(swq_expr_node * poNode)1232 FileGDBIterator* OGROpenFileGDBLayer::BuildIteratorFromExprNode(swq_expr_node* poNode)
1233 {
1234 if( m_bIteratorSufficientToEvaluateFilter == FALSE )
1235 return nullptr;
1236
1237 if( poNode->eNodeType == SNT_OPERATION &&
1238 poNode->nOperation == SWQ_AND && poNode->nSubExprCount == 2 )
1239 {
1240 // Even if there is only one branch of the 2 that results to an
1241 // iterator, it is useful. Of course, the iterator will not be
1242 // sufficient to evaluatethe filter, but it will be a super-set of the
1243 // features
1244 FileGDBIterator* poIter1 =
1245 BuildIteratorFromExprNode(poNode->papoSubExpr[0]);
1246
1247 /* In case the first branch didn't result to an iterator, temporarily */
1248 /* restore the flag */
1249 const bool bSaveIteratorSufficientToEvaluateFilter =
1250 CPL_TO_BOOL(m_bIteratorSufficientToEvaluateFilter);
1251 m_bIteratorSufficientToEvaluateFilter = -1;
1252 FileGDBIterator* poIter2 = BuildIteratorFromExprNode(poNode->papoSubExpr[1]);
1253 m_bIteratorSufficientToEvaluateFilter = bSaveIteratorSufficientToEvaluateFilter;
1254
1255 if( poIter1 != nullptr && poIter2 != nullptr )
1256 return FileGDBIterator::BuildAnd(poIter1, poIter2, true);
1257 m_bIteratorSufficientToEvaluateFilter = FALSE;
1258 if( poIter1 != nullptr )
1259 return poIter1;
1260 if( poIter2 != nullptr )
1261 return poIter2;
1262 }
1263
1264 else if( poNode->eNodeType == SNT_OPERATION &&
1265 poNode->nOperation == SWQ_OR && poNode->nSubExprCount == 2 )
1266 {
1267 /* For a OR, we need an iterator for the 2 branches */
1268 FileGDBIterator* poIter1 = BuildIteratorFromExprNode(poNode->papoSubExpr[0]);
1269 if( poIter1 != nullptr )
1270 {
1271 FileGDBIterator* poIter2 = BuildIteratorFromExprNode(poNode->papoSubExpr[1]);
1272 if( poIter2 == nullptr )
1273 {
1274 delete poIter1;
1275 }
1276 else
1277 {
1278 return FileGDBIterator::BuildOr(poIter1, poIter2,
1279 AreExprExclusive(GetLayerDefn(),
1280 poNode->papoSubExpr[0],
1281 poNode->papoSubExpr[1]));
1282 }
1283 }
1284 }
1285
1286 else if( poNode->eNodeType == SNT_OPERATION &&
1287 OGROpenFileGDBIsComparisonOp(poNode->nOperation) &&
1288 poNode->nSubExprCount == 2 )
1289 {
1290 swq_expr_node *poColumn = GetColumnSubNode(poNode);
1291 swq_expr_node *poValue = GetConstantSubNode(poNode);
1292 if( poColumn != nullptr && poValue != nullptr &&
1293 poColumn->field_index < GetLayerDefn()->GetFieldCount())
1294 {
1295 OGRFieldDefn *poFieldDefn =
1296 GetLayerDefn()->GetFieldDefn(poColumn->field_index);
1297
1298 int nTableColIdx = m_poLyrTable->GetFieldIdx(poFieldDefn->GetNameRef());
1299 if( nTableColIdx >= 0 && m_poLyrTable->GetField(nTableColIdx)->HasIndex() )
1300 {
1301 OGRField sValue;
1302
1303 if( FillTargetValueFromSrcExpr(poFieldDefn, &sValue, poValue) )
1304 {
1305 FileGDBSQLOp eOp = FGSO_EQ;
1306 CPL_IGNORE_RET_VAL(eOp);
1307 if( poColumn == poNode->papoSubExpr[0] )
1308 {
1309 switch( poNode->nOperation )
1310 {
1311 case SWQ_LE: eOp = FGSO_LE; break;
1312 case SWQ_LT: eOp = FGSO_LT; break;
1313 case SWQ_NE: eOp = FGSO_EQ; /* yes : EQ */ break;
1314 case SWQ_EQ: eOp = FGSO_EQ; break;
1315 case SWQ_GE: eOp = FGSO_GE; break;
1316 case SWQ_GT: eOp = FGSO_GT; break;
1317 default: CPLAssert(false); break;
1318 }
1319 }
1320 else
1321 {
1322 /* If "constant op column", then we must reverse */
1323 /* the operator */
1324 switch( poNode->nOperation )
1325 {
1326 case SWQ_LE: eOp = FGSO_GE; break;
1327 case SWQ_LT: eOp = FGSO_GT; break;
1328 case SWQ_NE: eOp = FGSO_EQ; /* yes : EQ */ break;
1329 case SWQ_EQ: eOp = FGSO_EQ; break;
1330 case SWQ_GE: eOp = FGSO_LE; break;
1331 case SWQ_GT: eOp = FGSO_LT; break;
1332 default: CPLAssert(false); break;
1333 }
1334 }
1335
1336 FileGDBIterator* poIter = FileGDBIterator::Build(
1337 m_poLyrTable, nTableColIdx, TRUE,
1338 eOp, poFieldDefn->GetType(), &sValue);
1339 if( poIter != nullptr )
1340 m_bIteratorSufficientToEvaluateFilter = TRUE;
1341 if( poIter && poNode->nOperation == SWQ_NE )
1342 return FileGDBIterator::BuildNot(poIter);
1343 else
1344 return poIter;
1345 }
1346 }
1347 }
1348 }
1349 else if( poNode->eNodeType == SNT_OPERATION &&
1350 poNode->nOperation == SWQ_ISNULL && poNode->nSubExprCount == 1 )
1351 {
1352 swq_expr_node *poColumn = poNode->papoSubExpr[0];
1353 if( poColumn->eNodeType == SNT_COLUMN &&
1354 poColumn->field_index < GetLayerDefn()->GetFieldCount() )
1355 {
1356 OGRFieldDefn *poFieldDefn =
1357 GetLayerDefn()->GetFieldDefn(poColumn->field_index);
1358
1359 int nTableColIdx = m_poLyrTable->GetFieldIdx(poFieldDefn->GetNameRef());
1360 if( nTableColIdx >= 0 && m_poLyrTable->GetField(nTableColIdx)->HasIndex() )
1361 {
1362 FileGDBIterator* poIter = FileGDBIterator::BuildIsNotNull(
1363 m_poLyrTable, nTableColIdx, TRUE);
1364 if( poIter )
1365 {
1366 m_bIteratorSufficientToEvaluateFilter = TRUE;
1367 poIter = FileGDBIterator::BuildNot(poIter);
1368 }
1369 return poIter;
1370 }
1371 }
1372 }
1373 else if( poNode->eNodeType == SNT_OPERATION &&
1374 poNode->nOperation == SWQ_NOT && poNode->nSubExprCount == 1 &&
1375 poNode->papoSubExpr[0]->eNodeType == SNT_OPERATION &&
1376 poNode->papoSubExpr[0]->nOperation == SWQ_ISNULL &&
1377 poNode->papoSubExpr[0]->nSubExprCount == 1 )
1378 {
1379 swq_expr_node *poColumn = poNode->papoSubExpr[0]->papoSubExpr[0];
1380 if( poColumn->eNodeType == SNT_COLUMN &&
1381 poColumn->field_index < GetLayerDefn()->GetFieldCount() )
1382 {
1383 OGRFieldDefn *poFieldDefn =
1384 GetLayerDefn()->GetFieldDefn(poColumn->field_index);
1385
1386 int nTableColIdx = m_poLyrTable->GetFieldIdx(poFieldDefn->GetNameRef());
1387 if( nTableColIdx >= 0 && m_poLyrTable->GetField(nTableColIdx)->HasIndex() )
1388 {
1389 FileGDBIterator* poIter = FileGDBIterator::BuildIsNotNull(
1390 m_poLyrTable, nTableColIdx, TRUE);
1391 if( poIter )
1392 m_bIteratorSufficientToEvaluateFilter = TRUE;
1393 return poIter;
1394 }
1395 }
1396 }
1397 else if( poNode->eNodeType == SNT_OPERATION &&
1398 poNode->nOperation == SWQ_IN && poNode->nSubExprCount >= 2 )
1399 {
1400 swq_expr_node *poColumn = poNode->papoSubExpr[0];
1401 if( poColumn->eNodeType == SNT_COLUMN &&
1402 poColumn->field_index < GetLayerDefn()->GetFieldCount() )
1403 {
1404 bool bAllConstants = true;
1405 for( int i=1; i<poNode->nSubExprCount; i++ )
1406 {
1407 if( poNode->papoSubExpr[i]->eNodeType != SNT_CONSTANT )
1408 bAllConstants = false;
1409 }
1410 OGRFieldDefn *poFieldDefn =
1411 GetLayerDefn()->GetFieldDefn(poColumn->field_index);
1412
1413 int nTableColIdx =
1414 m_poLyrTable->GetFieldIdx(poFieldDefn->GetNameRef());
1415 if( bAllConstants && nTableColIdx >= 0 &&
1416 m_poLyrTable->GetField(nTableColIdx)->HasIndex() )
1417 {
1418 FileGDBIterator* poRet = nullptr;
1419 for( int i=1; i<poNode->nSubExprCount; i++ )
1420 {
1421 OGRField sValue;
1422 if( !FillTargetValueFromSrcExpr(poFieldDefn, &sValue,
1423 poNode->papoSubExpr[i]) )
1424 {
1425 delete poRet;
1426 poRet = nullptr;
1427 break;
1428 }
1429 FileGDBIterator* poIter = FileGDBIterator::Build(
1430 m_poLyrTable, nTableColIdx, TRUE, FGSO_EQ,
1431 poFieldDefn->GetType(), &sValue);
1432 if( poIter == nullptr )
1433 {
1434 delete poRet;
1435 poRet = nullptr;
1436 break;
1437 }
1438 if( poRet == nullptr )
1439 poRet = poIter;
1440 else
1441 poRet = FileGDBIterator::BuildOr(poRet, poIter);
1442 }
1443 if( poRet != nullptr )
1444 {
1445 m_bIteratorSufficientToEvaluateFilter = TRUE;
1446 return poRet;
1447 }
1448 }
1449 }
1450 }
1451 else if( poNode->eNodeType == SNT_OPERATION &&
1452 poNode->nOperation == SWQ_NOT && poNode->nSubExprCount == 1 )
1453 {
1454 FileGDBIterator* poIter = BuildIteratorFromExprNode(poNode->papoSubExpr[0]);
1455 /* If we have an iterator that is only partial w.r.t the full clause */
1456 /* then we cannot do anything with it unfortunately */
1457 if( m_bIteratorSufficientToEvaluateFilter == FALSE )
1458 {
1459 if( poIter != nullptr )
1460 CPLDebug("OpenFileGDB", "Disabling use of indexes");
1461 delete poIter;
1462 }
1463 else if( poIter != nullptr )
1464 {
1465 return FileGDBIterator::BuildNot(poIter);
1466 }
1467 }
1468
1469 if( m_bIteratorSufficientToEvaluateFilter == TRUE )
1470 CPLDebug("OpenFileGDB", "Disabling use of indexes");
1471 m_bIteratorSufficientToEvaluateFilter = FALSE;
1472 return nullptr;
1473 }
1474
1475 /***********************************************************************/
1476 /* SetAttributeFilter() */
1477 /***********************************************************************/
1478
SetAttributeFilter(const char * pszFilter)1479 OGRErr OGROpenFileGDBLayer::SetAttributeFilter( const char* pszFilter )
1480 {
1481 if( !BuildLayerDefinition() )
1482 return OGRERR_FAILURE;
1483
1484 delete m_poAttributeIterator;
1485 m_poAttributeIterator = nullptr;
1486 delete m_poCombinedIterator;
1487 m_poCombinedIterator = nullptr;
1488 m_bIteratorSufficientToEvaluateFilter = FALSE;
1489
1490 OGRErr eErr = OGRLayer::SetAttributeFilter(pszFilter);
1491 if( eErr != OGRERR_NONE ||
1492 !CPLTestBool(CPLGetConfigOption("OPENFILEGDB_USE_INDEX", "YES")) )
1493 return eErr;
1494
1495 if( m_poAttrQuery != nullptr && m_nFilteredFeatureCount < 0 )
1496 {
1497 swq_expr_node* poNode = (swq_expr_node*) m_poAttrQuery->GetSWQExpr();
1498 poNode->ReplaceBetweenByGEAndLERecurse();
1499 m_bIteratorSufficientToEvaluateFilter = -1;
1500 m_poAttributeIterator = BuildIteratorFromExprNode(poNode);
1501 if( m_poAttributeIterator != nullptr && m_eSpatialIndexState == SPI_IN_BUILDING )
1502 m_eSpatialIndexState = SPI_INVALID;
1503 if( m_bIteratorSufficientToEvaluateFilter < 0 )
1504 m_bIteratorSufficientToEvaluateFilter = FALSE;
1505 }
1506
1507 BuildCombinedIterator();
1508
1509 return eErr;
1510 }
1511
1512 /***********************************************************************/
1513 /* BuildCombinedIterator() */
1514 /***********************************************************************/
1515
BuildCombinedIterator()1516 void OGROpenFileGDBLayer::BuildCombinedIterator()
1517 {
1518 delete m_poCombinedIterator;
1519 if( m_poAttributeIterator && m_poSpatialIndexIterator )
1520 {
1521 m_poCombinedIterator = FileGDBIterator::BuildAnd(m_poAttributeIterator,
1522 m_poSpatialIndexIterator,
1523 false);
1524 }
1525 else
1526 {
1527 m_poCombinedIterator = nullptr;
1528 }
1529 }
1530
1531 /***********************************************************************/
1532 /* GetCurrentFeature() */
1533 /***********************************************************************/
1534
GetCurrentFeature()1535 OGRFeature* OGROpenFileGDBLayer::GetCurrentFeature()
1536 {
1537 OGRFeature *poFeature = nullptr;
1538 int iOGRIdx = 0;
1539 int iRow = m_poLyrTable->GetCurRow();
1540 for(int iGDBIdx=0;iGDBIdx<m_poLyrTable->GetFieldCount();iGDBIdx++)
1541 {
1542 if( iGDBIdx == m_iGeomFieldIdx )
1543 {
1544 if( m_poFeatureDefn->GetGeomFieldDefn(0)->IsIgnored() )
1545 {
1546 if( m_eSpatialIndexState == SPI_IN_BUILDING )
1547 m_eSpatialIndexState = SPI_INVALID;
1548 continue;
1549 }
1550
1551 const OGRField* psField = m_poLyrTable->GetFieldValue(iGDBIdx);
1552 if( psField != nullptr )
1553 {
1554 if( m_eSpatialIndexState == SPI_IN_BUILDING )
1555 {
1556 OGREnvelope sFeatureEnvelope;
1557 if( m_poLyrTable->GetFeatureExtent(psField,
1558 &sFeatureEnvelope) )
1559 {
1560 CPLRectObj sBounds;
1561 sBounds.minx = sFeatureEnvelope.MinX;
1562 sBounds.miny = sFeatureEnvelope.MinY;
1563 sBounds.maxx = sFeatureEnvelope.MaxX;
1564 sBounds.maxy = sFeatureEnvelope.MaxY;
1565 CPLQuadTreeInsertWithBounds(m_pQuadTree,
1566 (void*)(size_t)iRow,
1567 &sBounds);
1568 }
1569 }
1570
1571 if( m_poFilterGeom != nullptr &&
1572 m_eSpatialIndexState != SPI_COMPLETED &&
1573 !m_poLyrTable->DoesGeometryIntersectsFilterEnvelope(psField) )
1574 {
1575 delete poFeature;
1576 return nullptr;
1577 }
1578
1579 OGRGeometry* poGeom = m_poGeomConverter->GetAsGeometry(psField);
1580 if( poGeom != nullptr )
1581 {
1582 OGRwkbGeometryType eFlattenType = wkbFlatten(poGeom->getGeometryType());
1583 if( eFlattenType == wkbPolygon )
1584 poGeom = OGRGeometryFactory::forceToMultiPolygon(poGeom);
1585 else if( eFlattenType == wkbCurvePolygon)
1586 {
1587 OGRMultiSurface* poMS = new OGRMultiSurface();
1588 poMS->addGeometryDirectly( poGeom );
1589 poGeom = poMS;
1590 }
1591 else if( eFlattenType == wkbLineString )
1592 poGeom = OGRGeometryFactory::forceToMultiLineString(poGeom);
1593 else if (eFlattenType == wkbCompoundCurve)
1594 {
1595 OGRMultiCurve* poMC = new OGRMultiCurve();
1596 poMC->addGeometryDirectly( poGeom );
1597 poGeom = poMC;
1598 }
1599
1600 poGeom->assignSpatialReference(
1601 m_poFeatureDefn->GetGeomFieldDefn(0)->GetSpatialRef() );
1602
1603 if( poFeature == nullptr )
1604 poFeature = new OGRFeature(m_poFeatureDefn);
1605 poFeature->SetGeometryDirectly( poGeom );
1606 }
1607 }
1608 }
1609 else
1610 {
1611 const OGRFieldDefn* poFieldDefn = m_poFeatureDefn->GetFieldDefn(iOGRIdx);
1612 if( !poFieldDefn->IsIgnored() )
1613 {
1614 OGRField* psField = m_poLyrTable->GetFieldValue(iGDBIdx);
1615 if( poFeature == nullptr )
1616 poFeature = new OGRFeature(m_poFeatureDefn);
1617 if( psField == nullptr )
1618 {
1619 poFeature->SetFieldNull(iOGRIdx);
1620 }
1621 else
1622 {
1623
1624 if( iGDBIdx == m_iFieldToReadAsBinary )
1625 poFeature->SetField(iOGRIdx, reinterpret_cast<const char*>(psField->Binary.paData));
1626 else if( poFieldDefn->GetType() == OFTDateTime )
1627 {
1628 psField->Date.TZFlag = m_bTimeInUTC ? 100 : 0;
1629 poFeature->SetField(iOGRIdx, psField);
1630 }
1631 else
1632 poFeature->SetField(iOGRIdx, psField);
1633 }
1634 }
1635 iOGRIdx ++;
1636 }
1637 }
1638
1639 if( poFeature == nullptr )
1640 poFeature = new OGRFeature(m_poFeatureDefn);
1641
1642 if( m_poLyrTable->HasDeletedFeaturesListed() )
1643 {
1644 poFeature->SetField(poFeature->GetFieldCount() - 1,
1645 m_poLyrTable->IsCurRowDeleted());
1646 }
1647
1648 poFeature->SetFID(iRow + 1);
1649 return poFeature;
1650 }
1651
1652 /***********************************************************************/
1653 /* GetNextFeature() */
1654 /***********************************************************************/
1655
GetNextFeature()1656 OGRFeature* OGROpenFileGDBLayer::GetNextFeature()
1657 {
1658 if( !BuildLayerDefinition() || m_bEOF )
1659 return nullptr;
1660
1661 FileGDBIterator* poIterator =
1662 m_poCombinedIterator ? m_poCombinedIterator:
1663 m_poSpatialIndexIterator ? m_poSpatialIndexIterator:
1664 m_poAttributeIterator;
1665
1666 while( true )
1667 {
1668 OGRFeature *poFeature = nullptr;
1669
1670 if( m_nFilteredFeatureCount >= 0 )
1671 {
1672 while( true )
1673 {
1674 if( m_iCurFeat >= m_nFilteredFeatureCount )
1675 {
1676 return nullptr;
1677 }
1678 int iRow = (int)(GUIntptr_t)m_pahFilteredFeatures[m_iCurFeat++];
1679 if( m_poLyrTable->SelectRow(iRow) )
1680 {
1681 poFeature = GetCurrentFeature();
1682 if( poFeature )
1683 break;
1684 }
1685 else if( m_poLyrTable->HasGotError() )
1686 {
1687 m_bEOF = TRUE;
1688 return nullptr;
1689 }
1690 }
1691 }
1692 else if( poIterator != nullptr )
1693 {
1694 while( true )
1695 {
1696 int iRow = poIterator->GetNextRowSortedByFID();
1697 if( iRow < 0 )
1698 return nullptr;
1699 if( m_poLyrTable->SelectRow(iRow) )
1700 {
1701 poFeature = GetCurrentFeature();
1702 if( poFeature )
1703 break;
1704 }
1705 else if( m_poLyrTable->HasGotError() )
1706 {
1707 m_bEOF = TRUE;
1708 return nullptr;
1709 }
1710 }
1711 }
1712 else
1713 {
1714 while( true )
1715 {
1716 if( m_iCurFeat == m_poLyrTable->GetTotalRecordCount() )
1717 {
1718 return nullptr;
1719 }
1720 m_iCurFeat = m_poLyrTable->GetAndSelectNextNonEmptyRow(m_iCurFeat);
1721 if( m_iCurFeat < 0 )
1722 {
1723 m_bEOF = TRUE;
1724 return nullptr;
1725 }
1726 else
1727 {
1728 m_iCurFeat ++;
1729 poFeature = GetCurrentFeature();
1730 if( m_eSpatialIndexState == SPI_IN_BUILDING &&
1731 m_iCurFeat == m_poLyrTable->GetTotalRecordCount() )
1732 {
1733 CPLDebug("OpenFileGDB", "SPI_COMPLETED");
1734 m_eSpatialIndexState = SPI_COMPLETED;
1735 }
1736 if( poFeature )
1737 break;
1738 }
1739 }
1740 }
1741
1742 if( (m_poFilterGeom == nullptr
1743 || FilterGeometry( poFeature->GetGeometryRef() ) )
1744 && (m_poAttrQuery == nullptr ||
1745 (m_poAttributeIterator != nullptr && m_bIteratorSufficientToEvaluateFilter) ||
1746 m_poAttrQuery->Evaluate( poFeature ) ) )
1747 {
1748 return poFeature;
1749 }
1750
1751 delete poFeature;
1752 }
1753 }
1754
1755 /***********************************************************************/
1756 /* GetFeature() */
1757 /***********************************************************************/
1758
GetFeature(GIntBig nFeatureId)1759 OGRFeature* OGROpenFileGDBLayer::GetFeature( GIntBig nFeatureId )
1760 {
1761 if( !BuildLayerDefinition() )
1762 return nullptr;
1763
1764 if( nFeatureId < 1 || nFeatureId > m_poLyrTable->GetTotalRecordCount() )
1765 return nullptr;
1766 if( !m_poLyrTable->SelectRow((int)nFeatureId - 1) )
1767 return nullptr;
1768
1769 /* Temporarily disable spatial filter */
1770 OGRGeometry* poOldSpatialFilter = m_poFilterGeom;
1771 m_poFilterGeom = nullptr;
1772 /* and also spatial index state to avoid features to be inserted */
1773 /* multiple times in spatial index */
1774 SPIState eOldState = m_eSpatialIndexState;
1775 m_eSpatialIndexState = SPI_INVALID;
1776
1777 OGRFeature* poFeature = GetCurrentFeature();
1778
1779 /* Set it back */
1780 m_poFilterGeom = poOldSpatialFilter;
1781 m_eSpatialIndexState = eOldState;
1782
1783 return poFeature;
1784 }
1785
1786 /***********************************************************************/
1787 /* SetNextByIndex() */
1788 /***********************************************************************/
1789
SetNextByIndex(GIntBig nIndex)1790 OGRErr OGROpenFileGDBLayer::SetNextByIndex( GIntBig nIndex )
1791 {
1792 if( m_poAttributeIterator != nullptr || m_poSpatialIndexIterator != nullptr )
1793 return OGRLayer::SetNextByIndex(nIndex);
1794
1795 if( !BuildLayerDefinition() )
1796 return OGRERR_FAILURE;
1797
1798 if( m_eSpatialIndexState == SPI_IN_BUILDING )
1799 m_eSpatialIndexState = SPI_INVALID;
1800
1801 if( m_nFilteredFeatureCount >= 0 )
1802 {
1803 if( nIndex < 0 || nIndex >= m_nFilteredFeatureCount )
1804 return OGRERR_FAILURE;
1805 m_iCurFeat = (int) nIndex;
1806 return OGRERR_NONE;
1807 }
1808 else if( m_poLyrTable->GetValidRecordCount() == m_poLyrTable->GetTotalRecordCount() )
1809 {
1810 if( nIndex < 0 || nIndex >= m_poLyrTable->GetValidRecordCount() )
1811 return OGRERR_FAILURE;
1812 m_iCurFeat = (int) nIndex;
1813 return OGRERR_NONE;
1814 }
1815 else
1816 return OGRLayer::SetNextByIndex(nIndex);
1817 }
1818
1819 /***********************************************************************/
1820 /* GetExtent() */
1821 /***********************************************************************/
1822
GetExtent(OGREnvelope * psExtent,int)1823 OGRErr OGROpenFileGDBLayer::GetExtent( OGREnvelope *psExtent, int /* bForce */)
1824 {
1825 if( !BuildLayerDefinition() )
1826 return OGRERR_FAILURE;
1827
1828 if( m_iGeomFieldIdx >= 0 && m_poLyrTable->GetValidRecordCount() > 0 )
1829 {
1830 FileGDBGeomField* poGDBGeomField = reinterpret_cast<FileGDBGeomField *>(
1831 m_poLyrTable->GetField(m_iGeomFieldIdx));
1832 psExtent->MinX = poGDBGeomField->GetXMin();
1833 psExtent->MinY = poGDBGeomField->GetYMin();
1834 psExtent->MaxX = poGDBGeomField->GetXMax();
1835 psExtent->MaxY = poGDBGeomField->GetYMax();
1836 return OGRERR_NONE;
1837 }
1838
1839 return OGRERR_FAILURE;
1840 }
1841
1842 /***********************************************************************/
1843 /* GetFeatureCount() */
1844 /***********************************************************************/
1845
GetFeatureCount(int bForce)1846 GIntBig OGROpenFileGDBLayer::GetFeatureCount( int bForce )
1847 {
1848 if( !BuildLayerDefinition() )
1849 return 0;
1850
1851 /* No filter */
1852 if( (m_poFilterGeom == nullptr || m_iGeomFieldIdx < 0 ) &&
1853 m_poAttrQuery == nullptr )
1854 {
1855 return m_poLyrTable->GetValidRecordCount();
1856 }
1857 else if( m_nFilteredFeatureCount >= 0 && m_poAttrQuery == nullptr )
1858 {
1859 return m_nFilteredFeatureCount;
1860 }
1861
1862 /* Only geometry filter ? */
1863 if( m_poAttrQuery == nullptr && m_bFilterIsEnvelope )
1864 {
1865 if( m_poSpatialIndexIterator )
1866 {
1867 m_poSpatialIndexIterator->Reset();
1868 int nCount = 0;
1869 while( true )
1870 {
1871 const int nRowIdx = m_poSpatialIndexIterator->GetNextRowSortedByFID();
1872 if( nRowIdx < 0 )
1873 break;
1874 if( !m_poLyrTable->SelectRow(nRowIdx) )
1875 {
1876 if( m_poLyrTable->HasGotError() )
1877 break;
1878 else
1879 continue;
1880 }
1881
1882 const OGRField* psField = m_poLyrTable->GetFieldValue(m_iGeomFieldIdx);
1883 if( psField != nullptr )
1884 {
1885 if( m_poLyrTable->DoesGeometryIntersectsFilterEnvelope(psField) )
1886 {
1887 OGRGeometry* poGeom = m_poGeomConverter->GetAsGeometry(psField);
1888 if( poGeom != nullptr && FilterGeometry( poGeom ))
1889 {
1890 nCount ++;
1891 }
1892 delete poGeom;
1893 }
1894 }
1895 }
1896 return nCount;
1897 }
1898
1899 int nCount = 0;
1900 if( m_eSpatialIndexState == SPI_IN_BUILDING && m_iCurFeat != 0 )
1901 m_eSpatialIndexState = SPI_INVALID;
1902
1903 int nFilteredFeatureCountAlloc = 0;
1904 if( m_eSpatialIndexState == SPI_IN_BUILDING )
1905 {
1906 CPLFree(m_pahFilteredFeatures);
1907 m_pahFilteredFeatures = nullptr;
1908 m_nFilteredFeatureCount = 0;
1909 }
1910
1911 for(int i=0;i<m_poLyrTable->GetTotalRecordCount();i++)
1912 {
1913 if( !m_poLyrTable->SelectRow(i) )
1914 {
1915 if( m_poLyrTable->HasGotError() )
1916 break;
1917 else
1918 continue;
1919 }
1920
1921 const OGRField* psField = m_poLyrTable->GetFieldValue(m_iGeomFieldIdx);
1922 if( psField != nullptr )
1923 {
1924 if( m_eSpatialIndexState == SPI_IN_BUILDING )
1925 {
1926 OGREnvelope sFeatureEnvelope;
1927 if( m_poLyrTable->GetFeatureExtent(psField,
1928 &sFeatureEnvelope) )
1929 {
1930 CPLRectObj sBounds;
1931 sBounds.minx = sFeatureEnvelope.MinX;
1932 sBounds.miny = sFeatureEnvelope.MinY;
1933 sBounds.maxx = sFeatureEnvelope.MaxX;
1934 sBounds.maxy = sFeatureEnvelope.MaxY;
1935 CPLQuadTreeInsertWithBounds(m_pQuadTree,
1936 (void*)(size_t)i,
1937 &sBounds);
1938 }
1939 }
1940
1941 if( m_poLyrTable->DoesGeometryIntersectsFilterEnvelope(psField) )
1942 {
1943 OGRGeometry* poGeom = m_poGeomConverter->GetAsGeometry(psField);
1944 if( poGeom != nullptr && FilterGeometry( poGeom ))
1945 {
1946 if( m_eSpatialIndexState == SPI_IN_BUILDING )
1947 {
1948 if( nCount == nFilteredFeatureCountAlloc )
1949 {
1950 nFilteredFeatureCountAlloc =
1951 4 * nFilteredFeatureCountAlloc / 3 + 1024;
1952 m_pahFilteredFeatures = static_cast<void**>(
1953 CPLRealloc(
1954 m_pahFilteredFeatures,
1955 sizeof(void*) *
1956 nFilteredFeatureCountAlloc));
1957 }
1958 m_pahFilteredFeatures[nCount] = (void*)(size_t)i;
1959 }
1960 nCount ++;
1961 }
1962 delete poGeom;
1963 }
1964 }
1965 }
1966 if( m_eSpatialIndexState == SPI_IN_BUILDING )
1967 {
1968 m_nFilteredFeatureCount = nCount;
1969 m_eSpatialIndexState = SPI_COMPLETED;
1970 }
1971
1972 return nCount;
1973 }
1974 /* Only simple attribute filter ? */
1975 else if( m_poFilterGeom == nullptr &&
1976 m_poAttributeIterator != nullptr && m_bIteratorSufficientToEvaluateFilter )
1977 {
1978 return m_poAttributeIterator->GetRowCount();
1979 }
1980
1981 return OGRLayer::GetFeatureCount(bForce);
1982 }
1983
1984 /***********************************************************************/
1985 /* TestCapability() */
1986 /***********************************************************************/
1987
TestCapability(const char * pszCap)1988 int OGROpenFileGDBLayer::TestCapability( const char * pszCap )
1989 {
1990 if( !BuildLayerDefinition() )
1991 return FALSE;
1992
1993 if( EQUAL(pszCap,OLCFastFeatureCount) )
1994 {
1995 return( (m_poFilterGeom == nullptr || m_iGeomFieldIdx < 0 ) &&
1996 m_poAttrQuery == nullptr );
1997 }
1998 else if( EQUAL(pszCap,OLCFastSetNextByIndex) )
1999 {
2000 return ( m_poLyrTable->GetValidRecordCount() ==
2001 m_poLyrTable->GetTotalRecordCount() &&
2002 m_poAttributeIterator == nullptr &&
2003 m_poSpatialIndexIterator == nullptr );
2004 }
2005 else if( EQUAL(pszCap,OLCRandomRead) )
2006 {
2007 return TRUE;
2008 }
2009 else if( EQUAL(pszCap,OLCFastGetExtent) )
2010 {
2011 return TRUE;
2012 }
2013 else if( EQUAL(pszCap,OLCIgnoreFields) )
2014 {
2015 return TRUE;
2016 }
2017 else if( EQUAL(pszCap,OLCStringsAsUTF8) )
2018 {
2019 return TRUE; /* ? */
2020 }
2021 else if( EQUAL(pszCap, OLCFastSpatialFilter) )
2022 {
2023 return m_eSpatialIndexState == SPI_COMPLETED || m_poLyrTable->HasSpatialIndex();
2024 }
2025
2026 return FALSE;
2027 }
2028
2029 /***********************************************************************/
2030 /* HasIndexForField() */
2031 /***********************************************************************/
2032
HasIndexForField(const char * pszFieldName)2033 int OGROpenFileGDBLayer::HasIndexForField(const char* pszFieldName)
2034 {
2035 if( !BuildLayerDefinition() )
2036 return FALSE;
2037
2038 int nTableColIdx = m_poLyrTable->GetFieldIdx(pszFieldName);
2039 return ( nTableColIdx >= 0 &&
2040 m_poLyrTable->GetField(nTableColIdx)->HasIndex() );
2041 }
2042
2043 /***********************************************************************/
2044 /* BuildIndex() */
2045 /***********************************************************************/
2046
BuildIndex(const char * pszFieldName,int bAscending,int op,swq_expr_node * poValue)2047 FileGDBIterator* OGROpenFileGDBLayer::BuildIndex(const char* pszFieldName,
2048 int bAscending,
2049 int op,
2050 swq_expr_node* poValue)
2051 {
2052 if( !BuildLayerDefinition() )
2053 return nullptr;
2054
2055 int idx = GetLayerDefn()->GetFieldIndex(pszFieldName);
2056 if( idx < 0 )
2057 return nullptr;
2058 OGRFieldDefn* poFieldDefn = GetLayerDefn()->GetFieldDefn(idx);
2059
2060 int nTableColIdx = m_poLyrTable->GetFieldIdx(pszFieldName);
2061 if( nTableColIdx >= 0 && m_poLyrTable->GetField(nTableColIdx)->HasIndex() )
2062 {
2063 if( op < 0 )
2064 return FileGDBIterator::BuildIsNotNull(m_poLyrTable, nTableColIdx, bAscending);
2065
2066 OGRField sValue;
2067 if( FillTargetValueFromSrcExpr(poFieldDefn, &sValue, poValue) )
2068 {
2069 FileGDBSQLOp eOp;
2070 switch( op )
2071 {
2072 case SWQ_LE: eOp = FGSO_LE; break;
2073 case SWQ_LT: eOp = FGSO_LT; break;
2074 case SWQ_EQ: eOp = FGSO_EQ; break;
2075 case SWQ_GE: eOp = FGSO_GE; break;
2076 case SWQ_GT: eOp = FGSO_GT; break;
2077 default: return nullptr;
2078 }
2079
2080 return FileGDBIterator::Build(
2081 m_poLyrTable, nTableColIdx, bAscending,
2082 eOp, poFieldDefn->GetType(), &sValue);
2083 }
2084 }
2085 return nullptr;
2086 }
2087
2088 /***********************************************************************/
2089 /* GetMinMaxValue() */
2090 /***********************************************************************/
2091
GetMinMaxValue(OGRFieldDefn * poFieldDefn,int bIsMin,int & eOutType)2092 const OGRField* OGROpenFileGDBLayer::GetMinMaxValue(OGRFieldDefn* poFieldDefn,
2093 int bIsMin,
2094 int& eOutType)
2095 {
2096 eOutType = -1;
2097 if( !BuildLayerDefinition() )
2098 return nullptr;
2099
2100 const int nTableColIdx = m_poLyrTable->GetFieldIdx(poFieldDefn->GetNameRef());
2101 if( nTableColIdx >= 0 && m_poLyrTable->GetField(nTableColIdx)->HasIndex() )
2102 {
2103 delete m_poIterMinMax;
2104 m_poIterMinMax = FileGDBIterator::BuildIsNotNull(
2105 m_poLyrTable, nTableColIdx, TRUE);
2106 if( m_poIterMinMax != nullptr )
2107 {
2108 const OGRField* poRet = (bIsMin ) ?
2109 m_poIterMinMax->GetMinValue(eOutType) :
2110 m_poIterMinMax->GetMaxValue(eOutType);
2111 if( poRet == nullptr )
2112 eOutType = poFieldDefn->GetType();
2113 return poRet;
2114 }
2115 }
2116 return nullptr;
2117 }
2118
2119 /***********************************************************************/
2120 /* GetMinMaxSumCount() */
2121 /***********************************************************************/
2122
GetMinMaxSumCount(OGRFieldDefn * poFieldDefn,double & dfMin,double & dfMax,double & dfSum,int & nCount)2123 int OGROpenFileGDBLayer::GetMinMaxSumCount(OGRFieldDefn* poFieldDefn,
2124 double& dfMin, double& dfMax,
2125 double& dfSum, int& nCount)
2126 {
2127 dfMin = 0.0;
2128 dfMax = 0.0;
2129 dfSum = 0.0;
2130 nCount = 0;
2131 if( !BuildLayerDefinition() )
2132 return FALSE;
2133
2134 int nTableColIdx = m_poLyrTable->GetFieldIdx(poFieldDefn->GetNameRef());
2135 if( nTableColIdx >= 0 && m_poLyrTable->GetField(nTableColIdx)->HasIndex() )
2136 {
2137 FileGDBIterator* poIter = FileGDBIterator::BuildIsNotNull(
2138 m_poLyrTable, nTableColIdx, TRUE);
2139 if( poIter != nullptr )
2140 {
2141 int nRet = poIter->GetMinMaxSumCount(dfMin, dfMax, dfSum, nCount);
2142 delete poIter;
2143 return nRet;
2144 }
2145 }
2146 return FALSE;
2147 }
2148