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 * Permission is hereby granted, free of charge, to any person obtaining a
14 * copy of this software and associated documentation files (the "Software"),
15 * to deal in the Software without restriction, including without limitation
16 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
17 * and/or sell copies of the Software, and to permit persons to whom the
18 * Software is furnished to do so, subject to the following conditions:
19 *
20 * The above copyright notice and this permission notice shall be included
21 * in all copies or substantial portions of the Software.
22 *
23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29 * DEALINGS IN THE SOFTWARE.
30 ****************************************************************************/
31
32 #include <cassert>
33 #include "ogr_fgdb.h"
34 #include "ogrpgeogeometry.h"
35 #include "cpl_conv.h"
36 #include "cpl_string.h"
37 #include "FGdbUtils.h"
38 #include "cpl_minixml.h" // the only way right now to extract schema information
39 #include "filegdb_gdbtoogrfieldtype.h"
40
41 CPL_CVSID("$Id: FGdbLayer.cpp 7819e495986c82c51d7023358b14ecb2b0fd4d98 2021-04-01 20:02:10 +0200 Even Rouault $")
42
43 using std::string;
44 using std::wstring;
45
46 /************************************************************************/
47 /* FGdbBaseLayer() */
48 /************************************************************************/
FGdbBaseLayer()49 FGdbBaseLayer::FGdbBaseLayer() :
50 m_pFeatureDefn(nullptr), m_pSRS(nullptr), m_pEnumRows(nullptr),
51 m_suppressColumnMappingError(false), m_forceMulti(false)
52 {
53 }
54
55 /************************************************************************/
56 /* ~FGdbBaseLayer() */
57 /************************************************************************/
~FGdbBaseLayer()58 FGdbBaseLayer::~FGdbBaseLayer()
59 {
60 if (m_pFeatureDefn)
61 {
62 m_pFeatureDefn->Release();
63 m_pFeatureDefn = nullptr;
64 }
65
66 FGdbBaseLayer::CloseGDBObjects();
67
68 if (m_pSRS)
69 {
70 m_pSRS->Release();
71 m_pSRS = nullptr;
72 }
73 }
74
75 /************************************************************************/
76 /* CloseGDBObjects() */
77 /************************************************************************/
78
CloseGDBObjects()79 void FGdbBaseLayer::CloseGDBObjects()
80 {
81 if (m_pEnumRows)
82 {
83 delete m_pEnumRows;
84 m_pEnumRows = nullptr;
85 }
86 }
87
88 /************************************************************************/
89 /* GetNextFeature() */
90 /************************************************************************/
91
GetNextFeature()92 OGRFeature* FGdbBaseLayer::GetNextFeature()
93 {
94 while (true) //want to skip errors
95 {
96 if (m_pEnumRows == nullptr)
97 return nullptr;
98
99 long hr;
100
101 Row row;
102
103 if (FAILED(hr = m_pEnumRows->Next(row)))
104 {
105 GDBErr(hr, "Failed fetching features");
106 return nullptr;
107 }
108
109 if (hr != S_OK)
110 {
111 // It's OK, we are done fetching - failure is caught by FAILED macro
112 return nullptr;
113 }
114
115 OGRFeature* pOGRFeature = nullptr;
116
117 if (!OGRFeatureFromGdbRow(&row, &pOGRFeature))
118 {
119 int32 oid = -1;
120 row.GetOID(oid);
121
122 GDBErr(hr, CPLSPrintf("Failed translating FGDB row [%d] to OGR Feature", oid));
123
124 //return NULL;
125 continue; //skip feature
126 }
127
128 if( (m_poFilterGeom == nullptr
129 || FilterGeometry( pOGRFeature->GetGeometryRef() )) )
130 {
131 return pOGRFeature;
132 }
133 delete pOGRFeature;
134 }
135 }
136
137 /************************************************************************/
138 /* FGdbLayer() */
139 /************************************************************************/
FGdbLayer()140 FGdbLayer::FGdbLayer():
141 m_pDS(nullptr), m_pTable(nullptr), m_wstrSubfields(L"*"),
142 m_bFilterDirty(true),
143 m_bLaunderReservedKeywords(true)
144 {
145 m_bBulkLoadAllowed = -1; /* uninitialized */
146 m_bBulkLoadInProgress = FALSE;
147 m_pEnumRows = new EnumRows;
148
149 #ifdef EXTENT_WORKAROUND
150 m_bLayerEnvelopeValid = false;
151 m_bLayerJustCreated = false;
152 #endif
153 m_papszOptions = nullptr;
154 m_bCreateMultipatch = FALSE;
155 m_nResyncThreshold = atoi(CPLGetConfigOption("FGDB_RESYNC_THRESHOLD", "1000000"));
156 m_bSymlinkFlag = FALSE;
157 }
158
159 /************************************************************************/
160 /* ~FGdbLayer() */
161 /************************************************************************/
162
~FGdbLayer()163 FGdbLayer::~FGdbLayer()
164 {
165 FGdbLayer::CloseGDBObjects();
166
167 for(size_t i = 0; i < m_apoByteArrays.size(); i++ )
168 delete m_apoByteArrays[i];
169 m_apoByteArrays.resize(0);
170
171 CSLDestroy(m_papszOptions);
172 m_papszOptions = nullptr;
173 }
174
175 /************************************************************************/
176 /* CloseGDBObjects() */
177 /************************************************************************/
178
CloseGDBObjects()179 void FGdbLayer::CloseGDBObjects()
180 {
181 EndBulkLoad();
182
183 #ifdef EXTENT_WORKAROUND
184 WorkAroundExtentProblem();
185 #endif
186
187 if (m_pTable)
188 {
189 delete m_pTable;
190 m_pTable = nullptr;
191 }
192
193 FGdbBaseLayer::CloseGDBObjects();
194 }
195
196 /************************************************************************/
197 /* EditIndexesForFIDHack() */
198 /************************************************************************/
199
EditIndexesForFIDHack(const char * pszRadixTablename)200 int FGdbLayer::EditIndexesForFIDHack(const char* pszRadixTablename)
201 {
202 // Fix FIDs in .gdbtablx, .spx and .atx's
203
204 CPLString osGDBTablX = CPLResetExtension(pszRadixTablename, "gdbtablx");
205 CPLString osNewGDBTablX = CPLResetExtension(pszRadixTablename, "gdbtablx.new");
206
207 if( !EditGDBTablX(osGDBTablX, osNewGDBTablX) )
208 {
209 CPLError( CE_Failure, CPLE_AppDefined,
210 "Error occurred when editing %s", osNewGDBTablX.c_str());
211 VSIUnlink(osNewGDBTablX);
212 return FALSE;
213 }
214
215 CPLString osDirectory(CPLGetPath(pszRadixTablename));
216 char** papszFiles = VSIReadDir(osDirectory);
217 CPLString osBasename(CPLGetBasename(pszRadixTablename));
218 int bRet = TRUE;
219 for(char** papszIter = papszFiles; papszIter && *papszIter; papszIter++)
220 {
221 if( strncmp(*papszIter, osBasename.c_str(), osBasename.size()) == 0 &&
222 (EQUAL(CPLGetExtension(*papszIter), "atx") ||
223 EQUAL(CPLGetExtension(*papszIter), "spx")) )
224 {
225 CPLString osIndex(CPLFormFilename(osDirectory, *papszIter, nullptr));
226 if( !EditATXOrSPX(osIndex) )
227 {
228 CPLError( CE_Failure, CPLE_AppDefined,
229 "Error occurred when editing %s", osIndex.c_str());
230 bRet = FALSE;
231 }
232 }
233 }
234 CSLDestroy(papszFiles);
235
236 CPLString osGDBTablXTmp(CPLSPrintf("%s.tmp", osGDBTablX.c_str()));
237 int bRet2 = (VSIRename(osGDBTablX,osGDBTablXTmp) == 0 &&
238 VSIRename(osNewGDBTablX, osGDBTablX) == 0);
239 VSIUnlink(osGDBTablXTmp);
240 if( !bRet2 )
241 {
242 CPLError(CE_Failure, CPLE_AppDefined,
243 "Cannot rename %s to %s", osNewGDBTablX.c_str(), osGDBTablX.c_str());
244 bRet = FALSE;
245 }
246
247 return bRet;
248 }
249
250 /************************************************************************/
251 /* EditATXOrSPX() */
252 /************************************************************************/
253
254 /* See https://github.com/rouault/dump_gdbtable/wiki/FGDB-Spec */
EditATXOrSPX(const CPLString & osIndex)255 int FGdbLayer::EditATXOrSPX( const CPLString& osIndex )
256 {
257 VSILFILE* fp = VSIFOpenL(osIndex, "rb+");
258 if (fp == nullptr )
259 {
260 CPLError(CE_Failure, CPLE_FileIO, "Cannot open %s", osIndex.c_str());
261 return FALSE;
262 }
263 VSIFSeekL(fp, 0, SEEK_END);
264 vsi_l_offset nPos = VSIFTellL(fp);
265 int bRet = FALSE;
266 int bInvalidateIndex = FALSE;
267 if( nPos > 22 )
268 {
269 VSIFSeekL(fp, nPos - 22, SEEK_SET);
270 GByte nSizeIndexedValue;
271 if( VSIFReadL(&nSizeIndexedValue, 1, 1, fp) == 1 &&
272 nSizeIndexedValue > 0 )
273 {
274 GByte abyIndexedValue[255];
275 VSIFSeekL(fp, nPos - 22 + 6, SEEK_SET);
276 int nDepth;
277 if( VSIFReadL(&nDepth, 1, 4, fp) == 4 )
278 {
279 CPL_LSBPTR32(&nDepth);
280
281 int bIndexedValueIsValid = FALSE;
282 int nFirstIndexAtThisValue = -1;
283 std::vector<int> anPagesAtThisValue;
284 int bSortThisValue = FALSE;
285 int nLastPageVisited = 0;
286 bRet = EditATXOrSPX(fp,
287 1,
288 nLastPageVisited,
289 nDepth,
290 nSizeIndexedValue,
291 abyIndexedValue,
292 bIndexedValueIsValid,
293 nFirstIndexAtThisValue,
294 anPagesAtThisValue,
295 bSortThisValue,
296 bInvalidateIndex);
297 }
298 }
299 }
300 VSIFCloseL(fp);
301 if( bInvalidateIndex )
302 {
303 //CPLDebug("FGDB", "Invalidate %s", osIndex.c_str());
304 CPLError(CE_Warning, CPLE_AppDefined, "Invalidate %s", osIndex.c_str());
305 VSIUnlink(osIndex);
306 }
307 return bRet;
308 }
309
FGdbLayerSortATX(const void * _pa,const void * _pb)310 static int FGdbLayerSortATX(const void* _pa, const void* _pb)
311 {
312 int a = CPL_LSBWORD32(*(int*)_pa);
313 int b = CPL_LSBWORD32(*(int*)_pb);
314 if( a < b )
315 return -1;
316 else if( a > b )
317 return 1;
318 CPLAssert(false);
319 return 0;
320 }
321
EditATXOrSPX(VSILFILE * fp,int nThisPage,int & nLastPageVisited,int nDepth,int nSizeIndexedValue,GByte * pabyLastIndexedValue,int & bIndexedValueIsValid,int & nFirstIndexAtThisValue,std::vector<int> & anPagesAtThisValue,int & bSortThisValue,int & bInvalidateIndex)322 int FGdbLayer::EditATXOrSPX(VSILFILE* fp,
323 int nThisPage,
324 int& nLastPageVisited,
325 int nDepth,
326 int nSizeIndexedValue,
327 GByte* pabyLastIndexedValue,
328 int& bIndexedValueIsValid,
329 int& nFirstIndexAtThisValue,
330 std::vector<int>& anPagesAtThisValue,
331 int& bSortThisValue,
332 int& bInvalidateIndex)
333 {
334 GByte abyBuffer[4096];
335
336 VSIFSeekL(fp, (nThisPage - 1) * 4096, SEEK_SET);
337
338 if( nDepth == 1 )
339 {
340 if( nThisPage == nLastPageVisited )
341 return TRUE;
342
343 /* This page directly references features */
344 int bRewritePage = FALSE;
345 if( VSIFReadL(abyBuffer, 1, 4096, fp) != 4096 )
346 return FALSE;
347 int nNextPageID;
348 memcpy(&nNextPageID, abyBuffer, 4);
349 int nFeatures;
350 memcpy(&nFeatures, abyBuffer + 4, 4);
351 CPL_LSBPTR32(&nFeatures);
352
353 //if( nLastPageVisited == 0 )
354 // printf("nFeatures = %d\n", nFeatures);
355
356 const int nMaxPerPages = (4096 - 12) / (4 + nSizeIndexedValue);
357 const int nOffsetFirstValInPage = 12 + nMaxPerPages * 4;
358 if( nFeatures > nMaxPerPages )
359 return FALSE;
360 for(int i=0; i < nFeatures; i++)
361 {
362 int bNewVal = ( !bIndexedValueIsValid ||
363 memcmp(pabyLastIndexedValue,
364 abyBuffer + nOffsetFirstValInPage + i * nSizeIndexedValue,
365 nSizeIndexedValue) != 0 );
366
367 int nFID;
368 memcpy(&nFID, abyBuffer + 12 + 4 * i, 4);
369 CPL_LSBPTR32(&nFID);
370 int nOGRFID = m_oMapFGDBFIDToOGRFID[nFID];
371 if( nOGRFID )
372 {
373 nFID = nOGRFID;
374 CPL_LSBPTR32(&nOGRFID);
375 memcpy(abyBuffer + 12 + 4 * i, &nOGRFID, 4);
376 bRewritePage = TRUE;
377
378 if( bIndexedValueIsValid && i == nFeatures - 1 && nNextPageID == 0 )
379 bSortThisValue = TRUE;
380 }
381
382 // We must make sure that features with same indexed values are
383 // sorted by increasing FID, even when that spans over several
384 // pages
385 if( bSortThisValue && (bNewVal || (i == nFeatures - 1 && nNextPageID == 0)) )
386 {
387 if( anPagesAtThisValue[0] == nThisPage )
388 {
389 CPLAssert(anPagesAtThisValue.size() == 1);
390 int nFeaturesToSortThisPage = i - nFirstIndexAtThisValue;
391 if( !bNewVal && i == nFeatures - 1 && nNextPageID == 0 )
392 nFeaturesToSortThisPage ++;
393 CPLAssert(nFeaturesToSortThisPage > 0);
394
395 bRewritePage = TRUE;
396 qsort(abyBuffer + 12 + 4 * nFirstIndexAtThisValue,
397 nFeaturesToSortThisPage, 4, FGdbLayerSortATX);
398 }
399 else
400 {
401 std::vector<int> anValues;
402 int nFeaturesToSort = 0;
403 anValues.resize(anPagesAtThisValue.size() * nMaxPerPages);
404
405 int nFeaturesToSortLastPage = i;
406 if( !bNewVal && i == nFeatures - 1 && nNextPageID == 0 )
407 nFeaturesToSortLastPage ++;
408
409 for(size_t j=0;j<anPagesAtThisValue.size();j++)
410 {
411 int nFeaturesPrevPage;
412 VSIFSeekL(fp, (anPagesAtThisValue[j]-1) * 4096 + 4, SEEK_SET);
413 VSIFReadL(&nFeaturesPrevPage, 1, 4, fp);
414 CPL_LSBPTR32(&nFeaturesPrevPage);
415 if( j == 0 )
416 {
417 VSIFSeekL(fp, (anPagesAtThisValue[j]-1) * 4096 + 12 + 4 * nFirstIndexAtThisValue, SEEK_SET);
418 VSIFReadL(&anValues[nFeaturesToSort], 4, nFeaturesPrevPage - nFirstIndexAtThisValue, fp);
419 nFeaturesToSort += nFeaturesPrevPage - nFirstIndexAtThisValue;
420 }
421 else if( j == anPagesAtThisValue.size() - 1 && anPagesAtThisValue[j] == nThisPage )
422 {
423 bRewritePage = TRUE;
424 memcpy(&anValues[nFeaturesToSort], abyBuffer + 12, nFeaturesToSortLastPage * 4);
425 nFeaturesToSort += nFeaturesToSortLastPage;
426 }
427 else
428 {
429 VSIFSeekL(fp, (anPagesAtThisValue[j]-1) * 4096 + 12, SEEK_SET);
430 VSIFReadL(&anValues[nFeaturesToSort], 4, nFeaturesPrevPage, fp);
431 nFeaturesToSort += nFeaturesPrevPage;
432 }
433 }
434
435 qsort(&anValues[0], nFeaturesToSort, 4, FGdbLayerSortATX);
436
437 nFeaturesToSort = 0;
438 for(size_t j=0;j<anPagesAtThisValue.size();j++)
439 {
440 int nFeaturesPrevPage;
441 VSIFSeekL(fp, (anPagesAtThisValue[j]-1) * 4096 + 4, SEEK_SET);
442 VSIFReadL(&nFeaturesPrevPage, 1, 4, fp);
443 CPL_LSBPTR32(&nFeaturesPrevPage);
444 if( j == 0 )
445 {
446 VSIFSeekL(fp, (anPagesAtThisValue[j]-1) * 4096 + 12 + 4 * nFirstIndexAtThisValue, SEEK_SET);
447 VSIFWriteL(&anValues[nFeaturesToSort], 4, nFeaturesPrevPage - nFirstIndexAtThisValue, fp);
448 nFeaturesToSort += nFeaturesPrevPage - nFirstIndexAtThisValue;
449 }
450 else if( j == anPagesAtThisValue.size() - 1 && anPagesAtThisValue[j] == nThisPage )
451 {
452 memcpy(abyBuffer + 12, &anValues[nFeaturesToSort], nFeaturesToSortLastPage * 4);
453 nFeaturesToSort += nFeaturesToSortLastPage;
454 }
455 else
456 {
457 VSIFSeekL(fp, (anPagesAtThisValue[j]-1) * 4096 + 12, SEEK_SET);
458 VSIFWriteL(&anValues[nFeaturesToSort], 4, nFeaturesPrevPage, fp);
459 nFeaturesToSort += nFeaturesPrevPage;
460 }
461 }
462 }
463 }
464
465 if( bNewVal )
466 {
467 nFirstIndexAtThisValue = i;
468 anPagesAtThisValue.clear();
469 anPagesAtThisValue.push_back(nThisPage);
470
471 memcpy(pabyLastIndexedValue,
472 abyBuffer + nOffsetFirstValInPage + i * nSizeIndexedValue,
473 nSizeIndexedValue);
474 bSortThisValue = FALSE;
475 }
476 else if( i == 0 )
477 {
478 if( anPagesAtThisValue.size() > 100000 )
479 {
480 bInvalidateIndex = TRUE;
481 return FALSE;
482 }
483 else
484 {
485 anPagesAtThisValue.push_back(nThisPage);
486 }
487 }
488
489 if( nOGRFID )
490 bSortThisValue = TRUE;
491
492 bIndexedValueIsValid = TRUE;
493 }
494
495 if( bRewritePage )
496 {
497 VSIFSeekL(fp, (nThisPage - 1) * 4096, SEEK_SET);
498 if( VSIFWriteL(abyBuffer, 1, 4096, fp) != 4096 )
499 return FALSE;
500 }
501
502 nLastPageVisited = nThisPage;
503
504 return TRUE;
505 }
506 else
507 {
508 /* This page references other pages */
509 if( VSIFReadL(abyBuffer, 1, 4096, fp) != 4096 )
510 return FALSE;
511 int nSubPages;
512 memcpy(&nSubPages, abyBuffer + 4, 4);
513 CPL_LSBPTR32(&nSubPages);
514 nSubPages ++;
515 if( nSubPages > (4096 - 8) / 4 )
516 return FALSE;
517 for(int i=0; i < nSubPages; i++)
518 {
519 int nSubPageID;
520 memcpy(&nSubPageID, abyBuffer + 8 + 4 * i, 4);
521 CPL_LSBPTR32(&nSubPageID);
522 if( nSubPageID < 1 )
523 return FALSE;
524 if( !EditATXOrSPX(fp,
525 nSubPageID,
526 nLastPageVisited,
527 nDepth - 1,
528 nSizeIndexedValue,
529 pabyLastIndexedValue,
530 bIndexedValueIsValid,
531 nFirstIndexAtThisValue,
532 anPagesAtThisValue,
533 bSortThisValue,
534 bInvalidateIndex) )
535 {
536 return FALSE;
537 }
538 }
539
540 return TRUE;
541 }
542 }
543
544 /************************************************************************/
545 /* GetInt32() */
546 /************************************************************************/
547
GetInt32(const GByte * pBaseAddr,int iOffset)548 static GInt32 GetInt32(const GByte* pBaseAddr, int iOffset)
549 {
550 GInt32 nVal;
551 memcpy(&nVal, pBaseAddr + sizeof(nVal) * iOffset, sizeof(nVal));
552 CPL_LSBPTR32(&nVal);
553 return nVal;
554 }
555
556 /************************************************************************/
557 /* UpdateNextOGRFIDAndFGDBFID() */
558 /************************************************************************/
559
UpdateNextOGRFIDAndFGDBFID(int i,std::map<int,int> & oMapOGRFIDToFGDBFID,std::map<int,int>::iterator & oIterO2F,int & nNextOGRFID,std::map<int,int> & oMapFGDBFIDToOGRFID,std::map<int,int>::iterator & oIterF2O,int & nNextFGDBFID)560 static CPL_INLINE void UpdateNextOGRFIDAndFGDBFID(int i,
561 std::map<int,int>& oMapOGRFIDToFGDBFID,
562 std::map<int,int>::iterator& oIterO2F,
563 int& nNextOGRFID,
564 std::map<int,int>& oMapFGDBFIDToOGRFID,
565 std::map<int,int>::iterator& oIterF2O,
566 int& nNextFGDBFID)
567 {
568 while( nNextOGRFID > 0 && i > nNextOGRFID )
569 {
570 ++ oIterO2F;
571 if( oIterO2F == oMapOGRFIDToFGDBFID.end() )
572 nNextOGRFID = -1;
573 else
574 nNextOGRFID = oIterO2F->first;
575 }
576
577 while( nNextFGDBFID > 0 && i > nNextFGDBFID )
578 {
579 ++ oIterF2O;
580 if( oIterF2O == oMapFGDBFIDToOGRFID.end() )
581 nNextFGDBFID = -1;
582 else
583 nNextFGDBFID = oIterF2O->first;
584 }
585 }
586
587 /************************************************************************/
588 /* EditGDBTablX() */
589 /************************************************************************/
590
591 #define TEST_BIT(ar, bit) (ar[(bit) / 8] & (1 << ((bit) % 8)))
592 #define SET_BIT(ar,bit) ar[(bit)/8] |= (1 << ((bit) % 8))
593 #define BIT_ARRAY_SIZE_IN_BYTES(bitsize) (((bitsize)+7)/8)
594
595 /* See https://github.com/rouault/dump_gdbtable/wiki/FGDB-Spec */
EditGDBTablX(const CPLString & osGDBTablX,const CPLString & osNewGDBTablX)596 int FGdbLayer::EditGDBTablX( const CPLString& osGDBTablX,
597 const CPLString& osNewGDBTablX )
598 {
599 VSILFILE* fp = VSIFOpenL(osGDBTablX, "rb");
600 if (fp == nullptr )
601 {
602 CPLError(CE_Failure, CPLE_FileIO, "Cannot open %s", osGDBTablX.c_str());
603 return FALSE;
604 }
605 VSILFILE* fpNew = VSIFOpenL(osNewGDBTablX, "wb");
606 if (fpNew == nullptr )
607 {
608 CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s", osNewGDBTablX.c_str());
609 VSIFCloseL(fp);
610 return FALSE;
611 }
612 GByte abyBuffer[16];
613 VSIFReadL(abyBuffer, 1, 16, fp);
614 int n1024Blocks = GetInt32(abyBuffer, 1);
615 int nInMaxFID = GetInt32(abyBuffer, 2);
616 #ifdef DEBUG
617 const int nInMaxFIDOri = nInMaxFID;
618 #endif
619 int nRecordSize = GetInt32(abyBuffer, 3);
620 CPLAssert(nRecordSize >= 4 && nRecordSize <= 6);
621
622 std::map<int,int>::iterator oIterO2F = m_oMapOGRFIDToFGDBFID.begin();
623 int nMaxOGRFID = 0;
624 for(; oIterO2F != m_oMapOGRFIDToFGDBFID.end(); ++oIterO2F )
625 nMaxOGRFID = oIterO2F->first;
626 //printf("nInMaxFID = %d\n", nInMaxFID);
627 //printf("nMaxOGRFID = %d\n", nMaxOGRFID);
628 int nOutMaxFID = MAX(nInMaxFID, nMaxOGRFID);
629
630 // Optimization: If the feature ids at the end of the file all map to a OGR fid
631 // then they don't need to be included in the final file
632 for(int i=nInMaxFID; i>nMaxOGRFID; i--)
633 {
634 if( m_oMapFGDBFIDToOGRFID.find(i) != m_oMapFGDBFIDToOGRFID.end() )
635 {
636 nOutMaxFID --;
637 nInMaxFID --;
638 }
639 else
640 break;
641 }
642
643 //printf("nInMaxFID = %d\n", nInMaxFID);
644 //printf("nOutMaxFID = %d\n", nOutMaxFID);
645
646 int n1024BlocksOut = (int)(((GIntBig)nOutMaxFID + 1023) / 1024);
647 int nTmp;
648
649 nTmp = CPL_LSBWORD32(n1024BlocksOut);
650 memcpy(abyBuffer + 4, &nTmp, 4);
651
652 nTmp = CPL_LSBWORD32(nOutMaxFID);
653 memcpy(abyBuffer + 8, &nTmp, 4);
654 VSIFWriteL(abyBuffer, 1, 16, fpNew);
655
656 VSIFSeekL(fp, 1024 * n1024Blocks * nRecordSize, SEEK_CUR);
657 VSIFReadL(abyBuffer, 1, 16, fp);
658 int nBitmapInt32Words = GetInt32(abyBuffer, 0);
659 int n1024BlocksTotal = GetInt32(abyBuffer, 1);
660 CPLAssert(n1024BlocksTotal == (int)(((GIntBig)nInMaxFIDOri + 1023 ) / 1024) );
661 GByte* pabyBlockMap = nullptr;
662 if( nBitmapInt32Words != 0 )
663 {
664 int nSizeInBytes = BIT_ARRAY_SIZE_IN_BYTES(n1024BlocksTotal);
665 pabyBlockMap = (GByte*) CPLMalloc( nSizeInBytes );
666 VSIFReadL( pabyBlockMap, nSizeInBytes, 1, fp );
667 }
668 int nSizeInBytesOut = BIT_ARRAY_SIZE_IN_BYTES(n1024BlocksOut);
669 /* Round to the next multiple of 128 bytes (32 int4 words) */
670 nSizeInBytesOut = ((nSizeInBytesOut + 127) / 128) * 128;
671 GByte* pabyBlockMapOut = (GByte*) VSI_CALLOC_VERBOSE( 1, nSizeInBytesOut );
672 GByte* pabyPage = (GByte*)VSI_MALLOC_VERBOSE( 1024 * nRecordSize );
673 if( pabyBlockMapOut == nullptr || pabyPage == nullptr )
674 {
675 VSIFree(pabyBlockMapOut);
676 VSIFree(pabyPage);
677 VSIFCloseL(fp);
678 return FALSE;
679 }
680 GByte abyEmptyOffset[6];
681 memset(abyEmptyOffset, 0, 6);
682 int nNonEmptyPages = 0;
683 int nOffsetInPage = 0, nLastWrittenOffset = 0;
684 int bDisableSparsePages = CPLTestBool(CPLGetConfigOption("FILEGDB_DISABLE_SPARSE_PAGES", "NO"));
685
686 oIterO2F = m_oMapOGRFIDToFGDBFID.begin();
687 int nNextOGRFID = oIterO2F->first;
688 std::map<int,int>::iterator oIterF2O = m_oMapFGDBFIDToOGRFID.begin();
689 int nNextFGDBFID = oIterF2O->first;
690
691 int nCountBlocksBeforeIBlockIdx = 0;
692 int nCountBlocksBeforeIBlockValue = 0;
693
694 int bRet = TRUE;
695 for(int i=1; i<=nOutMaxFID;i++, nOffsetInPage += nRecordSize)
696 {
697 if( nOffsetInPage == 1024 * nRecordSize )
698 {
699 if( nLastWrittenOffset > 0 || bDisableSparsePages )
700 {
701 SET_BIT(pabyBlockMapOut, (i - 2) / 1024);
702 nNonEmptyPages ++;
703 if( nLastWrittenOffset < nOffsetInPage )
704 memset(pabyPage + nLastWrittenOffset, 0, nOffsetInPage - nLastWrittenOffset);
705 if( VSIFWriteL(pabyPage, 1024 * nRecordSize, 1, fpNew) != 1 )
706 {
707 bRet = FALSE;
708 goto end;
709 }
710 }
711 nOffsetInPage = 0;
712 nLastWrittenOffset = 0;
713
714 // A few optimizations :
715 if( !bDisableSparsePages && i > nInMaxFID && nNextOGRFID > 0 && i < nNextOGRFID - 1024 )
716 {
717 // If we created a OGR FID far away from the latest FGDB FID
718 // then skip to it
719 i = ((nNextOGRFID-1) / 1024) * 1024 + 1;
720 }
721 // coverity[negative_shift]
722 else if( !bDisableSparsePages && pabyBlockMap != nullptr && i <= nInMaxFID &&
723 TEST_BIT(pabyBlockMap, (i-1)/1024) == 0 )
724 {
725 // Skip empty pages
726 UpdateNextOGRFIDAndFGDBFID(i,
727 m_oMapOGRFIDToFGDBFID, oIterO2F, nNextOGRFID,
728 m_oMapFGDBFIDToOGRFID, oIterF2O, nNextFGDBFID);
729 if( (nNextOGRFID < 0 || i < nNextOGRFID - 1024) &&
730 (nNextFGDBFID < 0 || i < nNextFGDBFID - 1024) )
731 {
732 if( i > INT_MAX - 1024 )
733 break;
734 i += 1023;
735 nOffsetInPage += 1023 * nRecordSize;
736 continue;
737 }
738 }
739 }
740
741 UpdateNextOGRFIDAndFGDBFID(i,
742 m_oMapOGRFIDToFGDBFID, oIterO2F, nNextOGRFID,
743 m_oMapFGDBFIDToOGRFID, oIterF2O, nNextFGDBFID);
744
745 int nSrcFID;
746 if( i == nNextOGRFID )
747 {
748 // This FID matches a user defined OGR FID, then find the
749 // corresponding FGDB record
750 nSrcFID = oIterO2F->second;
751 //printf("(1) i = %d, nSrcFID = %d\n", i, nSrcFID);
752 }
753 else if( i == nNextFGDBFID || i > nInMaxFID )
754 {
755 // This record is a temporary one (will be moved to a user-define FID)
756 // or we are out of the validity zone of input records
757 //printf("(2) i = %d, nNextFGDBFID = %d, nInMaxFID = %d\n", i, nNextFGDBFID, nInMaxFID);
758 continue;
759 }
760 else
761 {
762 // Regular record, not overloaded by user defined FID
763 nSrcFID = i;
764 //printf("(3) i = %d, nSrcFID = %d\n", i, nSrcFID);
765 }
766
767 if( pabyBlockMap != nullptr )
768 {
769 int iBlock = (nSrcFID-1) / 1024;
770
771 // Check if the block is not empty
772 // coverity[negative_shift]
773 if( TEST_BIT(pabyBlockMap, iBlock) )
774 {
775 int nCountBlocksBefore;
776 if( iBlock >= nCountBlocksBeforeIBlockIdx )
777 {
778 nCountBlocksBefore = nCountBlocksBeforeIBlockValue;
779 for(int j=nCountBlocksBeforeIBlockIdx;j<iBlock;j++)
780 {
781 // coverity[negative_shift]
782 nCountBlocksBefore += TEST_BIT(pabyBlockMap, j) != 0;
783 }
784 }
785 else
786 {
787 nCountBlocksBefore = 0;
788 for(int j=0;j<iBlock;j++)
789 {
790 // coverity[negative_shift]
791 nCountBlocksBefore += TEST_BIT(pabyBlockMap, j) != 0;
792 }
793 }
794 nCountBlocksBeforeIBlockIdx = iBlock;
795 nCountBlocksBeforeIBlockValue = nCountBlocksBefore;
796 int iCorrectedRow = nCountBlocksBefore * 1024 + ((nSrcFID-1) % 1024);
797 VSIFSeekL(fp, 16 + nRecordSize * iCorrectedRow, SEEK_SET);
798 VSIFReadL(abyBuffer, 1, nRecordSize, fp);
799 if( memcmp( abyBuffer, abyEmptyOffset, nRecordSize) != 0 )
800 {
801 if( nLastWrittenOffset < nOffsetInPage )
802 memset(pabyPage + nLastWrittenOffset, 0, nOffsetInPage - nLastWrittenOffset);
803 memcpy(pabyPage + nOffsetInPage, abyBuffer, nRecordSize);
804 nLastWrittenOffset = nOffsetInPage + nRecordSize;
805 }
806 }
807 }
808 else
809 {
810 VSIFSeekL(fp, 16 + nRecordSize * (nSrcFID-1), SEEK_SET);
811 VSIFReadL(abyBuffer, 1, nRecordSize, fp);
812 if( memcmp( abyBuffer, abyEmptyOffset, nRecordSize) != 0 )
813 {
814 if( nLastWrittenOffset < nOffsetInPage )
815 memset(pabyPage + nLastWrittenOffset, 0, nOffsetInPage - nLastWrittenOffset);
816 memcpy(pabyPage + nOffsetInPage, abyBuffer, nRecordSize);
817 nLastWrittenOffset = nOffsetInPage + nRecordSize;
818 }
819 }
820 }
821 //printf("nLastWrittenOffset = %d\n", nLastWrittenOffset);
822 if( nLastWrittenOffset > 0 || bDisableSparsePages )
823 {
824 assert( nOutMaxFID >= 1 );
825 SET_BIT(pabyBlockMapOut, (nOutMaxFID - 1) / 1024);
826 nNonEmptyPages ++;
827 if( nLastWrittenOffset < 1024 * nRecordSize )
828 memset(pabyPage + nLastWrittenOffset, 0, 1024 * nRecordSize - nLastWrittenOffset);
829 if( VSIFWriteL(pabyPage, 1024 * nRecordSize, 1, fpNew) != 1 )
830 {
831 bRet = FALSE;
832 goto end;
833 }
834 }
835
836 memset(abyBuffer, 0, 16);
837
838 /* Number of total blocks, including omitted ones */
839 nTmp = CPL_LSBWORD32(n1024BlocksOut);
840 memcpy(abyBuffer + 4, &nTmp, 4);
841
842 nTmp = CPL_LSBWORD32(nNonEmptyPages);
843 memcpy(abyBuffer + 8, &nTmp, 4);
844
845 if( nNonEmptyPages < n1024BlocksOut )
846 {
847 /* Number of int4 words for the bitmap (rounded to the next multiple of 32) */
848 nTmp = CPL_LSBWORD32(nSizeInBytesOut / 4);
849 memcpy(abyBuffer + 0, &nTmp, 4);
850
851 /* Number of int4 words in the bitmap where there's at least a non-zero bit */
852 /* Seems to be unused */
853 nTmp = CPL_LSBWORD32(((nOutMaxFID - 1) / 1024 + 31) / 32);
854 memcpy(abyBuffer + 12, &nTmp, 4);
855 }
856
857 if( VSIFWriteL(abyBuffer, 1, 16, fpNew) != 16 )
858 {
859 bRet = FALSE;
860 goto end;
861 }
862
863 if( nNonEmptyPages < n1024BlocksOut )
864 {
865 VSIFWriteL(pabyBlockMapOut, 1, nSizeInBytesOut, fpNew);
866
867 VSIFSeekL(fpNew, 4, SEEK_SET);
868 nTmp = CPL_LSBWORD32(nNonEmptyPages);
869 VSIFWriteL(&nTmp, 1, 4, fpNew);
870 }
871
872 end:
873 CPLFree(pabyBlockMap);
874 CPLFree(pabyBlockMapOut);
875 CPLFree(pabyPage);
876 VSIFCloseL(fpNew);
877 VSIFCloseL(fp);
878
879 return bRet;
880 }
881
882 #ifdef EXTENT_WORKAROUND
883
884 /************************************************************************/
885 /* UpdateRowWithGeometry() */
886 /************************************************************************/
887
UpdateRowWithGeometry(Row & row,OGRGeometry * poGeom)888 bool FGdbLayer::UpdateRowWithGeometry(Row& row, OGRGeometry* poGeom)
889 {
890 ShapeBuffer shape;
891 long hr;
892
893 /* Write geometry to a buffer */
894 GByte *pabyShape = nullptr;
895 int nShapeSize = 0;
896 if ( OGRWriteToShapeBin( poGeom, &pabyShape, &nShapeSize ) != OGRERR_NONE )
897 {
898 CPLFree(pabyShape);
899 return false;
900 }
901
902 /* Copy it into a ShapeBuffer */
903 if ( nShapeSize > 0 )
904 {
905 shape.Allocate(nShapeSize);
906 memcpy(shape.shapeBuffer, pabyShape, nShapeSize);
907 shape.inUseLength = nShapeSize;
908 }
909
910 /* Free the shape buffer */
911 CPLFree(pabyShape);
912
913 /* Write ShapeBuffer into the Row */
914 hr = row.SetGeometry(shape);
915 if (FAILED(hr))
916 {
917 return false;
918 }
919
920 /* Update row */
921 hr = m_pTable->Update(row);
922 if (FAILED(hr))
923 {
924 return false;
925 }
926
927 return true;
928 }
929
930 /************************************************************************/
931 /* WorkAroundExtentProblem() */
932 /* */
933 /* Work-around problem with FileGDB API 1.1 on Linux 64bit. See #4455 */
934 /************************************************************************/
935
WorkAroundExtentProblem()936 void FGdbLayer::WorkAroundExtentProblem()
937 {
938 if (!m_bLayerJustCreated || !m_bLayerEnvelopeValid)
939 return;
940 m_bLayerJustCreated = FALSE;
941
942 OGREnvelope sEnvelope;
943 if (FGdbLayer::GetExtent(&sEnvelope, TRUE) != OGRERR_NONE)
944 return;
945
946 /* The characteristic of the bug is that the reported extent */
947 /* is the real extent truncated incorrectly to integer values */
948 /* We work around that by temporary updating one feature with a geometry */
949 /* whose coordinates are integer values but ceil'ed and floor'ed */
950 /* such that they include the real layer extent. */
951 if (((double)(int)sEnvelope.MinX == sEnvelope.MinX &&
952 (double)(int)sEnvelope.MinY == sEnvelope.MinY &&
953 (double)(int)sEnvelope.MaxX == sEnvelope.MaxX &&
954 (double)(int)sEnvelope.MaxY == sEnvelope.MaxY) &&
955 (fabs(sEnvelope.MinX - sLayerEnvelope.MinX) > 1e-5 ||
956 fabs(sEnvelope.MinY - sLayerEnvelope.MinY) > 1e-5 ||
957 fabs(sEnvelope.MaxX - sLayerEnvelope.MaxX) > 1e-5 ||
958 fabs(sEnvelope.MaxY - sLayerEnvelope.MaxY) > 1e-5))
959 {
960 long hr;
961 Row row;
962 EnumRows enumRows;
963
964 if (FAILED(hr = m_pTable->Search(StringToWString("*"), StringToWString(""), true, enumRows)))
965 return;
966
967 if (FAILED(hr = enumRows.Next(row)))
968 return;
969
970 if (hr != S_OK)
971 return;
972
973 /* Backup original shape buffer */
974 ShapeBuffer originalGdbGeometry;
975 if (FAILED(hr = row.GetGeometry(originalGdbGeometry)))
976 return;
977
978 OGRGeometry* pOGRGeo = nullptr;
979 if ((!GDBGeometryToOGRGeometry(m_forceMulti, &originalGdbGeometry, m_pSRS, &pOGRGeo)) || pOGRGeo == nullptr)
980 {
981 delete pOGRGeo;
982 return;
983 }
984
985 OGRwkbGeometryType eType = wkbFlatten(pOGRGeo->getGeometryType());
986
987 delete pOGRGeo;
988 pOGRGeo = nullptr;
989
990 OGRPoint oP1(floor(sLayerEnvelope.MinX), floor(sLayerEnvelope.MinY));
991 OGRPoint oP2(ceil(sLayerEnvelope.MaxX), ceil(sLayerEnvelope.MaxY));
992
993 OGRLinearRing oLR;
994 oLR.addPoint(&oP1);
995 oLR.addPoint(&oP2);
996 oLR.addPoint(&oP1);
997
998 if ( eType == wkbPoint )
999 {
1000 UpdateRowWithGeometry(row, &oP1);
1001 UpdateRowWithGeometry(row, &oP2);
1002 }
1003 else if ( eType == wkbLineString )
1004 {
1005 UpdateRowWithGeometry(row, &oLR);
1006 }
1007 else if ( eType == wkbPolygon )
1008 {
1009 OGRPolygon oPoly;
1010 oPoly.addRing(&oLR);
1011
1012 UpdateRowWithGeometry(row, &oPoly);
1013 }
1014 else if ( eType == wkbMultiPoint )
1015 {
1016 OGRMultiPoint oColl;
1017 oColl.addGeometry(&oP1);
1018 oColl.addGeometry(&oP2);
1019
1020 UpdateRowWithGeometry(row, &oColl);
1021 }
1022 else if ( eType == wkbMultiLineString )
1023 {
1024 OGRMultiLineString oColl;
1025 oColl.addGeometry(&oLR);
1026
1027 UpdateRowWithGeometry(row, &oColl);
1028 }
1029 else if ( eType == wkbMultiPolygon )
1030 {
1031 OGRMultiPolygon oColl;
1032 OGRPolygon oPoly;
1033 oPoly.addRing(&oLR);
1034 oColl.addGeometry(&oPoly);
1035
1036 UpdateRowWithGeometry(row, &oColl);
1037 }
1038 else
1039 return;
1040
1041 /* Restore original ShapeBuffer */
1042 hr = row.SetGeometry(originalGdbGeometry);
1043 if (FAILED(hr))
1044 return;
1045
1046 /* Update Row */
1047 hr = m_pTable->Update(row);
1048 if (FAILED(hr))
1049 return;
1050
1051 CPLDebug("FGDB", "Workaround extent problem with Linux 64bit FGDB SDK 1.1");
1052 }
1053 }
1054 #endif // EXTENT_WORKAROUND
1055
1056 /************************************************************************/
1057 /* ICreateFeature() */
1058 /* Create an FGDB Row and populate it from an OGRFeature. */
1059 /* */
1060 /************************************************************************/
1061
ICreateFeature(OGRFeature * poFeature)1062 OGRErr FGdbLayer::ICreateFeature( OGRFeature *poFeature )
1063 {
1064 Row fgdb_row;
1065 fgdbError hr;
1066
1067 if( !m_pDS->GetUpdate() || m_pTable == nullptr )
1068 return OGRERR_FAILURE;
1069
1070 GIntBig nFID = poFeature->GetFID();
1071 if( nFID < -1 || nFID == 0 || !CPL_INT64_FITS_ON_INT32(nFID) )
1072 {
1073 CPLError(CE_Failure, CPLE_NotSupported,
1074 "Only 32 bit positive integers FID supported by FileGDB");
1075 return OGRERR_FAILURE;
1076 }
1077
1078 if( nFID > 0 )
1079 {
1080 if( m_pDS->GetOpenFileGDBDrv() == nullptr )
1081 {
1082 CPLError(CE_Failure, CPLE_AppDefined,
1083 "Cannot call CreateFeature() with a set FID when OpenFileGDB driver not available");
1084 return OGRERR_FAILURE;
1085 }
1086
1087 if( m_pDS->HasSelectLayers() )
1088 {
1089 CPLError(CE_Failure, CPLE_AppDefined,
1090 "Cannot call CreateFeature() with a set FID when a layer resulting from ExecuteSQL() is still opened");
1091 return OGRERR_FAILURE;
1092 }
1093
1094 if( m_pDS->GetConnection()->GetRefCount() > 1 )
1095 {
1096 CPLError(CE_Failure, CPLE_AppDefined,
1097 "Cannot call CreateFeature() with a set FID when a dataset is opened more than once");
1098 return OGRERR_FAILURE;
1099 }
1100
1101 if( m_oMapOGRFIDToFGDBFID.find((int)poFeature->GetFID()) != m_oMapOGRFIDToFGDBFID.end() )
1102 {
1103 CPLError(CE_Failure, CPLE_AppDefined,
1104 "A feature with same FID already exists");
1105 return OGRERR_FAILURE;
1106 }
1107
1108 if( m_oMapFGDBFIDToOGRFID.find((int)poFeature->GetFID()) == m_oMapFGDBFIDToOGRFID.end() )
1109 {
1110 EnumRows enumRows;
1111 Row row;
1112 if (GetRow(enumRows, row, (int)poFeature->GetFID()) == OGRERR_NONE)
1113 {
1114 CPLError(CE_Failure, CPLE_AppDefined,
1115 "A feature with same FID already exists");
1116 return OGRERR_FAILURE;
1117 }
1118 }
1119
1120 if( (int)m_oMapOGRFIDToFGDBFID.size() == m_nResyncThreshold )
1121 ResyncIDs();
1122 }
1123
1124 if( m_bSymlinkFlag && !CreateRealCopy() )
1125 return OGRERR_FAILURE;
1126
1127 if (m_bBulkLoadAllowed < 0)
1128 m_bBulkLoadAllowed = CPLTestBool(CPLGetConfigOption("FGDB_BULK_LOAD", "NO"));
1129
1130 if (m_bBulkLoadAllowed && !m_bBulkLoadInProgress)
1131 StartBulkLoad();
1132
1133 hr = m_pTable->CreateRowObject(fgdb_row);
1134
1135 /* Check the status of the Row create */
1136 if (FAILED(hr))
1137 {
1138 GDBErr(hr, "Failed at creating Row in CreateFeature.");
1139 return OGRERR_FAILURE;
1140 }
1141
1142 /* As we have issues with fixed values for dates, or CURRENT_xxxx isn't */
1143 /* handled anyway, let's fill ourselves all unset fields with their default */
1144 poFeature->FillUnsetWithDefault(FALSE, nullptr);
1145
1146 /* Populate the row with the feature content */
1147 if (PopulateRowWithFeature(fgdb_row, poFeature) != OGRERR_NONE)
1148 return OGRERR_FAILURE;
1149
1150 /* Cannot write to FID field - it is managed by GDB*/
1151 //std::wstring wfield_name = StringToWString(m_strOIDFieldName);
1152 //hr = fgdb_row.SetInteger(wfield_name, poFeature->GetFID());
1153
1154 /* Write the row to the table */
1155 hr = m_pTable->Insert(fgdb_row);
1156 if (FAILED(hr))
1157 {
1158 GDBErr(hr, "Failed at writing Row to Table in CreateFeature.");
1159 return OGRERR_FAILURE;
1160 }
1161
1162 int32 oid = -1;
1163 if (!FAILED(hr = fgdb_row.GetOID(oid)))
1164 {
1165 if( poFeature->GetFID() < 0 )
1166 {
1167 // Avoid colliding with a user set FID
1168 while( m_oMapOGRFIDToFGDBFID.find(oid) != m_oMapOGRFIDToFGDBFID.end() )
1169 {
1170 EndBulkLoad();
1171
1172 CPLDebug("FGDB", "Collision with user set FID %d", oid);
1173 if (FAILED(hr = m_pTable->Delete(fgdb_row)))
1174 {
1175 GDBErr(hr, "Failed deleting row ");
1176 return OGRERR_FAILURE;
1177 }
1178 hr = m_pTable->Insert(fgdb_row);
1179 if (FAILED(hr))
1180 {
1181 GDBErr(hr, "Failed at writing Row to Table in CreateFeature.");
1182 return OGRERR_FAILURE;
1183 }
1184 if (FAILED(hr = fgdb_row.GetOID(oid)))
1185 {
1186 return OGRERR_FAILURE;
1187 }
1188 }
1189 poFeature->SetFID(oid);
1190 }
1191 else if( (int)poFeature->GetFID() != oid )
1192 {
1193 m_pDS->GetConnection()->SetFIDHackInProgress(TRUE);
1194 m_oMapOGRFIDToFGDBFID[(int)poFeature->GetFID()] = oid;
1195 m_oMapFGDBFIDToOGRFID[oid] = (int)poFeature->GetFID();
1196 }
1197 }
1198
1199 #ifdef EXTENT_WORKAROUND
1200 /* For WorkAroundExtentProblem() needs */
1201 OGRGeometry* poGeom = poFeature->GetGeometryRef();
1202 if ( m_bLayerJustCreated && poGeom != nullptr && !poGeom->IsEmpty() )
1203 {
1204 OGREnvelope sFeatureGeomEnvelope;
1205 poGeom->getEnvelope(&sFeatureGeomEnvelope);
1206 if (!m_bLayerEnvelopeValid)
1207 {
1208 sLayerEnvelope = sFeatureGeomEnvelope;
1209 m_bLayerEnvelopeValid = true;
1210 }
1211 else
1212 {
1213 sLayerEnvelope.Merge(sFeatureGeomEnvelope);
1214 }
1215 }
1216 #endif
1217
1218 return OGRERR_NONE;
1219 }
1220
1221 /************************************************************************/
1222 /* PopulateRowWithFeature() */
1223 /* */
1224 /************************************************************************/
1225
PopulateRowWithFeature(Row & fgdb_row,OGRFeature * poFeature)1226 OGRErr FGdbLayer::PopulateRowWithFeature( Row& fgdb_row, OGRFeature *poFeature )
1227 {
1228 ShapeBuffer shape;
1229 fgdbError hr;
1230
1231 OGRFeatureDefn* poFeatureDefn = m_pFeatureDefn;
1232 int nFieldCount = poFeatureDefn->GetFieldCount();
1233
1234 /* Copy the OGR visible fields (everything except geometry and FID) */
1235 int nCountBinaryField = 0;
1236 for( int i = 0; i < nFieldCount; i++ )
1237 {
1238 std::string field_name = poFeatureDefn->GetFieldDefn(i)->GetNameRef();
1239 std::wstring wfield_name = StringToWString(field_name);
1240 const std::string & strFieldType = m_vOGRFieldToESRIFieldType[i];
1241
1242 /* Set empty fields to NULL */
1243 if( !poFeature->IsFieldSetAndNotNull( i ) )
1244 {
1245 if( strFieldType == "esriFieldTypeGlobalID" )
1246 continue;
1247
1248 if (FAILED(hr = fgdb_row.SetNull(wfield_name)))
1249 {
1250 GDBErr(hr, "Failed setting field to NULL.");
1251 return OGRERR_FAILURE;
1252 }
1253 continue;
1254 }
1255
1256 /* Set the information using the appropriate FGDB function */
1257 int nOGRFieldType = poFeatureDefn->GetFieldDefn(i)->GetType();
1258
1259 if ( nOGRFieldType == OFTInteger )
1260 {
1261 int fldvalue = poFeature->GetFieldAsInteger(i);
1262 if( strFieldType == "esriFieldTypeInteger" )
1263 hr = fgdb_row.SetInteger(wfield_name, fldvalue);
1264 else
1265 {
1266 if( fldvalue < -32768 || fldvalue > 32767 )
1267 {
1268 static int bHasWarned = FALSE;
1269 if( !bHasWarned )
1270 {
1271 bHasWarned = TRUE;
1272 CPLError(CE_Warning, CPLE_NotSupported,
1273 "Value %d for field %s does not fit into a short and will be clamped. "
1274 "This warning will not be emitted any more",
1275 fldvalue, field_name.c_str());
1276 }
1277 if( fldvalue < -32768 )
1278 fldvalue = -32768;
1279 else
1280 fldvalue = 32767;
1281 }
1282 hr = fgdb_row.SetShort(wfield_name, (short) fldvalue);
1283 }
1284 }
1285 else if ( nOGRFieldType == OFTReal || nOGRFieldType == OFTInteger64 )
1286 {
1287 /* Doubles (we don't handle FGDB Floats) */
1288 double fldvalue = poFeature->GetFieldAsDouble(i);
1289 if( strFieldType == "esriFieldTypeDouble" )
1290 hr = fgdb_row.SetDouble(wfield_name, fldvalue);
1291 else
1292 hr = fgdb_row.SetFloat(wfield_name, (float) fldvalue);
1293 }
1294 else if ( nOGRFieldType == OFTString )
1295 {
1296 /* Strings we convert to wstring */
1297 std::string fldvalue = poFeature->GetFieldAsString(i);
1298 if( strFieldType == "esriFieldTypeString" )
1299 {
1300 std::wstring wfldvalue = StringToWString(fldvalue);
1301 hr = fgdb_row.SetString(wfield_name, wfldvalue);
1302 }
1303 // Apparently, esriFieldTypeGlobalID can not be set, but is
1304 // initialized by the FileGDB SDK itself.
1305 else if( strFieldType == "esriFieldTypeGUID" /*||
1306 strFieldType == "esriFieldTypeGlobalID" */ )
1307 {
1308 Guid guid;
1309 std::wstring wfldvalue = StringToWString(fldvalue);
1310 if( FAILED(hr = guid.FromString(wfldvalue)) )
1311 {
1312 CPLError( CE_Failure, CPLE_AppDefined,
1313 "Cannot parse GUID value %s for field %s.",
1314 fldvalue.c_str(), field_name.c_str() );
1315 }
1316 else
1317 {
1318 hr = fgdb_row.SetGUID(wfield_name, guid);
1319 }
1320 }
1321 else if( strFieldType == "esriFieldTypeXML" )
1322 {
1323 hr = fgdb_row.SetXML(wfield_name, fldvalue);
1324 }
1325 else
1326 hr = 0;
1327 }
1328 else if ( nOGRFieldType == OFTDateTime || nOGRFieldType == OFTDate )
1329 {
1330 /* Dates we need to coerce a little */
1331 struct tm val;
1332 poFeature->GetFieldAsDateTime(i, &(val.tm_year), &(val.tm_mon), &(val.tm_mday),
1333 &(val.tm_hour), &(val.tm_min), &(val.tm_sec), nullptr);
1334 val.tm_year -= 1900;
1335 val.tm_mon = val.tm_mon - 1; /* OGR months go 1-12, FGDB go 0-11 */
1336 hr = fgdb_row.SetDate(wfield_name, val);
1337 }
1338 else if ( nOGRFieldType == OFTBinary )
1339 {
1340 /* Binary data */
1341 int bytesize;
1342 GByte *bytes = poFeature->GetFieldAsBinary(i, &bytesize);
1343 if ( bytesize )
1344 {
1345 /* This is annoying but SetBinary() doesn't keep the binary */
1346 /* content. The ByteArray object must still be alive at */
1347 /* the time Insert() is called */
1348 m_apoByteArrays[nCountBinaryField]->Allocate(bytesize);
1349 memcpy(m_apoByteArrays[nCountBinaryField]->byteArray, bytes, bytesize);
1350 m_apoByteArrays[nCountBinaryField]->inUseLength = bytesize;
1351 hr = fgdb_row.SetBinary(wfield_name, *(m_apoByteArrays[nCountBinaryField]));
1352 }
1353 else
1354 {
1355 hr = fgdb_row.SetNull(wfield_name);
1356 }
1357 nCountBinaryField ++;
1358 }
1359 else
1360 {
1361 /* We can't handle this type */
1362 CPLError( CE_Failure, CPLE_AppDefined,
1363 "FGDB driver does not support OGR type." );
1364 return OGRERR_FAILURE;
1365 }
1366
1367 if (FAILED(hr))
1368 {
1369 CPLError(CE_Warning, CPLE_AppDefined, "Cannot set value for field %s",
1370 field_name.c_str());
1371 }
1372 }
1373
1374 if ( m_pFeatureDefn->GetGeomType() != wkbNone )
1375 {
1376 /* Done with attribute fields, now do geometry */
1377 OGRGeometry *poGeom = poFeature->GetGeometryRef();
1378
1379 if (poGeom == nullptr || poGeom->IsEmpty())
1380 {
1381 /* EMPTY geometries should be treated as NULL, see #4832 */
1382 hr = fgdb_row.SetNull(StringToWString(m_strShapeFieldName));
1383 if (FAILED(hr))
1384 {
1385 GDBErr(hr, "Failed at writing EMPTY Geometry to Row in CreateFeature.");
1386 return OGRERR_FAILURE;
1387 }
1388 }
1389 else
1390 {
1391 /* Write geometry to a buffer */
1392 GByte *pabyShape = nullptr;
1393 int nShapeSize = 0;
1394 OGRErr err;
1395
1396 const OGRwkbGeometryType eType = wkbFlatten(poGeom->getGeometryType());
1397 if( m_bCreateMultipatch && (eType == wkbMultiPolygon ||
1398 eType == wkbMultiSurface ||
1399 eType == wkbTIN ||
1400 eType == wkbPolyhedralSurface ||
1401 eType == wkbGeometryCollection) )
1402 {
1403 err = OGRWriteMultiPatchToShapeBin( poGeom, &pabyShape, &nShapeSize );
1404 if( err == OGRERR_UNSUPPORTED_OPERATION )
1405 err = OGRWriteToShapeBin( poGeom, &pabyShape, &nShapeSize );
1406 }
1407 else
1408 {
1409 err = OGRWriteToShapeBin( poGeom, &pabyShape, &nShapeSize );
1410 }
1411 if ( err != OGRERR_NONE )
1412 {
1413 CPLFree(pabyShape);
1414 return err;
1415 }
1416
1417 /* Copy it into a ShapeBuffer */
1418 if ( nShapeSize > 0 )
1419 {
1420 shape.Allocate(nShapeSize);
1421 memcpy(shape.shapeBuffer, pabyShape, nShapeSize);
1422 shape.inUseLength = nShapeSize;
1423 }
1424
1425 /* Free the shape buffer */
1426 CPLFree(pabyShape);
1427
1428 /* Write ShapeBuffer into the Row */
1429 hr = fgdb_row.SetGeometry(shape);
1430 if (FAILED(hr))
1431 {
1432 GDBErr(hr, "Failed at writing Geometry to Row in CreateFeature.");
1433 return OGRERR_FAILURE;
1434 }
1435 }
1436 }
1437
1438 return OGRERR_NONE;
1439 }
1440
1441 /************************************************************************/
1442 /* GetRow() */
1443 /************************************************************************/
1444
GetRow(EnumRows & enumRows,Row & row,GIntBig nFID)1445 OGRErr FGdbLayer::GetRow( EnumRows& enumRows, Row& row, GIntBig nFID )
1446 {
1447 long hr;
1448 CPLString osQuery;
1449
1450 /* Querying a 64bit FID causes a runtime exception in FileGDB... */
1451 if( !CPL_INT64_FITS_ON_INT32(nFID) )
1452 {
1453 return OGRERR_FAILURE;
1454 }
1455
1456 osQuery.Printf("%s = " CPL_FRMT_GIB, m_strOIDFieldName.c_str(), nFID);
1457
1458 if (FAILED(hr = m_pTable->Search(m_wstrSubfields, StringToWString(osQuery.c_str()), true, enumRows)))
1459 {
1460 GDBErr(hr, "Failed fetching row ");
1461 return OGRERR_FAILURE;
1462 }
1463
1464 if (FAILED(hr = enumRows.Next(row)))
1465 {
1466 GDBErr(hr, "Failed fetching row ");
1467 return OGRERR_FAILURE;
1468 }
1469
1470 if (hr != S_OK)
1471 return OGRERR_NON_EXISTING_FEATURE; //none found - but no failure
1472
1473 return OGRERR_NONE;
1474 }
1475
1476 /************************************************************************/
1477 /* DeleteFeature() */
1478 /************************************************************************/
1479
DeleteFeature(GIntBig nFID)1480 OGRErr FGdbLayer::DeleteFeature( GIntBig nFID )
1481
1482 {
1483 long hr;
1484 EnumRows enumRows;
1485 Row row;
1486
1487 if( !m_pDS->GetUpdate() || m_pTable == nullptr )
1488 return OGRERR_FAILURE;
1489 if( !CPL_INT64_FITS_ON_INT32(nFID) )
1490 return OGRERR_NON_EXISTING_FEATURE;
1491
1492 if( m_bSymlinkFlag && !CreateRealCopy() )
1493 return OGRERR_FAILURE;
1494
1495 int nFID32 = (int)nFID;
1496 std::map<int,int>::iterator oIter = m_oMapOGRFIDToFGDBFID.find(nFID32);
1497 if( oIter != m_oMapOGRFIDToFGDBFID.end() )
1498 {
1499 nFID32 = oIter->second;
1500 m_oMapFGDBFIDToOGRFID.erase(nFID32);
1501 m_oMapOGRFIDToFGDBFID.erase(oIter);
1502 }
1503 else if( m_oMapFGDBFIDToOGRFID.find(nFID32) != m_oMapFGDBFIDToOGRFID.end() )
1504 return OGRERR_NON_EXISTING_FEATURE;
1505
1506 EndBulkLoad();
1507
1508 OGRErr eErr = GetRow(enumRows, row, nFID32);
1509 if( eErr != OGRERR_NONE)
1510 return eErr;
1511
1512 if (FAILED(hr = m_pTable->Delete(row)))
1513 {
1514 GDBErr(hr, "Failed deleting row ");
1515 return OGRERR_FAILURE;
1516 }
1517
1518 return OGRERR_NONE;
1519 }
1520
1521 /************************************************************************/
1522 /* ISetFeature() */
1523 /************************************************************************/
1524
ISetFeature(OGRFeature * poFeature)1525 OGRErr FGdbLayer::ISetFeature( OGRFeature* poFeature )
1526
1527 {
1528 long hr;
1529 EnumRows enumRows;
1530 Row row;
1531
1532 if( !m_pDS->GetUpdate()|| m_pTable == nullptr )
1533 return OGRERR_FAILURE;
1534
1535 GIntBig nFID64 = poFeature->GetFID();
1536 if( nFID64 == OGRNullFID )
1537 {
1538 CPLError( CE_Failure, CPLE_AppDefined,
1539 "SetFeature() with unset FID fails." );
1540 return OGRERR_FAILURE;
1541 }
1542 if( !CPL_INT64_FITS_ON_INT32(nFID64) )
1543 return OGRERR_NON_EXISTING_FEATURE;
1544
1545 EndBulkLoad();
1546
1547 if( m_bSymlinkFlag && !CreateRealCopy() )
1548 return OGRERR_FAILURE;
1549
1550 int nFID = (int)nFID64;
1551 std::map<int,int>::iterator oIter = m_oMapOGRFIDToFGDBFID.find(nFID);
1552 if( oIter != m_oMapOGRFIDToFGDBFID.end() )
1553 nFID = oIter->second;
1554 else if( m_oMapFGDBFIDToOGRFID.find((int)poFeature->GetFID()) != m_oMapFGDBFIDToOGRFID.end() )
1555 return OGRERR_NON_EXISTING_FEATURE;
1556
1557 OGRErr eErr = GetRow(enumRows, row, nFID);
1558 if( eErr != OGRERR_NONE)
1559 return eErr;
1560
1561 /* Populate the row with the feature content */
1562 if (PopulateRowWithFeature(row, poFeature) != OGRERR_NONE)
1563 return OGRERR_FAILURE;
1564
1565 if (FAILED(hr = m_pTable->Update(row)))
1566 {
1567 GDBErr(hr, "Failed updating row ");
1568 return OGRERR_FAILURE;
1569 }
1570
1571 return OGRERR_NONE;
1572 }
1573
1574 /************************************************************************/
1575 /* CreateFieldDefn() */
1576 /************************************************************************/
1577
CreateFieldDefn(OGRFieldDefn & oField,int bApproxOK,std::string & fieldname_clean,std::string & gdbFieldType)1578 char* FGdbLayer::CreateFieldDefn(OGRFieldDefn& oField,
1579 int bApproxOK,
1580 std::string& fieldname_clean,
1581 std::string& gdbFieldType)
1582 {
1583 std::string fieldname = oField.GetNameRef();
1584 //std::string fidname = std::string(GetFIDColumn());
1585 std::string nullable = (oField.IsNullable()) ? "true" : "false";
1586
1587 /* Try to map the OGR type to an ESRI type */
1588 OGRFieldType fldtype = oField.GetType();
1589 if ( ! OGRToGDBFieldType(fldtype, oField.GetSubType(), &gdbFieldType) )
1590 {
1591 GDBErr(-1, "Failed converting field type.");
1592 return nullptr;
1593 }
1594
1595 if( oField.GetType() == OFTInteger64 && !bApproxOK )
1596 {
1597 CPLError(CE_Failure, CPLE_AppDefined,
1598 "Integer64 not supported in FileGDB");
1599 return nullptr;
1600 }
1601
1602 const char* pszColumnTypes = CSLFetchNameValue(m_papszOptions, "COLUMN_TYPES");
1603 if( pszColumnTypes != nullptr )
1604 {
1605 char** papszTokens = CSLTokenizeString2(pszColumnTypes, ",", 0);
1606 const char* pszFieldType = CSLFetchNameValue(papszTokens, fieldname.c_str());
1607 if( pszFieldType != nullptr )
1608 {
1609 OGRFieldType fldtypeCheck;
1610 OGRFieldSubType eSubType;
1611 if( GDBToOGRFieldType(pszFieldType, &fldtypeCheck, &eSubType) )
1612 {
1613 if( fldtypeCheck != fldtype )
1614 {
1615 CPLError(CE_Warning, CPLE_AppDefined, "Ignoring COLUMN_TYPES=%s=%s : %s not consistent with OGR data type",
1616 fieldname.c_str(), pszFieldType, pszFieldType);
1617 }
1618 else
1619 gdbFieldType = pszFieldType;
1620 }
1621 else
1622 CPLError(CE_Warning, CPLE_AppDefined, "Ignoring COLUMN_TYPES=%s=%s : %s not recognized",
1623 fieldname.c_str(), pszFieldType, pszFieldType);
1624 }
1625 CSLDestroy(papszTokens);
1626 }
1627
1628 if (!fieldname_clean.empty())
1629 {
1630 oField.SetName(fieldname_clean.c_str());
1631 }
1632 else
1633 {
1634 /* Clean field names */
1635 fieldname_clean = FGDBLaunderName(fieldname);
1636
1637 if (m_bLaunderReservedKeywords)
1638 fieldname_clean = FGDBEscapeReservedKeywords(fieldname_clean);
1639
1640 /* Truncate to 64 characters */
1641 if (fieldname_clean.size() > 64)
1642 fieldname_clean.resize(64);
1643
1644 std::string temp_fieldname = fieldname_clean;
1645
1646 /* Ensures uniqueness of field name */
1647 int numRenames = 1;
1648 while ((m_pFeatureDefn->GetFieldIndex(temp_fieldname.c_str()) >= 0) && (numRenames < 10))
1649 {
1650 temp_fieldname = CPLSPrintf("%s_%d", fieldname_clean.substr(0, 62).c_str(), numRenames);
1651 numRenames ++;
1652 }
1653 while ((m_pFeatureDefn->GetFieldIndex(temp_fieldname.c_str()) >= 0) && (numRenames < 100))
1654 {
1655 temp_fieldname = CPLSPrintf("%s_%d", fieldname_clean.substr(0, 61).c_str(), numRenames);
1656 numRenames ++;
1657 }
1658
1659 if (temp_fieldname != fieldname)
1660 {
1661 if( !bApproxOK || (m_pFeatureDefn->GetFieldIndex(temp_fieldname.c_str()) >= 0) )
1662 {
1663 CPLError( CE_Failure, CPLE_NotSupported,
1664 "Failed to add field named '%s'",
1665 fieldname.c_str() );
1666 return nullptr;
1667 }
1668 CPLError(CE_Warning, CPLE_NotSupported,
1669 "Normalized/laundered field name: '%s' to '%s'",
1670 fieldname.c_str(), temp_fieldname.c_str());
1671
1672 fieldname_clean = temp_fieldname;
1673 oField.SetName(fieldname_clean.c_str());
1674 }
1675 }
1676
1677 /* Then the Field definition */
1678 CPLXMLNode *defn_xml = CPLCreateXMLNode(nullptr, CXT_Element, "esri:Field");
1679
1680 /* Add the XML attributes to the Field node */
1681 FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
1682 FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:xs", "http://www.w3.org/2001/XMLSchema");
1683 FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:esri", "http://www.esri.com/schemas/ArcGIS/10.1");
1684 FGDB_CPLAddXMLAttribute(defn_xml, "xsi:type", "esri:Field");
1685
1686 /* Basic field information */
1687 CPLCreateXMLElementAndValue(defn_xml, "Name", fieldname_clean.c_str());
1688 CPLCreateXMLElementAndValue(defn_xml, "Type", gdbFieldType.c_str());
1689 CPLCreateXMLElementAndValue(defn_xml, "IsNullable", nullable.c_str());
1690
1691 /* Get the Width and Precision if we know them */
1692 int width = oField.GetWidth();
1693 int precision = oField.GetPrecision();
1694 if ( width <= 0 )
1695 GDBFieldTypeToWidthPrecision(gdbFieldType, &width, &precision);
1696
1697 /* Write out the Width and Precision */
1698 char buf[100];
1699 snprintf(buf, 100, "%d", width);
1700 CPLCreateXMLElementAndValue(defn_xml,"Length", buf);
1701 snprintf(buf, 100, "%d", precision);
1702 CPLCreateXMLElementAndValue(defn_xml,"Precision", buf);
1703
1704 /* We know nothing about Scale, so zero it out */
1705 CPLCreateXMLElementAndValue(defn_xml,"Scale", "0");
1706
1707 const char* pszAlternativeName = oField.GetAlternativeNameRef();
1708 if( pszAlternativeName != nullptr && pszAlternativeName[0] )
1709 {
1710 CPLCreateXMLElementAndValue(defn_xml, "AliasName", pszAlternativeName);
1711 }
1712 else if (fieldname != fieldname_clean)
1713 {
1714 /* Attempt to preserve the original fieldname */
1715 CPLCreateXMLElementAndValue(defn_xml, "AliasName", fieldname.c_str());
1716 }
1717
1718 if( oField.GetDefault() != nullptr )
1719 {
1720 const char* pszDefault = oField.GetDefault();
1721 /*int nYear, nMonth, nDay, nHour, nMinute;
1722 float fSecond;*/
1723 if( oField.GetType() == OFTString )
1724 {
1725 CPLString osVal = pszDefault;
1726 if( osVal[0] == '\'' && osVal.back() == '\'' )
1727 {
1728 osVal = osVal.substr(1);
1729 osVal.resize(osVal.size()-1);
1730 char* pszTmp = CPLUnescapeString(osVal, nullptr, CPLES_SQL);
1731 osVal = pszTmp;
1732 CPLFree(pszTmp);
1733 }
1734 CPLXMLNode* psDefaultValue =
1735 CPLCreateXMLElementAndValue(defn_xml, "DefaultValue", osVal);
1736 FGDB_CPLAddXMLAttribute(psDefaultValue, "xsi:type", "xs:string");
1737 }
1738 else if( oField.GetType() == OFTInteger &&
1739 !EQUAL(gdbFieldType.c_str(), "esriFieldTypeSmallInteger") &&
1740 CPLGetValueType(pszDefault) == CPL_VALUE_INTEGER )
1741 {
1742 CPLXMLNode* psDefaultValue =
1743 CPLCreateXMLElementAndValue(defn_xml, "DefaultValue", pszDefault);
1744 FGDB_CPLAddXMLAttribute(psDefaultValue, "xsi:type", "xs:int");
1745 }
1746 else if( oField.GetType() == OFTReal &&
1747 !EQUAL(gdbFieldType.c_str(), "esriFieldTypeSingle") &&
1748 CPLGetValueType(pszDefault) != CPL_VALUE_STRING )
1749 {
1750 CPLXMLNode* psDefaultValue =
1751 CPLCreateXMLElementAndValue(defn_xml, "DefaultValue", pszDefault);
1752 FGDB_CPLAddXMLAttribute(psDefaultValue, "xsi:type", "xs:double");
1753 }
1754 /*else if( oField.GetType() == OFTDateTime &&
1755 sscanf(pszDefault, "'%d/%d/%d %d:%d:%f'", &nYear, &nMonth, &nDay,
1756 &nHour, &nMinute, &fSecond) == 6 )
1757 {
1758 CPLXMLNode* psDefaultValue =
1759 CPLCreateXMLElementAndValue(defn_xml, "DefaultValue",
1760 CPLSPrintf("%04d-%02d-%02dT%02d:%02d:%02d",
1761 nYear, nMonth, nDay, nHour, nMinute, (int)(fSecond + 0.5)));
1762 FGDB_CPLAddXMLAttribute(psDefaultValue, "xsi:type", "xs:dateTime");
1763 }*/
1764 }
1765 /* <DefaultValue xsi:type="xs:string">afternoon</DefaultValue> */
1766
1767 /* Convert our XML tree into a string for FGDB */
1768 char *defn_str = CPLSerializeXMLTree(defn_xml);
1769 CPLDebug("FGDB", "CreateField() generated XML for FGDB\n%s", defn_str);
1770
1771 /* Free the XML */
1772 CPLDestroyXMLNode(defn_xml);
1773
1774 return defn_str;
1775 }
1776
1777 /************************************************************************/
1778 /* CreateField() */
1779 /* Build up an FGDB XML field definition and use it to create a Field */
1780 /* Update the OGRFeatureDefn to reflect the new field. */
1781 /* */
1782 /************************************************************************/
1783
CreateField(OGRFieldDefn * poField,int bApproxOK)1784 OGRErr FGdbLayer::CreateField(OGRFieldDefn* poField, int bApproxOK)
1785 {
1786 OGRFieldDefn oField(poField);
1787 std::string fieldname_clean;
1788 std::string gdbFieldType;
1789
1790 if( !m_pDS->GetUpdate()|| m_pTable == nullptr )
1791 return OGRERR_FAILURE;
1792
1793 char* defn_str = CreateFieldDefn(oField, bApproxOK,
1794 fieldname_clean, gdbFieldType);
1795 if (defn_str == nullptr)
1796 return OGRERR_FAILURE;
1797
1798 /* Add the FGDB Field to the FGDB Table. */
1799 fgdbError hr = m_pTable->AddField(defn_str);
1800
1801 CPLFree(defn_str);
1802
1803 /* Check the status of the Field add */
1804 if (FAILED(hr))
1805 {
1806 GDBErr(hr, "Failed at creating Field for " + std::string(oField.GetNameRef()));
1807 return OGRERR_FAILURE;
1808 }
1809
1810 /* Now add the OGRFieldDefn to the OGRFeatureDefn */
1811 m_pFeatureDefn->AddFieldDefn(&oField);
1812
1813 m_vOGRFieldToESRIField.push_back(StringToWString(fieldname_clean));
1814 m_vOGRFieldToESRIFieldType.push_back( gdbFieldType );
1815
1816 if( oField.GetType() == OFTBinary )
1817 m_apoByteArrays.push_back(new ByteArray());
1818
1819 /* All done and happy */
1820 return OGRERR_NONE;
1821 }
1822
1823 /************************************************************************/
1824 /* DeleteField() */
1825 /************************************************************************/
1826
DeleteField(int iFieldToDelete)1827 OGRErr FGdbLayer::DeleteField( int iFieldToDelete )
1828 {
1829
1830 if( !m_pDS->GetUpdate()|| m_pTable == nullptr )
1831 return OGRERR_FAILURE;
1832
1833 if (iFieldToDelete < 0 || iFieldToDelete >= m_pFeatureDefn->GetFieldCount())
1834 {
1835 CPLError( CE_Failure, CPLE_NotSupported,
1836 "Invalid field index");
1837 return OGRERR_FAILURE;
1838 }
1839
1840 ResetReading();
1841
1842 const char* pszFieldName = m_pFeatureDefn->GetFieldDefn(iFieldToDelete)->GetNameRef();
1843
1844 fgdbError hr;
1845 if (FAILED(hr = m_pTable->DeleteField(StringToWString(pszFieldName))))
1846 {
1847 GDBErr(hr, "Failed deleting field " + std::string(pszFieldName));
1848 return OGRERR_FAILURE;
1849 }
1850
1851 m_vOGRFieldToESRIField.erase (m_vOGRFieldToESRIField.begin() + iFieldToDelete);
1852 m_vOGRFieldToESRIFieldType.erase( m_vOGRFieldToESRIFieldType.begin() + iFieldToDelete );
1853
1854 return m_pFeatureDefn->DeleteFieldDefn( iFieldToDelete );
1855 }
1856
1857 #ifdef AlterFieldDefn_implemented_but_not_working
1858
1859 /************************************************************************/
1860 /* AlterFieldDefn() */
1861 /************************************************************************/
1862
AlterFieldDefn(int iFieldToAlter,OGRFieldDefn * poNewFieldDefn,int nFlags)1863 OGRErr FGdbLayer::AlterFieldDefn( int iFieldToAlter, OGRFieldDefn* poNewFieldDefn, int nFlags )
1864 {
1865
1866 if( !m_pDS->GetUpdate()|| m_pTable == NULL )
1867 return OGRERR_FAILURE;
1868
1869 if (iFieldToAlter < 0 || iFieldToAlter >= m_pFeatureDefn->GetFieldCount())
1870 {
1871 CPLError( CE_Failure, CPLE_NotSupported,
1872 "Invalid field index");
1873 return OGRERR_FAILURE;
1874 }
1875
1876 OGRFieldDefn* poFieldDefn = m_pFeatureDefn->GetFieldDefn(iFieldToAlter);
1877 OGRFieldDefn oField(poFieldDefn);
1878
1879 if (nFlags & ALTER_TYPE_FLAG)
1880 {
1881 oField.SetSubType(OFSTNone);
1882 oField.SetType(poNewFieldDefn->GetType());
1883 oField.SetSubType(poNewFieldDefn->GetSubType());
1884 }
1885 if (nFlags & ALTER_NAME_FLAG)
1886 {
1887 if (strcmp(poNewFieldDefn->GetNameRef(), oField.GetNameRef()) != 0)
1888 {
1889 CPLError( CE_Failure, CPLE_NotSupported,
1890 "Altering field name is not supported" );
1891 return OGRERR_FAILURE;
1892 }
1893 oField.SetName(poNewFieldDefn->GetNameRef());
1894 }
1895 if (nFlags & ALTER_WIDTH_PRECISION_FLAG)
1896 {
1897 oField.SetWidth(poNewFieldDefn->GetWidth());
1898 oField.SetPrecision(poNewFieldDefn->GetPrecision());
1899 }
1900
1901 std::string fieldname_clean = WStringToString(m_vOGRFieldToESRIField[iFieldToAlter]);
1902 std::string gdbFieldType;
1903
1904 char* defn_str = CreateFieldDefn(oField, TRUE,
1905 fieldname_clean, gdbFieldType);
1906 if (defn_str == NULL)
1907 return OGRERR_FAILURE;
1908
1909 ResetReading();
1910
1911 /* Add the FGDB Field to the FGDB Table. */
1912 fgdbError hr = m_pTable->AlterField(defn_str);
1913
1914 CPLFree(defn_str);
1915
1916 /* Check the status of the AlterField */
1917 if (FAILED(hr))
1918 {
1919 GDBErr(hr, "Failed at altering field " + std::string(oField.GetNameRef()));
1920 return OGRERR_FAILURE;
1921 }
1922
1923 m_vOGRFieldToESRIFieldType[iFieldToAlter] = gdbFieldType;
1924
1925 poFieldDefn->SetSubType(OFSTNone);
1926 poFieldDefn->SetType(oField.GetType());
1927 poFieldDefn->SetType(oField.GetSubType());
1928 poFieldDefn->SetWidth(oField.GetWidth());
1929 poFieldDefn->SetPrecision(oField.GetPrecision());
1930
1931 return OGRERR_NONE;
1932 }
1933 #endif // AlterFieldDefn_implemented_but_not_working
1934
1935 /************************************************************************/
1936 /* XMLSpatialReference() */
1937 /* Build up an XML representation of an OGRSpatialReference. */
1938 /* Used in layer creation. */
1939 /* */
1940 /************************************************************************/
1941
XMLSpatialReference(OGRSpatialReference * poSRS,char ** papszOptions)1942 static CPLXMLNode* XMLSpatialReference(OGRSpatialReference* poSRS, char** papszOptions)
1943 {
1944 /* We always need a SpatialReference */
1945 CPLXMLNode *srs_xml = CPLCreateXMLNode(nullptr, CXT_Element, "SpatialReference");
1946
1947 /* Extract the WKID before morphing */
1948 int nSRID = 0;
1949 if ( poSRS && poSRS->GetAuthorityCode(nullptr) )
1950 {
1951 nSRID = atoi(poSRS->GetAuthorityCode(nullptr));
1952 }
1953
1954 /* NULL poSRS => UnknownCoordinateSystem */
1955 if ( ! poSRS )
1956 {
1957 FGDB_CPLAddXMLAttribute(srs_xml, "xsi:type", "esri:UnknownCoordinateSystem");
1958 }
1959 else
1960 {
1961 /* Set the SpatialReference type attribute correctly for GEOGCS/PROJCS */
1962 if ( poSRS->IsProjected() )
1963 FGDB_CPLAddXMLAttribute(srs_xml, "xsi:type", "esri:ProjectedCoordinateSystem");
1964 else
1965 FGDB_CPLAddXMLAttribute(srs_xml, "xsi:type", "esri:GeographicCoordinateSystem");
1966
1967 /* Add the WKT to the XML */
1968 SpatialReferenceInfo oESRI_SRS;
1969
1970 /* Do we have a known SRID ? If so, directly query the ESRI SRS DB */
1971 if( nSRID && SpatialReferences::FindSpatialReferenceBySRID(nSRID, oESRI_SRS) )
1972 {
1973 CPLDebug("FGDB",
1974 "Layer SRS has a SRID (%d). Using WKT from ESRI SRS DBFound perfect match. ",
1975 nSRID);
1976 CPLCreateXMLElementAndValue(srs_xml,"WKT", WStringToString(oESRI_SRS.srtext).c_str());
1977 }
1978 else
1979 {
1980 /* Make a clone so we can morph it without morphing the original */
1981 OGRSpatialReference* poSRSClone = poSRS->Clone();
1982
1983 /* Flip the WKT to ESRI form, return UnknownCoordinateSystem if we can't */
1984 if ( poSRSClone->morphToESRI() != OGRERR_NONE )
1985 {
1986 delete poSRSClone;
1987 FGDB_CPLAddXMLAttribute(srs_xml, "xsi:type", "esri:UnknownCoordinateSystem");
1988 return srs_xml;
1989 }
1990
1991 char *wkt = nullptr;
1992 poSRSClone->exportToWkt(&wkt);
1993 if (wkt)
1994 {
1995 EnumSpatialReferenceInfo oEnumESRI_SRS;
1996 std::vector<int> oaiCandidateSRS;
1997 nSRID = 0;
1998
1999 /* Enumerate SRS from ESRI DB and find a match */
2000 while( true )
2001 {
2002 if ( poSRS->IsProjected() )
2003 {
2004 if( !oEnumESRI_SRS.NextProjectedSpatialReference(oESRI_SRS) )
2005 break;
2006 }
2007 else
2008 {
2009 if( !oEnumESRI_SRS.NextGeographicSpatialReference(oESRI_SRS) )
2010 break;
2011 }
2012
2013 std::string osESRI_WKT = WStringToString(oESRI_SRS.srtext);
2014 const char* pszESRI_WKT = osESRI_WKT.c_str();
2015 if( strcmp(pszESRI_WKT, wkt) == 0 )
2016 {
2017 /* Exact match found (not sure this case happens) */
2018 nSRID = oESRI_SRS.auth_srid;
2019 break;
2020 }
2021 OGRSpatialReference oSRS_FromESRI;
2022 if( oSRS_FromESRI.SetFromUserInput(pszESRI_WKT) == OGRERR_NONE &&
2023 poSRSClone->IsSame(&oSRS_FromESRI) )
2024 {
2025 /* Potential match found */
2026 oaiCandidateSRS.push_back(oESRI_SRS.auth_srid);
2027 }
2028 }
2029
2030 if( nSRID != 0 )
2031 {
2032 CPLDebug("FGDB",
2033 "Found perfect match in ESRI SRS DB "
2034 "for layer SRS. SRID is %d", nSRID);
2035 }
2036 else if( oaiCandidateSRS.empty() )
2037 {
2038 CPLDebug("FGDB",
2039 "Did not found a match in ESRI SRS DB for layer SRS. "
2040 "Using morphed SRS WKT. Failure is to be expected");
2041 }
2042 else if( oaiCandidateSRS.size() == 1 )
2043 {
2044 nSRID = oaiCandidateSRS[0];
2045 if( SpatialReferences::FindSpatialReferenceBySRID(
2046 nSRID, oESRI_SRS) )
2047 {
2048 CPLDebug("FGDB",
2049 "Found a single match in ESRI SRS DB "
2050 "for layer SRS. SRID is %d",
2051 nSRID);
2052 nSRID = oESRI_SRS.auth_srid;
2053 CPLFree(wkt);
2054 wkt = CPLStrdup(WStringToString(oESRI_SRS.srtext).c_str());
2055 }
2056 }
2057 else
2058 {
2059 /* Not sure this case can happen */
2060
2061 CPLString osCandidateSRS;
2062 for(int i=0; i<(int)oaiCandidateSRS.size() && i < 10; i++)
2063 {
2064 if( !osCandidateSRS.empty() )
2065 osCandidateSRS += ", ";
2066 osCandidateSRS += CPLSPrintf("%d", oaiCandidateSRS[i]);
2067 }
2068 if(oaiCandidateSRS.size() > 10)
2069 osCandidateSRS += "...";
2070
2071 CPLDebug("FGDB",
2072 "As several candidates (%s) have been found in "
2073 "ESRI SRS DB for layer SRS, none has been selected. "
2074 "Using morphed SRS WKT. Failure is to be expected",
2075 osCandidateSRS.c_str());
2076 }
2077
2078 CPLCreateXMLElementAndValue(srs_xml,"WKT", wkt);
2079 CPLFree(wkt);
2080 }
2081
2082 /* Dispose of our close */
2083 delete poSRSClone;
2084 }
2085 }
2086
2087 /* Handle Origin/Scale/Tolerance */
2088 const char* grid[7] = {
2089 "XOrigin", "YOrigin", "XYScale",
2090 "ZOrigin", "ZScale",
2091 "XYTolerance", "ZTolerance" };
2092 const char* gridvalues[7];
2093
2094 /*
2095 Need different default parameters for geographic and projected coordinate systems.
2096 Try and use ArcGIS 10 default values.
2097 */
2098 // default tolerance is 1mm in the units of the coordinate system
2099 double ztol = 0.001 * (poSRS ? poSRS->GetTargetLinearUnits("VERT_CS") : 1.0);
2100 // default scale is 10x the tolerance
2101 long zscale = (long)(1 / ztol * 10);
2102
2103 char s_xyscale[50], s_xytol[50], s_zscale[50], s_ztol[50];
2104 CPLsnprintf(s_ztol, 50, "%f", ztol);
2105 snprintf(s_zscale, 50, "%ld", zscale);
2106
2107 if ( poSRS == nullptr || poSRS->IsProjected() )
2108 {
2109 // default tolerance is 1mm in the units of the coordinate system
2110 double xytol = 0.001 * (poSRS ? poSRS->GetTargetLinearUnits("PROJCS") : 1.0);
2111 // default scale is 10x the tolerance
2112 long xyscale = (long)(1 / xytol * 10);
2113
2114 CPLsnprintf(s_xytol, 50, "%f", xytol);
2115 snprintf(s_xyscale, 50, "%ld", xyscale);
2116
2117 // Ideally we would use the same X/Y origins as ArcGIS, but we need the algorithm they use.
2118 gridvalues[0] = "-2147483647";
2119 gridvalues[1] = "-2147483647";
2120 gridvalues[2] = s_xyscale;
2121 gridvalues[3] = "-100000";
2122 gridvalues[4] = s_zscale;
2123 gridvalues[5] = s_xytol;
2124 gridvalues[6] = s_ztol;
2125 }
2126 else
2127 {
2128 gridvalues[0] = "-400";
2129 gridvalues[1] = "-400";
2130 gridvalues[2] = "1000000000";
2131 gridvalues[3] = "-100000";
2132 gridvalues[4] = s_zscale;
2133 gridvalues[5] = "0.000000008983153";
2134 gridvalues[6] = s_ztol;
2135 }
2136
2137 /* Convert any layer creation options available, use defaults otherwise */
2138 for( int i = 0; i < 7; i++ )
2139 {
2140 if ( CSLFetchNameValue( papszOptions, grid[i] ) != nullptr )
2141 gridvalues[i] = CSLFetchNameValue( papszOptions, grid[i] );
2142
2143 CPLCreateXMLElementAndValue(srs_xml, grid[i], gridvalues[i]);
2144 }
2145
2146 /* FGDB is always High Precision */
2147 CPLCreateXMLElementAndValue(srs_xml, "HighPrecision", "true");
2148
2149 /* Add the WKID to the XML */
2150 if ( nSRID )
2151 {
2152 CPLCreateXMLElementAndValue(srs_xml, "WKID", CPLSPrintf("%d", nSRID));
2153 }
2154
2155 return srs_xml;
2156 }
2157
2158 /************************************************************************/
2159 /* CreateFeatureDataset() */
2160 /************************************************************************/
2161
CreateFeatureDataset(FGdbDataSource * pParentDataSource,const std::string & feature_dataset_name,OGRSpatialReference * poSRS,char ** papszOptions)2162 bool FGdbLayer::CreateFeatureDataset(FGdbDataSource* pParentDataSource,
2163 const std::string& feature_dataset_name,
2164 OGRSpatialReference* poSRS,
2165 char** papszOptions )
2166 {
2167 /* XML node */
2168 CPLXMLNode *xml_xml = CPLCreateXMLNode(nullptr, CXT_Element, "?xml");
2169 FGDB_CPLAddXMLAttribute(xml_xml, "version", "1.0");
2170 FGDB_CPLAddXMLAttribute(xml_xml, "encoding", "UTF-8");
2171
2172 /* First build up a bare-bones feature definition */
2173 CPLXMLNode *defn_xml = CPLCreateXMLNode(nullptr, CXT_Element, "esri:DataElement");
2174 CPLAddXMLSibling(xml_xml, defn_xml);
2175
2176 /* Add the attributes to the DataElement */
2177 FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
2178 FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:xs", "http://www.w3.org/2001/XMLSchema");
2179 FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:esri", "http://www.esri.com/schemas/ArcGIS/10.1");
2180
2181 /* Need to set this to esri:DEFeatureDataset or esri:DETable */
2182 FGDB_CPLAddXMLAttribute(defn_xml, "xsi:type", "esri:DEFeatureDataset");
2183
2184 /* Add in more children */
2185 std::string catalog_page = "\\" + feature_dataset_name;
2186 CPLCreateXMLElementAndValue(defn_xml,"CatalogPath", catalog_page.c_str());
2187 CPLCreateXMLElementAndValue(defn_xml,"Name", feature_dataset_name.c_str());
2188 CPLCreateXMLElementAndValue(defn_xml,"ChildrenExpanded", "false");
2189 CPLCreateXMLElementAndValue(defn_xml,"DatasetType", "esriDTFeatureDataset");
2190 CPLCreateXMLElementAndValue(defn_xml,"Versioned", "false");
2191 CPLCreateXMLElementAndValue(defn_xml,"CanVersion", "false");
2192
2193 /* Add in empty extent */
2194 CPLXMLNode *extent_xml = CPLCreateXMLNode(nullptr, CXT_Element, "Extent");
2195 FGDB_CPLAddXMLAttribute(extent_xml, "xsi:nil", "true");
2196 CPLAddXMLChild(defn_xml, extent_xml);
2197
2198 /* Add the SRS */
2199 if( true ) // TODO: conditional on existence of SRS
2200 {
2201 CPLXMLNode *srs_xml = XMLSpatialReference(poSRS, papszOptions);
2202 if ( srs_xml )
2203 CPLAddXMLChild(defn_xml, srs_xml);
2204 }
2205
2206 /* Convert our XML tree into a string for FGDB */
2207 char *defn_str = CPLSerializeXMLTree(xml_xml);
2208 CPLDestroyXMLNode(xml_xml);
2209
2210 /* TODO, tie this to debugging levels */
2211 CPLDebug("FGDB", "%s", defn_str);
2212
2213 /* Create the FeatureDataset. */
2214 Geodatabase *gdb = pParentDataSource->GetGDB();
2215 fgdbError hr = gdb->CreateFeatureDataset(defn_str);
2216
2217 /* Free the XML */
2218 CPLFree(defn_str);
2219
2220 /* Check table create status */
2221 if (FAILED(hr))
2222 {
2223 return GDBErr(hr, "Failed at creating FeatureDataset " + feature_dataset_name);
2224 }
2225
2226 return true;
2227 }
2228
2229 /************************************************************************/
2230 /* Create() */
2231 /* Build up an FGDB XML layer definition and use it to create a Table */
2232 /* or Feature Class to work from. */
2233 /* */
2234 /* Layer creation options: */
2235 /* FEATURE_DATASET, nest layer inside a FeatureDataset folder */
2236 /* GEOMETRY_NAME, user-selected name for the geometry column */
2237 /* FID/OID_NAME, user-selected name for the FID column */
2238 /* XORIGIN, YORIGIN, ZORIGIN, origin of the snapping grid */
2239 /* XYSCALE, ZSCALE, inverse resolution of the snapping grid */
2240 /* XYTOLERANCE, ZTOLERANCE, snapping tolerance for topology/networks */
2241 /* */
2242 /************************************************************************/
2243
Create(FGdbDataSource * pParentDataSource,const char * pszLayerNameIn,OGRSpatialReference * poSRS,OGRwkbGeometryType eType,char ** papszOptions)2244 bool FGdbLayer::Create(FGdbDataSource* pParentDataSource,
2245 const char* pszLayerNameIn,
2246 OGRSpatialReference* poSRS,
2247 OGRwkbGeometryType eType,
2248 char** papszOptions)
2249 {
2250 std::string parent_path = "";
2251 std::wstring wtable_path, wparent_path;
2252 std::string geometry_name = FGDB_GEOMETRY_NAME;
2253 std::string fid_name = FGDB_OID_NAME;
2254 std::string esri_type;
2255 bool has_z = false;
2256 bool has_m = false;
2257
2258 #ifdef EXTENT_WORKAROUND
2259 m_bLayerJustCreated = true;
2260 #endif
2261
2262 /* Launder the Layer name */
2263 std::string layerName;
2264
2265 layerName = FGDBLaunderName(pszLayerNameIn);
2266 layerName = FGDBEscapeReservedKeywords(layerName);
2267 layerName = FGDBEscapeUnsupportedPrefixes(layerName);
2268
2269 if (layerName.size() > 160)
2270 layerName.resize(160);
2271
2272 /* Ensures uniqueness of layer name */
2273 int numRenames = 1;
2274 while ((pParentDataSource->GetLayerByName(layerName.c_str()) != nullptr) && (numRenames < 10))
2275 {
2276 layerName = CPLSPrintf("%s_%d", layerName.substr(0, 158).c_str(), numRenames);
2277 numRenames ++;
2278 }
2279 while ((pParentDataSource->GetLayerByName(layerName.c_str()) != nullptr) && (numRenames < 100))
2280 {
2281 layerName = CPLSPrintf("%s_%d", layerName.substr(0, 157).c_str(), numRenames);
2282 numRenames ++;
2283 }
2284
2285 if (layerName != pszLayerNameIn)
2286 {
2287 CPLError(CE_Warning, CPLE_NotSupported,
2288 "Normalized/laundered layer name: '%s' to '%s'",
2289 pszLayerNameIn, layerName.c_str());
2290 }
2291
2292 std::string table_path = "\\" + std::string(layerName);
2293
2294 /* Handle the FEATURE_DATASET case */
2295 if ( CSLFetchNameValue( papszOptions, "FEATURE_DATASET") != nullptr )
2296 {
2297 std::string feature_dataset = CSLFetchNameValue( papszOptions, "FEATURE_DATASET");
2298
2299 /* Check if FEATURE_DATASET exists. Otherwise create it */
2300 std::vector<wstring> featuredatasets;
2301 Geodatabase *gdb = pParentDataSource->GetGDB();
2302 int bFeatureDataSetExists = FALSE;
2303 fgdbError hr;
2304 if ( !FAILED(hr = gdb->GetChildDatasets(L"\\", L"Feature Dataset", featuredatasets)) )
2305 {
2306 std::wstring feature_dataset_with_slash = L"\\" + StringToWString(feature_dataset);
2307 for ( unsigned int i = 0; i < featuredatasets.size(); i++ )
2308 {
2309 if (featuredatasets[i] == feature_dataset_with_slash)
2310 bFeatureDataSetExists = TRUE;
2311 }
2312 }
2313
2314 if (!bFeatureDataSetExists)
2315 {
2316 bool rv = CreateFeatureDataset(pParentDataSource, feature_dataset, poSRS, papszOptions);
2317 if ( ! rv )
2318 return rv;
2319 }
2320
2321 table_path = "\\" + feature_dataset + table_path;
2322 parent_path = "\\" + feature_dataset;
2323 }
2324
2325 /* Convert table_path into wstring */
2326 wtable_path = StringToWString(table_path);
2327 wparent_path = StringToWString(parent_path);
2328
2329 /* Over-ride the geometry name if necessary */
2330 if ( CSLFetchNameValue( papszOptions, "GEOMETRY_NAME") != nullptr )
2331 geometry_name = CSLFetchNameValue( papszOptions, "GEOMETRY_NAME");
2332
2333 /* Over-ride the OID name if necessary */
2334 if ( CSLFetchNameValue( papszOptions, "FID") != nullptr )
2335 fid_name = CSLFetchNameValue( papszOptions, "FID");
2336 else if ( CSLFetchNameValue( papszOptions, "OID_NAME") != nullptr )
2337 fid_name = CSLFetchNameValue( papszOptions, "OID_NAME");
2338
2339 m_bCreateMultipatch = CPLTestBool(CSLFetchNameValueDef(
2340 papszOptions, "CREATE_MULTIPATCH", "NO"));
2341
2342 /* Figure out our geometry type */
2343 if ( eType != wkbNone )
2344 {
2345 if ( wkbFlatten(eType) == wkbUnknown )
2346 {
2347 return GDBErr(-1, "FGDB layers cannot be created with a wkbUnknown layer geometry type.");
2348 }
2349 if ( ! OGRGeometryToGDB(eType, &esri_type, &has_z, &has_m) )
2350 return GDBErr(-1, "Unable to map OGR type to ESRI type");
2351
2352 if( wkbFlatten(eType) == wkbMultiPolygon && m_bCreateMultipatch )
2353 {
2354 esri_type = "esriGeometryMultiPatch";
2355 has_z = true;
2356 }
2357 // For TIN and PolyhedralSurface, default to create a multipatch,
2358 // unless the user explicitly disabled it
2359 else if( (wkbFlatten(eType) == wkbTIN ||
2360 wkbFlatten(eType) == wkbPolyhedralSurface ) &&
2361 CPLTestBool(CSLFetchNameValueDef(papszOptions,
2362 "CREATE_MULTIPATCH", "YES")) )
2363 {
2364 m_bCreateMultipatch = true;
2365 esri_type = "esriGeometryMultiPatch";
2366 has_z = true;
2367 }
2368 }
2369
2370 m_bLaunderReservedKeywords =
2371 CPLFetchBool( papszOptions, "LAUNDER_RESERVED_KEYWORDS", true);
2372
2373 /* XML node */
2374 CPLXMLNode *xml_xml = CPLCreateXMLNode(nullptr, CXT_Element, "?xml");
2375 FGDB_CPLAddXMLAttribute(xml_xml, "version", "1.0");
2376 FGDB_CPLAddXMLAttribute(xml_xml, "encoding", "UTF-8");
2377
2378 /* First build up a bare-bones feature definition */
2379 CPLXMLNode *defn_xml = CPLCreateXMLNode(nullptr, CXT_Element, "esri:DataElement");
2380 CPLAddXMLSibling(xml_xml, defn_xml);
2381
2382 /* Add the attributes to the DataElement */
2383 FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
2384 FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:xs", "http://www.w3.org/2001/XMLSchema");
2385 FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:esri", "http://www.esri.com/schemas/ArcGIS/10.1");
2386
2387 /* Need to set this to esri:DEFeatureDataset or esri:DETable */
2388 FGDB_CPLAddXMLAttribute(defn_xml, "xsi:type", (eType == wkbNone ? "esri:DETable" : "esri:DEFeatureClass"));
2389
2390 /* Add in more children */
2391 CPLCreateXMLElementAndValue(defn_xml,"CatalogPath",table_path.c_str());
2392 CPLCreateXMLElementAndValue(defn_xml,"Name", layerName.c_str());
2393 CPLCreateXMLElementAndValue(defn_xml,"ChildrenExpanded", "false");
2394
2395 /* WKB type of none implies this is a 'Table' otherwise it is a 'Feature Class' */
2396 std::string datasettype = (eType == wkbNone ? "esriDTTable" : "esriDTFeatureClass");
2397 CPLCreateXMLElementAndValue(defn_xml,"DatasetType", datasettype.c_str() );
2398 CPLCreateXMLElementAndValue(defn_xml,"Versioned", "false");
2399 CPLCreateXMLElementAndValue(defn_xml,"CanVersion", "false");
2400
2401 if ( CSLFetchNameValue( papszOptions, "CONFIGURATION_KEYWORD") != nullptr )
2402 CPLCreateXMLElementAndValue(defn_xml,"ConfigurationKeyword",
2403 CSLFetchNameValue( papszOptions, "CONFIGURATION_KEYWORD"));
2404
2405 /* We might need to make OID optional later, but OGR likes to have a FID */
2406 CPLCreateXMLElementAndValue(defn_xml,"HasOID", "true");
2407 CPLCreateXMLElementAndValue(defn_xml,"OIDFieldName", fid_name.c_str());
2408
2409 /* Add in empty Fields */
2410 CPLXMLNode *fields_xml = CPLCreateXMLNode(defn_xml, CXT_Element, "Fields");
2411 FGDB_CPLAddXMLAttribute(fields_xml, "xsi:type", "esri:Fields");
2412 CPLXMLNode *fieldarray_xml = CPLCreateXMLNode(fields_xml, CXT_Element, "FieldArray");
2413 FGDB_CPLAddXMLAttribute(fieldarray_xml, "xsi:type", "esri:ArrayOfField");
2414
2415 /* Feature Classes have an implicit geometry column, so we'll add it at creation time */
2416 CPLXMLNode *srs_xml = nullptr;
2417 if ( eType != wkbNone )
2418 {
2419 CPLXMLNode *shape_xml = CPLCreateXMLNode(fieldarray_xml, CXT_Element, "Field");
2420 FGDB_CPLAddXMLAttribute(shape_xml, "xsi:type", "esri:Field");
2421 CPLCreateXMLElementAndValue(shape_xml, "Name", geometry_name.c_str());
2422 CPLCreateXMLElementAndValue(shape_xml, "Type", "esriFieldTypeGeometry");
2423 if( CPLFetchBool( papszOptions, "GEOMETRY_NULLABLE", true) )
2424 CPLCreateXMLElementAndValue(shape_xml, "IsNullable", "true");
2425 else
2426 CPLCreateXMLElementAndValue(shape_xml, "IsNullable", "false");
2427 CPLCreateXMLElementAndValue(shape_xml, "Length", "0");
2428 CPLCreateXMLElementAndValue(shape_xml, "Precision", "0");
2429 CPLCreateXMLElementAndValue(shape_xml, "Scale", "0");
2430 CPLCreateXMLElementAndValue(shape_xml, "Required", "true");
2431 CPLXMLNode *geom_xml = CPLCreateXMLNode(shape_xml, CXT_Element, "GeometryDef");
2432 FGDB_CPLAddXMLAttribute(geom_xml, "xsi:type", "esri:GeometryDef");
2433 CPLCreateXMLElementAndValue(geom_xml, "AvgNumPoints", "0");
2434 CPLCreateXMLElementAndValue(geom_xml, "GeometryType", esri_type.c_str());
2435 CPLCreateXMLElementAndValue(geom_xml,"HasM", (has_m ? "true" : "false"));
2436 CPLCreateXMLElementAndValue(geom_xml,"HasZ", (has_z ? "true" : "false"));
2437
2438 /* Add the SRS if we have one */
2439 srs_xml = XMLSpatialReference(poSRS, papszOptions);
2440 if ( srs_xml )
2441 CPLAddXMLChild(geom_xml, srs_xml);
2442 }
2443
2444 /* All (?) Tables and Feature Classes will have an ObjectID */
2445 CPLXMLNode *oid_xml = CPLCreateXMLNode(fieldarray_xml, CXT_Element, "Field");
2446 FGDB_CPLAddXMLAttribute(oid_xml, "xsi:type", "esri:Field");
2447 CPLCreateXMLElementAndValue(oid_xml, "Name", fid_name.c_str());
2448 CPLCreateXMLElementAndValue(oid_xml, "Type", "esriFieldTypeOID");
2449 CPLCreateXMLElementAndValue(oid_xml, "IsNullable", "false");
2450 CPLCreateXMLElementAndValue(oid_xml, "Length", "12");
2451 CPLCreateXMLElementAndValue(oid_xml, "Precision", "0");
2452 CPLCreateXMLElementAndValue(oid_xml, "Scale", "0");
2453 CPLCreateXMLElementAndValue(oid_xml, "Required", "true");
2454
2455 /* Add in empty Indexes */
2456 CPLXMLNode *indexes_xml = CPLCreateXMLNode(defn_xml, CXT_Element, "Indexes");
2457 FGDB_CPLAddXMLAttribute(indexes_xml, "xsi:type", "esri:Indexes");
2458 CPLXMLNode *indexarray_xml = CPLCreateXMLNode(indexes_xml, CXT_Element, "IndexArray");
2459 FGDB_CPLAddXMLAttribute(indexarray_xml, "xsi:type", "esri:ArrayOfIndex");
2460
2461 /* CLSID http://forums.arcgis.com/threads/34536?p=118484#post118484 */
2462 if ( eType == wkbNone )
2463 {
2464 CPLCreateXMLElementAndValue(defn_xml, "CLSID", "{7A566981-C114-11D2-8A28-006097AFF44E}");
2465 CPLCreateXMLElementAndValue(defn_xml, "EXTCLSID", "");
2466 }
2467 else
2468 {
2469 CPLCreateXMLElementAndValue(defn_xml, "CLSID", "{52353152-891A-11D0-BEC6-00805F7C4268}");
2470 CPLCreateXMLElementAndValue(defn_xml, "EXTCLSID", "");
2471 }
2472
2473 /* Set the alias for the Feature Class, check if we received an */
2474 /* explicit one in the options vector. */
2475 const char* pszLayerAlias = CSLFetchNameValue( papszOptions, "LAYER_ALIAS");
2476 if ( pszLayerAlias != nullptr )
2477 {
2478 CPLCreateXMLElementAndValue(defn_xml, "AliasName", pszLayerAlias);
2479 }
2480 else if (pszLayerNameIn != layerName)
2481 {
2482 CPLCreateXMLElementAndValue(defn_xml, "AliasName", pszLayerNameIn);
2483 }
2484
2485 /* Map from OGR WKB type to ESRI type */
2486 if ( eType != wkbNone )
2487 {
2488 /* Declare our feature type */
2489 CPLCreateXMLElementAndValue(defn_xml,"FeatureType", "esriFTSimple");
2490 CPLCreateXMLElementAndValue(defn_xml,"ShapeType", esri_type.c_str());
2491 CPLCreateXMLElementAndValue(defn_xml,"ShapeFieldName", geometry_name.c_str());
2492
2493 /* Dimensionality */
2494 CPLCreateXMLElementAndValue(defn_xml,"HasM", (has_m ? "true" : "false"));
2495 CPLCreateXMLElementAndValue(defn_xml,"HasZ", (has_z ? "true" : "false"));
2496
2497 CPLCreateXMLElementAndValue(defn_xml,"HasSpatialIndex", "true");
2498
2499 /* These field are required for Arcmap to display aliases correctly */
2500 CPLCreateXMLNode(defn_xml, CXT_Element, "AreaFieldName");
2501 CPLCreateXMLNode(defn_xml, CXT_Element, "LengthFieldName");
2502
2503 /* We can't know the extent at this point <Extent xsi:nil='true'/> */
2504 CPLXMLNode *extn_xml = CPLCreateXMLNode(defn_xml, CXT_Element, "Extent");
2505 FGDB_CPLAddXMLAttribute(extn_xml, "xsi:nil", "true");
2506 }
2507
2508 /* Feature Class with known SRS gets an SRS entry */
2509 if( eType != wkbNone && srs_xml != nullptr)
2510 {
2511 CPLAddXMLChild(defn_xml, CPLCloneXMLTree(srs_xml));
2512 }
2513
2514 /* Convert our XML tree into a string for FGDB */
2515 char *defn_str;
2516
2517 if( CSLFetchNameValue( papszOptions, "XML_DEFINITION") != nullptr )
2518 defn_str = CPLStrdup(CSLFetchNameValue( papszOptions, "XML_DEFINITION"));
2519 else
2520 defn_str = CPLSerializeXMLTree(xml_xml);
2521 CPLDestroyXMLNode(xml_xml);
2522
2523 /* TODO, tie this to debugging levels */
2524 CPLDebug("FGDB", "%s", defn_str);
2525 //std::cout << defn_str << std::endl;
2526
2527 /* Create the table. */
2528 Table *table = new Table;
2529 Geodatabase *gdb = pParentDataSource->GetGDB();
2530 fgdbError hr = gdb->CreateTable(defn_str, wparent_path, *table);
2531
2532 /* Free the XML */
2533 CPLFree(defn_str);
2534
2535 /* Check table create status */
2536 if (FAILED(hr))
2537 {
2538 delete table;
2539 return GDBErr(hr, "Failed at creating table for " + table_path);
2540 }
2541
2542 m_papszOptions = CSLDuplicate(papszOptions);
2543
2544 // Default to YES here assuming ogr2ogr scenario
2545 m_bBulkLoadAllowed = CPLTestBool(CPLGetConfigOption("FGDB_BULK_LOAD", "YES"));
2546
2547 /* Store the new FGDB Table pointer and set up the OGRFeatureDefn */
2548 return FGdbLayer::Initialize(pParentDataSource, table, wtable_path, L"Table");
2549 }
2550
2551 /*************************************************************************/
2552 /* Initialize() */
2553 /* Has ownership of the table as soon as it is called. */
2554 /************************************************************************/
2555
Initialize(FGdbDataSource * pParentDataSource,Table * pTable,const std::wstring & wstrTablePath,const std::wstring & wstrType)2556 bool FGdbLayer::Initialize(FGdbDataSource* pParentDataSource, Table* pTable,
2557 const std::wstring& wstrTablePath,
2558 const std::wstring& wstrType)
2559 {
2560 long hr;
2561
2562 m_pDS = pParentDataSource; // we never assume ownership of the parent - so our destructor should not delete
2563
2564 m_pTable = pTable;
2565
2566 m_wstrTablePath = wstrTablePath;
2567 m_wstrType = wstrType;
2568
2569 wstring wstrQueryName;
2570 if (FAILED(hr = pParentDataSource->GetGDB()->GetQueryName(wstrTablePath, wstrQueryName)))
2571 return GDBErr(hr, "Failed at getting underlying table name for " +
2572 WStringToString(wstrTablePath));
2573
2574 m_strName = WStringToString(wstrQueryName);
2575
2576 m_pFeatureDefn = new OGRFeatureDefn(m_strName.c_str()); //TODO: Should I "new" an OGR smart pointer - sample says so, but it doesn't seem right
2577 SetDescription( m_pFeatureDefn->GetName() );
2578 //as long as we use the same compiler & settings in both the ogr build and this
2579 //driver, we should be OK
2580 m_pFeatureDefn->Reference();
2581
2582 string tableDef;
2583 if (FAILED(hr = m_pTable->GetDefinition(tableDef)))
2584 return GDBErr(hr, "Failed at getting table definition for " +
2585 WStringToString(wstrTablePath));
2586
2587 //xxx printf("Table definition = %s", tableDef.c_str() );
2588
2589 bool abort = false;
2590
2591 // extract schema information from table
2592 CPLXMLNode *psRoot = CPLParseXMLString( tableDef.c_str() );
2593
2594 if (psRoot == nullptr)
2595 {
2596 CPLError( CE_Failure, CPLE_AppDefined, "%s",
2597 ("Failed parsing GDB Table Schema XML for " + m_strName).c_str());
2598 return false;
2599 }
2600
2601 CPLXMLNode *pDataElementNode = psRoot->psNext; // Move to next field which should be DataElement
2602
2603 if( pDataElementNode != nullptr
2604 && pDataElementNode->psChild != nullptr
2605 && pDataElementNode->eType == CXT_Element
2606 && EQUAL(pDataElementNode->pszValue,"esri:DataElement") )
2607 {
2608 CPLXMLNode *psNode;
2609
2610 m_bTimeInUTC = CPLTestBool(CPLGetXMLValue(pDataElementNode, "IsTimeInUTC", "false"));
2611
2612 for( psNode = pDataElementNode->psChild;
2613 psNode != nullptr;
2614 psNode = psNode->psNext )
2615 {
2616 if( psNode->eType == CXT_Element && psNode->psChild != nullptr )
2617 {
2618 if (EQUAL(psNode->pszValue,"OIDFieldName") )
2619 {
2620 char* pszUnescaped = CPLUnescapeString(
2621 psNode->psChild->pszValue, nullptr, CPLES_XML);
2622 m_strOIDFieldName = pszUnescaped;
2623 CPLFree(pszUnescaped);
2624 }
2625 else if (EQUAL(psNode->pszValue,"ShapeFieldName") )
2626 {
2627 char* pszUnescaped = CPLUnescapeString(
2628 psNode->psChild->pszValue, nullptr, CPLES_XML);
2629 m_strShapeFieldName = pszUnescaped;
2630 CPLFree(pszUnescaped);
2631 }
2632 else if (EQUAL(psNode->pszValue,"Fields") )
2633 {
2634 if (!GDBToOGRFields(psNode))
2635 {
2636 abort = true;
2637 break;
2638 }
2639 }
2640 }
2641 }
2642
2643 if (m_strShapeFieldName.empty())
2644 m_pFeatureDefn->SetGeomType(wkbNone);
2645 }
2646 else
2647 {
2648 CPLError( CE_Failure, CPLE_AppDefined, "%s",
2649 ("Failed parsing GDB Table Schema XML (DataElement) for " + m_strName).c_str());
2650 return false;
2651 }
2652 CPLDestroyXMLNode( psRoot );
2653
2654 if( m_pFeatureDefn->GetGeomFieldCount() != 0 )
2655 {
2656 m_pFeatureDefn->GetGeomFieldDefn(0)->SetName(m_strShapeFieldName.c_str());
2657 m_pFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(m_pSRS);
2658 }
2659
2660 if (abort)
2661 return false;
2662
2663 return true; //AOToOGRFields(ipFields, m_pFeatureDefn, m_vOGRFieldToESRIField);
2664 }
2665
2666 /************************************************************************/
2667 /* ParseGeometryDef() */
2668 /************************************************************************/
2669
ParseGeometryDef(CPLXMLNode * psRoot)2670 bool FGdbLayer::ParseGeometryDef(CPLXMLNode* psRoot)
2671 {
2672 CPLXMLNode *psGeometryDefItem;
2673
2674 string geometryType;
2675 bool hasZ = false, hasM = false;
2676 string wkt, wkid, latestwkid;
2677
2678 for (psGeometryDefItem = psRoot->psChild;
2679 psGeometryDefItem != nullptr;
2680 psGeometryDefItem = psGeometryDefItem->psNext )
2681 {
2682 //loop through all "GeometryDef" elements
2683 //
2684
2685 if (psGeometryDefItem->eType == CXT_Element &&
2686 psGeometryDefItem->psChild != nullptr)
2687 {
2688 if (EQUAL(psGeometryDefItem->pszValue,"GeometryType"))
2689 {
2690 char* pszUnescaped = CPLUnescapeString(
2691 psGeometryDefItem->psChild->pszValue, nullptr, CPLES_XML);
2692
2693 geometryType = pszUnescaped;
2694
2695 CPLFree(pszUnescaped);
2696 }
2697 else if (EQUAL(psGeometryDefItem->pszValue,"SpatialReference"))
2698 {
2699 ParseSpatialReference(psGeometryDefItem, &wkt, &wkid, &latestwkid); // we don't check for success because it
2700 // may not be there
2701 }
2702 else if (EQUAL(psGeometryDefItem->pszValue,"HasM"))
2703 {
2704 char* pszUnescaped = CPLUnescapeString(psGeometryDefItem->psChild->pszValue, nullptr, CPLES_XML);
2705
2706 if (!strcmp(pszUnescaped, "true"))
2707 hasM = true;
2708
2709 CPLFree(pszUnescaped);
2710 }
2711 else if (EQUAL(psGeometryDefItem->pszValue,"HasZ"))
2712 {
2713 char* pszUnescaped = CPLUnescapeString(
2714 psGeometryDefItem->psChild->pszValue, nullptr, CPLES_XML);
2715
2716 if (!strcmp(pszUnescaped, "true"))
2717 hasZ = true;
2718
2719 CPLFree(pszUnescaped);
2720 }
2721 }
2722 }
2723
2724 OGRwkbGeometryType ogrGeoType;
2725 if (!GDBToOGRGeometry(geometryType, hasZ, hasM, &ogrGeoType))
2726 return false;
2727
2728 m_pFeatureDefn->SetGeomType(ogrGeoType);
2729
2730 if (wkbFlatten(ogrGeoType) == wkbMultiLineString ||
2731 wkbFlatten(ogrGeoType) == wkbMultiPoint)
2732 m_forceMulti = true;
2733
2734 if (latestwkid.length() > 0 || wkid.length() > 0)
2735 {
2736 int bSuccess = FALSE;
2737 m_pSRS = new OGRSpatialReference();
2738 m_pSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
2739 CPLPushErrorHandler(CPLQuietErrorHandler);
2740 if( latestwkid.length() > 0 )
2741 {
2742 if( m_pSRS->importFromEPSG(atoi(latestwkid.c_str())) == OGRERR_NONE )
2743 {
2744 bSuccess = TRUE;
2745 }
2746 else
2747 {
2748 CPLDebug("FGDB", "Cannot import SRID %s", latestwkid.c_str());
2749 }
2750 }
2751 if( !bSuccess && wkid.length() > 0 )
2752 {
2753 if( m_pSRS->importFromEPSG(atoi(wkid.c_str())) == OGRERR_NONE )
2754 {
2755 bSuccess = TRUE;
2756 }
2757 else
2758 {
2759 CPLDebug("FGDB", "Cannot import SRID %s", wkid.c_str());
2760 }
2761 }
2762 CPLPopErrorHandler();
2763 CPLErrorReset();
2764 if( !bSuccess )
2765 {
2766 delete m_pSRS;
2767 m_pSRS = nullptr;
2768 }
2769 else
2770 return true;
2771 }
2772
2773 if (wkt.length() > 0)
2774 {
2775 if (!GDBToOGRSpatialReference(wkt, &m_pSRS))
2776 {
2777 //report error, but be passive about it
2778 CPLError( CE_Warning, CPLE_AppDefined,
2779 "Failed Mapping ESRI Spatial Reference");
2780 }
2781 }
2782 else
2783 {
2784 //report error, but be passive about it
2785 CPLError( CE_Warning, CPLE_AppDefined, "Empty Spatial Reference");
2786 }
2787
2788 return true;
2789 }
2790
2791 /************************************************************************/
2792 /* ParseSpatialReference() */
2793 /************************************************************************/
2794
ParseSpatialReference(CPLXMLNode * psSpatialRefNode,string * pOutWkt,string * pOutWKID,string * pOutLatestWKID)2795 bool FGdbLayer::ParseSpatialReference(CPLXMLNode* psSpatialRefNode,
2796 string* pOutWkt, string* pOutWKID,
2797 string* pOutLatestWKID)
2798 {
2799 *pOutWkt = "";
2800 *pOutWKID = "";
2801 *pOutLatestWKID = "";
2802
2803 CPLXMLNode* psSRItemNode;
2804
2805 /* Loop through all the SRS elements we want to store */
2806 for( psSRItemNode = psSpatialRefNode->psChild;
2807 psSRItemNode != nullptr;
2808 psSRItemNode = psSRItemNode->psNext )
2809 {
2810 /* The WKID maps (mostly) to an EPSG code */
2811 if( psSRItemNode->eType == CXT_Element &&
2812 psSRItemNode->psChild != nullptr &&
2813 EQUAL(psSRItemNode->pszValue,"WKID") )
2814 {
2815 char* pszUnescaped = CPLUnescapeString(psSRItemNode->psChild->pszValue, nullptr, CPLES_XML);
2816 *pOutWKID = pszUnescaped;
2817 CPLFree(pszUnescaped);
2818
2819 // Needed with FileGDB v1.4 with layers with empty SRS
2820 if( *pOutWKID == "0" )
2821 *pOutWKID = "";
2822 }
2823 /* The concept of LatestWKID is explained in http://resources.arcgis.com/en/help/arcgis-rest-api/index.html#//02r3000000n1000000 */
2824 else if( psSRItemNode->eType == CXT_Element &&
2825 psSRItemNode->psChild != nullptr &&
2826 EQUAL(psSRItemNode->pszValue,"LatestWKID") )
2827 {
2828 char* pszUnescaped = CPLUnescapeString(psSRItemNode->psChild->pszValue, nullptr, CPLES_XML);
2829 *pOutLatestWKID = pszUnescaped;
2830 CPLFree(pszUnescaped);
2831 }
2832 /* The WKT well-known text can be converted by OGR */
2833 else if( psSRItemNode->eType == CXT_Element &&
2834 psSRItemNode->psChild != nullptr &&
2835 EQUAL(psSRItemNode->pszValue,"WKT") )
2836 {
2837 char* pszUnescaped = CPLUnescapeString(psSRItemNode->psChild->pszValue, nullptr, CPLES_XML);
2838 *pOutWkt = pszUnescaped;
2839 CPLFree(pszUnescaped);
2840 }
2841 }
2842 return *pOutWkt != "" || *pOutWKID != "";
2843 }
2844
2845 /************************************************************************/
2846 /* GDBToOGRFields() */
2847 /************************************************************************/
2848
GDBToOGRFields(CPLXMLNode * psRoot)2849 bool FGdbLayer::GDBToOGRFields(CPLXMLNode* psRoot)
2850 {
2851 m_vOGRFieldToESRIField.clear();
2852
2853 if (psRoot->psChild == nullptr || psRoot->psChild->psNext == nullptr)
2854 {
2855 CPLError( CE_Failure, CPLE_AppDefined, "Unrecognized GDB XML Schema");
2856
2857 return false;
2858 }
2859
2860 psRoot = psRoot->psChild->psNext; //change root to "FieldArray"
2861
2862 //CPLAssert(ogrToESRIFieldMapping.size() == pOGRFeatureDef->GetFieldCount());
2863
2864 CPLXMLNode* psFieldNode;
2865 int bShouldQueryOpenFileGDB = FALSE;
2866
2867 for( psFieldNode = psRoot->psChild;
2868 psFieldNode != nullptr;
2869 psFieldNode = psFieldNode->psNext )
2870 {
2871 //loop through all "Field" elements
2872 //
2873
2874 if( psFieldNode->eType == CXT_Element && psFieldNode->psChild != nullptr &&
2875 EQUAL(psFieldNode->pszValue,"Field"))
2876 {
2877
2878 CPLXMLNode* psFieldItemNode;
2879 std::string fieldName;
2880 std::string fieldAlias;
2881 std::string fieldType;
2882 int nLength = 0;
2883 //int nPrecision = 0;
2884 int bNullable = TRUE;
2885 std::string osDefault;
2886 std::string osDomainName;
2887
2888 // loop through all items in Field element
2889 //
2890
2891 for( psFieldItemNode = psFieldNode->psChild;
2892 psFieldItemNode != nullptr;
2893 psFieldItemNode = psFieldItemNode->psNext )
2894 {
2895 if (psFieldItemNode->eType == CXT_Element)
2896 {
2897 if (EQUAL(psFieldItemNode->pszValue,"Name"))
2898 {
2899 char* pszUnescaped = CPLUnescapeString(
2900 psFieldItemNode->psChild->pszValue, nullptr, CPLES_XML);
2901 fieldName = pszUnescaped;
2902 CPLFree(pszUnescaped);
2903 }
2904 else if (EQUAL(psFieldItemNode->pszValue,"AliasName"))
2905 {
2906 char* pszUnescaped = CPLUnescapeString(
2907 psFieldItemNode->psChild->pszValue, nullptr, CPLES_XML);
2908 fieldAlias = pszUnescaped;
2909 CPLFree(pszUnescaped);
2910 }
2911 else if (EQUAL(psFieldItemNode->pszValue,"Type") )
2912 {
2913 char* pszUnescaped = CPLUnescapeString(
2914 psFieldItemNode->psChild->pszValue, nullptr, CPLES_XML);
2915 fieldType = pszUnescaped;
2916 CPLFree(pszUnescaped);
2917 }
2918 else if (EQUAL(psFieldItemNode->pszValue,"GeometryDef") )
2919 {
2920 if (!ParseGeometryDef(psFieldItemNode))
2921 return false; // if we failed parsing the GeometryDef, we are done!
2922 }
2923 else if (EQUAL(psFieldItemNode->pszValue,"Length") )
2924 {
2925 nLength = atoi(psFieldItemNode->psChild->pszValue);
2926 }
2927 else if (EQUAL(psFieldItemNode->pszValue,"Precision") )
2928 {
2929 //nPrecision = atoi(psFieldItemNode->psChild->pszValue);
2930 }
2931 else if (EQUAL(psFieldItemNode->pszValue,"IsNullable") )
2932 {
2933 bNullable = EQUAL(psFieldItemNode->psChild->pszValue, "true");
2934 }
2935 else if (EQUAL(psFieldItemNode->pszValue,"DefaultValue"))
2936 {
2937 osDefault = CPLGetXMLValue(psFieldItemNode, nullptr, "");
2938 }
2939 // NOTE: when using the GetDefinition() API, the domain name
2940 // is set in <Domain><DomainName>, whereas the raw XML is
2941 // just <DomainName>
2942 else if (EQUAL(psFieldItemNode->pszValue,"Domain"))
2943 {
2944 osDomainName = CPLGetXMLValue(psFieldItemNode, "DomainName", "");
2945 }
2946 }
2947 }
2948
2949 ///////////////////////////////////////////////////////////////////
2950 // At this point we have parsed everything about the current field
2951
2952 if (fieldType == "esriFieldTypeGeometry")
2953 {
2954 m_strShapeFieldName = fieldName;
2955 m_pFeatureDefn->GetGeomFieldDefn(0)->SetNullable(bNullable);
2956
2957 continue; // finish here for special field - don't add as OGR fielddef
2958 }
2959 else if (fieldType == "esriFieldTypeOID")
2960 {
2961 //m_strOIDFieldName = fieldName; // already set by this point
2962
2963 continue; // finish here for special field - don't add as OGR fielddef
2964 }
2965
2966 OGRFieldType ogrType;
2967 OGRFieldSubType eSubType;
2968 //CPLDebug("FGDB", "name = %s, type = %s", fieldName.c_str(), fieldType.c_str() );
2969 if (!GDBToOGRFieldType(fieldType, &ogrType, &eSubType))
2970 {
2971 // field cannot be mapped, skipping further processing
2972 CPLError( CE_Warning, CPLE_AppDefined, "Skipping field: [%s] type: [%s] ",
2973 fieldName.c_str(), fieldType.c_str() );
2974 continue;
2975 }
2976
2977 //TODO: Optimization - modify m_wstrSubFields so it only fetches fields that are mapped
2978
2979 OGRFieldDefn fieldTemplate( fieldName.c_str(), ogrType);
2980 if( fieldAlias != fieldName )
2981 {
2982 // The SDK generates an alias even with it is not explicitly written
2983 fieldTemplate.SetAlternativeName(fieldAlias.c_str());
2984 }
2985 fieldTemplate.SetSubType(eSubType);
2986 /* On creation (GDBFieldTypeToWidthPrecision) if string width is 0, we pick up */
2987 /* 65535 by default to mean unlimited string length, but we don't want */
2988 /* to advertise such a big number */
2989 if( ogrType == OFTString && nLength < 65535 )
2990 fieldTemplate.SetWidth(nLength);
2991 //fieldTemplate.SetPrecision(nPrecision);
2992 fieldTemplate.SetNullable(bNullable);
2993 if( !osDefault.empty() )
2994 {
2995 if( ogrType == OFTString )
2996 {
2997 char* pszTmp = CPLEscapeString(osDefault.c_str(), -1, CPLES_SQL);
2998 osDefault = "'";
2999 osDefault += pszTmp;
3000 CPLFree(pszTmp);
3001 osDefault += "'";
3002 fieldTemplate.SetDefault(osDefault.c_str());
3003 }
3004 else if( ogrType == OFTInteger ||
3005 ogrType == OFTReal )
3006 {
3007 #ifdef unreliable
3008 /* Disabling this as GDBs and the FileGDB SDK aren't reliable for numeric values */
3009 /* It often occurs that the XML definition in a00000004.gdbtable doesn't */
3010 /* match the default values (in binary) found in the field definition */
3011 /* section of the .gdbtable of the layers themselves */
3012 /* The Table::GetDefinition() API of FileGDB doesn't seem to use the */
3013 /* XML definition, but rather the values found in the field definition */
3014 /* section of the .gdbtable of the layers themselves */
3015 /* It seems that the XML definition in a00000004.gdbtable is authoritative */
3016 /* in ArcGIS, so we're screwed... */
3017
3018 fieldTemplate.SetDefault(osDefault.c_str());
3019 #endif
3020 bShouldQueryOpenFileGDB = TRUE;
3021 }
3022 else if( ogrType == OFTDateTime )
3023 {
3024 int nYear, nMonth, nDay, nHour, nMinute;
3025 float fSecond;
3026 if( sscanf(osDefault.c_str(), "%d-%d-%dT%d:%d:%fZ", &nYear, &nMonth, &nDay,
3027 &nHour, &nMinute, &fSecond) == 6 ||
3028 sscanf(osDefault.c_str(), "'%d-%d-%d %d:%d:%fZ'", &nYear, &nMonth, &nDay,
3029 &nHour, &nMinute, &fSecond) == 6 )
3030 {
3031 fieldTemplate.SetDefault(CPLSPrintf("'%04d/%02d/%02d %02d:%02d:%02d'",
3032 nYear, nMonth, nDay, nHour, nMinute, (int)(fSecond + 0.5)));
3033 }
3034 }
3035 }
3036 if( !osDomainName.empty() )
3037 {
3038 fieldTemplate.SetDomainName(osDomainName);
3039 }
3040
3041 m_pFeatureDefn->AddFieldDefn( &fieldTemplate );
3042
3043 m_vOGRFieldToESRIField.push_back(StringToWString(fieldName));
3044 m_vOGRFieldToESRIFieldType.push_back( fieldType );
3045 if( ogrType == OFTBinary )
3046 m_apoByteArrays.push_back(new ByteArray());
3047 }
3048 }
3049
3050 /* Using OpenFileGDB to get reliable default values for integer/real fields */
3051 if( bShouldQueryOpenFileGDB )
3052 {
3053 const char* apszDrivers[] = { "OpenFileGDB", nullptr };
3054 GDALDataset* poDS = (GDALDataset*) GDALOpenEx(m_pDS->GetFSName(),
3055 GDAL_OF_VECTOR, (char**)apszDrivers, nullptr, nullptr);
3056 if( poDS != nullptr )
3057 {
3058 OGRLayer* poLyr = poDS->GetLayerByName(GetName());
3059 if( poLyr )
3060 {
3061 for(int i=0;i<poLyr->GetLayerDefn()->GetFieldCount();i++)
3062 {
3063 OGRFieldDefn* poSrcDefn = poLyr->GetLayerDefn()->GetFieldDefn(i);
3064 if( (poSrcDefn->GetType() == OFTInteger || poSrcDefn->GetType() == OFTReal) &&
3065 poSrcDefn->GetDefault() != nullptr )
3066 {
3067 int nIdxDst = m_pFeatureDefn->GetFieldIndex(poSrcDefn->GetNameRef());
3068 if( nIdxDst >= 0 )
3069 m_pFeatureDefn->GetFieldDefn(nIdxDst)->SetDefault(poSrcDefn->GetDefault());
3070 }
3071 }
3072 }
3073 GDALClose( poDS );
3074 }
3075 }
3076
3077 return true;
3078 }
3079
3080 /************************************************************************/
3081 /* ResetReading() */
3082 /************************************************************************/
3083
ResetReading()3084 void FGdbLayer::ResetReading()
3085 {
3086 long hr;
3087
3088 if( m_pTable == nullptr )
3089 return;
3090
3091 EndBulkLoad();
3092
3093 if (m_poFilterGeom && !m_poFilterGeom->IsEmpty())
3094 {
3095 // Search spatial
3096 // As of beta1, FileGDB only supports bbox searched, if we have GEOS installed,
3097 // we can do the rest ourselves.
3098
3099 OGREnvelope ogrEnv;
3100
3101 m_poFilterGeom->getEnvelope(&ogrEnv);
3102
3103 //spatial query
3104 FileGDBAPI::Envelope env(ogrEnv.MinX, ogrEnv.MaxX, ogrEnv.MinY, ogrEnv.MaxY);
3105
3106 if( FAILED(hr = m_pTable->Search(m_wstrSubfields, m_wstrWhereClause, env, true, *m_pEnumRows)) )
3107 GDBErr(hr, "Failed Searching");
3108
3109 m_bFilterDirty = false;
3110
3111 return;
3112 }
3113
3114 // Search non-spatial
3115
3116 if( FAILED(hr = m_pTable->Search(m_wstrSubfields, m_wstrWhereClause, true, *m_pEnumRows)) )
3117 GDBErr(hr, "Failed Searching");
3118
3119 m_bFilterDirty = false;
3120 }
3121
3122 /************************************************************************/
3123 /* SetSpatialFilter() */
3124 /************************************************************************/
3125
SetSpatialFilter(OGRGeometry * pOGRGeom)3126 void FGdbLayer::SetSpatialFilter( OGRGeometry* pOGRGeom )
3127 {
3128 m_bFilterDirty = true;
3129 OGRLayer::SetSpatialFilter(pOGRGeom);
3130 }
3131
3132 /************************************************************************/
3133 /* ResyncIDs() */
3134 /************************************************************************/
3135
ResyncIDs()3136 void FGdbLayer::ResyncIDs()
3137 {
3138 if( m_oMapOGRFIDToFGDBFID.empty() )
3139 return;
3140 if( m_pDS->Close() )
3141 m_pDS->ReOpen();
3142 }
3143
3144 /************************************************************************/
3145 /* SetAttributeFilter() */
3146 /************************************************************************/
3147
SetAttributeFilter(const char * pszQuery)3148 OGRErr FGdbLayer::SetAttributeFilter( const char* pszQuery )
3149 {
3150 if( pszQuery != nullptr && CPLString(pszQuery).ifind(GetFIDColumn()) != std::string::npos )
3151 ResyncIDs();
3152
3153 m_wstrWhereClause = StringToWString( (pszQuery != nullptr) ? pszQuery : "" );
3154
3155 m_bFilterDirty = true;
3156
3157 return OGRERR_NONE;
3158 }
3159
3160 /************************************************************************/
3161 /* OGRFeatureFromGdbRow() */
3162 /************************************************************************/
3163
OGRFeatureFromGdbRow(Row * pRow,OGRFeature ** ppFeature)3164 bool FGdbBaseLayer::OGRFeatureFromGdbRow(Row* pRow, OGRFeature** ppFeature)
3165 {
3166 long hr;
3167
3168 OGRFeature* pOutFeature = new OGRFeature(m_pFeatureDefn);
3169
3170 /////////////////////////////////////////////////////////
3171 // Translate OID
3172 //
3173
3174 int32 oid = -1;
3175 if (FAILED(hr = pRow->GetOID(oid)))
3176 {
3177 //this should never happen
3178 delete pOutFeature;
3179 return false;
3180 }
3181 pOutFeature->SetFID(oid);
3182
3183 /////////////////////////////////////////////////////////
3184 // Translate Geometry
3185 //
3186
3187 ShapeBuffer gdbGeometry;
3188 // Row::GetGeometry() will fail with -2147467259 for NULL geometries
3189 // Row::GetGeometry() will fail with -2147219885 for tables without a geometry field
3190 if (!m_pFeatureDefn->IsGeometryIgnored() &&
3191 !FAILED(hr = pRow->GetGeometry(gdbGeometry)))
3192 {
3193 OGRGeometry* pOGRGeo = nullptr;
3194
3195 if ((!GDBGeometryToOGRGeometry(m_forceMulti, &gdbGeometry, m_pSRS, &pOGRGeo)))
3196 {
3197 delete pOutFeature;
3198 return GDBErr(hr, "Failed to translate FileGDB Geometry to OGR Geometry for row " + string(CPLSPrintf("%d", (int)oid)));
3199 }
3200
3201 pOutFeature->SetGeometryDirectly(pOGRGeo);
3202 }
3203
3204 //////////////////////////////////////////////////////////
3205 // Map fields
3206 //
3207
3208 int mappedFieldCount = static_cast<int>(m_vOGRFieldToESRIField.size());
3209
3210 bool foundBadColumn = false;
3211
3212 for (int i = 0; i < mappedFieldCount; ++i)
3213 {
3214 OGRFieldDefn* poFieldDefn = m_pFeatureDefn->GetFieldDefn(i);
3215 // The IsNull() and GetXXX() API are very slow when there are a
3216 // big number of fields, for example with Tiger database.
3217 if (poFieldDefn->IsIgnored() )
3218 continue;
3219
3220 const wstring & wstrFieldName = m_vOGRFieldToESRIField[i];
3221 const std::string & strFieldType = m_vOGRFieldToESRIFieldType[i];
3222
3223 bool isNull = false;
3224
3225 if (FAILED(hr = pRow->IsNull(wstrFieldName, isNull)))
3226 {
3227 GDBErr(hr, "Failed to determine NULL status from column " +
3228 WStringToString(wstrFieldName));
3229 foundBadColumn = true;
3230 continue;
3231 }
3232
3233 if (isNull)
3234 {
3235 pOutFeature->SetFieldNull(i);
3236 continue;
3237 }
3238
3239 //
3240 // NOTE: This switch statement needs to be kept in sync with GDBToOGRFieldType utility function
3241 // since we are only checking for types we mapped in that utility function
3242
3243 switch (poFieldDefn->GetType())
3244 {
3245
3246 case OFTInteger:
3247 {
3248 int32 val;
3249
3250 if (FAILED(hr = pRow->GetInteger(wstrFieldName, val)))
3251 {
3252 int16 shortval;
3253 if (FAILED(hr = pRow->GetShort(wstrFieldName, shortval)))
3254 {
3255 GDBErr(hr, "Failed to determine integer value for column " +
3256 WStringToString(wstrFieldName));
3257 foundBadColumn = true;
3258 continue;
3259 }
3260 val = shortval;
3261 }
3262
3263 pOutFeature->SetField(i, (int)val);
3264 }
3265 break;
3266
3267 case OFTReal:
3268 {
3269 if (strFieldType == "esriFieldTypeSingle")
3270 {
3271 float val;
3272
3273 if (FAILED(hr = pRow->GetFloat(wstrFieldName, val)))
3274 {
3275 GDBErr(hr, "Failed to determine float value for column " +
3276 WStringToString(wstrFieldName));
3277 foundBadColumn = true;
3278 continue;
3279 }
3280
3281 pOutFeature->SetField(i, val);
3282 }
3283 else
3284 {
3285 double val;
3286
3287 if (FAILED(hr = pRow->GetDouble(wstrFieldName, val)))
3288 {
3289 GDBErr(hr, "Failed to determine real value for column " +
3290 WStringToString(wstrFieldName));
3291 foundBadColumn = true;
3292 continue;
3293 }
3294
3295 pOutFeature->SetField(i, val);
3296 }
3297 }
3298 break;
3299 case OFTString:
3300 {
3301 wstring val;
3302 std::string strValue;
3303
3304 if( strFieldType == "esriFieldTypeGlobalID" )
3305 {
3306 Guid guid;
3307 if( FAILED(hr = pRow->GetGlobalID(guid)) ||
3308 FAILED(hr = guid.ToString(val)) )
3309 {
3310 GDBErr(hr, "Failed to determine string value for column " +
3311 WStringToString(wstrFieldName));
3312 foundBadColumn = true;
3313 continue;
3314 }
3315 strValue = WStringToString(val);
3316 }
3317 else if( strFieldType == "esriFieldTypeGUID" )
3318 {
3319 Guid guid;
3320 if( FAILED(hr = pRow->GetGUID(wstrFieldName, guid)) ||
3321 FAILED(hr = guid.ToString(val)) )
3322 {
3323 GDBErr(hr, "Failed to determine string value for column " +
3324 WStringToString(wstrFieldName));
3325 foundBadColumn = true;
3326 continue;
3327 }
3328 strValue = WStringToString(val);
3329 }
3330 else if( strFieldType == "esriFieldTypeXML" )
3331 {
3332 if (FAILED(hr = pRow->GetXML(wstrFieldName, strValue)))
3333 {
3334 GDBErr(hr, "Failed to determine XML value for column " +
3335 WStringToString(wstrFieldName));
3336 foundBadColumn = true;
3337 continue;
3338 }
3339 }
3340 else
3341 {
3342 if (FAILED(hr = pRow->GetString(wstrFieldName, val)))
3343 {
3344 GDBErr(hr, "Failed to determine string value for column " +
3345 WStringToString(wstrFieldName));
3346 foundBadColumn = true;
3347 continue;
3348 }
3349 strValue = WStringToString(val);
3350 }
3351
3352 pOutFeature->SetField(i, strValue.c_str());
3353 }
3354 break;
3355
3356 case OFTBinary:
3357 {
3358 ByteArray binaryBuf;
3359
3360 if (FAILED(hr = pRow->GetBinary(wstrFieldName, binaryBuf)))
3361 {
3362 GDBErr(hr, "Failed to determine binary value for column " + WStringToString(wstrFieldName));
3363 foundBadColumn = true;
3364 continue;
3365 }
3366
3367 pOutFeature->SetField(i, (int)binaryBuf.inUseLength, (GByte*)binaryBuf.byteArray);
3368 }
3369 break;
3370
3371 case OFTDateTime:
3372 {
3373 struct tm val;
3374
3375 if (FAILED(hr = pRow->GetDate(wstrFieldName, val)))
3376 {
3377 GDBErr(hr, "Failed to determine date value for column " +
3378 WStringToString(wstrFieldName));
3379 foundBadColumn = true;
3380 continue;
3381 }
3382
3383 pOutFeature->SetField(i, val.tm_year + 1900, val.tm_mon + 1,
3384 val.tm_mday, val.tm_hour, val.tm_min, (float)val.tm_sec,
3385 m_bTimeInUTC ? 100 : 0);
3386 // Examine test data to figure out how to extract that
3387 }
3388 break;
3389
3390 default:
3391 {
3392 if (!m_suppressColumnMappingError)
3393 {
3394 foundBadColumn = true;
3395 CPLError( CE_Warning, CPLE_AppDefined,
3396 "Row id: %d col:%d has unhandled col type (%d). Setting to NULL.",
3397 (int)oid, (int)i, m_pFeatureDefn->GetFieldDefn(i)->GetType());
3398 }
3399 }
3400 }
3401 }
3402
3403 if (foundBadColumn)
3404 m_suppressColumnMappingError = true;
3405
3406 *ppFeature = pOutFeature;
3407
3408 return true;
3409 }
3410
3411 /************************************************************************/
3412 /* GetNextFeature() */
3413 /************************************************************************/
3414
GetNextFeature()3415 OGRFeature* FGdbLayer::GetNextFeature()
3416 {
3417 if (m_bFilterDirty)
3418 ResetReading();
3419
3420 EndBulkLoad();
3421
3422 OGRFeature* poFeature = FGdbBaseLayer::GetNextFeature();
3423 if( poFeature )
3424 {
3425 std::map<int,int>::iterator oIter = m_oMapFGDBFIDToOGRFID.find((int)poFeature->GetFID());
3426 if( oIter != m_oMapFGDBFIDToOGRFID.end() )
3427 {
3428 poFeature->SetFID(oIter->second);
3429 }
3430 }
3431 return poFeature;
3432 }
3433
3434 /************************************************************************/
3435 /* GetFeature() */
3436 /************************************************************************/
3437
GetFeature(GIntBig oid)3438 OGRFeature *FGdbLayer::GetFeature( GIntBig oid )
3439 {
3440 // do query to fetch individual row
3441 EnumRows enumRows;
3442 Row row;
3443 if( !CPL_INT64_FITS_ON_INT32(oid) || m_pTable == nullptr )
3444 return nullptr;
3445
3446 EndBulkLoad();
3447
3448 int nFID32 = (int)oid;
3449 std::map<int,int>::iterator oIter = m_oMapOGRFIDToFGDBFID.find(nFID32);
3450 if( oIter != m_oMapOGRFIDToFGDBFID.end() )
3451 nFID32 = oIter->second;
3452 else if( m_oMapFGDBFIDToOGRFID.find(nFID32) != m_oMapFGDBFIDToOGRFID.end() )
3453 return nullptr;
3454
3455 if (GetRow(enumRows, row, nFID32) != OGRERR_NONE)
3456 return nullptr;
3457
3458 OGRFeature* pOGRFeature = nullptr;
3459
3460 if (!OGRFeatureFromGdbRow(&row, &pOGRFeature))
3461 {
3462 return nullptr;
3463 }
3464 if( pOGRFeature )
3465 {
3466 pOGRFeature->SetFID(oid);
3467 }
3468
3469 return pOGRFeature;
3470 }
3471
3472 /************************************************************************/
3473 /* GetFeatureCount() */
3474 /************************************************************************/
3475
GetFeatureCount(CPL_UNUSED int bForce)3476 GIntBig FGdbLayer::GetFeatureCount( CPL_UNUSED int bForce )
3477 {
3478 int32 rowCount = 0;
3479
3480 if( m_pTable == nullptr )
3481 return 0;
3482
3483 EndBulkLoad();
3484
3485 if (m_poFilterGeom != nullptr || !m_wstrWhereClause.empty())
3486 {
3487 ResetReading();
3488 if (m_pEnumRows == nullptr)
3489 return 0;
3490
3491 int nFeatures = 0;
3492 while( true )
3493 {
3494 long hr;
3495
3496 Row row;
3497
3498 if (FAILED(hr = m_pEnumRows->Next(row)))
3499 {
3500 GDBErr(hr, "Failed fetching features");
3501 return 0;
3502 }
3503
3504 if (hr != S_OK)
3505 {
3506 break;
3507 }
3508
3509 if( m_poFilterGeom == nullptr )
3510 {
3511 nFeatures++;
3512 }
3513 else
3514 {
3515 ShapeBuffer gdbGeometry;
3516 if (FAILED(hr = row.GetGeometry(gdbGeometry)))
3517 {
3518 continue;
3519 }
3520
3521 OGRGeometry* pOGRGeo = nullptr;
3522 if( !GDBGeometryToOGRGeometry(m_forceMulti, &gdbGeometry, m_pSRS, &pOGRGeo) || pOGRGeo == nullptr)
3523 {
3524 delete pOGRGeo;
3525 continue;
3526 }
3527
3528 if( FilterGeometry( pOGRGeo ) )
3529 {
3530 nFeatures ++;
3531 }
3532
3533 delete pOGRGeo;
3534 }
3535 }
3536 ResetReading();
3537 return nFeatures;
3538 }
3539
3540 long hr;
3541 if (FAILED(hr = m_pTable->GetRowCount(rowCount)))
3542 {
3543 GDBErr(hr, "Failed counting rows");
3544 return 0;
3545 }
3546
3547 return static_cast<int>(rowCount);
3548 }
3549
3550 /************************************************************************/
3551 /* GetMetadataItem() */
3552 /************************************************************************/
3553
GetMetadataItem(const char * pszName,const char * pszDomain)3554 const char* FGdbLayer::GetMetadataItem(const char* pszName, const char* pszDomain)
3555 {
3556 if( pszDomain != nullptr && EQUAL(pszDomain, "MAP_OGR_FID_TO_FGDB_FID") )
3557 {
3558 if( m_oMapOGRFIDToFGDBFID.find(atoi(pszName)) != m_oMapOGRFIDToFGDBFID.end() )
3559 return CPLSPrintf("%d", m_oMapOGRFIDToFGDBFID[atoi(pszName)]);
3560 }
3561 else if( pszDomain != nullptr && EQUAL(pszDomain, "MAP_FGDB_FID_TO_OGR_FID") )
3562 {
3563 if( m_oMapFGDBFIDToOGRFID.find(atoi(pszName)) != m_oMapFGDBFIDToOGRFID.end() )
3564 return CPLSPrintf("%d", m_oMapFGDBFIDToOGRFID[atoi(pszName)]);
3565 }
3566 return OGRLayer::GetMetadataItem(pszName, pszDomain);
3567 }
3568
3569 /************************************************************************/
3570 /* GetExtent() */
3571 /************************************************************************/
3572
GetExtent(OGREnvelope * psExtent,int bForce)3573 OGRErr FGdbLayer::GetExtent (OGREnvelope* psExtent, int bForce)
3574 {
3575 if( m_pTable == nullptr )
3576 return OGRERR_FAILURE;
3577
3578 if (m_poFilterGeom != nullptr || !m_wstrWhereClause.empty() ||
3579 m_strShapeFieldName.empty())
3580 {
3581 const int nFieldCount = m_pFeatureDefn->GetFieldCount();
3582 int* pabSaveFieldIgnored = new int[nFieldCount];
3583 for(int i=0;i<nFieldCount;i++)
3584 {
3585 // cppcheck-suppress uninitdata
3586 pabSaveFieldIgnored[i] = m_pFeatureDefn->GetFieldDefn(i)->IsIgnored();
3587 m_pFeatureDefn->GetFieldDefn(i)->SetIgnored(TRUE);
3588 }
3589 OGRErr eErr = OGRLayer::GetExtent(psExtent, bForce);
3590 for(int i=0;i<nFieldCount;i++)
3591 {
3592 m_pFeatureDefn->GetFieldDefn(i)->SetIgnored(pabSaveFieldIgnored[i]);
3593 }
3594 delete[] pabSaveFieldIgnored;
3595 return eErr;
3596 }
3597
3598 long hr;
3599 Envelope envelope;
3600 if (FAILED(hr = m_pTable->GetExtent(envelope)))
3601 {
3602 GDBErr(hr, "Failed fetching extent");
3603 return OGRERR_FAILURE;
3604 }
3605
3606 psExtent->MinX = envelope.xMin;
3607 psExtent->MinY = envelope.yMin;
3608 psExtent->MaxX = envelope.xMax;
3609 psExtent->MaxY = envelope.yMax;
3610
3611 if (CPLIsNan(psExtent->MinX) ||
3612 CPLIsNan(psExtent->MinY) ||
3613 CPLIsNan(psExtent->MaxX) ||
3614 CPLIsNan(psExtent->MaxY))
3615 return OGRERR_FAILURE;
3616
3617 return OGRERR_NONE;
3618 }
3619
3620 /************************************************************************/
3621 /* StartBulkLoad() */
3622 /************************************************************************/
3623
StartBulkLoad()3624 void FGdbLayer::StartBulkLoad ()
3625 {
3626 if ( ! m_pTable )
3627 return;
3628
3629 if ( m_bBulkLoadInProgress )
3630 return;
3631
3632 m_bBulkLoadInProgress = TRUE;
3633 m_pTable->LoadOnlyMode(true);
3634 m_pTable->SetWriteLock();
3635 }
3636
3637 /************************************************************************/
3638 /* EndBulkLoad() */
3639 /************************************************************************/
3640
EndBulkLoad()3641 void FGdbLayer::EndBulkLoad ()
3642 {
3643 if ( ! m_pTable )
3644 return;
3645
3646 if ( ! m_bBulkLoadInProgress )
3647 return;
3648
3649 m_bBulkLoadInProgress = FALSE;
3650 m_bBulkLoadAllowed = -1; /* so that the configuration option is read the first time we CreateFeature() again */
3651 m_pTable->LoadOnlyMode(false);
3652 m_pTable->FreeWriteLock();
3653 }
3654
3655 /* OGRErr FGdbLayer::StartTransaction ()
3656 {
3657 if ( ! m_pTable )
3658 return OGRERR_FAILURE;
3659
3660 m_pTable->LoadOnlyMode(true);
3661 m_pTable->SetWriteLock();
3662 return OGRERR_NONE;
3663 } */
3664
3665 /* OGRErr FGdbLayer::CommitTransaction ()
3666 {
3667 if ( ! m_pTable )
3668 return OGRERR_FAILURE;
3669
3670 m_pTable->LoadOnlyMode(false);
3671 m_pTable->FreeWriteLock();
3672 return OGRERR_NONE;
3673 } */
3674
3675 /* OGRErr FGdbLayer::RollbackTransaction ()
3676 {
3677 if ( ! m_pTable )
3678 return OGRERR_FAILURE;
3679
3680 m_pTable->LoadOnlyMode(false);
3681 m_pTable->FreeWriteLock();
3682 return OGRERR_NONE;
3683 } */
3684
3685 /************************************************************************/
3686 /* GetLayerXML() */
3687 /* Return XML definition of the Layer as provided by FGDB. Caller must */
3688 /* free result. */
3689 /* Not currently used by the driver, but can be used by external code */
3690 /* for specific purposes. */
3691 /************************************************************************/
3692
GetLayerXML(char ** ppXml)3693 OGRErr FGdbLayer::GetLayerXML (char **ppXml)
3694 {
3695 long hr;
3696 std::string xml;
3697
3698 if( m_pTable == nullptr )
3699 return OGRERR_FAILURE;
3700
3701 if ( FAILED(hr = m_pTable->GetDefinition(xml)) )
3702 {
3703 GDBErr(hr, "Failed fetching XML table definition");
3704 return OGRERR_FAILURE;
3705 }
3706
3707 *ppXml = CPLStrdup(xml.c_str());
3708 return OGRERR_NONE;
3709 }
3710
3711 /************************************************************************/
3712 /* GetLayerMetadataXML() */
3713 /* Return XML metadata for the Layer as provided by FGDB. Caller must */
3714 /* free result. */
3715 /* Not currently used by the driver, but can be used by external code */
3716 /* for specific purposes. */
3717 /************************************************************************/
3718
GetLayerMetadataXML(char ** ppXml)3719 OGRErr FGdbLayer::GetLayerMetadataXML (char **ppXml)
3720 {
3721 long hr;
3722 std::string xml;
3723
3724 if( m_pTable == nullptr )
3725 return OGRERR_FAILURE;
3726
3727 if ( FAILED(hr = m_pTable->GetDocumentation(xml)) )
3728 {
3729 GDBErr(hr, "Failed fetching XML table metadata");
3730 return OGRERR_FAILURE;
3731 }
3732
3733 *ppXml = CPLStrdup(xml.c_str());
3734 return OGRERR_NONE;
3735 }
3736
3737 /************************************************************************/
3738 /* TestCapability() */
3739 /************************************************************************/
3740
TestCapability(const char * pszCap)3741 int FGdbLayer::TestCapability( const char* pszCap )
3742 {
3743
3744 if (EQUAL(pszCap,OLCRandomRead))
3745 return TRUE;
3746
3747 else if (EQUAL(pszCap,OLCFastFeatureCount))
3748 return m_poFilterGeom == nullptr && m_wstrWhereClause.empty();
3749
3750 else if (EQUAL(pszCap,OLCFastSpatialFilter))
3751 return TRUE;
3752
3753 else if (EQUAL(pszCap,OLCFastGetExtent))
3754 return m_poFilterGeom == nullptr && m_wstrWhereClause.empty();
3755
3756 else if (EQUAL(pszCap,OLCCreateField)) /* CreateField() */
3757 return m_pDS->GetUpdate();
3758
3759 else if (EQUAL(pszCap,OLCSequentialWrite)) /* ICreateFeature() */
3760 return m_pDS->GetUpdate();
3761
3762 else if (EQUAL(pszCap,OLCStringsAsUTF8)) /* Native UTF16, converted to UTF8 */
3763 return TRUE;
3764
3765 else if (EQUAL(pszCap,OLCReorderFields)) /* TBD ReorderFields() */
3766 return m_pDS->GetUpdate();
3767
3768 else if (EQUAL(pszCap,OLCDeleteFeature)) /* DeleteFeature() */
3769 return m_pDS->GetUpdate();
3770
3771 else if (EQUAL(pszCap,OLCRandomWrite)) /* ISetFeature() */
3772 return m_pDS->GetUpdate();
3773
3774 else if (EQUAL(pszCap,OLCDeleteField)) /* DeleteField() */
3775 return m_pDS->GetUpdate();
3776
3777 #ifdef AlterFieldDefn_implemented_but_not_working
3778 else if (EQUAL(pszCap,OLCAlterFieldDefn)) /* AlterFieldDefn() */
3779 return m_pDS->GetUpdate();
3780 #endif
3781
3782 else if (EQUAL(pszCap,OLCFastSetNextByIndex)) /* TBD FastSetNextByIndex() */
3783 return FALSE;
3784
3785 else if (EQUAL(pszCap,OLCTransactions)) /* TBD Start/End Transactions() */
3786 return FALSE;
3787
3788 else if( EQUAL(pszCap,OLCIgnoreFields) )
3789 return TRUE;
3790
3791 else if( EQUAL(pszCap,OLCMeasuredGeometries) )
3792 return TRUE;
3793
3794 else
3795 return FALSE;
3796 }
3797
3798 /************************************************************************/
3799 /* CreateRealCopy() */
3800 /************************************************************************/
3801
CreateRealCopy()3802 int FGdbLayer::CreateRealCopy()
3803 {
3804 CPLAssert( m_bSymlinkFlag );
3805
3806 // Find the FID of the layer in the system catalog
3807 char* apszDrivers[2] = { nullptr };
3808 apszDrivers[0] = (char*) "OpenFileGDB";
3809 apszDrivers[1] = nullptr;
3810 const char* pszSystemCatalog
3811 = CPLFormFilename(m_pDS->GetFSName(), "a00000001.gdbtable", nullptr);
3812 GDALDataset* poOpenFileGDBDS = (GDALDataset*)
3813 GDALOpenEx(pszSystemCatalog, GDAL_OF_VECTOR,
3814 apszDrivers, nullptr, nullptr);
3815 if( poOpenFileGDBDS == nullptr || poOpenFileGDBDS->GetLayer(0) == nullptr )
3816 {
3817 CPLError( CE_Failure, CPLE_AppDefined,
3818 "Cannot open %s with OpenFileGDB driver. Should not happen.",
3819 pszSystemCatalog);
3820 GDALClose(poOpenFileGDBDS);
3821 return FALSE;
3822 }
3823
3824 OGRLayer* poLayer = poOpenFileGDBDS->GetLayer(0);
3825 CPLString osFilter = "name = '";
3826 osFilter += GetName();
3827 osFilter += "'";
3828 poLayer->SetAttributeFilter(osFilter);
3829 poLayer->ResetReading();
3830 OGRFeature* poF = poLayer->GetNextFeature();
3831 if( poF == nullptr )
3832 {
3833 CPLError( CE_Failure, CPLE_AppDefined,
3834 "Cannot find filename for layer %s",
3835 GetName());
3836 GDALClose(poOpenFileGDBDS);
3837 return FALSE;
3838 }
3839 int nLayerFID = (int)poF->GetFID();
3840 delete poF;
3841 GDALClose(poOpenFileGDBDS);
3842
3843 if( !m_pDS->Close(TRUE) )
3844 return FALSE;
3845
3846 // Create real copies (in .tmp files now) instead of symlinks
3847 char** papszFiles = VSIReadDir(m_pDS->GetFSName());
3848 CPLString osBasename(CPLSPrintf("a%08x", nLayerFID));
3849 int bError = FALSE;
3850 std::vector<CPLString> aoFiles;
3851 for(char** papszIter = papszFiles; !bError && papszIter && *papszIter; papszIter++)
3852 {
3853 if( strncmp(*papszIter, osBasename.c_str(), osBasename.size()) == 0 )
3854 {
3855 if(CPLCopyFile( CPLFormFilename(m_pDS->GetFSName(), *papszIter, "tmp"),
3856 CPLFormFilename(m_pDS->GetFSName(), *papszIter, nullptr) ) != 0 )
3857 {
3858 CPLError(CE_Failure, CPLE_AppDefined,
3859 "Cannot copy %s", *papszIter);
3860 bError = TRUE;
3861 }
3862 else
3863 aoFiles.push_back(*papszIter);
3864 }
3865 }
3866 CSLDestroy(papszFiles);
3867
3868 // Rename the .tmp into normal filenames
3869 for(size_t i=0; !bError && i<aoFiles.size(); i++ )
3870 {
3871 if( VSIUnlink( CPLFormFilename(m_pDS->GetFSName(), aoFiles[i], nullptr) ) != 0 ||
3872 VSIRename( CPLFormFilename(m_pDS->GetFSName(), aoFiles[i], "tmp"),
3873 CPLFormFilename(m_pDS->GetFSName(), aoFiles[i], nullptr) ) != 0 )
3874 {
3875 CPLError(CE_Failure, CPLE_AppDefined, "Cannot rename %s.tmp", aoFiles[i].c_str());
3876 bError = TRUE;
3877 }
3878 }
3879
3880 int bRet = !bError && m_pDS->ReOpen();
3881 if( bRet )
3882 m_bSymlinkFlag = FALSE;
3883 return bRet;
3884 }
3885