LCOV - code coverage report
Current view: directory - ogr/ogrsf_frmts/filegdb - FGdbLayer.cpp (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 882 648 73.5 %
Date: 2012-04-28 Functions: 39 33 84.6 %

       1                 : /******************************************************************************
       2                 : * $Id: FGdbLayer.cpp 23925 2012-02-08 21:56:47Z rcoup $
       3                 : *
       4                 : * Project:  OpenGIS Simple Features Reference Implementation
       5                 : * Purpose:  Implements FileGDB OGR layer.
       6                 : * Author:   Ragi Yaser Burhum, ragi@burhum.com
       7                 : *           Paul Ramsey, pramsey at cleverelephant.ca
       8                 : *
       9                 : ******************************************************************************
      10                 : * Copyright (c) 2010, Ragi Yaser Burhum
      11                 : * Copyright (c) 2011, Paul Ramsey <pramsey at cleverelephant.ca>
      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 "ogr_fgdb.h"
      33                 : #include "ogrpgeogeometry.h"
      34                 : #include "cpl_conv.h"
      35                 : #include "cpl_string.h"
      36                 : #include "FGdbUtils.h"
      37                 : #include "cpl_minixml.h" // the only way right now to extract schema information
      38                 : 
      39                 : CPL_CVSID("$Id: FGdbLayer.cpp 23925 2012-02-08 21:56:47Z rcoup $");
      40                 : 
      41                 : using std::string;
      42                 : using std::wstring;
      43                 : 
      44                 : /************************************************************************/
      45                 : /*                              FGdbLayer()                               */
      46                 : /************************************************************************/
      47             298 : FGdbLayer::FGdbLayer():
      48                 :     OGRLayer(), m_pDS(NULL), m_pTable(NULL), m_pFeatureDefn(NULL), 
      49                 :     m_pSRS(NULL), m_wstrSubfields(L"*"), m_pOGRFilterGeometry(NULL), 
      50                 :     m_pEnumRows(NULL), m_bFilterDirty(true), 
      51             298 :     m_supressColumnMappingError(false), m_forceMulti(false), m_bLaunderReservedKeywords(true)
      52                 : {
      53             298 :     m_bBulkLoadAllowed = -1; /* uninitialized */
      54             298 :     m_bBulkLoadInProgress = FALSE;
      55             298 :     m_pEnumRows = new EnumRows;
      56                 : 
      57                 : #ifdef EXTENT_WORKAROUND
      58             298 :     m_bLayerEnvelopeValid = false;
      59             298 :     m_bLayerJustCreated = false;
      60                 : #endif
      61             298 : }
      62                 : 
      63                 : /************************************************************************/
      64                 : /*                            ~FGdbLayer()                              */
      65                 : /************************************************************************/
      66                 : 
      67             298 : FGdbLayer::~FGdbLayer()
      68                 : {
      69             298 :     EndBulkLoad();
      70                 : 
      71                 : #ifdef EXTENT_WORKAROUND
      72             298 :     WorkAroundExtentProblem();
      73                 : #endif
      74                 : 
      75             298 :     if (m_pFeatureDefn)
      76                 :     {
      77             298 :         m_pFeatureDefn->Release();
      78             298 :         m_pFeatureDefn = NULL;
      79                 :     }
      80                 : 
      81             298 :     if (m_pSRS)
      82                 :     {
      83             272 :         m_pSRS->Release();
      84             272 :         m_pSRS = NULL;
      85                 :     }
      86                 : 
      87             298 :     if (m_pEnumRows)
      88                 :     {
      89             298 :         delete m_pEnumRows;
      90             298 :         m_pEnumRows = NULL;
      91                 :     }
      92                 : 
      93                 :     // NOTE: never delete m_pDS - the memory doesn't belong to us
      94                 :     // TODO: check if we need to close the table or if the destructor 
      95                 :     // takes care of closing as it should
      96             298 :     if (m_pTable)
      97                 :     {
      98             298 :         delete m_pTable;
      99             298 :         m_pTable = NULL;
     100                 :     }
     101                 : 
     102             298 :     if (m_pOGRFilterGeometry)
     103                 :     {
     104               0 :         OGRGeometryFactory::destroyGeometry(m_pOGRFilterGeometry);
     105               0 :         m_pOGRFilterGeometry = NULL;
     106                 :     }
     107                 :     
     108             298 : }
     109                 : 
     110                 : #ifdef EXTENT_WORKAROUND
     111                 : 
     112                 : /************************************************************************/
     113                 : /*                     UpdateRowWithGeometry()                          */
     114                 : /************************************************************************/
     115                 : 
     116               0 : bool FGdbLayer::UpdateRowWithGeometry(Row& row, OGRGeometry* poGeom)
     117                 : {
     118               0 :     ShapeBuffer shape;
     119                 :     long hr;
     120                 : 
     121                 :     /* Write geometry to a buffer */
     122               0 :     GByte *pabyShape = NULL;
     123               0 :     int nShapeSize = 0;
     124               0 :     if ( OGRWriteToShapeBin( poGeom, &pabyShape, &nShapeSize ) != OGRERR_NONE )
     125               0 :         return false;
     126                 : 
     127                 :     /* Copy it into a ShapeBuffer */
     128               0 :     if ( nShapeSize > 0 )
     129                 :     {
     130               0 :         shape.Allocate(nShapeSize);
     131               0 :         memcpy(shape.shapeBuffer, pabyShape, nShapeSize);
     132               0 :         shape.inUseLength = nShapeSize;
     133                 :     }
     134                 : 
     135                 :     /* Free the shape buffer */
     136               0 :     CPLFree(pabyShape);
     137                 : 
     138                 :     /* Write ShapeBuffer into the Row */
     139               0 :     hr = row.SetGeometry(shape);
     140               0 :     if (FAILED(hr))
     141                 :     {
     142               0 :         return false;
     143                 :     }
     144                 : 
     145                 :     /* Update row */
     146               0 :     hr = m_pTable->Update(row);
     147               0 :     if (FAILED(hr))
     148                 :     {
     149               0 :         return false;
     150                 :     }
     151                 : 
     152               0 :     return true;
     153                 : }
     154                 : 
     155                 : /************************************************************************/
     156                 : /*                    WorkAroundExtentProblem()                         */
     157                 : /*                                                                      */
     158                 : /* Work-around problem with FileGDB API 1.1 on Linux 64bit. See #4455   */
     159                 : /************************************************************************/
     160                 : 
     161             298 : void FGdbLayer::WorkAroundExtentProblem()
     162                 : {
     163             298 :     if (!m_bLayerJustCreated || !m_bLayerEnvelopeValid)
     164             270 :         return;
     165                 : 
     166              28 :     OGREnvelope sEnvelope;
     167              28 :     if (GetExtent(&sEnvelope, TRUE) != OGRERR_NONE)
     168               0 :         return;
     169                 : 
     170                 :     /* The characteristic of the bug is that the reported extent */
     171                 :     /* is the real extent truncated incorrectly to integer values */
     172                 :     /* We work around that by temporary updating one feature with a geometry */
     173                 :     /* whose coordinates are integer values but ceil'ed and floor'ed */
     174                 :     /* such that they include the real layer extent. */
     175              28 :     if (((double)(int)sEnvelope.MinX == sEnvelope.MinX &&
     176                 :          (double)(int)sEnvelope.MinY == sEnvelope.MinY &&
     177                 :          (double)(int)sEnvelope.MaxX == sEnvelope.MaxX &&
     178                 :          (double)(int)sEnvelope.MaxY == sEnvelope.MaxY) &&
     179                 :         (fabs(sEnvelope.MinX - sLayerEnvelope.MinX) > 1e-5 ||
     180                 :          fabs(sEnvelope.MinY - sLayerEnvelope.MinY) > 1e-5 ||
     181                 :          fabs(sEnvelope.MaxX - sLayerEnvelope.MaxX) > 1e-5 ||
     182                 :          fabs(sEnvelope.MaxY - sLayerEnvelope.MaxY) > 1e-5))
     183                 :     {
     184                 :         long           hr;
     185               0 :         Row            row;
     186               0 :         EnumRows       enumRows;
     187                 : 
     188               0 :         if (FAILED(hr = m_pTable->Search(StringToWString("*"), StringToWString(""), true, enumRows)))
     189                 :             return;
     190                 : 
     191               0 :         if (FAILED(hr = enumRows.Next(row)))
     192                 :             return;
     193                 : 
     194               0 :         if (hr != S_OK)
     195                 :             return;
     196                 : 
     197                 :         /* Backup original shape buffer */
     198               0 :         ShapeBuffer originalGdbGeometry;
     199               0 :         if (FAILED(hr = row.GetGeometry(originalGdbGeometry)))
     200                 :             return;
     201                 : 
     202               0 :         OGRGeometry* pOGRGeo = NULL;
     203               0 :         if ((!GDBGeometryToOGRGeometry(m_forceMulti, &originalGdbGeometry, m_pSRS, &pOGRGeo)) || pOGRGeo == NULL)
     204                 :         {
     205               0 :             delete pOGRGeo;
     206                 :             return;
     207                 :         }
     208                 : 
     209               0 :         OGRwkbGeometryType eType = wkbFlatten(pOGRGeo->getGeometryType());
     210                 : 
     211               0 :         delete pOGRGeo;
     212               0 :         pOGRGeo = NULL;
     213                 : 
     214               0 :         OGRPoint oP1(floor(sLayerEnvelope.MinX), floor(sLayerEnvelope.MinY));
     215               0 :         OGRPoint oP2(ceil(sLayerEnvelope.MaxX), ceil(sLayerEnvelope.MaxY));
     216                 : 
     217               0 :         OGRLinearRing oLR;
     218               0 :         oLR.addPoint(&oP1);
     219               0 :         oLR.addPoint(&oP2);
     220               0 :         oLR.addPoint(&oP1);
     221                 : 
     222               0 :         if ( eType == wkbPoint )
     223                 :         {
     224               0 :             UpdateRowWithGeometry(row, &oP1);
     225               0 :             UpdateRowWithGeometry(row, &oP2);
     226                 :         }
     227               0 :         else if ( eType == wkbLineString )
     228                 :         {
     229               0 :             UpdateRowWithGeometry(row, &oLR);
     230                 :         }
     231               0 :         else if ( eType == wkbPolygon )
     232                 :         {
     233               0 :             OGRPolygon oPoly;
     234               0 :             oPoly.addRing(&oLR);
     235                 : 
     236               0 :             UpdateRowWithGeometry(row, &oPoly);
     237                 :         }
     238               0 :         else if ( eType == wkbMultiPoint )
     239                 :         {
     240               0 :             OGRMultiPoint oColl;
     241               0 :             oColl.addGeometry(&oP1);
     242               0 :             oColl.addGeometry(&oP2);
     243                 : 
     244               0 :             UpdateRowWithGeometry(row, &oColl);
     245                 :         }
     246               0 :         else if ( eType == wkbMultiLineString )
     247                 :         {
     248               0 :             OGRMultiLineString oColl;
     249               0 :             oColl.addGeometry(&oLR);
     250                 : 
     251               0 :             UpdateRowWithGeometry(row, &oColl);
     252                 :         }
     253               0 :         else if ( eType == wkbMultiPolygon )
     254                 :         {
     255               0 :             OGRMultiPolygon oColl;
     256               0 :             OGRPolygon oPoly;
     257               0 :             oPoly.addRing(&oLR);
     258               0 :             oColl.addGeometry(&oPoly);
     259                 : 
     260               0 :             UpdateRowWithGeometry(row, &oColl);
     261                 :         }
     262                 :         else
     263                 :             return;
     264                 : 
     265                 :         /* Restore original ShapeBuffer */
     266               0 :         hr = row.SetGeometry(originalGdbGeometry);
     267               0 :         if (FAILED(hr))
     268                 :             return;
     269                 : 
     270                 :         /* Update Row */
     271               0 :         hr = m_pTable->Update(row);
     272               0 :         if (FAILED(hr))
     273                 :             return;
     274                 : 
     275               0 :         CPLDebug("FGDB", "Workaround extent problem with Linux 64bit FGDB SDK 1.1");
     276                 :     }
     277                 : }
     278                 : #endif // EXTENT_WORKAROUND
     279                 : 
     280                 : /************************************************************************/
     281                 : /*                            CreateFeature()                           */
     282                 : /* Create an FGDB Row and populate it from an OGRFeature.               */
     283                 : /*                                                                      */
     284                 : /************************************************************************/
     285                 : 
     286            2178 : OGRErr FGdbLayer::CreateFeature( OGRFeature *poFeature )
     287                 : {
     288            2178 :     Table *fgdb_table = m_pTable;
     289            2178 :     Row fgdb_row;
     290                 :     fgdbError hr;
     291                 : 
     292            2178 :     if (m_bBulkLoadAllowed < 0)
     293              58 :         m_bBulkLoadAllowed = CSLTestBoolean(CPLGetConfigOption("FGDB_BULK_LOAD", "NO"));
     294                 : 
     295            2178 :     if (m_bBulkLoadAllowed && !m_bBulkLoadInProgress)
     296               2 :         StartBulkLoad();
     297                 : 
     298            2178 :     hr = fgdb_table->CreateRowObject(fgdb_row);
     299                 : 
     300                 :     /* Check the status of the Row create */
     301            2178 :     if (FAILED(hr))
     302                 :     {
     303               0 :         GDBErr(hr, "Failed at creating Row in CreateFeature.");
     304               0 :         return OGRERR_FAILURE;
     305                 :     }
     306                 : 
     307                 :     /* Populate the row with the feature content */
     308            2178 :     if (PopulateRowWithFeature(fgdb_row, poFeature) != OGRERR_NONE)
     309               0 :         return OGRERR_FAILURE;
     310                 : 
     311                 :     /* Cannot write to FID field - it is managed by GDB*/
     312                 :     //std::wstring wfield_name = StringToWString(m_strOIDFieldName);
     313                 :     //hr = fgdb_row.SetInteger(wfield_name, poFeature->GetFID());
     314                 : 
     315                 :     /* Write the row to the table */
     316            2178 :     hr = fgdb_table->Insert(fgdb_row);
     317            2178 :     if (FAILED(hr))
     318                 :     {
     319               0 :         GDBErr(hr, "Failed at writing Row to Table in CreateFeature.");
     320               0 :         return OGRERR_FAILURE;
     321                 :     }
     322                 : 
     323            2178 :     int32 oid = -1;
     324            2178 :     if (!FAILED(hr = fgdb_row.GetOID(oid)))
     325                 :     {
     326            2178 :         poFeature->SetFID(oid);
     327                 :     }
     328                 : 
     329                 : #ifdef EXTENT_WORKAROUND
     330                 :     /* For WorkAroundExtentProblem() needs */
     331            2178 :     OGRGeometry* poGeom = poFeature->GetGeometryRef();
     332            2178 :     if ( m_bLayerJustCreated && poGeom != NULL && !poGeom->IsEmpty() )
     333                 :     {
     334            2140 :         OGREnvelope sFeatureGeomEnvelope;
     335            2140 :         poGeom->getEnvelope(&sFeatureGeomEnvelope);
     336            2140 :         if (!m_bLayerEnvelopeValid)
     337                 :         {
     338              28 :             memcpy(&sLayerEnvelope, &sFeatureGeomEnvelope, sizeof(sLayerEnvelope));
     339              28 :             m_bLayerEnvelopeValid = true;
     340                 :         }
     341                 :         else
     342                 :         {
     343            2112 :             sLayerEnvelope.Merge(sFeatureGeomEnvelope);
     344                 :         }
     345                 :     }
     346                 : #endif
     347                 : 
     348            2178 :     return OGRERR_NONE;
     349                 : }
     350                 : 
     351                 : /************************************************************************/
     352                 : /*                    PopulateRowWithFeature()                          */
     353                 : /*                                                                      */
     354                 : /************************************************************************/
     355                 : 
     356            2292 : OGRErr FGdbLayer::PopulateRowWithFeature( Row& fgdb_row, OGRFeature *poFeature )
     357                 : {
     358            2292 :     ShapeBuffer shape;
     359                 :     fgdbError hr;
     360                 : 
     361            2292 :     OGRFeatureDefn* poFeatureDefn = m_pFeatureDefn;
     362            2292 :     int nFieldCount = poFeatureDefn->GetFieldCount();
     363                 : 
     364                 :     /* Copy the OGR visible fields (everything except geometry and FID) */
     365            2292 :     for( int i = 0; i < nFieldCount; i++ )
     366                 :     {
     367            3138 :         std::string field_name = poFeatureDefn->GetFieldDefn(i)->GetNameRef();
     368            3138 :         std::wstring wfield_name = StringToWString(field_name);
     369                 : 
     370                 :         /* Set empty fields to NULL */
     371            3138 :         if( !poFeature->IsFieldSet( i ) )
     372                 :         {
     373              10 :             if (FAILED(hr = fgdb_row.SetNull(wfield_name)))
     374                 :             {
     375               0 :                 GDBErr(hr, "Failed setting field to NULL.");
     376               0 :                 return OGRERR_FAILURE;
     377                 :             }
     378              10 :             continue;
     379                 :         }
     380                 : 
     381                 :         /* Set the information using the appropriate FGDB function */
     382            3128 :         int nOGRFieldType = poFeatureDefn->GetFieldDefn(i)->GetType();
     383                 : 
     384            3128 :         if ( nOGRFieldType == OFTInteger )
     385                 :         {
     386                 :             /* Integers (we don't do FGDB Shorts) */
     387            2524 :             int fldvalue = poFeature->GetFieldAsInteger(i);
     388            2524 :             hr = fgdb_row.SetInteger(wfield_name, fldvalue);
     389                 :         }
     390             604 :         else if ( nOGRFieldType == OFTReal )
     391                 :         {
     392                 :             /* Doubles (we don't handle FGDB Floats) */
     393             322 :             double fldvalue = poFeature->GetFieldAsDouble(i);
     394             322 :             hr = fgdb_row.SetDouble(wfield_name, fldvalue);
     395                 :         }
     396             282 :         else if ( nOGRFieldType == OFTString )
     397                 :         {
     398                 :             /* Strings we convert to wstring */
     399             282 :             std::string fldvalue = poFeature->GetFieldAsString(i);
     400             282 :             std::wstring wfldvalue = StringToWString(fldvalue);
     401             282 :             hr = fgdb_row.SetString(wfield_name, wfldvalue);
     402                 :         }
     403               0 :         else if ( nOGRFieldType == OFTDateTime || nOGRFieldType == OFTDate )
     404                 :         {
     405                 :             /* Dates we need to coerce a little */
     406                 :             struct tm val;
     407                 :             poFeature->GetFieldAsDateTime(i, &(val.tm_year), &(val.tm_mon), &(val.tm_mday),
     408               0 :                                           &(val.tm_hour), &(val.tm_min), &(val.tm_sec), NULL);
     409               0 :             val.tm_year -= 1900;
     410               0 :             val.tm_mon = val.tm_mon - 1; /* OGR months go 1-12, FGDB go 0-11 */
     411               0 :             hr = fgdb_row.SetDate(wfield_name, val);
     412                 :         }
     413               0 :         else if ( nOGRFieldType == OFTBinary )
     414                 :         {
     415                 :             /* Binary data */
     416               0 :             ByteArray fgdb_bytearray;
     417                 :             int bytesize;
     418               0 :             GByte *bytes = poFeature->GetFieldAsBinary(i, &bytesize);
     419               0 :             if ( bytesize )
     420                 :             {
     421               0 :                 fgdb_bytearray.Allocate(bytesize);
     422               0 :                 memcpy(fgdb_bytearray.byteArray, bytes, bytesize);
     423               0 :                 fgdb_bytearray.inUseLength = bytesize;
     424               0 :                 hr = fgdb_row.SetBinary(wfield_name, fgdb_bytearray);
     425                 :             }
     426                 :             else
     427                 :             {
     428               0 :                 hr = fgdb_row.SetNull(wfield_name);
     429               0 :             }
     430                 :         }
     431                 :         else
     432                 :         {
     433                 :             /* We can't handle this type */
     434                 :             CPLError( CE_Failure, CPLE_AppDefined,
     435               0 :                 "FGDB driver does not support OGR type." );
     436               0 :             return OGRERR_FAILURE;
     437                 :         }
     438                 :     }
     439                 : 
     440            2292 :     if ( m_pFeatureDefn->GetGeomType() != wkbNone )
     441                 :     {
     442                 :         /* Done with attribute fields, now do geometry */
     443            2270 :         OGRGeometry *poGeom = poFeature->GetGeometryRef();
     444                 : 
     445                 :         /* Write geometry to a buffer */
     446            2270 :         GByte *pabyShape = NULL;
     447            2270 :         int nShapeSize = 0;
     448            2270 :         OGRErr err = OGRWriteToShapeBin( poGeom, &pabyShape, &nShapeSize );
     449            2270 :         if ( err != OGRERR_NONE )
     450               0 :             return err;
     451                 : 
     452                 :         /* Copy it into a ShapeBuffer */
     453            2270 :         if ( nShapeSize > 0 )
     454                 :         {
     455            2270 :             shape.Allocate(nShapeSize);
     456            2270 :             memcpy(shape.shapeBuffer, pabyShape, nShapeSize);
     457            2270 :             shape.inUseLength = nShapeSize;
     458                 :         }
     459                 : 
     460                 :         /* Free the shape buffer */
     461            2270 :         CPLFree(pabyShape);
     462                 : 
     463                 :         /* Write ShapeBuffer into the Row */
     464            2270 :         hr = fgdb_row.SetGeometry(shape);
     465            2270 :         if (FAILED(hr))
     466                 :         {
     467               0 :             GDBErr(hr, "Failed at writing Geometry to Row in CreateFeature.");
     468               0 :             return OGRERR_FAILURE;
     469                 :         }
     470                 :     }
     471                 : 
     472            2292 :     return OGRERR_NONE;
     473                 : 
     474                 : }
     475                 : 
     476                 : /************************************************************************/
     477                 : /*                             GetRow()                                 */
     478                 : /************************************************************************/
     479                 : 
     480             282 : OGRErr FGdbLayer::GetRow( EnumRows& enumRows, Row& row, long nFID )
     481                 : {
     482                 :     long           hr;
     483             282 :     CPLString      osQuery;
     484                 : 
     485             282 :     osQuery.Printf("%s = %ld", m_strOIDFieldName.c_str(), nFID);
     486                 : 
     487             282 :     if (FAILED(hr = m_pTable->Search(m_wstrSubfields, StringToWString(osQuery.c_str()), true, enumRows)))
     488                 :     {
     489               0 :         GDBErr(hr, "Failed fetching row ");
     490               0 :         return OGRERR_FAILURE;
     491                 :     }
     492                 : 
     493             282 :     if (FAILED(hr = enumRows.Next(row)))
     494                 :     {
     495               0 :         GDBErr(hr, "Failed fetching row ");
     496               0 :         return OGRERR_FAILURE;
     497                 :     }
     498                 : 
     499             282 :     if (hr != S_OK)
     500              28 :         return OGRERR_FAILURE; //none found - but no failure
     501                 : 
     502             254 :     return OGRERR_NONE;
     503                 : }
     504                 : 
     505                 : /************************************************************************/
     506                 : /*                           DeleteFeature()                            */
     507                 : /************************************************************************/
     508                 : 
     509              28 : OGRErr FGdbLayer::DeleteFeature( long nFID )
     510                 : 
     511                 : {
     512                 :     long           hr;
     513              28 :     EnumRows       enumRows;
     514              28 :     Row            row;
     515                 : 
     516              28 :     EndBulkLoad();
     517                 : 
     518              28 :     if (GetRow(enumRows, row, nFID) != OGRERR_NONE)
     519               0 :         return OGRERR_FAILURE;
     520                 : 
     521              28 :     if (FAILED(hr = m_pTable->Delete(row)))
     522                 :     {
     523               0 :         GDBErr(hr, "Failed deleting row ");
     524               0 :         return OGRERR_FAILURE;
     525                 :     }
     526                 : 
     527              28 :     return OGRERR_NONE;
     528                 : }
     529                 : 
     530                 : /************************************************************************/
     531                 : /*                            SetFeature()                              */
     532                 : /************************************************************************/
     533                 : 
     534             114 : OGRErr FGdbLayer::SetFeature( OGRFeature* poFeature )
     535                 : 
     536                 : {
     537                 :     long           hr;
     538             114 :     EnumRows       enumRows;
     539             114 :     Row            row;
     540                 : 
     541             114 :     if( poFeature->GetFID() == OGRNullFID )
     542                 :     {
     543                 :         CPLError( CE_Failure, CPLE_AppDefined,
     544               0 :                   "SetFeature() with unset FID fails." );
     545               0 :         return OGRERR_FAILURE;
     546                 :     }
     547                 : 
     548             114 :     EndBulkLoad();
     549                 : 
     550             114 :     if (GetRow(enumRows, row, poFeature->GetFID()) != OGRERR_NONE)
     551               0 :         return OGRERR_FAILURE;
     552                 : 
     553                 :     /* Populate the row with the feature content */
     554             114 :     if (PopulateRowWithFeature(row, poFeature) != OGRERR_NONE)
     555               0 :         return OGRERR_FAILURE;
     556                 : 
     557             114 :     if (FAILED(hr = m_pTable->Update(row)))
     558                 :     {
     559               0 :         GDBErr(hr, "Failed updating row ");
     560               0 :         return OGRERR_FAILURE;
     561                 :     }
     562                 : 
     563             114 :     return OGRERR_NONE;
     564                 : }
     565                 : 
     566                 : /************************************************************************/
     567                 : /*                          CreateFieldDefn()                           */
     568                 : /************************************************************************/
     569                 : 
     570             130 : char* FGdbLayer::CreateFieldDefn(OGRFieldDefn& oField,
     571                 :                                  int bApproxOK,
     572                 :                                  std::string& fieldname_clean,
     573                 :                                  std::string& gdbFieldType)
     574                 : {
     575             130 :     std::string fieldname = oField.GetNameRef();
     576             260 :     std::string fidname = std::string(GetFIDColumn());
     577             260 :     std::string nullable = "true";
     578                 : 
     579                 :     /* Try to map the OGR type to an ESRI type */
     580             260 :     OGRFieldType fldtype = oField.GetType();
     581             130 :     if ( ! OGRToGDBFieldType(fldtype, &gdbFieldType) )
     582                 :     {
     583               0 :         GDBErr(-1, "Failed converting field type.");
     584               0 :         return NULL;
     585                 :     }
     586                 : 
     587             130 :     if (fieldname_clean.size() != 0)
     588                 :     {
     589               0 :         oField.SetName(fieldname_clean.c_str());
     590                 :     }
     591                 :     else
     592                 :     {
     593                 :         /* Clean field names */
     594             130 :         fieldname_clean = FGDBLaunderName(fieldname);
     595                 : 
     596             130 :         if (m_bLaunderReservedKeywords)
     597             130 :             fieldname_clean = FGDBEscapeReservedKeywords(fieldname_clean);
     598                 : 
     599                 :         /* Truncate to 64 characters */
     600             130 :         if (fieldname_clean.size() > 64)
     601               4 :             fieldname_clean.resize(64);
     602                 : 
     603             130 :         std::string temp_fieldname = fieldname_clean;
     604                 : 
     605                 :         /* Ensures uniqueness of field name */
     606             130 :         int numRenames = 1;
     607             266 :         while ((m_pFeatureDefn->GetFieldIndex(temp_fieldname.c_str()) >= 0) && (numRenames < 10))
     608                 :         {
     609               6 :             temp_fieldname = CPLSPrintf("%s_%d", fieldname_clean.substr(0, 62).c_str(), numRenames);
     610               6 :             numRenames ++;
     611                 :         }
     612             260 :         while ((m_pFeatureDefn->GetFieldIndex(temp_fieldname.c_str()) >= 0) && (numRenames < 100))
     613                 :         {
     614               0 :             temp_fieldname = CPLSPrintf("%s_%d", fieldname_clean.substr(0, 61).c_str(), numRenames);
     615               0 :             numRenames ++;
     616                 :         }
     617                 : 
     618             130 :         if (temp_fieldname != fieldname)
     619                 :         {
     620              10 :             if( !bApproxOK || (m_pFeatureDefn->GetFieldIndex(temp_fieldname.c_str()) >= 0) )
     621                 :             {
     622                 :                 CPLError( CE_Failure, CPLE_NotSupported,
     623                 :                     "Failed to add field named '%s'",
     624               0 :                     fieldname.c_str() );
     625               0 :                 return NULL;
     626                 :             }
     627                 :             CPLError(CE_Warning, CPLE_NotSupported,
     628                 :                 "Normalized/laundered field name: '%s' to '%s'",
     629              10 :                 fieldname.c_str(), temp_fieldname.c_str());
     630                 : 
     631              10 :             fieldname_clean = temp_fieldname;
     632              10 :             oField.SetName(fieldname_clean.c_str());
     633               0 :         }
     634                 :     }
     635                 : 
     636                 :     /* Then the Field definition */
     637             130 :     CPLXMLNode *defn_xml = CPLCreateXMLNode(NULL, CXT_Element, "esri:Field");
     638                 : 
     639                 :     /* Add the XML attributes to the Field node */
     640             130 :     FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
     641             130 :     FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:xs", "http://www.w3.org/2001/XMLSchema");
     642             130 :     FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:esri", "http://www.esri.com/schemas/ArcGIS/10.1");
     643             130 :     FGDB_CPLAddXMLAttribute(defn_xml, "xsi:type", "esri:Field");
     644                 : 
     645                 :     /* Basic field information */
     646             130 :     CPLCreateXMLElementAndValue(defn_xml, "Name", fieldname_clean.c_str());
     647             130 :     CPLCreateXMLElementAndValue(defn_xml, "Type", gdbFieldType.c_str());
     648             130 :     CPLCreateXMLElementAndValue(defn_xml, "IsNullable", nullable.c_str());
     649                 : 
     650                 :     /* Get the Width and Precision if we know them */
     651             130 :     int width = oField.GetWidth();
     652             130 :     int precision = oField.GetPrecision();
     653             130 :     if ( width <= 0 )
     654             124 :         GDBFieldTypeToWidthPrecision(gdbFieldType, &width, &precision);
     655                 : 
     656                 :     /* Write out the Width and Precision */
     657                 :     char buf[100];
     658             130 :     snprintf(buf, 100, "%d", width);
     659             130 :     CPLCreateXMLElementAndValue(defn_xml,"Length", buf);
     660             130 :     snprintf(buf, 100, "%d", precision);
     661             130 :     CPLCreateXMLElementAndValue(defn_xml,"Precision", buf);
     662                 : 
     663                 :     /* We know nothing about Scale, so zero it out */
     664             130 :     CPLCreateXMLElementAndValue(defn_xml,"Scale", "0");
     665                 : 
     666                 :     /*  Attempt to preserve the original fieldname */
     667             130 :     if (fieldname != fieldname_clean)
     668                 :     {
     669              10 :         CPLCreateXMLElementAndValue(defn_xml, "AliasName", fieldname.c_str());
     670                 :     }
     671                 : 
     672                 :     /* Default values are discouraged in OGR API docs */
     673                 :     /* <DefaultValue xsi:type="xs:string">afternoon</DefaultValue> */
     674                 : 
     675                 :     /* Convert our XML tree into a string for FGDB */
     676             130 :     char *defn_str = CPLSerializeXMLTree(defn_xml);
     677             130 :     CPLDebug("FGDB", "CreateField() generated XML for FGDB\n%s", defn_str);
     678                 : 
     679                 :     /* Free the XML */
     680             130 :     CPLDestroyXMLNode(defn_xml);
     681                 : 
     682             130 :     return defn_str;
     683                 : }
     684                 : 
     685                 : /************************************************************************/
     686                 : /*                            CreateField()                             */
     687                 : /*  Build up an FGDB XML field definition and use it to create a Field  */
     688                 : /*  Update the OGRFeatureDefn to reflect the new field.                 */
     689                 : /*                                                                      */
     690                 : /************************************************************************/
     691                 : 
     692             130 : OGRErr FGdbLayer::CreateField(OGRFieldDefn* poField, int bApproxOK)
     693                 : {
     694             130 :     OGRFieldDefn oField(poField);
     695             130 :     std::string fieldname_clean;
     696             130 :     std::string gdbFieldType;
     697                 : 
     698                 :     char* defn_str = CreateFieldDefn(oField, bApproxOK,
     699             130 :                                      fieldname_clean, gdbFieldType);
     700             130 :     if (defn_str == NULL)
     701               0 :         return OGRERR_FAILURE;
     702                 : 
     703                 :     /* Add the FGDB Field to the FGDB Table. */
     704             130 :     fgdbError hr = m_pTable->AddField(defn_str);
     705                 : 
     706             130 :     CPLFree(defn_str);
     707                 : 
     708                 :     /* Check the status of the Field add */
     709             130 :     if (FAILED(hr))
     710                 :     {
     711               0 :         GDBErr(hr, "Failed at creating Field for " + std::string(oField.GetNameRef()));
     712               0 :         return OGRERR_FAILURE;
     713                 :     }
     714                 : 
     715                 :     /* Now add the OGRFieldDefn to the OGRFeatureDefn */
     716             130 :     m_pFeatureDefn->AddFieldDefn(&oField);
     717                 : 
     718             130 :     m_vOGRFieldToESRIField.push_back(StringToWString(fieldname_clean));
     719             130 :     m_vOGRFieldToESRIFieldType.push_back( gdbFieldType );
     720                 : 
     721                 :     /* All done and happy */
     722             130 :     return OGRERR_NONE;
     723                 : 
     724                 : }
     725                 : 
     726                 : /************************************************************************/
     727                 : /*                             DeleteField()                            */
     728                 : /************************************************************************/
     729                 : 
     730               2 : OGRErr FGdbLayer::DeleteField( int iFieldToDelete )
     731                 : {
     732               2 :     if (iFieldToDelete < 0 || iFieldToDelete >= m_pFeatureDefn->GetFieldCount())
     733                 :     {
     734                 :         CPLError( CE_Failure, CPLE_NotSupported,
     735               0 :                   "Invalid field index");
     736               0 :         return OGRERR_FAILURE;
     737                 :     }
     738                 : 
     739               2 :     ResetReading();
     740                 : 
     741               2 :     const char* pszFieldName = m_pFeatureDefn->GetFieldDefn(iFieldToDelete)->GetNameRef();
     742                 : 
     743                 :     fgdbError hr;
     744               2 :     if (FAILED(hr = m_pTable->DeleteField(StringToWString(pszFieldName))))
     745                 :     {
     746               0 :         GDBErr(hr, "Failed deleting field " + std::string(pszFieldName));
     747               0 :         return OGRERR_FAILURE;
     748                 :     }
     749                 : 
     750               2 :     m_vOGRFieldToESRIField.erase (m_vOGRFieldToESRIField.begin() + iFieldToDelete);
     751               2 :     m_vOGRFieldToESRIFieldType.erase( m_vOGRFieldToESRIFieldType.begin() + iFieldToDelete );
     752                 : 
     753                 : 
     754               2 :     return m_pFeatureDefn->DeleteFieldDefn( iFieldToDelete );
     755                 : }
     756                 : 
     757                 : #ifdef AlterFieldDefn_implemented_but_not_working
     758                 : 
     759                 : /************************************************************************/
     760                 : /*                           AlterFieldDefn()                           */
     761                 : /************************************************************************/
     762                 : 
     763                 : OGRErr FGdbLayer::AlterFieldDefn( int iFieldToAlter, OGRFieldDefn* poNewFieldDefn, int nFlags )
     764                 : {
     765                 :     if (iFieldToAlter < 0 || iFieldToAlter >= m_pFeatureDefn->GetFieldCount())
     766                 :     {
     767                 :         CPLError( CE_Failure, CPLE_NotSupported,
     768                 :                   "Invalid field index");
     769                 :         return OGRERR_FAILURE;
     770                 :     }
     771                 : 
     772                 :     OGRFieldDefn* poFieldDefn = m_pFeatureDefn->GetFieldDefn(iFieldToAlter);
     773                 :     OGRFieldDefn oField(poFieldDefn);
     774                 : 
     775                 :     if (nFlags & ALTER_TYPE_FLAG)
     776                 :         oField.SetType(poNewFieldDefn->GetType());
     777                 :     if (nFlags & ALTER_NAME_FLAG)
     778                 :     {
     779                 :         if (strcmp(poNewFieldDefn->GetNameRef(), oField.GetNameRef()) != 0)
     780                 :         {
     781                 :             CPLError( CE_Failure, CPLE_NotSupported,
     782                 :                       "Altering field name is not supported" );
     783                 :             return OGRERR_FAILURE;
     784                 :         }
     785                 :         oField.SetName(poNewFieldDefn->GetNameRef());
     786                 :     }
     787                 :     if (nFlags & ALTER_WIDTH_PRECISION_FLAG)
     788                 :     {
     789                 :         oField.SetWidth(poNewFieldDefn->GetWidth());
     790                 :         oField.SetPrecision(poNewFieldDefn->GetPrecision());
     791                 :     }
     792                 : 
     793                 :     std::string fieldname_clean = WStringToString(m_vOGRFieldToESRIField[iFieldToAlter]);
     794                 :     std::string gdbFieldType;
     795                 : 
     796                 :     char* defn_str = CreateFieldDefn(oField, TRUE,
     797                 :                                      fieldname_clean, gdbFieldType);
     798                 :     if (defn_str == NULL)
     799                 :         return OGRERR_FAILURE;
     800                 : 
     801                 :     ResetReading();
     802                 : 
     803                 :     /* Add the FGDB Field to the FGDB Table. */
     804                 :     fgdbError hr = m_pTable->AlterField(defn_str);
     805                 : 
     806                 :     CPLFree(defn_str);
     807                 : 
     808                 :     /* Check the status of the AlterField */
     809                 :     if (FAILED(hr))
     810                 :     {
     811                 :         GDBErr(hr, "Failed at altering field " + std::string(oField.GetNameRef()));
     812                 :         return OGRERR_FAILURE;
     813                 :     }
     814                 : 
     815                 :     m_vOGRFieldToESRIFieldType[iFieldToAlter] = gdbFieldType;
     816                 : 
     817                 :     poFieldDefn->SetType(oField.GetType());
     818                 :     poFieldDefn->SetWidth(oField.GetWidth());
     819                 :     poFieldDefn->SetPrecision(oField.GetPrecision());
     820                 : 
     821                 :     return OGRERR_NONE;
     822                 : }
     823                 : #endif // AlterFieldDefn_implemented_but_not_working
     824                 : 
     825                 : /************************************************************************/
     826                 : /*                      XMLSpatialReference()                           */
     827                 : /*  Build up an XML representation of an OGRSpatialReference.           */
     828                 : /*  Used in layer creation.                                             */
     829                 : /*                                                                      */
     830                 : /************************************************************************/
     831                 : 
     832             106 : CPLXMLNode* XMLSpatialReference(OGRSpatialReference* poSRS, char** papszOptions)
     833                 : {
     834                 :     /* We always need a SpatialReference */
     835             106 :     CPLXMLNode *srs_xml = CPLCreateXMLNode(NULL, CXT_Element, "SpatialReference");
     836                 : 
     837                 :     /* Extract the WKID before morphing */
     838             106 :     char *wkid = NULL;
     839             106 :     if ( poSRS && poSRS->GetAuthorityCode(NULL) )
     840                 :     {
     841             102 :         wkid = CPLStrdup(poSRS->GetAuthorityCode(NULL));
     842                 :     }
     843                 : 
     844                 :     /* NULL poSRS => UnknownCoordinateSystem */
     845             106 :     if ( ! poSRS )
     846                 :     {
     847               4 :         FGDB_CPLAddXMLAttribute(srs_xml, "xsi:type", "esri:UnknownCoordinateSystem");
     848                 :     }
     849                 :     else
     850                 :     {
     851                 :         /* Make a clone so we can morph it without morphing the original */
     852             102 :         OGRSpatialReference* poSRSClone = poSRS->Clone();
     853                 : 
     854                 :         /* Flip the WKT to ESRI form, return UnknownCoordinateSystem if we can't */
     855             102 :         if ( poSRSClone->morphToESRI() != OGRERR_NONE )
     856                 :         {
     857               0 :             delete poSRSClone;
     858               0 :             FGDB_CPLAddXMLAttribute(srs_xml, "xsi:type", "esri:UnknownCoordinateSystem");
     859               0 :             return srs_xml;
     860                 :         }
     861                 : 
     862                 :         /* Set the SpatialReference type attribute correctly for GEOGCS/PROJCS */
     863             102 :         if ( poSRSClone->IsProjected() )
     864               0 :             FGDB_CPLAddXMLAttribute(srs_xml, "xsi:type", "esri:ProjectedCoordinateSystem");
     865                 :         else
     866             102 :             FGDB_CPLAddXMLAttribute(srs_xml, "xsi:type", "esri:GeographicCoordinateSystem");
     867                 : 
     868                 :         /* Add the WKT to the XML */
     869             102 :         char *wkt = NULL;
     870             102 :         poSRSClone->exportToWkt(&wkt);
     871             102 :         if (wkt)
     872                 :         {
     873             102 :             CPLCreateXMLElementAndValue(srs_xml,"WKT", wkt);
     874             102 :             OGRFree(wkt);
     875                 :         }
     876                 : 
     877                 :         /* Dispose of our close */
     878             102 :         delete poSRSClone;
     879                 :     }
     880                 :     
     881                 :     /* Handle Origin/Scale/Tolerance */
     882                 :     const char* grid[7] = {
     883                 :       "XOrigin", "YOrigin", "XYScale",
     884                 :       "ZOrigin", "ZScale",
     885             106 :       "XYTolerance", "ZTolerance" };
     886                 :     const char* gridvalues[7];
     887                 : 
     888                 :     /* 
     889                 :     Need different default paramters for geographic and projected coordinate systems.
     890                 :     Try and use ArcGIS 10 default values.
     891                 :     */
     892                 :     // default tolerance is 1mm in the units of the coordinate system
     893             106 :     double ztol = 0.001 * (poSRS ? poSRS->GetTargetLinearUnits("VERT_CS") : 1.0);
     894                 :     // default scale is 10x the tolerance
     895             106 :     long zscale = 1 / ztol * 10;
     896                 : 
     897                 :     char s_xyscale[50], s_xytol[50], s_zscale[50], s_ztol[50];
     898             106 :     snprintf(s_ztol, 50, "%f", ztol);
     899             106 :     snprintf(s_zscale, 50, "%ld", zscale);
     900                 :     
     901             106 :     if ( poSRS == NULL || poSRS->IsProjected() )
     902                 :     {
     903                 :         // default tolerance is 1mm in the units of the coordinate system
     904               4 :         double xytol = 0.001 * (poSRS ? poSRS->GetTargetLinearUnits("PROJCS") : 1.0);
     905                 :         // default scale is 10x the tolerance
     906               4 :         long xyscale = 1 / xytol * 10;
     907                 : 
     908               4 :         snprintf(s_xytol, 50, "%f", xytol);
     909               4 :         snprintf(s_xyscale, 50, "%ld", xyscale);
     910                 : 
     911                 :         // Ideally we would use the same X/Y origins as ArcGIS, but we need the algorithm they use.
     912               4 :         gridvalues[0] = "-2147483647";
     913               4 :         gridvalues[1] = "-2147483647";
     914               4 :         gridvalues[2] = s_xyscale;
     915               4 :         gridvalues[3] = "-100000";
     916               4 :         gridvalues[4] = s_zscale;
     917               4 :         gridvalues[5] = s_xytol;
     918               4 :         gridvalues[6] = s_ztol;
     919                 :     }
     920                 :     else
     921                 :     {
     922             102 :         gridvalues[0] = "-400";
     923             102 :         gridvalues[1] = "-400";
     924             102 :         gridvalues[2] = "1000000000";
     925             102 :         gridvalues[3] = "-100000";
     926             102 :         gridvalues[4] = s_zscale;
     927             102 :         gridvalues[5] = "0.000000008983153";
     928             102 :         gridvalues[6] = s_ztol;
     929                 :     }
     930                 : 
     931                 :     /* Convert any layer creation options available, use defaults otherwise */
     932             848 :     for( int i = 0; i < 7; i++ )
     933                 :     {
     934             742 :         if ( CSLFetchNameValue( papszOptions, grid[i] ) != NULL )
     935               0 :             gridvalues[i] = CSLFetchNameValue( papszOptions, grid[i] );
     936                 : 
     937             742 :         CPLCreateXMLElementAndValue(srs_xml, grid[i], gridvalues[i]);
     938                 :     }
     939                 : 
     940                 :     /* FGDB is always High Precision */
     941             106 :     CPLCreateXMLElementAndValue(srs_xml, "HighPrecision", "true");     
     942                 : 
     943                 :     /* Add the WKID to the XML */
     944             106 :     if ( wkid ) 
     945                 :     {
     946             102 :         CPLCreateXMLElementAndValue(srs_xml, "WKID", wkid);
     947             102 :         CPLFree(wkid);
     948                 :     }
     949                 : 
     950             106 :     return srs_xml;
     951                 : }
     952                 : 
     953                 : /************************************************************************/
     954                 : /*                    CreateFeatureDataset()                            */
     955                 : /************************************************************************/
     956                 : 
     957               2 : bool FGdbLayer::CreateFeatureDataset(FGdbDataSource* pParentDataSource, 
     958                 :                                      std::string feature_dataset_name,
     959                 :                                      OGRSpatialReference* poSRS,
     960                 :                                      char** papszOptions )
     961                 : {
     962                 :     /* XML node */
     963               2 :     CPLXMLNode *xml_xml = CPLCreateXMLNode(NULL, CXT_Element, "?xml");
     964               2 :     FGDB_CPLAddXMLAttribute(xml_xml, "version", "1.0");
     965               2 :     FGDB_CPLAddXMLAttribute(xml_xml, "encoding", "UTF-8");
     966                 : 
     967                 :     /* First build up a bare-bones feature definition */
     968               2 :     CPLXMLNode *defn_xml = CPLCreateXMLNode(NULL, CXT_Element, "esri:DataElement");
     969               2 :     CPLAddXMLSibling(xml_xml, defn_xml);
     970                 : 
     971                 :     /* Add the attributes to the DataElement */
     972               2 :     FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
     973               2 :     FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:xs", "http://www.w3.org/2001/XMLSchema");
     974               2 :     FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:esri", "http://www.esri.com/schemas/ArcGIS/10.1");
     975                 : 
     976                 :     /* Need to set this to esri:DEFeatureDataset or esri:DETable */
     977               2 :     FGDB_CPLAddXMLAttribute(defn_xml, "xsi:type", "esri:DEFeatureDataset");
     978                 : 
     979                 :     /* Add in more children */
     980               2 :     std::string catalog_page = "\\" + feature_dataset_name;
     981               2 :     CPLCreateXMLElementAndValue(defn_xml,"CatalogPath", catalog_page.c_str());
     982               2 :     CPLCreateXMLElementAndValue(defn_xml,"Name", feature_dataset_name.c_str());
     983               2 :     CPLCreateXMLElementAndValue(defn_xml,"ChildrenExpanded", "false");
     984               2 :     CPLCreateXMLElementAndValue(defn_xml,"DatasetType", "esriDTFeatureDataset");
     985               2 :     CPLCreateXMLElementAndValue(defn_xml,"Versioned", "false");
     986               2 :     CPLCreateXMLElementAndValue(defn_xml,"CanVersion", "false");
     987                 : 
     988                 :     /* Add in empty extent */
     989               2 :     CPLXMLNode *extent_xml = CPLCreateXMLNode(NULL, CXT_Element, "Extent");
     990               2 :     FGDB_CPLAddXMLAttribute(extent_xml, "xsi:nil", "true");
     991               2 :     CPLAddXMLChild(defn_xml, extent_xml);
     992                 : 
     993                 :     /* Add the SRS */
     994                 :     if( TRUE ) // TODO: conditional on existence of SRS
     995                 :     {
     996               2 :         CPLXMLNode *srs_xml = XMLSpatialReference(poSRS, papszOptions);
     997               2 :         if ( srs_xml )
     998               2 :             CPLAddXMLChild(defn_xml, srs_xml);
     999                 :     }
    1000                 : 
    1001                 :     /* Convert our XML tree into a string for FGDB */
    1002               2 :     char *defn_str = CPLSerializeXMLTree(xml_xml);
    1003               2 :     CPLDestroyXMLNode(xml_xml);
    1004                 : 
    1005                 :     /* TODO, tie this to debugging levels */
    1006               2 :     CPLDebug("FGDB", "%s", defn_str);
    1007                 : 
    1008                 :     /* Create the FeatureDataset. */
    1009               2 :     Geodatabase *gdb = pParentDataSource->GetGDB();
    1010               2 :     fgdbError hr = gdb->CreateFeatureDataset(defn_str);
    1011                 : 
    1012                 :     /* Free the XML */
    1013               2 :     CPLFree(defn_str);
    1014                 : 
    1015                 :     /* Check table create status */
    1016               2 :     if (FAILED(hr))
    1017                 :     {
    1018               0 :         return GDBErr(hr, "Failed at creating FeatureDataset " + feature_dataset_name);
    1019                 :     }
    1020                 : 
    1021               2 :     return true;
    1022                 : }
    1023                 : 
    1024                 : /************************************************************************/
    1025                 : /*                            Create()                                  */
    1026                 : /* Build up an FGDB XML layer definition and use it to create a Table   */
    1027                 : /* or Feature Class to work from.                                       */
    1028                 : /*                                                                      */
    1029                 : /* Layer creation options:                                              */
    1030                 : /*   FEATURE_DATASET, nest layer inside a FeatureDataset folder         */
    1031                 : /*   GEOMETRY_NAME, user-selected name for the geometry column          */
    1032                 : /*   OID_NAME, user-selected name for the FID column                    */
    1033                 : /*   XORIGIN, YORIGIN, ZORIGIN, origin of the snapping grid             */
    1034                 : /*   XYSCALE, ZSCALE, inverse resolution of the snapping grid           */
    1035                 : /*   XYTOLERANCE, ZTOLERANCE, snapping tolerance for topology/networks  */
    1036                 : /*                                                                      */
    1037                 : /************************************************************************/
    1038                 : 
    1039              54 : bool FGdbLayer::Create(FGdbDataSource* pParentDataSource, 
    1040                 :                        const char* pszLayerNameIn, 
    1041                 :                        OGRSpatialReference* poSRS, 
    1042                 :                        OGRwkbGeometryType eType, 
    1043                 :                        char** papszOptions)
    1044                 : {
    1045              54 :     std::string parent_path = "";
    1046             108 :     std::wstring wtable_path, wparent_path;
    1047              54 :     std::string geometry_name = FGDB_GEOMETRY_NAME;
    1048             108 :     std::string fid_name = FGDB_OID_NAME;
    1049              54 :     std::string esri_type;
    1050              54 :     bool has_z = false;
    1051                 : 
    1052                 : #ifdef EXTENT_WORKAROUND
    1053              54 :     m_bLayerJustCreated = true;
    1054                 : #endif
    1055                 : 
    1056                 :     /* Launder the Layer name */
    1057              54 :     std::string layerName = pszLayerNameIn;
    1058                 : 
    1059             108 :     layerName = FGDBLaunderName(pszLayerNameIn);
    1060             108 :     layerName = FGDBEscapeReservedKeywords(layerName);
    1061              54 :     layerName = FGDBEscapeUnsupportedPrefixes(layerName);
    1062                 : 
    1063              54 :     if (layerName.size() > 160)
    1064               4 :         layerName.resize(160);
    1065                 : 
    1066                 :     /* Ensures uniqueness of layer name */
    1067              54 :     int numRenames = 1;
    1068             114 :     while ((pParentDataSource->GetLayerByName(layerName.c_str()) != NULL) && (numRenames < 10))
    1069                 :     {
    1070               6 :         layerName = CPLSPrintf("%s_%d", layerName.substr(0, 158).c_str(), numRenames);
    1071               6 :         numRenames ++;
    1072                 :     }
    1073             108 :     while ((pParentDataSource->GetLayerByName(layerName.c_str()) != NULL) && (numRenames < 100))
    1074                 :     {
    1075               0 :         layerName = CPLSPrintf("%s_%d", layerName.substr(0, 157).c_str(), numRenames);
    1076               0 :         numRenames ++;
    1077                 :     }
    1078                 : 
    1079              54 :     if (layerName != pszLayerNameIn)
    1080                 :     {
    1081                 :         CPLError(CE_Warning, CPLE_NotSupported,
    1082                 :                 "Normalized/laundered layer name: '%s' to '%s'",
    1083              12 :                 pszLayerNameIn, layerName.c_str());
    1084                 :     }
    1085                 : 
    1086              54 :     std::string table_path = "\\" + std::string(layerName);
    1087                 : 
    1088                 :     /* Handle the FEATURE_DATASET case */
    1089              54 :     if (  CSLFetchNameValue( papszOptions, "FEATURE_DATASET") != NULL )
    1090                 :     {
    1091               4 :         std::string feature_dataset = CSLFetchNameValue( papszOptions, "FEATURE_DATASET");
    1092                 : 
    1093                 :         /* Check if FEATURE_DATASET exists. Otherwise create it */
    1094               4 :         std::vector<wstring> featuredatasets;
    1095               4 :         Geodatabase *gdb = pParentDataSource->GetGDB();
    1096               4 :         int bFeatureDataSetExists = FALSE;
    1097                 :         fgdbError hr;
    1098               4 :         if ( !FAILED(hr = gdb->GetChildDatasets(L"\\", L"Feature Dataset", featuredatasets)) )
    1099                 :         {
    1100               4 :             std::wstring feature_dataset_with_slash = L"\\" + StringToWString(feature_dataset);
    1101               6 :             for ( unsigned int i = 0; i < featuredatasets.size(); i++ )
    1102                 :             {
    1103               2 :                 if (featuredatasets[i] == feature_dataset_with_slash)
    1104               2 :                     bFeatureDataSetExists = TRUE;
    1105               4 :             }
    1106                 :         }
    1107                 : 
    1108               4 :         if (!bFeatureDataSetExists)
    1109                 :         {
    1110               2 :             bool rv = CreateFeatureDataset(pParentDataSource, feature_dataset, poSRS, papszOptions);
    1111               2 :             if ( ! rv )
    1112               0 :                 return rv;
    1113                 :         }
    1114                 : 
    1115               4 :         table_path = "\\" + feature_dataset + table_path;
    1116               4 :         parent_path = "\\" + feature_dataset;
    1117                 :     }
    1118                 : 
    1119                 :     /* Convert table_path into wstring */
    1120              54 :     wtable_path = StringToWString(table_path);
    1121              54 :     wparent_path = StringToWString(parent_path);
    1122                 : 
    1123                 :     /* Over-ride the geometry name if necessary */
    1124              54 :     if ( CSLFetchNameValue( papszOptions, "GEOMETRY_NAME") != NULL )
    1125               0 :         geometry_name = CSLFetchNameValue( papszOptions, "GEOMETRY_NAME");
    1126                 : 
    1127                 :     /* Over-ride the OID name if necessary */
    1128              54 :     if ( CSLFetchNameValue( papszOptions, "OID_NAME") != NULL )
    1129               0 :         fid_name = CSLFetchNameValue( papszOptions, "OID_NAME");
    1130                 : 
    1131                 :     /* Figure out our geometry type */
    1132              54 :     if ( eType != wkbNone )
    1133                 :     {
    1134              52 :         if ( wkbFlatten(eType) == wkbUnknown )
    1135                 :         {
    1136               0 :             return GDBErr(-1, "FGDB layers cannot be created with a wkbUnknown layer geometry type.");
    1137                 :         }
    1138              52 :         if ( ! OGRGeometryToGDB(eType, &esri_type, &has_z) )
    1139               0 :             return GDBErr(-1, "Unable to map OGR type to ESRI type");
    1140                 :     }
    1141                 : 
    1142              54 :     m_bLaunderReservedKeywords = CSLFetchBoolean( papszOptions, "LAUNDER_RESERVED_KEYWORDS", TRUE) == TRUE;
    1143                 : 
    1144                 :     /* XML node */
    1145              54 :     CPLXMLNode *xml_xml = CPLCreateXMLNode(NULL, CXT_Element, "?xml");
    1146              54 :     FGDB_CPLAddXMLAttribute(xml_xml, "version", "1.0");
    1147              54 :     FGDB_CPLAddXMLAttribute(xml_xml, "encoding", "UTF-8");
    1148                 : 
    1149                 :     /* First build up a bare-bones feature definition */
    1150              54 :     CPLXMLNode *defn_xml = CPLCreateXMLNode(NULL, CXT_Element, "esri:DataElement");
    1151              54 :     CPLAddXMLSibling(xml_xml, defn_xml);
    1152                 : 
    1153                 :     /* Add the attributes to the DataElement */
    1154              54 :     FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
    1155              54 :     FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:xs", "http://www.w3.org/2001/XMLSchema");
    1156              54 :     FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:esri", "http://www.esri.com/schemas/ArcGIS/10.1");
    1157                 : 
    1158                 :     /* Need to set this to esri:DEFeatureDataset or esri:DETable */
    1159              54 :     FGDB_CPLAddXMLAttribute(defn_xml, "xsi:type", (eType == wkbNone ? "esri:DETable" : "esri:DEFeatureClass"));
    1160                 : 
    1161                 :     /* Add in more children */
    1162              54 :     CPLCreateXMLElementAndValue(defn_xml,"CatalogPath",table_path.c_str());
    1163              54 :     CPLCreateXMLElementAndValue(defn_xml,"Name", layerName.c_str());
    1164              54 :     CPLCreateXMLElementAndValue(defn_xml,"ChildrenExpanded", "false");
    1165                 : 
    1166                 :     /* WKB type of none implies this is a 'Table' otherwise it's a 'Feature Class' */
    1167              54 :     std::string datasettype = (eType == wkbNone ? "esriDTTable" : "esriDTFeatureClass");
    1168             108 :     CPLCreateXMLElementAndValue(defn_xml,"DatasetType", datasettype.c_str() );
    1169              54 :     CPLCreateXMLElementAndValue(defn_xml,"Versioned", "false");
    1170              54 :     CPLCreateXMLElementAndValue(defn_xml,"CanVersion", "false");
    1171                 : 
    1172                 :     /* We might need to make OID optional later, but OGR likes to have a FID */
    1173              54 :     CPLCreateXMLElementAndValue(defn_xml,"HasOID", "true");
    1174              54 :     CPLCreateXMLElementAndValue(defn_xml,"OIDFieldName", fid_name.c_str());
    1175                 : 
    1176                 :     /* Add in empty Fields */
    1177              54 :     CPLXMLNode *fields_xml = CPLCreateXMLNode(defn_xml, CXT_Element, "Fields");
    1178              54 :     FGDB_CPLAddXMLAttribute(fields_xml, "xsi:type", "esri:Fields");
    1179              54 :     CPLXMLNode *fieldarray_xml = CPLCreateXMLNode(fields_xml, CXT_Element, "FieldArray");
    1180              54 :     FGDB_CPLAddXMLAttribute(fieldarray_xml, "xsi:type", "esri:ArrayOfField");
    1181                 : 
    1182                 :     /* Feature Classes have an implicit geometry column, so we'll add it at creation time */
    1183              54 :     if ( eType != wkbNone )
    1184                 :     {
    1185              52 :         CPLXMLNode *shape_xml = CPLCreateXMLNode(fieldarray_xml, CXT_Element, "Field");
    1186              52 :         FGDB_CPLAddXMLAttribute(shape_xml, "xsi:type", "esri:Field");
    1187              52 :         CPLCreateXMLElementAndValue(shape_xml, "Name", geometry_name.c_str());
    1188              52 :         CPLCreateXMLElementAndValue(shape_xml, "Type", "esriFieldTypeGeometry");
    1189              52 :         CPLCreateXMLElementAndValue(shape_xml, "IsNullable", "false");
    1190              52 :         CPLCreateXMLElementAndValue(shape_xml, "Length", "0");
    1191              52 :         CPLCreateXMLElementAndValue(shape_xml, "Precision", "0");
    1192              52 :         CPLCreateXMLElementAndValue(shape_xml, "Scale", "0");
    1193              52 :         CPLCreateXMLElementAndValue(shape_xml, "Required", "true");
    1194              52 :         CPLXMLNode *geom_xml = CPLCreateXMLNode(shape_xml, CXT_Element, "GeometryDef");
    1195              52 :         FGDB_CPLAddXMLAttribute(geom_xml, "xsi:type", "esri:GeometryDef");
    1196              52 :         CPLCreateXMLElementAndValue(geom_xml, "AvgNumPoints", "0");
    1197              52 :         CPLCreateXMLElementAndValue(geom_xml, "GeometryType", esri_type.c_str());
    1198              52 :         CPLCreateXMLElementAndValue(geom_xml,"HasM", "false");
    1199              52 :         CPLCreateXMLElementAndValue(geom_xml,"HasZ", (has_z ? "true" : "false"));
    1200                 : 
    1201                 :         /* Add the SRS if we have one */
    1202              52 :         CPLXMLNode *srs_xml = XMLSpatialReference(poSRS, papszOptions);
    1203              52 :         if ( srs_xml )
    1204              52 :             CPLAddXMLChild(geom_xml, srs_xml);
    1205                 :     }
    1206                 : 
    1207                 :     /* All (?) Tables and Feature Classes will have an ObjectID */
    1208              54 :     CPLXMLNode *oid_xml = CPLCreateXMLNode(fieldarray_xml, CXT_Element, "Field");
    1209              54 :     FGDB_CPLAddXMLAttribute(oid_xml, "xsi:type", "esri:Field");
    1210              54 :     CPLCreateXMLElementAndValue(oid_xml, "Name", fid_name.c_str());
    1211              54 :     CPLCreateXMLElementAndValue(oid_xml, "Type", "esriFieldTypeOID");
    1212              54 :     CPLCreateXMLElementAndValue(oid_xml, "IsNullable", "false");
    1213              54 :     CPLCreateXMLElementAndValue(oid_xml, "Length", "12");
    1214              54 :     CPLCreateXMLElementAndValue(oid_xml, "Precision", "0");
    1215              54 :     CPLCreateXMLElementAndValue(oid_xml, "Scale", "0");
    1216              54 :     CPLCreateXMLElementAndValue(oid_xml, "Required", "true");
    1217                 : 
    1218                 :     /* Add in empty Indexes */
    1219              54 :     CPLXMLNode *indexes_xml = CPLCreateXMLNode(defn_xml, CXT_Element, "Indexes");
    1220              54 :     FGDB_CPLAddXMLAttribute(indexes_xml, "xsi:type", "esri:Indexes");
    1221              54 :     CPLXMLNode *indexarray_xml = CPLCreateXMLNode(indexes_xml, CXT_Element, "IndexArray");
    1222              54 :     FGDB_CPLAddXMLAttribute(indexarray_xml, "xsi:type", "esri:ArrayOfIndex");
    1223                 : 
    1224                 :     /* CLSID http://forums.arcgis.com/threads/34536?p=118484#post118484 */
    1225              54 :     if ( eType == wkbNone )
    1226                 :     {
    1227               2 :         CPLCreateXMLElementAndValue(defn_xml, "CLSID", "{7A566981-C114-11D2-8A28-006097AFF44E}");
    1228               2 :         CPLCreateXMLElementAndValue(defn_xml, "EXTCLSID", "");
    1229                 :     }
    1230                 :     else
    1231                 :     {
    1232              52 :         CPLCreateXMLElementAndValue(defn_xml, "CLSID", "{52353152-891A-11D0-BEC6-00805F7C4268}");
    1233              52 :         CPLCreateXMLElementAndValue(defn_xml, "EXTCLSID", "");
    1234                 :     }
    1235                 : 
    1236                 :     /* Set the alias for the Feature Class */
    1237              54 :     if (pszLayerNameIn != layerName)
    1238                 :     {
    1239              12 :         CPLCreateXMLElementAndValue(defn_xml, "AliasName", pszLayerNameIn);
    1240                 :     }
    1241                 : 
    1242                 :     /* Map from OGR WKB type to ESRI type */
    1243              54 :     if ( eType != wkbNone )
    1244                 :     {
    1245                 :         /* Declare our feature type */
    1246              52 :         CPLCreateXMLElementAndValue(defn_xml,"FeatureType", "esriFTSimple");
    1247              52 :         CPLCreateXMLElementAndValue(defn_xml,"ShapeType", esri_type.c_str());
    1248              52 :         CPLCreateXMLElementAndValue(defn_xml,"ShapeFieldName", geometry_name.c_str());
    1249                 : 
    1250                 :         /* Dimensionality */
    1251              52 :         CPLCreateXMLElementAndValue(defn_xml,"HasM", "false");
    1252              52 :         CPLCreateXMLElementAndValue(defn_xml,"HasZ", (has_z ? "true" : "false"));
    1253                 : 
    1254                 :         /* TODO: Handle spatial indexes (layer creation option?) */
    1255              52 :         CPLCreateXMLElementAndValue(defn_xml,"HasSpatialIndex", "false");
    1256                 : 
    1257                 :         /* These field are required for Arcmap to display aliases correctly */
    1258              52 :         CPLCreateXMLNode(defn_xml, CXT_Element, "AreaFieldName");
    1259              52 :         CPLCreateXMLNode(defn_xml, CXT_Element, "LengthFieldName");
    1260                 : 
    1261                 :         /* We can't know the extent at this point <Extent xsi:nil='true'/> */
    1262              52 :         CPLXMLNode *extn_xml = CPLCreateXMLNode(defn_xml, CXT_Element, "Extent");
    1263              52 :         FGDB_CPLAddXMLAttribute(extn_xml, "xsi:nil", "true");
    1264                 :     }
    1265                 : 
    1266                 :     /* Feature Class with known SRS gets an SRS entry */
    1267              54 :     if( eType != wkbNone )
    1268                 :     {
    1269              52 :         CPLXMLNode *srs_xml = XMLSpatialReference(poSRS, papszOptions);
    1270              52 :         if ( srs_xml )
    1271              52 :             CPLAddXMLChild(defn_xml, srs_xml);
    1272                 :     }
    1273                 : 
    1274                 :     /* Convert our XML tree into a string for FGDB */
    1275              54 :     char *defn_str = CPLSerializeXMLTree(xml_xml);
    1276              54 :     CPLDestroyXMLNode(xml_xml);
    1277                 : 
    1278                 :     /* TODO, tie this to debugging levels */
    1279              54 :     CPLDebug("FGDB", "%s", defn_str);
    1280                 :     //std::cout << defn_str << std::endl;
    1281                 : 
    1282                 :     /* Create the table. */
    1283              54 :     Table *table = new Table;
    1284             108 :     Geodatabase *gdb = pParentDataSource->GetGDB();
    1285              54 :     fgdbError hr = gdb->CreateTable(defn_str, wparent_path, *table);
    1286                 :     
    1287                 :     /* Free the XML */
    1288              54 :     CPLFree(defn_str);
    1289                 : 
    1290                 :     /* Check table create status */
    1291              54 :     if (FAILED(hr))
    1292                 :     {
    1293               0 :         delete table;
    1294               0 :         return GDBErr(hr, "Failed at creating table for " + table_path);
    1295                 :     }
    1296                 : 
    1297                 :     /* Store the new FGDB Table pointer and set up the OGRFeatureDefn */
    1298              54 :     return FGdbLayer::Initialize(pParentDataSource, table, wtable_path, L"Table");
    1299                 : }
    1300                 : 
    1301                 : /*************************************************************************/
    1302                 : /*                            Initialize()                               */
    1303                 : /* Has ownership of the table as soon as it is called.                   */
    1304                 : /************************************************************************/
    1305                 : 
    1306             298 : bool FGdbLayer::Initialize(FGdbDataSource* pParentDataSource, Table* pTable,
    1307                 :                            std::wstring wstrTablePath, std::wstring wstrType)
    1308                 : {
    1309                 :     long hr;
    1310                 : 
    1311             298 :     m_pDS = pParentDataSource; // we never assume ownership of the parent - so our destructor should not delete
    1312                 : 
    1313             298 :     m_pTable = pTable;
    1314                 : 
    1315             298 :     m_wstrTablePath = wstrTablePath;
    1316             298 :     m_wstrType = wstrType;
    1317                 : 
    1318             298 :     wstring wstrQueryName;
    1319             298 :     if (FAILED(hr = pParentDataSource->GetGDB()->GetQueryName(wstrTablePath, wstrQueryName)))
    1320                 :         return GDBErr(hr, "Failed at getting underlying table name for " +
    1321               0 :                       WStringToString(wstrTablePath));
    1322                 : 
    1323             298 :     m_strName = WStringToString(wstrQueryName);
    1324                 : 
    1325             298 :     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
    1326                 :     //as long as we use the same compiler & settings in both the ogr build and this
    1327                 :     //driver, we should be OK
    1328             298 :     m_pFeatureDefn->Reference();
    1329                 : 
    1330             298 :     string tableDef;
    1331             298 :     if (FAILED(hr = m_pTable->GetDefinition(tableDef)))
    1332                 :         return GDBErr(hr, "Failed at getting table definition for " +
    1333               0 :                       WStringToString(wstrTablePath));
    1334                 : 
    1335                 :     //xxx  printf("Table definition = %s", tableDef.c_str() );
    1336                 : 
    1337             298 :     bool abort = false;
    1338                 : 
    1339                 :     // extract schema information from table
    1340             298 :     CPLXMLNode *psRoot = CPLParseXMLString( tableDef.c_str() );
    1341                 : 
    1342             298 :     if (psRoot == NULL)
    1343                 :     {
    1344                 :         CPLError( CE_Failure, CPLE_AppDefined, "%s",
    1345               0 :                   ("Failed parsing GDB Table Schema XML for " + m_strName).c_str());
    1346               0 :         return false;
    1347                 :     }
    1348                 : 
    1349             298 :     CPLXMLNode *pDataElementNode = psRoot->psNext; // Move to next field which should be DataElement
    1350                 : 
    1351             298 :     if( pDataElementNode != NULL
    1352                 :         && pDataElementNode->psChild != NULL
    1353                 :         && pDataElementNode->eType == CXT_Element
    1354                 :         && EQUAL(pDataElementNode->pszValue,"esri:DataElement") )
    1355                 :     {
    1356                 :         CPLXMLNode *psNode;
    1357                 : 
    1358            8156 :         for( psNode = pDataElementNode->psChild;
    1359                 :         psNode != NULL;
    1360                 :         psNode = psNode->psNext )
    1361                 :         {
    1362            7858 :             if( psNode->eType == CXT_Element && psNode->psChild != NULL )
    1363                 :             {
    1364            5812 :                 if (EQUAL(psNode->pszValue,"OIDFieldName") )
    1365                 :                 {
    1366                 :                     char* pszUnescaped = CPLUnescapeString(
    1367             298 :                     psNode->psChild->pszValue, NULL, CPLES_XML);
    1368             298 :                     m_strOIDFieldName = pszUnescaped;
    1369             298 :                     CPLFree(pszUnescaped);
    1370                 :                 }
    1371            5514 :                 else if (EQUAL(psNode->pszValue,"ShapeFieldName") )
    1372                 :                 {
    1373                 :                     char* pszUnescaped = CPLUnescapeString(
    1374             278 :                     psNode->psChild->pszValue, NULL, CPLES_XML);
    1375             278 :                     m_strShapeFieldName = pszUnescaped;
    1376             278 :                     CPLFree(pszUnescaped);
    1377                 :                 }
    1378            5236 :                 else if (EQUAL(psNode->pszValue,"Fields") )
    1379                 :                 {
    1380             298 :                     if (!GDBToOGRFields(psNode))
    1381                 :                     {
    1382               0 :                         abort = true;
    1383               0 :                         break;
    1384                 :                     }
    1385                 :                 }
    1386                 :             }
    1387                 :         }
    1388                 : 
    1389             298 :         if (m_strShapeFieldName.size() == 0)
    1390              20 :             m_pFeatureDefn->SetGeomType(wkbNone);
    1391                 :     }
    1392                 :     else
    1393                 :     {
    1394                 :         CPLError( CE_Failure, CPLE_AppDefined, "%s",
    1395               0 :                 ("Failed parsing GDB Table Schema XML (DataElement) for " + m_strName).c_str());
    1396               0 :         return false;
    1397                 :     }
    1398             298 :     CPLDestroyXMLNode( psRoot );
    1399                 : 
    1400             298 :     if (abort)
    1401               0 :         return false;
    1402                 : 
    1403             298 :     return true; //AOToOGRFields(ipFields, m_pFeatureDefn, m_vOGRFieldToESRIField);
    1404                 : }
    1405                 : 
    1406                 : /************************************************************************/
    1407                 : /*                          ParseGeometryDef()                          */
    1408                 : /************************************************************************/
    1409                 : 
    1410             278 : bool FGdbLayer::ParseGeometryDef(CPLXMLNode* psRoot)
    1411                 : {
    1412                 :     CPLXMLNode *psGeometryDefItem;
    1413                 : 
    1414             278 :     string geometryType;
    1415             278 :     bool hasZ = false;
    1416             278 :     string wkt, wkid;
    1417                 : 
    1418            2224 :     for (psGeometryDefItem = psRoot->psChild;
    1419                 :         psGeometryDefItem != NULL;
    1420                 :         psGeometryDefItem = psGeometryDefItem->psNext )
    1421                 :     {
    1422                 :         //loop through all "GeometryDef" elements
    1423                 :         //
    1424                 : 
    1425            1946 :         if (psGeometryDefItem->eType == CXT_Element &&
    1426                 :             psGeometryDefItem->psChild != NULL)
    1427                 :         {
    1428            1668 :             if (EQUAL(psGeometryDefItem->pszValue,"GeometryType"))
    1429                 :             {
    1430                 :                 char* pszUnescaped = CPLUnescapeString(
    1431             278 :                     psGeometryDefItem->psChild->pszValue, NULL, CPLES_XML);
    1432                 : 
    1433             278 :                 geometryType = pszUnescaped;
    1434                 : 
    1435             278 :                 CPLFree(pszUnescaped);
    1436                 :             }
    1437            1390 :             else if (EQUAL(psGeometryDefItem->pszValue,"SpatialReference"))
    1438                 :             {
    1439             278 :                 ParseSpatialReference(psGeometryDefItem, &wkt, &wkid); // we don't check for success because it
    1440                 :                                                                 // may not be there
    1441                 :             }
    1442                 :             /* No M support in OGR yet
    1443                 :             else if (EQUAL(psFieldNode->pszValue,"HasM")
    1444                 :             {
    1445                 :                 char* pszUnescaped = CPLUnescapeString(psNode->psChild->pszValue, NULL, CPLES_XML);
    1446                 : 
    1447                 :                 if (!strcmp(szUnescaped, "true"))
    1448                 :                 hasM = true;
    1449                 : 
    1450                 :                 CPLFree(pszUnescaped);
    1451                 :             }
    1452                 :             */
    1453            1112 :             else if (EQUAL(psGeometryDefItem->pszValue,"HasZ"))
    1454                 :             {
    1455                 :                 char* pszUnescaped = CPLUnescapeString(
    1456             278 :                     psGeometryDefItem->psChild->pszValue, NULL, CPLES_XML);
    1457                 : 
    1458             278 :                 if (!strcmp(pszUnescaped, "true"))
    1459             120 :                 hasZ = true;
    1460                 : 
    1461             278 :                 CPLFree(pszUnescaped);
    1462                 :             }
    1463                 :         }
    1464                 : 
    1465                 :     }
    1466                 : 
    1467                 :     OGRwkbGeometryType ogrGeoType;
    1468             278 :     if (!GDBToOGRGeometry(geometryType, hasZ, &ogrGeoType))
    1469               0 :         return false;
    1470                 : 
    1471             278 :     m_pFeatureDefn->SetGeomType(ogrGeoType);
    1472                 : 
    1473             278 :     if (wkbFlatten(ogrGeoType) == wkbMultiLineString ||
    1474                 :         wkbFlatten(ogrGeoType) == wkbMultiPoint)
    1475             120 :         m_forceMulti = true;
    1476                 : 
    1477             278 :     if (wkid.length() > 0)
    1478                 :     {
    1479             272 :         m_pSRS = new OGRSpatialReference();
    1480             544 :         if (m_pSRS->importFromEPSG(atoi(wkid.c_str())) != OGRERR_NONE)
    1481                 :         {
    1482               0 :             delete m_pSRS;
    1483               0 :             m_pSRS = NULL;
    1484                 :         }
    1485                 :         else
    1486             272 :             return true;
    1487                 :     }
    1488                 : 
    1489               6 :     if (wkt.length() > 0)
    1490                 :     {
    1491               0 :         if (!GDBToOGRSpatialReference(wkt, &m_pSRS))
    1492                 :         {
    1493                 :             //report error, but be passive about it
    1494                 :             CPLError( CE_Warning, CPLE_AppDefined,
    1495               0 :                       "Failed Mapping ESRI Spatial Reference");
    1496                 :         }
    1497                 :     }
    1498                 :     else
    1499                 :     {
    1500                 :         //report error, but be passive about it
    1501               6 :         CPLError( CE_Warning, CPLE_AppDefined, "Empty Spatial Reference");
    1502                 :     }
    1503                 : 
    1504               6 :     return true;
    1505                 : }
    1506                 : 
    1507                 : /************************************************************************/
    1508                 : /*                        ParseSpatialReference()                       */
    1509                 : /************************************************************************/
    1510                 : 
    1511             278 : bool FGdbLayer::ParseSpatialReference(CPLXMLNode* psSpatialRefNode,
    1512                 :                                       string* pOutWkt, string* pOutWKID)
    1513                 : {
    1514             278 :     *pOutWkt = "";
    1515             278 :     *pOutWKID = "";
    1516                 : 
    1517                 :     CPLXMLNode* psSRItemNode;
    1518                 : 
    1519                 :     /* Loop through all the SRS elements we want to store */
    1520            3324 :     for( psSRItemNode = psSpatialRefNode->psChild;
    1521                 :          psSRItemNode != NULL;
    1522                 :          psSRItemNode = psSRItemNode->psNext )
    1523                 :     {
    1524                 :         /* The WKID maps (mostly) to an EPSG code */
    1525            3318 :         if( psSRItemNode->eType == CXT_Element &&
    1526                 :             psSRItemNode->psChild != NULL &&
    1527                 :             EQUAL(psSRItemNode->pszValue,"WKID") )
    1528                 :         {
    1529             272 :             char* pszUnescaped = CPLUnescapeString(psSRItemNode->psChild->pszValue, NULL, CPLES_XML);
    1530             272 :             *pOutWKID = pszUnescaped;
    1531             272 :             CPLFree(pszUnescaped);
    1532                 :         }
    1533                 :         /* The WKT well-known text can be converted by OGR */
    1534            2774 :         else if( psSRItemNode->eType == CXT_Element &&
    1535                 :                 psSRItemNode->psChild != NULL &&
    1536                 :                 EQUAL(psSRItemNode->pszValue,"WKT") )
    1537                 :         {
    1538             272 :             char* pszUnescaped = CPLUnescapeString(psSRItemNode->psChild->pszValue, NULL, CPLES_XML);
    1539             272 :             *pOutWkt = pszUnescaped;
    1540             272 :             CPLFree(pszUnescaped);
    1541                 :         }
    1542                 : 
    1543                 :     }
    1544             278 :     return (*pOutWkt != "" || *pOutWKID != "");
    1545                 : }
    1546                 : 
    1547                 : /************************************************************************/
    1548                 : /*                          GDBToOGRFields()                           */
    1549                 : /************************************************************************/
    1550                 : 
    1551             298 : bool FGdbLayer::GDBToOGRFields(CPLXMLNode* psRoot)
    1552                 : {
    1553             298 :     m_vOGRFieldToESRIField.clear();
    1554                 : 
    1555             298 :     if (psRoot->psChild == NULL || psRoot->psChild->psNext == NULL)
    1556                 :     {
    1557               0 :         CPLError( CE_Failure, CPLE_AppDefined, "Unrecognized GDB XML Schema");
    1558                 : 
    1559               0 :         return false;
    1560                 :     }
    1561                 : 
    1562             298 :     psRoot = psRoot->psChild->psNext; //change root to "FieldArray"
    1563                 : 
    1564                 :     //CPLAssert(ogrToESRIFieldMapping.size() == pOGRFeatureDef->GetFieldCount());
    1565                 : 
    1566                 :     CPLXMLNode* psFieldNode;
    1567                 : 
    1568            2122 :     for( psFieldNode = psRoot->psChild;
    1569                 :         psFieldNode != NULL;
    1570                 :         psFieldNode = psFieldNode->psNext )
    1571                 :     {
    1572                 :         //loop through all "Field" elements
    1573                 :         //
    1574                 : 
    1575            1824 :         if( psFieldNode->eType == CXT_Element && psFieldNode->psChild != NULL &&
    1576                 :             EQUAL(psFieldNode->pszValue,"Field"))
    1577                 :         {
    1578                 : 
    1579                 :             CPLXMLNode* psFieldItemNode;
    1580            1526 :             std::string fieldName;
    1581            1526 :             std::string fieldType;
    1582            1526 :             int nLength = 0;
    1583            1526 :             int nPrecision = 0;
    1584                 : 
    1585                 :             // loop through all items in Field element
    1586                 :             //
    1587                 : 
    1588           16412 :             for( psFieldItemNode = psFieldNode->psChild;
    1589                 :                 psFieldItemNode != NULL;
    1590                 :                 psFieldItemNode = psFieldItemNode->psNext )
    1591                 :             {
    1592           14886 :                 if (psFieldItemNode->eType == CXT_Element)
    1593                 :                 {
    1594                 : 
    1595           13360 :                 if (EQUAL(psFieldItemNode->pszValue,"Name"))
    1596                 :                 {
    1597                 :                     char* pszUnescaped = CPLUnescapeString(
    1598            1526 :                         psFieldItemNode->psChild->pszValue, NULL, CPLES_XML);
    1599            1526 :                     fieldName = pszUnescaped;
    1600            1526 :                     CPLFree(pszUnescaped);
    1601                 :                 }
    1602           11834 :                 else if (EQUAL(psFieldItemNode->pszValue,"Type") )
    1603                 :                 {
    1604                 :                     char* pszUnescaped = CPLUnescapeString(
    1605            1526 :                         psFieldItemNode->psChild->pszValue, NULL, CPLES_XML);
    1606            1526 :                     fieldType = pszUnescaped;
    1607            1526 :                     CPLFree(pszUnescaped);
    1608                 :                 }
    1609           10308 :                 else if (EQUAL(psFieldItemNode->pszValue,"GeometryDef") )
    1610                 :                 {
    1611             278 :                     if (!ParseGeometryDef(psFieldItemNode))
    1612               0 :                         return false; // if we failed parsing the GeometryDef, we are done!
    1613                 :                 }
    1614           10030 :                 else if (EQUAL(psFieldItemNode->pszValue,"Length") )
    1615                 :                 {
    1616            1526 :                     nLength = atoi(psFieldItemNode->psChild->pszValue);
    1617                 :                 }
    1618            8504 :                 else if (EQUAL(psFieldItemNode->pszValue,"Precision") )
    1619                 :                 {
    1620            1526 :                     nPrecision = atoi(psFieldItemNode->psChild->pszValue);
    1621                 :                 }
    1622                 :                 }
    1623                 :             }
    1624                 : 
    1625                 : 
    1626                 :             ///////////////////////////////////////////////////////////////////
    1627                 :             // At this point we have parsed everything about the current field
    1628                 : 
    1629                 : 
    1630            1526 :             if (fieldType == "esriFieldTypeGeometry")
    1631                 :             {
    1632             278 :                 m_strShapeFieldName = fieldName;
    1633                 : 
    1634             278 :                 continue; // finish here for special field - don't add as OGR fielddef
    1635                 :             }
    1636            1248 :             else if (fieldType == "esriFieldTypeOID")
    1637                 :             {
    1638                 :                 //m_strOIDFieldName = fieldName; // already set by this point
    1639                 : 
    1640             298 :                 continue; // finish here for special field - don't add as OGR fielddef
    1641                 :             }
    1642                 : 
    1643                 :             OGRFieldType ogrType;
    1644                 :             //CPLDebug("FGDB", "name = %s, type = %s", fieldName.c_str(), fieldType.c_str() );
    1645             950 :             if (!GDBToOGRFieldType(fieldType, &ogrType))
    1646                 :             {
    1647                 :                 // field cannot be mapped, skipping further processing
    1648                 :                 CPLError( CE_Warning, CPLE_AppDefined, "Skipping field: [%s] type: [%s] ",
    1649               0 :                 fieldName.c_str(), fieldType.c_str() );
    1650               0 :                 continue;
    1651                 :             }
    1652                 : 
    1653                 : 
    1654                 :             //TODO: Optimization - modify m_wstrSubFields so it only fetches fields that are mapped
    1655                 : 
    1656             950 :             OGRFieldDefn fieldTemplate( fieldName.c_str(), ogrType);
    1657                 :             //fieldTemplate.SetWidth(nLength);
    1658                 :             //fieldTemplate.SetPrecision(nPrecision);
    1659             950 :             m_pFeatureDefn->AddFieldDefn( &fieldTemplate );
    1660                 : 
    1661             950 :             m_vOGRFieldToESRIField.push_back(StringToWString(fieldName));
    1662             950 :             m_vOGRFieldToESRIFieldType.push_back( fieldType );
    1663                 : 
    1664                 :         }
    1665                 :     }
    1666                 : 
    1667             298 :     return true;
    1668                 : }
    1669                 : 
    1670                 : 
    1671                 : /************************************************************************/
    1672                 : /*                            ResetReading()                            */
    1673                 : /************************************************************************/
    1674                 : 
    1675             696 : void FGdbLayer::ResetReading()
    1676                 : {
    1677                 :     long hr;
    1678                 : 
    1679             696 :     EndBulkLoad();
    1680                 : 
    1681             696 :     if (m_pOGRFilterGeometry && !m_pOGRFilterGeometry->IsEmpty())
    1682                 :     {
    1683                 :         // Search spatial
    1684                 :         // As of beta1, FileGDB only supports bbox searched, if we have GEOS installed,
    1685                 :         // we can do the rest ourselves.
    1686                 : 
    1687             156 :         OGREnvelope ogrEnv;
    1688                 : 
    1689             156 :         m_pOGRFilterGeometry->getEnvelope(&ogrEnv);
    1690                 : 
    1691                 :         //spatial query
    1692             156 :         FileGDBAPI::Envelope env(ogrEnv.MinX, ogrEnv.MaxX, ogrEnv.MinY, ogrEnv.MaxY);
    1693                 : 
    1694             156 :         if FAILED(hr = m_pTable->Search(m_wstrSubfields, m_wstrWhereClause, env, true, *m_pEnumRows))
    1695               0 :         GDBErr(hr, "Failed Searching");
    1696                 : 
    1697             156 :         m_bFilterDirty = false;
    1698                 : 
    1699             156 :         return;
    1700                 :     }
    1701                 : 
    1702                 :     // Search non-spatial
    1703                 : 
    1704             540 :     if FAILED(hr = m_pTable->Search(m_wstrSubfields, m_wstrWhereClause, true, *m_pEnumRows))
    1705               0 :         GDBErr(hr, "Failed Searching");
    1706                 : 
    1707             540 :     m_bFilterDirty = false;
    1708                 :   
    1709                 : }
    1710                 : 
    1711                 : /************************************************************************/
    1712                 : /*                         SetSpatialFilter()                           */
    1713                 : /************************************************************************/
    1714                 : 
    1715             274 : void FGdbLayer::SetSpatialFilter( OGRGeometry* pOGRGeom )
    1716                 : {
    1717             274 :     if (m_pOGRFilterGeometry)
    1718                 :     {
    1719              52 :         OGRGeometryFactory::destroyGeometry(m_pOGRFilterGeometry);
    1720              52 :         m_pOGRFilterGeometry = NULL;
    1721                 :     }
    1722                 : 
    1723             274 :     if (pOGRGeom == NULL || pOGRGeom->IsEmpty())
    1724                 :     {
    1725             222 :         m_bFilterDirty = true;
    1726                 : 
    1727             222 :         return;
    1728                 :     }
    1729                 : 
    1730              52 :     m_pOGRFilterGeometry = pOGRGeom->clone();
    1731                 : 
    1732              52 :     m_pOGRFilterGeometry->transformTo(m_pSRS);
    1733                 : 
    1734              52 :     m_bFilterDirty = true;
    1735                 : }
    1736                 : 
    1737                 : /************************************************************************/
    1738                 : /*                         SetSpatialFilterRect()                       */
    1739                 : /************************************************************************/
    1740                 : 
    1741               0 : void FGdbLayer::SetSpatialFilterRect (double dfMinX, double dfMinY, double dfMaxX, double dfMaxY)
    1742                 : {
    1743                 : 
    1744                 :     //TODO: can optimize this by changing how the filter gets generated -
    1745                 :     //this will work for now
    1746                 : 
    1747               0 :     OGRGeometry* pTemp = OGRGeometryFactory::createGeometry(wkbPolygon);
    1748                 : 
    1749               0 :     pTemp->assignSpatialReference(m_pSRS);
    1750                 : 
    1751               0 :     OGRLinearRing ring;
    1752                 : 
    1753               0 :     ring.addPoint( dfMinX, dfMinY );
    1754               0 :     ring.addPoint( dfMinX, dfMaxY );
    1755               0 :     ring.addPoint( dfMaxX, dfMaxY );
    1756               0 :     ring.addPoint( dfMaxX, dfMinY );
    1757               0 :     ring.addPoint( dfMinX, dfMinY );
    1758               0 :     ((OGRPolygon *) pTemp)->addRing( &ring );
    1759                 : 
    1760               0 :     SetSpatialFilter(pTemp);
    1761                 : 
    1762               0 :     OGRGeometryFactory::destroyGeometry(pTemp);
    1763               0 : }
    1764                 : 
    1765                 : 
    1766                 : /************************************************************************/
    1767                 : /*                         SetAttributeFilter()                         */
    1768                 : /************************************************************************/
    1769                 : 
    1770             196 : OGRErr FGdbLayer::SetAttributeFilter( const char* pszQuery )
    1771                 : {
    1772             196 :     m_wstrWhereClause = StringToWString( (pszQuery != NULL) ? pszQuery : "" );
    1773                 : 
    1774             196 :     m_bFilterDirty = true;
    1775                 : 
    1776             196 :     return OGRERR_NONE;
    1777                 : }
    1778                 : 
    1779                 : /************************************************************************/
    1780                 : /*                           OGRFeatureFromGdbRow()                      */
    1781                 : /************************************************************************/
    1782                 : 
    1783            1556 : bool FGdbLayer::OGRFeatureFromGdbRow(Row* pRow, OGRFeature** ppFeature)
    1784                 : {
    1785                 :     long hr;
    1786                 : 
    1787            1556 :     OGRFeature* pOutFeature = new OGRFeature(m_pFeatureDefn);
    1788                 : 
    1789                 :     /////////////////////////////////////////////////////////
    1790                 :     // Translate OID
    1791                 :     //
    1792                 : 
    1793            1556 :     int32 oid = -1;
    1794            1556 :     if (FAILED(hr = pRow->GetOID(oid)))
    1795                 :     {
    1796                 :         //this should never happen
    1797               0 :         delete pOutFeature;
    1798               0 :         return false;
    1799                 :     }
    1800            1556 :     pOutFeature->SetFID(oid);
    1801                 : 
    1802                 : 
    1803                 :     /////////////////////////////////////////////////////////
    1804                 :     // Translate Geometry
    1805                 :     //
    1806                 : 
    1807            1556 :     ShapeBuffer gdbGeometry;
    1808            1556 :     if (!FAILED(hr = pRow->GetGeometry(gdbGeometry)))
    1809                 :     {
    1810            1460 :         OGRGeometry* pOGRGeo = NULL;
    1811                 : 
    1812            1460 :         if ((!GDBGeometryToOGRGeometry(m_forceMulti, &gdbGeometry, m_pSRS, &pOGRGeo)) || pOGRGeo == NULL)
    1813                 :         {
    1814               0 :             delete pOutFeature;
    1815               0 :             return GDBErr(hr, "Failed to translate FileGDB Geometry to OGR Geometry for row " + string(CPLSPrintf("%d", (int)oid)));
    1816                 :         }
    1817                 : 
    1818            1460 :         pOutFeature->SetGeometryDirectly(pOGRGeo);
    1819                 :     }
    1820                 : 
    1821                 : 
    1822                 :     //////////////////////////////////////////////////////////
    1823                 :     // Map fields
    1824                 :     //
    1825                 : 
    1826                 : 
    1827            1556 :     size_t mappedFieldCount = m_vOGRFieldToESRIField.size();
    1828                 : 
    1829            1556 :     bool foundBadColumn = false;
    1830                 : 
    1831            7596 :     for (size_t i = 0; i < mappedFieldCount; ++i)
    1832                 :     {
    1833            6040 :         const wstring & wstrFieldName = m_vOGRFieldToESRIField[i];
    1834            6040 :         const std::string & strFieldType = m_vOGRFieldToESRIFieldType[i];
    1835                 : 
    1836            6040 :         bool isNull = false;
    1837                 : 
    1838            6040 :         if (FAILED(hr = pRow->IsNull(wstrFieldName, isNull)))
    1839                 :         {
    1840                 :             GDBErr(hr, "Failed to determine NULL status from column " +
    1841               0 :                    WStringToString(wstrFieldName));
    1842               0 :             foundBadColumn = true;
    1843               0 :             continue;
    1844                 :         }
    1845                 : 
    1846            6040 :         if (isNull)
    1847                 :         {
    1848              70 :             continue; //leave as unset
    1849                 :         }
    1850                 : 
    1851                 :         //
    1852                 :         // NOTE: This switch statement needs to be kept in sync with GDBToOGRFieldType utility function
    1853                 :         //       since we are only checking for types we mapped in that utility function
    1854                 : 
    1855            5970 :         switch (m_pFeatureDefn->GetFieldDefn(i)->GetType())
    1856                 :         {
    1857                 : 
    1858                 :             case OFTInteger:
    1859                 :             {
    1860                 :                 int32 val;
    1861                 : 
    1862            2754 :                 if (FAILED(hr = pRow->GetInteger(wstrFieldName, val)))
    1863                 :                 {
    1864                 :                     int16 shortval;
    1865               0 :                     if (FAILED(hr = pRow->GetShort(wstrFieldName, shortval)))
    1866                 :                     {
    1867                 :                         GDBErr(hr, "Failed to determine integer value for column " +
    1868               0 :                                WStringToString(wstrFieldName));
    1869               0 :                         foundBadColumn = true;
    1870               0 :                         continue;
    1871                 :                     }
    1872               0 :                     val = shortval;
    1873                 :                 }
    1874                 : 
    1875            2754 :                 pOutFeature->SetField(i, (int)val);
    1876                 :             }
    1877            2754 :             break;
    1878                 : 
    1879                 :             case OFTReal:
    1880                 :             {
    1881            1732 :                 if (strFieldType == "esriFieldTypeSingle")
    1882                 :                 {
    1883                 :                     float val;
    1884                 : 
    1885               0 :                     if (FAILED(hr = pRow->GetFloat(wstrFieldName, val)))
    1886                 :                     {
    1887                 :                         GDBErr(hr, "Failed to determine float value for column " +
    1888               0 :                                WStringToString(wstrFieldName));
    1889               0 :                         foundBadColumn = true;
    1890               0 :                         continue;
    1891                 :                     }
    1892                 : 
    1893               0 :                     pOutFeature->SetField(i, val);
    1894                 :                 }
    1895                 :                 else
    1896                 :                 {
    1897                 :                     double val;
    1898                 : 
    1899            1732 :                     if (FAILED(hr = pRow->GetDouble(wstrFieldName, val)))
    1900                 :                     {
    1901                 :                         GDBErr(hr, "Failed to determine real value for column " +
    1902               0 :                                WStringToString(wstrFieldName));
    1903               0 :                         foundBadColumn = true;
    1904               0 :                         continue;
    1905                 :                     }
    1906                 : 
    1907            1732 :                     pOutFeature->SetField(i, val);
    1908                 :                 }
    1909                 :             }
    1910            1732 :             break;
    1911                 :             case OFTString:
    1912                 :             {
    1913            1484 :                 wstring val;
    1914                 : 
    1915            1484 :                 if (FAILED(hr = pRow->GetString(wstrFieldName, val)))
    1916                 :                 {
    1917                 :                     GDBErr(hr, "Failed to determine string value for column " +
    1918               0 :                         WStringToString(wstrFieldName));
    1919               0 :                     foundBadColumn = true;
    1920               0 :                     continue;
    1921                 :                 }
    1922                 : 
    1923            1484 :                 pOutFeature->SetField(i, WStringToString(val).c_str());
    1924                 :             }
    1925            1484 :             break;
    1926                 : 
    1927                 :             /* TODO: Need to get test dataset to implement these leave it as NULL for now
    1928                 :             case OFTBinary:
    1929                 :             {
    1930                 :                 ByteArray binaryBuf;
    1931                 : 
    1932                 :                 if (FAILED(hr = pRow->GetBinary(wstrFieldName, binaryBuf)))
    1933                 :                 {
    1934                 :                 GDBErr(hr, "Failed to determine binary value for column " + WStringToString(wstrFieldName));
    1935                 :                 foundBadColumn = true;
    1936                 :                 continue;
    1937                 :                 }
    1938                 : 
    1939                 :                 pOutFeature->SetField(i, (int)binaryBuf.inUseLength, (GByte*)binaryBuf.byteArray);
    1940                 :             }
    1941                 :             break;
    1942                 :             */
    1943                 : 
    1944                 :             case OFTDateTime:
    1945                 :             {
    1946                 :                 struct tm val;
    1947                 : 
    1948               0 :                 if (FAILED(hr = pRow->GetDate(wstrFieldName, val)))
    1949                 :                 {
    1950                 :                     GDBErr(hr, "Failed to determine date value for column " +
    1951               0 :                            WStringToString(wstrFieldName));
    1952               0 :                     foundBadColumn = true;
    1953               0 :                     continue;
    1954                 :                 }
    1955                 : 
    1956                 :                 pOutFeature->SetField(i, val.tm_year + 1900, val.tm_mon + 1,
    1957               0 :                                       val.tm_mday, val.tm_hour, val.tm_min, val.tm_sec);
    1958                 :             // Examine test data to figure out how to extract that
    1959                 :             }
    1960               0 :             break;
    1961                 : 
    1962                 :             default:
    1963                 :             {
    1964               0 :                 if (!m_supressColumnMappingError)
    1965                 :                 {
    1966               0 :                     foundBadColumn = true;
    1967                 :                     CPLError( CE_Warning, CPLE_AppDefined,
    1968                 :                             "Row id: %d col:%d has unhandled col type (%d). Setting to NULL.",
    1969               0 :                             (int)oid, (int)i, m_pFeatureDefn->GetFieldDefn(i)->GetType());
    1970                 :                 }
    1971                 :             }
    1972                 :         }
    1973                 :     }
    1974                 : 
    1975            1556 :     if (foundBadColumn)
    1976               0 :         m_supressColumnMappingError = true;
    1977                 : 
    1978                 : 
    1979            1556 :     *ppFeature = pOutFeature;
    1980                 : 
    1981            1556 :     return true;
    1982                 : }
    1983                 : 
    1984                 : 
    1985                 : /************************************************************************/
    1986                 : /*                           GetNextFeature()                           */
    1987                 : /************************************************************************/
    1988                 : 
    1989            1688 : OGRFeature* FGdbLayer::GetNextFeature()
    1990                 : {
    1991            1688 :     if (m_bFilterDirty)
    1992              36 :         ResetReading();
    1993                 : 
    1994            1688 :     EndBulkLoad();
    1995                 : 
    1996               0 :     while (1) //want to skip errors
    1997                 :     {
    1998            1688 :         if (m_pEnumRows == NULL)
    1999               0 :             return NULL;
    2000                 : 
    2001                 :         long hr;
    2002                 : 
    2003            1688 :         Row row;
    2004                 : 
    2005            1688 :         if (FAILED(hr = m_pEnumRows->Next(row)))
    2006                 :         {
    2007               0 :             GDBErr(hr, "Failed fetching features");
    2008               0 :             return NULL;
    2009                 :         }
    2010                 : 
    2011            1688 :         if (hr != S_OK)
    2012                 :         {
    2013                 :         // It's OK, we are done fetching - failure is catched by FAILED macro
    2014             244 :             return NULL;
    2015                 :         }
    2016                 : 
    2017            1444 :         OGRFeature* pOGRFeature = NULL;
    2018                 : 
    2019            1444 :         if (!OGRFeatureFromGdbRow(&row,  &pOGRFeature))
    2020                 :         {
    2021               0 :             int32 oid = -1;
    2022               0 :             row.GetOID(oid);
    2023                 : 
    2024               0 :             GDBErr(hr, CPLSPrintf("Failed translating FGDB row [%d] to OGR Feature", oid));
    2025                 : 
    2026                 :             //return NULL;
    2027               0 :             continue; //skip feature
    2028                 :         }
    2029                 : 
    2030            1444 :         return pOGRFeature;
    2031                 :     }
    2032                 : }
    2033                 : 
    2034                 : /************************************************************************/
    2035                 : /*                             GetFeature()                             */
    2036                 : /************************************************************************/
    2037                 : 
    2038             140 : OGRFeature *FGdbLayer::GetFeature( long oid )
    2039                 : {
    2040                 :     // do query to fetch individual row
    2041             140 :     EnumRows       enumRows;
    2042             140 :     Row            row;
    2043                 : 
    2044             140 :     EndBulkLoad();
    2045                 : 
    2046             140 :     if (GetRow(enumRows, row, oid) != OGRERR_NONE)
    2047              28 :         return NULL;
    2048                 : 
    2049             112 :     OGRFeature* pOGRFeature = NULL;
    2050                 : 
    2051             112 :     if (!OGRFeatureFromGdbRow(&row,  &pOGRFeature))
    2052                 :     {
    2053               0 :         return NULL;
    2054                 :     }
    2055                 : 
    2056             112 :     return pOGRFeature;
    2057                 : }
    2058                 : 
    2059                 : 
    2060                 : /************************************************************************/
    2061                 : /*                          GetFeatureCount()                           */
    2062                 : /************************************************************************/
    2063                 : 
    2064             364 : int FGdbLayer::GetFeatureCount( int bForce )
    2065                 : {
    2066                 :     long           hr;
    2067             364 :     int32          rowCount = 0;
    2068                 : 
    2069             364 :     EndBulkLoad();
    2070                 : 
    2071             364 :     if (m_pOGRFilterGeometry != NULL || m_wstrWhereClause.size() != 0)
    2072             108 :         return OGRLayer::GetFeatureCount(bForce);
    2073                 : 
    2074             256 :     if (FAILED(hr = m_pTable->GetRowCount(rowCount)))
    2075                 :     {
    2076               0 :         GDBErr(hr, "Failed counting rows");
    2077               0 :         return 0;
    2078                 :     }
    2079                 : 
    2080                 : #if 0
    2081                 :   Row            row;
    2082                 :   EnumRows       enumRows;
    2083                 : 
    2084                 :   if (FAILED(hr = m_pTable->Search(StringToWString(m_strOIDFieldName), L"", true, enumRows)))
    2085                 :   {
    2086                 :     GDBErr(hr, "Failed counting rows");
    2087                 :     return -1;
    2088                 :   }
    2089                 : 
    2090                 :   while (S_OK == (hr = enumRows.Next(row)))
    2091                 :     ++rowCount;
    2092                 : 
    2093                 :   if (FAILED(hr))
    2094                 :   {
    2095                 :     GDBErr(hr, "Failed counting rows (during fetch)");
    2096                 :     return -1;
    2097                 :   }
    2098                 : #endif
    2099                 : 
    2100             256 :     return static_cast<int>(rowCount);
    2101                 : }
    2102                 : 
    2103                 : 
    2104                 : 
    2105                 : /************************************************************************/
    2106                 : /*                             GetExtent()                              */
    2107                 : /************************************************************************/
    2108                 : 
    2109              56 : OGRErr FGdbLayer::GetExtent (OGREnvelope* psExtent, int bForce)
    2110                 : {
    2111              56 :     if (m_pOGRFilterGeometry != NULL || m_wstrWhereClause.size() != 0 ||
    2112                 :         m_strShapeFieldName.size() == 0)
    2113               2 :         return OGRLayer::GetExtent(psExtent, bForce);
    2114                 : 
    2115                 :     long hr;
    2116              54 :     Envelope envelope;
    2117              54 :     if (FAILED(hr = m_pTable->GetExtent(envelope)))
    2118                 :     {
    2119               0 :         GDBErr(hr, "Failed fetching extent");
    2120               0 :         return OGRERR_FAILURE;
    2121                 :     }
    2122                 : 
    2123              54 :     psExtent->MinX = envelope.xMin;
    2124              54 :     psExtent->MinY = envelope.yMin;
    2125              54 :     psExtent->MaxX = envelope.xMax;
    2126              54 :     psExtent->MaxY = envelope.yMax;
    2127                 : 
    2128              54 :     if (CPLIsNan(psExtent->MinX) ||
    2129                 :         CPLIsNan(psExtent->MinY) ||
    2130                 :         CPLIsNan(psExtent->MaxX) ||
    2131                 :         CPLIsNan(psExtent->MaxY))
    2132               0 :         return OGRERR_FAILURE;
    2133                 : 
    2134              54 :     return OGRERR_NONE;
    2135                 : }
    2136                 : 
    2137                 : /************************************************************************/
    2138                 : /*                          StartBulkLoad()                             */
    2139                 : /************************************************************************/
    2140                 : 
    2141               2 : void FGdbLayer::StartBulkLoad ()
    2142                 : {
    2143               2 :     if ( ! m_pTable )
    2144               0 :         return;
    2145                 : 
    2146               2 :     if ( m_bBulkLoadInProgress )
    2147               0 :         return;
    2148                 : 
    2149               2 :     m_bBulkLoadInProgress = TRUE;
    2150               2 :     m_pTable->LoadOnlyMode(true);
    2151               2 :     m_pTable->SetWriteLock();
    2152                 : }
    2153                 : 
    2154                 : /************************************************************************/
    2155                 : /*                           EndBulkLoad()                              */
    2156                 : /************************************************************************/
    2157                 : 
    2158            3328 : void FGdbLayer::EndBulkLoad ()
    2159                 : {
    2160            3328 :     if ( ! m_pTable )
    2161               0 :         return;
    2162                 : 
    2163            3328 :     if ( ! m_bBulkLoadInProgress )
    2164            3326 :         return;
    2165                 : 
    2166               2 :     m_bBulkLoadInProgress = FALSE;
    2167               2 :     m_bBulkLoadAllowed = -1; /* so that the configuration option is read the first time we CreateFeature() again */
    2168               2 :     m_pTable->LoadOnlyMode(false);
    2169               2 :     m_pTable->FreeWriteLock();
    2170                 : }
    2171                 : 
    2172                 : /* OGRErr FGdbLayer::StartTransaction ()
    2173                 : {
    2174                 :     if ( ! m_pTable ) 
    2175                 :         return OGRERR_FAILURE;
    2176                 :         
    2177                 :     m_pTable->LoadOnlyMode(true);
    2178                 :     m_pTable->SetWriteLock();
    2179                 :     return OGRERR_NONE;
    2180                 :     
    2181                 : } */
    2182                 : 
    2183                 : 
    2184                 : /* OGRErr FGdbLayer::CommitTransaction ()
    2185                 : {
    2186                 :     if ( ! m_pTable ) 
    2187                 :         return OGRERR_FAILURE;
    2188                 :     
    2189                 :     m_pTable->LoadOnlyMode(false);
    2190                 :     m_pTable->FreeWriteLock();
    2191                 :     return OGRERR_NONE;
    2192                 : } */
    2193                 : 
    2194                 : /* OGRErr FGdbLayer::RollbackTransaction ()
    2195                 : {
    2196                 :     if ( ! m_pTable ) 
    2197                 :         return OGRERR_FAILURE;
    2198                 :     
    2199                 :     m_pTable->LoadOnlyMode(false);
    2200                 :     m_pTable->FreeWriteLock();
    2201                 :     return OGRERR_NONE;
    2202                 : } */
    2203                 : 
    2204                 : 
    2205                 : /************************************************************************/
    2206                 : /*                           GetLayerXML()                              */
    2207                 : /* Return XML definition of the Layer as provided by FGDB. Caller must  */
    2208                 : /* free result.                                                         */
    2209                 : /* Not currently used by the driver, but can be used by external code   */
    2210                 : /* for specific purposes.                                               */
    2211                 : /************************************************************************/
    2212                 : 
    2213              26 : OGRErr FGdbLayer::GetLayerXML (char **ppXml)
    2214                 : {
    2215                 :     long hr;
    2216              26 :     std::string xml;
    2217                 : 
    2218              26 :     if ( FAILED(hr = m_pTable->GetDefinition(xml)) )
    2219                 :     {
    2220               0 :         GDBErr(hr, "Failed fetching XML table definition");
    2221               0 :         return OGRERR_FAILURE;
    2222                 :     }
    2223                 : 
    2224              26 :     *ppXml = CPLStrdup(xml.c_str());
    2225              26 :     return OGRERR_NONE;
    2226                 : }
    2227                 : 
    2228                 : /************************************************************************/
    2229                 : /*                           GetLayerMetadataXML()                      */
    2230                 : /* Return XML metadata for the Layer as provided by FGDB. Caller must  */
    2231                 : /* free result.                                                         */
    2232                 : /* Not currently used by the driver, but can be used by external code   */
    2233                 : /* for specific purposes.                                               */
    2234                 : /************************************************************************/
    2235                 : 
    2236              26 : OGRErr FGdbLayer::GetLayerMetadataXML (char **ppXml)
    2237                 : {
    2238                 :     long hr;
    2239              26 :     std::string xml;
    2240                 : 
    2241              26 :     if ( FAILED(hr = m_pTable->GetDocumentation(xml)) )
    2242                 :     {
    2243               0 :         GDBErr(hr, "Failed fetching XML table metadata");
    2244               0 :         return OGRERR_FAILURE;
    2245                 :     }
    2246                 : 
    2247              26 :     *ppXml = CPLStrdup(xml.c_str());
    2248              26 :     return OGRERR_NONE;
    2249                 : }
    2250                 : 
    2251                 : /************************************************************************/
    2252                 : /*                           TestCapability()                           */
    2253                 : /************************************************************************/
    2254                 : 
    2255             280 : int FGdbLayer::TestCapability( const char* pszCap )
    2256                 : {
    2257                 : 
    2258             280 :     if (EQUAL(pszCap,OLCRandomRead))
    2259              84 :         return TRUE;
    2260                 : 
    2261             196 :     else if (EQUAL(pszCap,OLCFastFeatureCount)) 
    2262               0 :         return m_pOGRFilterGeometry == NULL && m_wstrWhereClause.size() == 0;
    2263                 : 
    2264             196 :     else if (EQUAL(pszCap,OLCFastSpatialFilter))
    2265               0 :         return TRUE;
    2266                 : 
    2267             196 :     else if (EQUAL(pszCap,OLCFastGetExtent))
    2268               0 :         return m_pOGRFilterGeometry == NULL && m_wstrWhereClause.size() == 0;
    2269                 : 
    2270             196 :     else if (EQUAL(pszCap,OLCCreateField)) /* CreateField() */
    2271               0 :         return TRUE;
    2272                 : 
    2273             196 :     else if (EQUAL(pszCap,OLCSequentialWrite)) /* CreateFeature() */
    2274              28 :         return TRUE;
    2275                 : 
    2276             168 :     else if (EQUAL(pszCap,OLCStringsAsUTF8)) /* Native UTF16, converted to UTF8 */
    2277              28 :         return TRUE;
    2278                 : 
    2279             140 :     else if (EQUAL(pszCap,OLCReorderFields)) /* TBD ReorderFields() */
    2280               0 :         return FALSE;
    2281                 : 
    2282             140 :     else if (EQUAL(pszCap,OLCDeleteFeature)) /* DeleteFeature() */
    2283              28 :         return TRUE;
    2284                 : 
    2285             112 :     else if (EQUAL(pszCap,OLCRandomWrite)) /* SetFeature() */
    2286              28 :         return TRUE;
    2287                 : 
    2288              84 :     else if (EQUAL(pszCap,OLCDeleteField)) /* DeleteField() */
    2289               0 :         return TRUE;
    2290                 : 
    2291                 : #ifdef AlterFieldDefn_implemented_but_not_working
    2292                 :     else if (EQUAL(pszCap,OLCAlterFieldDefn)) /* AlterFieldDefn() */
    2293                 :         return TRUE;
    2294                 : #endif
    2295                 : 
    2296              84 :     else if (EQUAL(pszCap,OLCFastSetNextByIndex)) /* TBD FastSetNextByIndex() */
    2297              28 :         return FALSE;
    2298                 : 
    2299              56 :     else if (EQUAL(pszCap,OLCTransactions)) /* TBD Start/End Transactions() */
    2300              56 :         return FALSE;
    2301                 :         
    2302                 :     else 
    2303               0 :         return FALSE;
    2304            4029 : }

Generated by: LCOV version 1.7