Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OpenGIS Simple Features Reference Implementation
4 : * Purpose: Implements FileGDB OGR layer.
5 : * Author: Ragi Yaser Burhum, ragi@burhum.com
6 : * Paul Ramsey, pramsey at cleverelephant.ca
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2010, Ragi Yaser Burhum
10 : * Copyright (c) 2011, Paul Ramsey <pramsey at cleverelephant.ca>
11 : * Copyright (c) 2011-2014, Even Rouault <even dot rouault at spatialys.com>
12 : *
13 : * SPDX-License-Identifier: MIT
14 : ****************************************************************************/
15 :
16 : #include <cassert>
17 : #include <cmath>
18 :
19 : #include "ogr_fgdb.h"
20 : #include "ogrpgeogeometry.h"
21 : #include "cpl_conv.h"
22 : #include "cpl_string.h"
23 : #include "FGdbUtils.h"
24 : #include "cpl_minixml.h" // the only way right now to extract schema information
25 : #include "filegdb_gdbtoogrfieldtype.h"
26 : #include "filegdb_fielddomain.h"
27 : #include "filegdb_coordprec_read.h"
28 :
29 : // See https://github.com/Esri/file-geodatabase-api/issues/46
30 : // On certain FileGDB datasets with binary fields, iterating over a result set
31 : // where the binary field is requested crashes in EnumRows::Next() at the
32 : // second iteration.
33 : // The workaround consists in iterating only over OBJECTID in the main loop,
34 : // and requesting each feature in a separate request.
35 : #define WORKAROUND_CRASH_ON_CDF_WITH_BINARY_FIELD
36 :
37 : using std::string;
38 : using std::wstring;
39 :
40 : /************************************************************************/
41 : /* FGdbBaseLayer() */
42 : /************************************************************************/
43 134 : FGdbBaseLayer::FGdbBaseLayer()
44 : : m_pFeatureDefn(nullptr), m_pSRS(nullptr), m_pEnumRows(nullptr),
45 134 : m_suppressColumnMappingError(false), m_forceMulti(false)
46 : {
47 134 : }
48 :
49 : /************************************************************************/
50 : /* ~FGdbBaseLayer() */
51 : /************************************************************************/
52 134 : FGdbBaseLayer::~FGdbBaseLayer()
53 : {
54 134 : if (m_pFeatureDefn)
55 : {
56 134 : m_pFeatureDefn->Release();
57 134 : m_pFeatureDefn = nullptr;
58 : }
59 :
60 134 : FGdbBaseLayer::CloseGDBObjects();
61 :
62 134 : if (m_pSRS)
63 : {
64 109 : m_pSRS->Release();
65 109 : m_pSRS = nullptr;
66 : }
67 134 : }
68 :
69 : /************************************************************************/
70 : /* CloseGDBObjects() */
71 : /************************************************************************/
72 :
73 398 : void FGdbBaseLayer::CloseGDBObjects()
74 : {
75 398 : if (m_pEnumRows)
76 : {
77 134 : delete m_pEnumRows;
78 134 : m_pEnumRows = nullptr;
79 : }
80 398 : }
81 :
82 : /************************************************************************/
83 : /* GetNextFeature() */
84 : /************************************************************************/
85 :
86 45 : OGRFeature *FGdbBaseLayer::GetNextFeature()
87 : {
88 : while (true) // want to skip errors
89 : {
90 45 : if (m_pEnumRows == nullptr)
91 45 : return nullptr;
92 :
93 : long hr;
94 :
95 45 : Row row;
96 :
97 45 : if (FAILED(hr = m_pEnumRows->Next(row)))
98 : {
99 0 : GDBErr(hr, "Failed fetching features");
100 0 : return nullptr;
101 : }
102 :
103 45 : if (hr != S_OK)
104 : {
105 : // It's OK, we are done fetching - failure is caught by FAILED macro
106 7 : return nullptr;
107 : }
108 :
109 38 : OGRFeature *pOGRFeature = nullptr;
110 :
111 38 : if (!OGRFeatureFromGdbRow(&row, &pOGRFeature) || !pOGRFeature)
112 : {
113 0 : int32 oid = -1;
114 0 : CPL_IGNORE_RET_VAL(row.GetOID(oid));
115 :
116 0 : GDBErr(hr,
117 : CPLSPrintf("Failed translating FGDB row [%d] to OGR Feature",
118 : oid));
119 :
120 : // return NULL;
121 0 : continue; // skip feature
122 : }
123 :
124 38 : if ((m_poFilterGeom == nullptr ||
125 0 : FilterGeometry(pOGRFeature->GetGeometryRef())))
126 : {
127 38 : return pOGRFeature;
128 : }
129 0 : delete pOGRFeature;
130 0 : }
131 : }
132 :
133 : /************************************************************************/
134 : /* FGdbLayer() */
135 : /************************************************************************/
136 132 : FGdbLayer::FGdbLayer()
137 : : m_pDS(nullptr), m_pTable(nullptr), m_wstrSubfields(L"*"),
138 132 : m_bFilterDirty(true), m_bLaunderReservedKeywords(true)
139 : {
140 132 : m_pEnumRows = new EnumRows;
141 :
142 : #ifdef EXTENT_WORKAROUND
143 132 : m_bLayerEnvelopeValid = false;
144 : #endif
145 132 : }
146 :
147 : /************************************************************************/
148 : /* ~FGdbLayer() */
149 : /************************************************************************/
150 :
151 264 : FGdbLayer::~FGdbLayer()
152 : {
153 132 : FGdbLayer::CloseGDBObjects();
154 :
155 305 : for (size_t i = 0; i < m_apoByteArrays.size(); i++)
156 173 : delete m_apoByteArrays[i];
157 132 : m_apoByteArrays.resize(0);
158 264 : }
159 :
160 : /************************************************************************/
161 : /* CloseGDBObjects() */
162 : /************************************************************************/
163 :
164 264 : void FGdbLayer::CloseGDBObjects()
165 : {
166 : #ifdef EXTENT_WORKAROUND
167 264 : WorkAroundExtentProblem();
168 : #endif
169 :
170 264 : if (m_pTable)
171 : {
172 132 : delete m_pTable;
173 132 : m_pTable = nullptr;
174 : }
175 :
176 264 : FGdbBaseLayer::CloseGDBObjects();
177 264 : }
178 :
179 : #ifdef EXTENT_WORKAROUND
180 :
181 : /************************************************************************/
182 : /* UpdateRowWithGeometry() */
183 : /************************************************************************/
184 :
185 0 : bool FGdbLayer::UpdateRowWithGeometry(Row &row, OGRGeometry *poGeom)
186 : {
187 0 : ShapeBuffer shape;
188 : long hr;
189 :
190 : /* Write geometry to a buffer */
191 0 : GByte *pabyShape = nullptr;
192 0 : int nShapeSize = 0;
193 0 : if (OGRWriteToShapeBin(poGeom, &pabyShape, &nShapeSize) != OGRERR_NONE)
194 : {
195 0 : CPLFree(pabyShape);
196 0 : return false;
197 : }
198 :
199 : /* Copy it into a ShapeBuffer */
200 0 : if (nShapeSize > 0)
201 : {
202 0 : shape.Allocate(nShapeSize);
203 0 : memcpy(shape.shapeBuffer, pabyShape, nShapeSize);
204 0 : shape.inUseLength = nShapeSize;
205 : }
206 :
207 : /* Free the shape buffer */
208 0 : CPLFree(pabyShape);
209 :
210 : /* Write ShapeBuffer into the Row */
211 0 : hr = row.SetGeometry(shape);
212 0 : if (FAILED(hr))
213 : {
214 0 : return false;
215 : }
216 :
217 : /* Update row */
218 0 : hr = m_pTable->Update(row);
219 0 : if (FAILED(hr))
220 : {
221 0 : return false;
222 : }
223 :
224 0 : return true;
225 : }
226 :
227 : /************************************************************************/
228 : /* WorkAroundExtentProblem() */
229 : /* */
230 : /* Work-around problem with FileGDB API 1.1 on Linux 64bit. See #4455 */
231 : /************************************************************************/
232 :
233 264 : void FGdbLayer::WorkAroundExtentProblem()
234 : {
235 264 : if (!m_bLayerEnvelopeValid)
236 264 : return;
237 :
238 0 : OGREnvelope sEnvelope;
239 0 : if (FGdbLayer::GetExtent(&sEnvelope, TRUE) != OGRERR_NONE)
240 0 : return;
241 :
242 : /* The characteristic of the bug is that the reported extent */
243 : /* is the real extent truncated incorrectly to integer values */
244 : /* We work around that by temporary updating one feature with a geometry */
245 : /* whose coordinates are integer values but ceil'ed and floor'ed */
246 : /* such that they include the real layer extent. */
247 0 : if (((double)(int)sEnvelope.MinX == sEnvelope.MinX &&
248 0 : (double)(int)sEnvelope.MinY == sEnvelope.MinY &&
249 0 : (double)(int)sEnvelope.MaxX == sEnvelope.MaxX &&
250 0 : (double)(int)sEnvelope.MaxY == sEnvelope.MaxY) &&
251 0 : (fabs(sEnvelope.MinX - sLayerEnvelope.MinX) > 1e-5 ||
252 0 : fabs(sEnvelope.MinY - sLayerEnvelope.MinY) > 1e-5 ||
253 0 : fabs(sEnvelope.MaxX - sLayerEnvelope.MaxX) > 1e-5 ||
254 0 : fabs(sEnvelope.MaxY - sLayerEnvelope.MaxY) > 1e-5))
255 : {
256 : long hr;
257 0 : Row row;
258 0 : EnumRows enumRows;
259 :
260 0 : if (FAILED(hr = m_pTable->Search(StringToWString("*"),
261 : StringToWString(""), true, enumRows)))
262 0 : return;
263 :
264 0 : if (FAILED(hr = enumRows.Next(row)))
265 0 : return;
266 :
267 0 : if (hr != S_OK)
268 0 : return;
269 :
270 : /* Backup original shape buffer */
271 0 : ShapeBuffer originalGdbGeometry;
272 0 : if (FAILED(hr = row.GetGeometry(originalGdbGeometry)))
273 0 : return;
274 :
275 0 : OGRGeometry *pOGRGeo = nullptr;
276 0 : if ((!GDBGeometryToOGRGeometry(m_forceMulti, &originalGdbGeometry,
277 0 : m_pSRS, &pOGRGeo)) ||
278 0 : pOGRGeo == nullptr)
279 : {
280 0 : delete pOGRGeo;
281 0 : return;
282 : }
283 :
284 0 : OGRwkbGeometryType eType = wkbFlatten(pOGRGeo->getGeometryType());
285 :
286 0 : delete pOGRGeo;
287 0 : pOGRGeo = nullptr;
288 :
289 0 : OGRPoint oP1(floor(sLayerEnvelope.MinX), floor(sLayerEnvelope.MinY));
290 0 : OGRPoint oP2(ceil(sLayerEnvelope.MaxX), ceil(sLayerEnvelope.MaxY));
291 :
292 0 : OGRLinearRing oLR;
293 0 : oLR.addPoint(&oP1);
294 0 : oLR.addPoint(&oP2);
295 0 : oLR.addPoint(&oP1);
296 :
297 0 : if (eType == wkbPoint)
298 : {
299 0 : UpdateRowWithGeometry(row, &oP1);
300 0 : UpdateRowWithGeometry(row, &oP2);
301 : }
302 0 : else if (eType == wkbLineString)
303 : {
304 0 : UpdateRowWithGeometry(row, &oLR);
305 : }
306 0 : else if (eType == wkbPolygon)
307 : {
308 0 : OGRPolygon oPoly;
309 0 : oPoly.addRing(&oLR);
310 :
311 0 : UpdateRowWithGeometry(row, &oPoly);
312 : }
313 0 : else if (eType == wkbMultiPoint)
314 : {
315 0 : OGRMultiPoint oColl;
316 0 : oColl.addGeometry(&oP1);
317 0 : oColl.addGeometry(&oP2);
318 :
319 0 : UpdateRowWithGeometry(row, &oColl);
320 : }
321 0 : else if (eType == wkbMultiLineString)
322 : {
323 0 : OGRMultiLineString oColl;
324 0 : oColl.addGeometry(&oLR);
325 :
326 0 : UpdateRowWithGeometry(row, &oColl);
327 : }
328 0 : else if (eType == wkbMultiPolygon)
329 : {
330 0 : OGRMultiPolygon oColl;
331 0 : OGRPolygon oPoly;
332 0 : oPoly.addRing(&oLR);
333 0 : oColl.addGeometry(&oPoly);
334 :
335 0 : UpdateRowWithGeometry(row, &oColl);
336 : }
337 : else
338 0 : return;
339 :
340 : /* Restore original ShapeBuffer */
341 0 : hr = row.SetGeometry(originalGdbGeometry);
342 0 : if (FAILED(hr))
343 0 : return;
344 :
345 : /* Update Row */
346 0 : hr = m_pTable->Update(row);
347 0 : if (FAILED(hr))
348 0 : return;
349 :
350 0 : CPLDebug("FGDB",
351 : "Workaround extent problem with Linux 64bit FGDB SDK 1.1");
352 : }
353 : }
354 : #endif // EXTENT_WORKAROUND
355 :
356 : /************************************************************************/
357 : /* GetRow() */
358 : /************************************************************************/
359 :
360 1783 : OGRErr FGdbLayer::GetRow(EnumRows &enumRows, Row &row, GIntBig nFID)
361 : {
362 : long hr;
363 3566 : CPLString osQuery;
364 :
365 : /* Querying a 64bit FID causes a runtime exception in FileGDB... */
366 1783 : if (!CPL_INT64_FITS_ON_INT32(nFID))
367 : {
368 0 : return OGRERR_FAILURE;
369 : }
370 :
371 1783 : osQuery.Printf("%s = " CPL_FRMT_GIB, m_strOIDFieldName.c_str(), nFID);
372 :
373 1783 : if (FAILED(hr = m_pTable->Search(m_wstrSubfields,
374 : StringToWString(osQuery.c_str()), true,
375 : enumRows)))
376 : {
377 0 : GDBErr(hr, "Failed fetching row ");
378 0 : return OGRERR_FAILURE;
379 : }
380 :
381 1783 : if (FAILED(hr = enumRows.Next(row)))
382 : {
383 0 : GDBErr(hr, "Failed fetching row ");
384 0 : return OGRERR_FAILURE;
385 : }
386 :
387 1783 : if (hr != S_OK)
388 34 : return OGRERR_NON_EXISTING_FEATURE; // none found - but no failure
389 :
390 1749 : return OGRERR_NONE;
391 : }
392 :
393 : /*************************************************************************/
394 : /* Initialize() */
395 : /* Has ownership of the table as soon as it is called. */
396 : /************************************************************************/
397 :
398 132 : bool FGdbLayer::Initialize(FGdbDataSource *pParentDataSource, Table *pTable,
399 : const std::wstring &wstrTablePath,
400 : const std::wstring &wstrType)
401 : {
402 : long hr;
403 :
404 132 : m_pDS = pParentDataSource; // we never assume ownership of the parent - so
405 : // our destructor should not delete
406 :
407 132 : m_pTable = pTable;
408 :
409 132 : m_wstrTablePath = wstrTablePath;
410 132 : m_wstrType = wstrType;
411 :
412 264 : wstring wstrQueryName;
413 132 : if (FAILED(hr = pParentDataSource->GetGDB()->GetQueryName(wstrTablePath,
414 : wstrQueryName)))
415 0 : return GDBErr(hr, "Failed at getting underlying table name for " +
416 0 : WStringToString(wstrTablePath));
417 :
418 132 : m_strName = WStringToString(wstrQueryName);
419 :
420 132 : m_pFeatureDefn = new OGRFeatureDefn(
421 132 : m_strName.c_str()); // TODO: Should I "new" an OGR smart pointer -
422 : // sample says so, but it doesn't seem right
423 132 : SetDescription(m_pFeatureDefn->GetName());
424 : // as long as we use the same compiler & settings in both the ogr build and
425 : // this driver, we should be OK
426 132 : m_pFeatureDefn->Reference();
427 :
428 264 : string tableDef;
429 132 : if (FAILED(hr = m_pTable->GetDefinition(tableDef)))
430 0 : return GDBErr(hr, "Failed at getting table definition for " +
431 0 : WStringToString(wstrTablePath));
432 :
433 : // CPLDebug("FGDB", "tableDef = %s", tableDef.c_str());
434 :
435 132 : bool abort = false;
436 :
437 : // extract schema information from table
438 132 : CPLXMLNode *psRoot = CPLParseXMLString(tableDef.c_str());
439 :
440 132 : if (psRoot == nullptr)
441 : {
442 0 : CPLError(
443 : CE_Failure, CPLE_AppDefined, "%s",
444 0 : ("Failed parsing GDB Table Schema XML for " + m_strName).c_str());
445 0 : return false;
446 : }
447 :
448 132 : CPLXMLNode *pDataElementNode =
449 : psRoot->psNext; // Move to next field which should be DataElement
450 :
451 132 : if (pDataElementNode != nullptr && pDataElementNode->psChild != nullptr &&
452 132 : pDataElementNode->eType == CXT_Element &&
453 132 : EQUAL(pDataElementNode->pszValue, "esri:DataElement"))
454 : {
455 : CPLXMLNode *psNode;
456 :
457 132 : m_bTimeInUTC = CPLTestBool(
458 : CPLGetXMLValue(pDataElementNode, "IsTimeInUTC", "false"));
459 :
460 264 : std::string osAreaFieldName;
461 264 : std::string osLengthFieldName;
462 4170 : for (psNode = pDataElementNode->psChild; psNode != nullptr;
463 4038 : psNode = psNode->psNext)
464 : {
465 4038 : if (psNode->eType == CXT_Element && psNode->psChild != nullptr)
466 : {
467 2932 : if (EQUAL(psNode->pszValue, "OIDFieldName"))
468 : {
469 132 : m_strOIDFieldName = CPLGetXMLValue(psNode, nullptr, "");
470 : }
471 2800 : else if (EQUAL(psNode->pszValue, "ShapeFieldName"))
472 : {
473 118 : m_strShapeFieldName = CPLGetXMLValue(psNode, nullptr, "");
474 : }
475 2682 : else if (EQUAL(psNode->pszValue, "AreaFieldName"))
476 : {
477 12 : osAreaFieldName = CPLGetXMLValue(psNode, nullptr, "");
478 : }
479 2670 : else if (EQUAL(psNode->pszValue, "LengthFieldName"))
480 : {
481 18 : osLengthFieldName = CPLGetXMLValue(psNode, nullptr, "");
482 : }
483 2652 : else if (EQUAL(psNode->pszValue, "Fields"))
484 : {
485 132 : if (!GDBToOGRFields(psNode))
486 : {
487 0 : abort = true;
488 0 : break;
489 : }
490 : }
491 : }
492 : }
493 :
494 132 : if (!osAreaFieldName.empty())
495 : {
496 : const int nIdx =
497 12 : m_pFeatureDefn->GetFieldIndex(osAreaFieldName.c_str());
498 12 : if (nIdx >= 0)
499 : {
500 12 : m_pFeatureDefn->GetFieldDefn(nIdx)->SetDefault(
501 : "FILEGEODATABASE_SHAPE_AREA");
502 : }
503 : }
504 :
505 132 : if (!osLengthFieldName.empty())
506 : {
507 : const int nIdx =
508 18 : m_pFeatureDefn->GetFieldIndex(osLengthFieldName.c_str());
509 18 : if (nIdx >= 0)
510 : {
511 18 : m_pFeatureDefn->GetFieldDefn(nIdx)->SetDefault(
512 : "FILEGEODATABASE_SHAPE_LENGTH");
513 : }
514 : }
515 :
516 132 : if (m_strShapeFieldName.empty())
517 146 : m_pFeatureDefn->SetGeomType(wkbNone);
518 : }
519 : else
520 : {
521 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
522 0 : ("Failed parsing GDB Table Schema XML (DataElement) for " +
523 0 : m_strName)
524 : .c_str());
525 0 : return false;
526 : }
527 132 : CPLDestroyXMLNode(psRoot);
528 :
529 132 : if (m_pFeatureDefn->GetGeomFieldCount() != 0)
530 : {
531 118 : m_pFeatureDefn->GetGeomFieldDefn(0)->SetName(
532 : m_strShapeFieldName.c_str());
533 118 : m_pFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(m_pSRS);
534 : }
535 :
536 132 : if (abort)
537 0 : return false;
538 :
539 132 : return true; // AOToOGRFields(ipFields, m_pFeatureDefn,
540 : // m_vOGRFieldToESRIField);
541 : }
542 :
543 : /************************************************************************/
544 : /* ParseGeometryDef() */
545 : /************************************************************************/
546 :
547 118 : bool FGdbLayer::ParseGeometryDef(const CPLXMLNode *psRoot)
548 : {
549 236 : string geometryType;
550 118 : bool hasZ = false, hasM = false;
551 236 : string wkt, wkid, latestwkid;
552 :
553 236 : OGRGeomCoordinatePrecision oCoordPrec;
554 118 : for (const CPLXMLNode *psGeometryDefItem = psRoot->psChild;
555 944 : psGeometryDefItem; psGeometryDefItem = psGeometryDefItem->psNext)
556 : {
557 : // loop through all "GeometryDef" elements
558 : //
559 :
560 826 : if (psGeometryDefItem->eType == CXT_Element &&
561 708 : psGeometryDefItem->psChild != nullptr)
562 : {
563 708 : if (EQUAL(psGeometryDefItem->pszValue, "GeometryType"))
564 : {
565 118 : geometryType = CPLGetXMLValue(psGeometryDefItem, nullptr, "");
566 : }
567 590 : else if (EQUAL(psGeometryDefItem->pszValue, "SpatialReference"))
568 : {
569 118 : ParseSpatialReference(
570 : psGeometryDefItem, &wkt, &wkid,
571 : &latestwkid); // we don't check for success because it
572 : // may not be there
573 118 : oCoordPrec = GDBGridSettingsToOGR(psGeometryDefItem);
574 : }
575 472 : else if (EQUAL(psGeometryDefItem->pszValue, "HasM"))
576 : {
577 118 : if (!strcmp(CPLGetXMLValue(psGeometryDefItem, nullptr, ""),
578 : "true"))
579 5 : hasM = true;
580 : }
581 354 : else if (EQUAL(psGeometryDefItem->pszValue, "HasZ"))
582 : {
583 118 : if (!strcmp(CPLGetXMLValue(psGeometryDefItem, nullptr, ""),
584 : "true"))
585 47 : hasZ = true;
586 : }
587 : }
588 : }
589 :
590 : OGRwkbGeometryType ogrGeoType;
591 118 : if (!GDBToOGRGeometry(geometryType, hasZ, hasM, &ogrGeoType))
592 0 : return false;
593 :
594 118 : m_pFeatureDefn->SetGeomType(ogrGeoType);
595 :
596 118 : if (m_pFeatureDefn->GetGeomFieldCount() != 0)
597 118 : m_pFeatureDefn->GetGeomFieldDefn(0)->SetCoordinatePrecision(oCoordPrec);
598 :
599 209 : if (wkbFlatten(ogrGeoType) == wkbMultiLineString ||
600 91 : wkbFlatten(ogrGeoType) == wkbMultiPoint)
601 37 : m_forceMulti = true;
602 :
603 118 : if (latestwkid.length() > 0 || wkid.length() > 0)
604 : {
605 108 : int bSuccess = FALSE;
606 108 : m_pSRS = new OGRSpatialReference();
607 108 : m_pSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
608 108 : CPLPushErrorHandler(CPLQuietErrorHandler);
609 108 : if (latestwkid.length() > 0)
610 : {
611 0 : if (m_pSRS->importFromEPSG(atoi(latestwkid.c_str())) == OGRERR_NONE)
612 : {
613 0 : bSuccess = TRUE;
614 : }
615 : else
616 : {
617 0 : CPLDebug("FGDB", "Cannot import SRID %s", latestwkid.c_str());
618 : }
619 : }
620 108 : if (!bSuccess && wkid.length() > 0)
621 : {
622 108 : if (m_pSRS->importFromEPSG(atoi(wkid.c_str())) == OGRERR_NONE)
623 : {
624 108 : bSuccess = TRUE;
625 : }
626 : else
627 : {
628 0 : CPLDebug("FGDB", "Cannot import SRID %s", wkid.c_str());
629 : }
630 : }
631 108 : CPLPopErrorHandler();
632 108 : CPLErrorReset();
633 108 : if (!bSuccess)
634 : {
635 0 : delete m_pSRS;
636 0 : m_pSRS = nullptr;
637 : }
638 : else
639 108 : return true;
640 : }
641 :
642 10 : if (wkt.length() > 0)
643 : {
644 1 : if (!GDBToOGRSpatialReference(wkt, &m_pSRS))
645 : {
646 : // report error, but be passive about it
647 0 : CPLError(CE_Warning, CPLE_AppDefined,
648 : "Failed Mapping ESRI Spatial Reference");
649 : }
650 : }
651 :
652 10 : return true;
653 : }
654 :
655 : /************************************************************************/
656 : /* ParseSpatialReference() */
657 : /************************************************************************/
658 :
659 118 : bool FGdbLayer::ParseSpatialReference(const CPLXMLNode *psSpatialRefNode,
660 : string *pOutWkt, string *pOutWKID,
661 : string *pOutLatestWKID)
662 : {
663 118 : *pOutWkt = "";
664 118 : *pOutWKID = "";
665 118 : *pOutLatestWKID = "";
666 :
667 : /* Loop through all the SRS elements we want to store */
668 118 : for (const CPLXMLNode *psSRItemNode = psSpatialRefNode->psChild;
669 1761 : psSRItemNode; psSRItemNode = psSRItemNode->psNext)
670 : {
671 : /* The WKID maps (mostly) to an EPSG code */
672 1643 : if (psSRItemNode->eType == CXT_Element &&
673 1525 : psSRItemNode->psChild != nullptr &&
674 1525 : EQUAL(psSRItemNode->pszValue, "WKID"))
675 : {
676 118 : *pOutWKID = CPLGetXMLValue(psSRItemNode, nullptr, "");
677 :
678 : // Needed with FileGDB v1.4 with layers with empty SRS
679 118 : if (*pOutWKID == "0")
680 10 : *pOutWKID = "";
681 : }
682 : /* The concept of LatestWKID is explained in
683 : * http://resources.arcgis.com/en/help/arcgis-rest-api/index.html#//02r3000000n1000000
684 : */
685 1525 : else if (psSRItemNode->eType == CXT_Element &&
686 1407 : psSRItemNode->psChild != nullptr &&
687 1407 : EQUAL(psSRItemNode->pszValue, "LatestWKID"))
688 : {
689 0 : *pOutLatestWKID = CPLGetXMLValue(psSRItemNode, nullptr, "");
690 : }
691 : /* The WKT well-known text can be converted by OGR */
692 1525 : else if (psSRItemNode->eType == CXT_Element &&
693 1407 : psSRItemNode->psChild != nullptr &&
694 1407 : EQUAL(psSRItemNode->pszValue, "WKT"))
695 : {
696 109 : *pOutWkt = CPLGetXMLValue(psSRItemNode, nullptr, "");
697 : }
698 : }
699 118 : return *pOutWkt != "" || *pOutWKID != "";
700 : }
701 :
702 : /************************************************************************/
703 : /* GDBToOGRFields() */
704 : /************************************************************************/
705 :
706 132 : bool FGdbLayer::GDBToOGRFields(CPLXMLNode *psRoot)
707 : {
708 132 : m_vOGRFieldToESRIField.clear();
709 :
710 132 : if (psRoot->psChild == nullptr || psRoot->psChild->psNext == nullptr)
711 : {
712 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unrecognized GDB XML Schema");
713 :
714 0 : return false;
715 : }
716 :
717 132 : psRoot = psRoot->psChild->psNext; // change root to "FieldArray"
718 :
719 : // CPLAssert(ogrToESRIFieldMapping.size() ==
720 : // pOGRFeatureDef->GetFieldCount());
721 :
722 : CPLXMLNode *psFieldNode;
723 :
724 1778 : for (psFieldNode = psRoot->psChild; psFieldNode != nullptr;
725 1646 : psFieldNode = psFieldNode->psNext)
726 : {
727 : // loop through all "Field" elements
728 : //
729 :
730 1646 : if (psFieldNode->eType == CXT_Element &&
731 1514 : psFieldNode->psChild != nullptr &&
732 1514 : EQUAL(psFieldNode->pszValue, "Field"))
733 : {
734 :
735 : CPLXMLNode *psFieldItemNode;
736 1514 : std::string fieldName;
737 1514 : std::string fieldAlias;
738 1514 : std::string fieldType;
739 1514 : int nLength = 0;
740 1514 : int bNullable = TRUE;
741 1514 : std::string osDefault;
742 1514 : std::string osDomainName;
743 :
744 : // loop through all items in Field element
745 : //
746 :
747 1514 : for (psFieldItemNode = psFieldNode->psChild;
748 15718 : psFieldItemNode != nullptr;
749 14204 : psFieldItemNode = psFieldItemNode->psNext)
750 : {
751 14204 : if (psFieldItemNode->eType == CXT_Element)
752 : {
753 : const char *pszValue =
754 12690 : CPLGetXMLValue(psFieldItemNode, nullptr, "");
755 12690 : if (EQUAL(psFieldItemNode->pszValue, "Name"))
756 : {
757 1514 : fieldName = pszValue;
758 : }
759 11176 : else if (EQUAL(psFieldItemNode->pszValue, "AliasName"))
760 : {
761 1514 : fieldAlias = pszValue;
762 : }
763 9662 : else if (EQUAL(psFieldItemNode->pszValue, "Type"))
764 : {
765 1514 : fieldType = pszValue;
766 : }
767 8148 : else if (EQUAL(psFieldItemNode->pszValue, "GeometryDef"))
768 : {
769 118 : if (!ParseGeometryDef(psFieldItemNode))
770 0 : return false; // if we failed parsing the
771 : // GeometryDef, we are done!
772 : }
773 8030 : else if (EQUAL(psFieldItemNode->pszValue, "Length"))
774 : {
775 1514 : nLength = atoi(pszValue);
776 : }
777 6516 : else if (EQUAL(psFieldItemNode->pszValue, "IsNullable"))
778 : {
779 1514 : bNullable = EQUAL(pszValue, "true");
780 : }
781 5002 : else if (EQUAL(psFieldItemNode->pszValue, "DefaultValue"))
782 : {
783 4 : osDefault = pszValue;
784 : }
785 : // NOTE: when using the GetDefinition() API, the domain name
786 : // is set in <Domain><DomainName>, whereas the raw XML is
787 : // just <DomainName>
788 4998 : else if (EQUAL(psFieldItemNode->pszValue, "Domain"))
789 : {
790 : osDomainName =
791 18 : CPLGetXMLValue(psFieldItemNode, "DomainName", "");
792 : }
793 : }
794 : }
795 :
796 : ///////////////////////////////////////////////////////////////////
797 : // At this point we have parsed everything about the current field
798 :
799 1514 : if (fieldType == "esriFieldTypeGeometry")
800 : {
801 118 : m_strShapeFieldName = fieldName;
802 118 : m_pFeatureDefn->GetGeomFieldDefn(0)->SetNullable(bNullable);
803 :
804 118 : continue; // finish here for special field - don't add as OGR
805 : // fielddef
806 : }
807 1396 : else if (fieldType == "esriFieldTypeOID")
808 : {
809 : // m_strOIDFieldName = fieldName; // already set by this point
810 :
811 132 : continue; // finish here for special field - don't add as OGR
812 : // fielddef
813 : }
814 :
815 : OGRFieldType ogrType;
816 : OGRFieldSubType eSubType;
817 : // CPLDebug("FGDB", "name = %s, type = %s", fieldName.c_str(),
818 : // fieldType.c_str() );
819 1264 : if (!GDBToOGRFieldType(fieldType, &ogrType, &eSubType))
820 : {
821 : // field cannot be mapped, skipping further processing
822 0 : CPLError(CE_Warning, CPLE_AppDefined,
823 : "Skipping field: [%s] type: [%s] ", fieldName.c_str(),
824 : fieldType.c_str());
825 0 : continue;
826 : }
827 :
828 : // TODO: Optimization - modify m_wstrSubFields so it only fetches
829 : // fields that are mapped
830 :
831 2528 : OGRFieldDefn fieldTemplate(fieldName.c_str(), ogrType);
832 1264 : if (fieldAlias != fieldName)
833 : {
834 : // The SDK generates an alias even with it is not explicitly
835 : // written
836 2 : fieldTemplate.SetAlternativeName(fieldAlias.c_str());
837 : }
838 1264 : fieldTemplate.SetSubType(eSubType);
839 : /* On creation (GDBFieldTypeToLengthInBytes) if string width is 0,
840 : * we pick up */
841 : /* 65536 by default to mean unlimited string length, but we don't
842 : * want */
843 : /* to advertise such a big number */
844 1264 : if (ogrType == OFTString && nLength < 65536)
845 210 : fieldTemplate.SetWidth(nLength);
846 1264 : fieldTemplate.SetNullable(bNullable);
847 1264 : if (!osDefault.empty())
848 : {
849 4 : if (ogrType == OFTString)
850 : {
851 : char *pszTmp =
852 0 : CPLEscapeString(osDefault.c_str(), -1, CPLES_SQL);
853 0 : osDefault = "'";
854 0 : osDefault += pszTmp;
855 0 : CPLFree(pszTmp);
856 0 : osDefault += "'";
857 0 : fieldTemplate.SetDefault(osDefault.c_str());
858 : }
859 4 : else if (ogrType == OFTInteger || ogrType == OFTReal)
860 : {
861 : #ifdef unreliable
862 : /* Disabling this as GDBs and the FileGDB SDK aren't
863 : * reliable for numeric values */
864 : /* It often occurs that the XML definition in
865 : * a00000004.gdbtable doesn't */
866 : /* match the default values (in binary) found in the field
867 : * definition */
868 : /* section of the .gdbtable of the layers themselves */
869 : /* The Table::GetDefinition() API of FileGDB doesn't seem to
870 : * use the */
871 : /* XML definition, but rather the values found in the field
872 : * definition */
873 : /* section of the .gdbtable of the layers themselves */
874 : /* It seems that the XML definition in a00000004.gdbtable is
875 : * authoritative */
876 : /* in ArcGIS, so we're screwed... */
877 :
878 : fieldTemplate.SetDefault(osDefault.c_str());
879 : #endif
880 : }
881 0 : else if (ogrType == OFTDateTime)
882 : {
883 : int nYear, nMonth, nDay, nHour, nMinute;
884 : float fSecond;
885 0 : if (sscanf(osDefault.c_str(), "%d-%d-%dT%d:%d:%fZ", &nYear,
886 : &nMonth, &nDay, &nHour, &nMinute,
887 0 : &fSecond) == 6 ||
888 0 : sscanf(osDefault.c_str(), "'%d-%d-%d %d:%d:%fZ'",
889 : &nYear, &nMonth, &nDay, &nHour, &nMinute,
890 : &fSecond) == 6)
891 : {
892 0 : fieldTemplate.SetDefault(CPLSPrintf(
893 : "'%04d/%02d/%02d %02d:%02d:%02d'", nYear, nMonth,
894 0 : nDay, nHour, nMinute, (int)(fSecond + 0.5)));
895 : }
896 : }
897 : }
898 1264 : if (!osDomainName.empty())
899 : {
900 18 : fieldTemplate.SetDomainName(osDomainName);
901 : }
902 :
903 1264 : m_pFeatureDefn->AddFieldDefn(&fieldTemplate);
904 :
905 1264 : m_vOGRFieldToESRIField.push_back(StringToWString(fieldName));
906 1264 : m_vOGRFieldToESRIFieldType.push_back(fieldType);
907 1264 : if (ogrType == OFTBinary)
908 173 : m_apoByteArrays.push_back(new ByteArray());
909 : }
910 : }
911 :
912 : /* Using OpenFileGDB to get reliable default values for integer/real fields
913 : */
914 : /* and alias */
915 132 : if (m_pDS->UseOpenFileGDB())
916 : {
917 130 : const char *const apszDrivers[] = {"OpenFileGDB", nullptr};
918 130 : GDALDataset *poDS = GDALDataset::Open(
919 130 : m_pDS->GetFSName(), GDAL_OF_VECTOR, apszDrivers, nullptr, nullptr);
920 130 : if (poDS != nullptr)
921 : {
922 19 : OGRLayer *poLyr = poDS->GetLayerByName(GetName());
923 19 : if (poLyr)
924 : {
925 19 : const auto poOFGBLayerDefn = poLyr->GetLayerDefn();
926 19 : const int nOFGDBFieldCount = poOFGBLayerDefn->GetFieldCount();
927 67 : for (int i = 0; i < nOFGDBFieldCount; i++)
928 : {
929 : const OGRFieldDefn *poSrcDefn =
930 48 : poOFGBLayerDefn->GetFieldDefn(i);
931 80 : if ((poSrcDefn->GetType() == OFTInteger ||
932 96 : poSrcDefn->GetType() == OFTReal) &&
933 28 : poSrcDefn->GetDefault() != nullptr)
934 : {
935 15 : int nIdxDst = m_pFeatureDefn->GetFieldIndex(
936 15 : poSrcDefn->GetNameRef());
937 15 : if (nIdxDst >= 0)
938 15 : m_pFeatureDefn->GetFieldDefn(nIdxDst)->SetDefault(
939 : poSrcDefn->GetDefault());
940 : }
941 :
942 : // XML parsing by the SDK fails when there are special
943 : // characters, like &, so fallback to using OpenFileGDB.
944 : const char *pszAlternativeName =
945 48 : poSrcDefn->GetAlternativeNameRef();
946 96 : if (pszAlternativeName != nullptr &&
947 49 : pszAlternativeName[0] != '\0' &&
948 1 : strcmp(pszAlternativeName, poSrcDefn->GetNameRef()) !=
949 : 0)
950 : {
951 1 : int nIdxDst = m_pFeatureDefn->GetFieldIndex(
952 1 : poSrcDefn->GetNameRef());
953 1 : if (nIdxDst >= 0)
954 1 : m_pFeatureDefn->GetFieldDefn(nIdxDst)
955 1 : ->SetAlternativeName(pszAlternativeName);
956 : }
957 : }
958 : }
959 19 : GDALClose(poDS);
960 : }
961 : }
962 :
963 132 : return true;
964 : }
965 :
966 : /************************************************************************/
967 : /* ResetReading() */
968 : /************************************************************************/
969 :
970 1216 : void FGdbLayer::ResetReading()
971 : {
972 : long hr;
973 :
974 1216 : if (m_pTable == nullptr)
975 0 : return;
976 :
977 : #ifdef WORKAROUND_CRASH_ON_CDF_WITH_BINARY_FIELD
978 1216 : const std::wstring wstrSubFieldBackup(m_wstrSubfields);
979 1216 : if (!m_apoByteArrays.empty())
980 : {
981 1204 : m_bWorkaroundCrashOnCDFWithBinaryField = CPLTestBool(CPLGetConfigOption(
982 : "OGR_FGDB_WORKAROUND_CRASH_ON_BINARY_FIELD", "YES"));
983 1204 : if (m_bWorkaroundCrashOnCDFWithBinaryField)
984 : {
985 1204 : m_wstrSubfields = StringToWString(m_strOIDFieldName);
986 1550 : if (!m_strShapeFieldName.empty() && m_poFilterGeom &&
987 346 : !m_poFilterGeom->IsEmpty())
988 : {
989 346 : m_wstrSubfields += StringToWString(", " + m_strShapeFieldName);
990 : }
991 : }
992 : }
993 : #endif
994 :
995 1216 : if (m_poFilterGeom && !m_poFilterGeom->IsEmpty())
996 : {
997 : // Search spatial
998 : // As of beta1, FileGDB only supports bbox searched, if we have GEOS
999 : // installed, we can do the rest ourselves.
1000 :
1001 346 : OGREnvelope ogrEnv;
1002 :
1003 346 : m_poFilterGeom->getEnvelope(&ogrEnv);
1004 :
1005 : // spatial query
1006 : FileGDBAPI::Envelope env(ogrEnv.MinX, ogrEnv.MaxX, ogrEnv.MinY,
1007 692 : ogrEnv.MaxY);
1008 :
1009 346 : if (FAILED(hr = m_pTable->Search(m_wstrSubfields, m_wstrWhereClause,
1010 : env, true, *m_pEnumRows)))
1011 0 : GDBErr(hr, "Failed Searching");
1012 : }
1013 : else
1014 : {
1015 : // Search non-spatial
1016 870 : if (FAILED(hr = m_pTable->Search(m_wstrSubfields, m_wstrWhereClause,
1017 : true, *m_pEnumRows)))
1018 0 : GDBErr(hr, "Failed Searching");
1019 : }
1020 :
1021 : #ifdef WORKAROUND_CRASH_ON_CDF_WITH_BINARY_FIELD
1022 1216 : if (!m_apoByteArrays.empty() && m_bWorkaroundCrashOnCDFWithBinaryField)
1023 1204 : m_wstrSubfields = wstrSubFieldBackup;
1024 : #endif
1025 :
1026 1216 : m_bFilterDirty = false;
1027 : }
1028 :
1029 : /************************************************************************/
1030 : /* ISetSpatialFilter() */
1031 : /************************************************************************/
1032 :
1033 379 : OGRErr FGdbLayer::ISetSpatialFilter(int iGeomField, const OGRGeometry *pOGRGeom)
1034 : {
1035 379 : m_bFilterDirty = true;
1036 379 : return OGRLayer::ISetSpatialFilter(iGeomField, pOGRGeom);
1037 : }
1038 :
1039 : /************************************************************************/
1040 : /* SetAttributeFilter() */
1041 : /************************************************************************/
1042 :
1043 514 : OGRErr FGdbLayer::SetAttributeFilter(const char *pszQuery)
1044 : {
1045 514 : m_wstrWhereClause = StringToWString((pszQuery != nullptr) ? pszQuery : "");
1046 :
1047 514 : m_bFilterDirty = true;
1048 :
1049 514 : return OGRERR_NONE;
1050 : }
1051 :
1052 : /************************************************************************/
1053 : /* OGRFeatureFromGdbRow() */
1054 : /************************************************************************/
1055 :
1056 1787 : bool FGdbBaseLayer::OGRFeatureFromGdbRow(Row *pRow, OGRFeature **ppFeature)
1057 : {
1058 : long hr;
1059 :
1060 1787 : OGRFeature *pOutFeature = new OGRFeature(m_pFeatureDefn);
1061 :
1062 : /////////////////////////////////////////////////////////
1063 : // Translate OID
1064 : //
1065 :
1066 1787 : int32 oid = -1;
1067 1787 : if (FAILED(hr = pRow->GetOID(oid)))
1068 : {
1069 : // this should never happen unless not selecting the OBJECTID
1070 : }
1071 : else
1072 : {
1073 1787 : pOutFeature->SetFID(oid);
1074 : }
1075 :
1076 : /////////////////////////////////////////////////////////
1077 : // Translate Geometry
1078 : //
1079 :
1080 3574 : ShapeBuffer gdbGeometry;
1081 : // Row::GetGeometry() will fail with -2147467259 for NULL geometries
1082 : // Row::GetGeometry() will fail with -2147219885 for tables without a
1083 : // geometry field
1084 3504 : if (!m_pFeatureDefn->IsGeometryIgnored() &&
1085 1717 : !FAILED(hr = pRow->GetGeometry(gdbGeometry)))
1086 : {
1087 1470 : OGRGeometry *pOGRGeo = nullptr;
1088 :
1089 1470 : if ((!GDBGeometryToOGRGeometry(m_forceMulti, &gdbGeometry, m_pSRS,
1090 : &pOGRGeo)))
1091 : {
1092 0 : delete pOutFeature;
1093 0 : return GDBErr(hr, "Failed to translate FileGDB Geometry to OGR "
1094 0 : "Geometry for row " +
1095 0 : string(CPLSPrintf("%d", (int)oid)));
1096 : }
1097 :
1098 1470 : pOutFeature->SetGeometryDirectly(pOGRGeo);
1099 : }
1100 :
1101 : //////////////////////////////////////////////////////////
1102 : // Map fields
1103 : //
1104 :
1105 1787 : int mappedFieldCount = static_cast<int>(m_vOGRFieldToESRIField.size());
1106 :
1107 1787 : bool foundBadColumn = false;
1108 :
1109 24602 : for (int i = 0; i < mappedFieldCount; ++i)
1110 : {
1111 22815 : OGRFieldDefn *poFieldDefn = m_pFeatureDefn->GetFieldDefn(i);
1112 : // The IsNull() and GetXXX() API are very slow when there are a
1113 : // big number of fields, for example with Tiger database.
1114 22815 : if (poFieldDefn->IsIgnored())
1115 86 : continue;
1116 :
1117 22730 : const wstring &wstrFieldName = m_vOGRFieldToESRIField[i];
1118 22730 : const std::string &strFieldType = m_vOGRFieldToESRIFieldType[i];
1119 :
1120 22730 : bool isNull = false;
1121 :
1122 22730 : if (FAILED(hr = pRow->IsNull(wstrFieldName, isNull)))
1123 : {
1124 0 : GDBErr(hr, "Failed to determine NULL status from column " +
1125 0 : WStringToString(wstrFieldName));
1126 0 : foundBadColumn = true;
1127 0 : continue;
1128 : }
1129 :
1130 22730 : if (isNull)
1131 : {
1132 1 : pOutFeature->SetFieldNull(i);
1133 1 : continue;
1134 : }
1135 :
1136 : //
1137 : // NOTE: This switch statement needs to be kept in sync with
1138 : // GDBToOGRFieldType utility function
1139 : // since we are only checking for types we mapped in that utility
1140 : // function
1141 :
1142 22729 : switch (poFieldDefn->GetType())
1143 : {
1144 :
1145 6915 : case OFTInteger:
1146 : {
1147 : int32 val;
1148 :
1149 6915 : if (FAILED(hr = pRow->GetInteger(wstrFieldName, val)))
1150 : {
1151 : int16 shortval;
1152 3496 : if (FAILED(hr = pRow->GetShort(wstrFieldName, shortval)))
1153 : {
1154 0 : GDBErr(hr,
1155 0 : "Failed to determine integer value for column " +
1156 0 : WStringToString(wstrFieldName));
1157 0 : foundBadColumn = true;
1158 0 : continue;
1159 : }
1160 3496 : val = shortval;
1161 : }
1162 :
1163 6915 : pOutFeature->SetField(i, (int)val);
1164 : }
1165 6915 : break;
1166 :
1167 5293 : case OFTReal:
1168 : {
1169 5293 : if (strFieldType == "esriFieldTypeSingle")
1170 : {
1171 : float val;
1172 :
1173 3496 : if (FAILED(hr = pRow->GetFloat(wstrFieldName, val)))
1174 : {
1175 0 : GDBErr(hr,
1176 0 : "Failed to determine float value for column " +
1177 0 : WStringToString(wstrFieldName));
1178 0 : foundBadColumn = true;
1179 0 : continue;
1180 : }
1181 :
1182 3496 : pOutFeature->SetField(i, val);
1183 : }
1184 : else
1185 : {
1186 : double val;
1187 :
1188 1797 : if (FAILED(hr = pRow->GetDouble(wstrFieldName, val)))
1189 : {
1190 0 : GDBErr(hr,
1191 0 : "Failed to determine real value for column " +
1192 0 : WStringToString(wstrFieldName));
1193 0 : foundBadColumn = true;
1194 0 : continue;
1195 : }
1196 :
1197 1797 : pOutFeature->SetField(i, val);
1198 : }
1199 : }
1200 5293 : break;
1201 5275 : case OFTString:
1202 : {
1203 5275 : wstring val;
1204 5275 : std::string strValue;
1205 :
1206 5275 : if (strFieldType == "esriFieldTypeGlobalID")
1207 : {
1208 1 : Guid guid;
1209 2 : if (FAILED(hr = pRow->GetGlobalID(guid)) ||
1210 1 : FAILED(hr = guid.ToString(val)))
1211 : {
1212 0 : GDBErr(hr,
1213 0 : "Failed to determine string value for column " +
1214 0 : WStringToString(wstrFieldName));
1215 0 : foundBadColumn = true;
1216 0 : continue;
1217 : }
1218 1 : strValue = WStringToString(val);
1219 : }
1220 5274 : else if (strFieldType == "esriFieldTypeGUID")
1221 : {
1222 1748 : Guid guid;
1223 3496 : if (FAILED(hr = pRow->GetGUID(wstrFieldName, guid)) ||
1224 1748 : FAILED(hr = guid.ToString(val)))
1225 : {
1226 0 : GDBErr(hr,
1227 0 : "Failed to determine string value for column " +
1228 0 : WStringToString(wstrFieldName));
1229 0 : foundBadColumn = true;
1230 0 : continue;
1231 : }
1232 1748 : strValue = WStringToString(val);
1233 : }
1234 3526 : else if (strFieldType == "esriFieldTypeXML")
1235 : {
1236 1748 : if (FAILED(hr = pRow->GetXML(wstrFieldName, strValue)))
1237 : {
1238 0 : GDBErr(hr, "Failed to determine XML value for column " +
1239 0 : WStringToString(wstrFieldName));
1240 0 : foundBadColumn = true;
1241 0 : continue;
1242 : }
1243 : }
1244 : else
1245 : {
1246 1778 : if (FAILED(hr = pRow->GetString(wstrFieldName, val)))
1247 : {
1248 0 : GDBErr(hr,
1249 0 : "Failed to determine string value for column " +
1250 0 : WStringToString(wstrFieldName));
1251 0 : foundBadColumn = true;
1252 0 : continue;
1253 : }
1254 1778 : strValue = WStringToString(val);
1255 : }
1256 :
1257 5275 : pOutFeature->SetField(i, strValue.c_str());
1258 : }
1259 5275 : break;
1260 :
1261 3496 : case OFTBinary:
1262 : {
1263 3496 : ByteArray binaryBuf;
1264 :
1265 3496 : if (FAILED(hr = pRow->GetBinary(wstrFieldName, binaryBuf)))
1266 : {
1267 0 : GDBErr(hr, "Failed to determine binary value for column " +
1268 0 : WStringToString(wstrFieldName));
1269 0 : foundBadColumn = true;
1270 0 : continue;
1271 : }
1272 :
1273 3496 : pOutFeature->SetField(i, (int)binaryBuf.inUseLength,
1274 3496 : (GByte *)binaryBuf.byteArray);
1275 : }
1276 3496 : break;
1277 :
1278 1750 : case OFTDateTime:
1279 : {
1280 : struct tm val;
1281 :
1282 1750 : if (FAILED(hr = pRow->GetDate(wstrFieldName, val)))
1283 : {
1284 0 : GDBErr(hr, "Failed to determine date value for column " +
1285 0 : WStringToString(wstrFieldName));
1286 0 : foundBadColumn = true;
1287 0 : continue;
1288 : }
1289 :
1290 1750 : pOutFeature->SetField(i, val.tm_year + 1900, val.tm_mon + 1,
1291 : val.tm_mday, val.tm_hour, val.tm_min,
1292 1750 : (float)val.tm_sec,
1293 1750 : m_bTimeInUTC ? 100 : 0);
1294 : // Examine test data to figure out how to extract that
1295 : }
1296 1750 : break;
1297 :
1298 0 : default:
1299 : {
1300 0 : if (!m_suppressColumnMappingError)
1301 : {
1302 0 : foundBadColumn = true;
1303 0 : CPLError(CE_Warning, CPLE_AppDefined,
1304 : "Row id: %d col:%d has unhandled col type (%d). "
1305 : "Setting to NULL.",
1306 : (int)oid, (int)i,
1307 0 : m_pFeatureDefn->GetFieldDefn(i)->GetType());
1308 : }
1309 : }
1310 : }
1311 : }
1312 :
1313 1787 : if (foundBadColumn)
1314 0 : m_suppressColumnMappingError = true;
1315 :
1316 1787 : *ppFeature = pOutFeature;
1317 :
1318 1787 : return true;
1319 : }
1320 :
1321 : /************************************************************************/
1322 : /* GetNextFeature() */
1323 : /************************************************************************/
1324 :
1325 2059 : OGRFeature *FGdbLayer::GetNextFeature()
1326 : {
1327 2059 : if (m_bFilterDirty)
1328 95 : ResetReading();
1329 :
1330 : #ifdef WORKAROUND_CRASH_ON_CDF_WITH_BINARY_FIELD
1331 2059 : if (!m_apoByteArrays.empty() && m_bWorkaroundCrashOnCDFWithBinaryField)
1332 : {
1333 : while (true)
1334 : {
1335 2018 : if (m_pEnumRows == nullptr)
1336 2018 : return nullptr;
1337 :
1338 : long hr;
1339 :
1340 2018 : Row rowOnlyOid;
1341 :
1342 2018 : if (FAILED(hr = m_pEnumRows->Next(rowOnlyOid)))
1343 : {
1344 17 : GDBErr(hr, "Failed fetching features");
1345 17 : return nullptr;
1346 : }
1347 :
1348 2001 : if (hr != S_OK)
1349 : {
1350 : // It's OK, we are done fetching - failure is caught by FAILED
1351 : // macro
1352 335 : return nullptr;
1353 : }
1354 :
1355 1666 : int32 oid = -1;
1356 1666 : if (FAILED(hr = rowOnlyOid.GetOID(oid)))
1357 : {
1358 0 : GDBErr(hr, "Failed to get oid");
1359 0 : continue;
1360 : }
1361 :
1362 1666 : EnumRows enumRows;
1363 1666 : OGRFeature *pOGRFeature = nullptr;
1364 1666 : Row rowFull;
1365 1666 : if (GetRow(enumRows, rowFull, oid) != OGRERR_NONE ||
1366 1666 : !OGRFeatureFromGdbRow(&rowFull, &pOGRFeature) || !pOGRFeature)
1367 : {
1368 0 : GDBErr(hr,
1369 : CPLSPrintf(
1370 : "Failed translating FGDB row [%d] to OGR Feature",
1371 : oid));
1372 :
1373 : // return NULL;
1374 0 : continue; // skip feature
1375 : }
1376 :
1377 1946 : if ((m_poFilterGeom == nullptr ||
1378 280 : FilterGeometry(pOGRFeature->GetGeometryRef())))
1379 : {
1380 1666 : return pOGRFeature;
1381 : }
1382 0 : delete pOGRFeature;
1383 0 : }
1384 : }
1385 : #endif
1386 :
1387 41 : OGRFeature *poFeature = FGdbBaseLayer::GetNextFeature();
1388 41 : return poFeature;
1389 : }
1390 :
1391 : /************************************************************************/
1392 : /* GetFeature() */
1393 : /************************************************************************/
1394 :
1395 134 : OGRFeature *FGdbLayer::GetFeature(GIntBig oid)
1396 : {
1397 : // do query to fetch individual row
1398 268 : EnumRows enumRows;
1399 268 : Row row;
1400 134 : if (!CPL_INT64_FITS_ON_INT32(oid) || m_pTable == nullptr)
1401 17 : return nullptr;
1402 :
1403 117 : int nFID32 = (int)oid;
1404 :
1405 117 : if (GetRow(enumRows, row, nFID32) != OGRERR_NONE)
1406 34 : return nullptr;
1407 :
1408 83 : OGRFeature *pOGRFeature = nullptr;
1409 :
1410 83 : if (!OGRFeatureFromGdbRow(&row, &pOGRFeature))
1411 : {
1412 0 : return nullptr;
1413 : }
1414 83 : if (pOGRFeature)
1415 : {
1416 83 : pOGRFeature->SetFID(oid);
1417 : }
1418 :
1419 83 : return pOGRFeature;
1420 : }
1421 :
1422 : /************************************************************************/
1423 : /* GetFeatureCount() */
1424 : /************************************************************************/
1425 :
1426 213 : GIntBig FGdbLayer::GetFeatureCount(CPL_UNUSED int bForce)
1427 : {
1428 213 : int32 rowCount = 0;
1429 :
1430 213 : if (m_pTable == nullptr)
1431 0 : return 0;
1432 :
1433 213 : if (m_poFilterGeom != nullptr || !m_wstrWhereClause.empty())
1434 : {
1435 90 : ResetReading();
1436 90 : if (m_pEnumRows == nullptr)
1437 0 : return 0;
1438 :
1439 90 : int nFeatures = 0;
1440 : while (true)
1441 : {
1442 : long hr;
1443 :
1444 315 : Row row;
1445 :
1446 315 : if (FAILED(hr = m_pEnumRows->Next(row)))
1447 : {
1448 0 : GDBErr(hr, "Failed fetching features");
1449 0 : return 0;
1450 : }
1451 :
1452 315 : if (hr != S_OK)
1453 : {
1454 90 : break;
1455 : }
1456 :
1457 225 : if (m_poFilterGeom == nullptr)
1458 : {
1459 85 : nFeatures++;
1460 : }
1461 : else
1462 : {
1463 140 : ShapeBuffer gdbGeometry;
1464 140 : if (FAILED(hr = row.GetGeometry(gdbGeometry)))
1465 : {
1466 0 : continue;
1467 : }
1468 :
1469 140 : OGRGeometry *pOGRGeo = nullptr;
1470 140 : if (!GDBGeometryToOGRGeometry(m_forceMulti, &gdbGeometry,
1471 280 : m_pSRS, &pOGRGeo) ||
1472 140 : pOGRGeo == nullptr)
1473 : {
1474 0 : delete pOGRGeo;
1475 0 : continue;
1476 : }
1477 :
1478 140 : if (FilterGeometry(pOGRGeo))
1479 : {
1480 140 : nFeatures++;
1481 : }
1482 :
1483 140 : delete pOGRGeo;
1484 : }
1485 225 : }
1486 90 : ResetReading();
1487 90 : return nFeatures;
1488 : }
1489 :
1490 : long hr;
1491 123 : if (FAILED(hr = m_pTable->GetRowCount(rowCount)))
1492 : {
1493 0 : GDBErr(hr, "Failed counting rows");
1494 0 : return 0;
1495 : }
1496 :
1497 123 : return static_cast<int>(rowCount);
1498 : }
1499 :
1500 : /************************************************************************/
1501 : /* GetMetadataItem() */
1502 : /************************************************************************/
1503 :
1504 189 : const char *FGdbLayer::GetMetadataItem(const char *pszName,
1505 : const char *pszDomain)
1506 : {
1507 189 : return OGRLayer::GetMetadataItem(pszName, pszDomain);
1508 : }
1509 :
1510 : /************************************************************************/
1511 : /* IGetExtent() */
1512 : /************************************************************************/
1513 :
1514 60 : OGRErr FGdbLayer::IGetExtent(int iGeomField, OGREnvelope *psExtent, bool bForce)
1515 : {
1516 60 : if (m_pTable == nullptr)
1517 0 : return OGRERR_FAILURE;
1518 :
1519 120 : if (m_poFilterGeom != nullptr || !m_wstrWhereClause.empty() ||
1520 60 : m_strShapeFieldName.empty())
1521 : {
1522 0 : const int nFieldCount = m_pFeatureDefn->GetFieldCount();
1523 0 : int *pabSaveFieldIgnored = new int[nFieldCount];
1524 0 : for (int i = 0; i < nFieldCount; i++)
1525 : {
1526 : // cppcheck-suppress uninitdata
1527 0 : pabSaveFieldIgnored[i] =
1528 0 : m_pFeatureDefn->GetFieldDefn(i)->IsIgnored();
1529 0 : m_pFeatureDefn->GetFieldDefn(i)->SetIgnored(TRUE);
1530 : }
1531 0 : OGRErr eErr = OGRLayer::IGetExtent(iGeomField, psExtent, bForce);
1532 0 : for (int i = 0; i < nFieldCount; i++)
1533 : {
1534 0 : m_pFeatureDefn->GetFieldDefn(i)->SetIgnored(pabSaveFieldIgnored[i]);
1535 : }
1536 0 : delete[] pabSaveFieldIgnored;
1537 0 : return eErr;
1538 : }
1539 :
1540 : long hr;
1541 120 : Envelope envelope;
1542 60 : if (FAILED(hr = m_pTable->GetExtent(envelope)))
1543 : {
1544 0 : GDBErr(hr, "Failed fetching extent");
1545 0 : return OGRERR_FAILURE;
1546 : }
1547 :
1548 60 : psExtent->MinX = envelope.xMin;
1549 60 : psExtent->MinY = envelope.yMin;
1550 60 : psExtent->MaxX = envelope.xMax;
1551 60 : psExtent->MaxY = envelope.yMax;
1552 :
1553 172 : if (std::isnan(psExtent->MinX) || std::isnan(psExtent->MinY) ||
1554 172 : std::isnan(psExtent->MaxX) || std::isnan(psExtent->MaxY))
1555 4 : return OGRERR_FAILURE;
1556 :
1557 56 : return OGRERR_NONE;
1558 : }
1559 :
1560 : /************************************************************************/
1561 : /* GetLayerXML() */
1562 : /* Return XML definition of the Layer as provided by FGDB. Caller must */
1563 : /* free result. */
1564 : /* Not currently used by the driver, but can be used by external code */
1565 : /* for specific purposes. */
1566 : /************************************************************************/
1567 :
1568 17 : OGRErr FGdbLayer::GetLayerXML(char **ppXml)
1569 : {
1570 : long hr;
1571 34 : std::string xml;
1572 :
1573 17 : if (m_pTable == nullptr)
1574 0 : return OGRERR_FAILURE;
1575 :
1576 17 : if (FAILED(hr = m_pTable->GetDefinition(xml)))
1577 : {
1578 0 : GDBErr(hr, "Failed fetching XML table definition");
1579 0 : return OGRERR_FAILURE;
1580 : }
1581 :
1582 17 : *ppXml = CPLStrdup(xml.c_str());
1583 17 : return OGRERR_NONE;
1584 : }
1585 :
1586 : /************************************************************************/
1587 : /* GetLayerMetadataXML() */
1588 : /* Return XML metadata for the Layer as provided by FGDB. Caller must */
1589 : /* free result. */
1590 : /* Not currently used by the driver, but can be used by external code */
1591 : /* for specific purposes. */
1592 : /************************************************************************/
1593 :
1594 17 : OGRErr FGdbLayer::GetLayerMetadataXML(char **ppXml)
1595 : {
1596 : long hr;
1597 34 : std::string xml;
1598 :
1599 17 : if (m_pTable == nullptr)
1600 0 : return OGRERR_FAILURE;
1601 :
1602 17 : if (FAILED(hr = m_pTable->GetDocumentation(xml)))
1603 : {
1604 0 : GDBErr(hr, "Failed fetching XML table metadata");
1605 0 : return OGRERR_FAILURE;
1606 : }
1607 :
1608 17 : *ppXml = CPLStrdup(xml.c_str());
1609 17 : return OGRERR_NONE;
1610 : }
1611 :
1612 : /************************************************************************/
1613 : /* TestCapability() */
1614 : /************************************************************************/
1615 :
1616 454 : int FGdbLayer::TestCapability(const char *pszCap)
1617 : {
1618 :
1619 454 : if (EQUAL(pszCap, OLCRandomRead))
1620 0 : return TRUE;
1621 :
1622 454 : else if (EQUAL(pszCap, OLCFastFeatureCount))
1623 0 : return m_poFilterGeom == nullptr && m_wstrWhereClause.empty();
1624 :
1625 454 : else if (EQUAL(pszCap, OLCFastSpatialFilter))
1626 0 : return TRUE;
1627 :
1628 454 : else if (EQUAL(pszCap, OLCFastGetExtent))
1629 28 : return m_poFilterGeom == nullptr && m_wstrWhereClause.empty();
1630 :
1631 426 : else if (EQUAL(pszCap,
1632 : OLCStringsAsUTF8)) /* Native UTF16, converted to UTF8 */
1633 17 : return TRUE;
1634 :
1635 409 : else if (EQUAL(pszCap,
1636 : OLCFastSetNextByIndex)) /* TBD FastSetNextByIndex() */
1637 0 : return FALSE;
1638 :
1639 409 : else if (EQUAL(pszCap, OLCIgnoreFields))
1640 17 : return TRUE;
1641 :
1642 392 : else if (EQUAL(pszCap, OLCMeasuredGeometries))
1643 85 : return TRUE;
1644 :
1645 307 : else if (EQUAL(pszCap, OLCZGeometries))
1646 51 : return TRUE;
1647 :
1648 : else
1649 256 : return FALSE;
1650 : }
1651 :
1652 : /************************************************************************/
1653 : /* GetDataset() */
1654 : /************************************************************************/
1655 :
1656 34 : GDALDataset *FGdbLayer::GetDataset()
1657 : {
1658 34 : return m_pDS;
1659 : }
|