LCOV - code coverage report
Current view: directory - ogr/ogrsf_frmts/filegdb - FGdbLayer.cpp (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 643 484 75.3 %
Date: 2011-12-18 Functions: 29 22 75.9 %

       1                 : /******************************************************************************
       2                 : * $Id: FGdbLayer.cpp 23394 2011-11-19 19:20:12Z rouault $
       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 23394 2011-11-19 19:20:12Z rouault $");
      40                 : 
      41                 : using std::string;
      42                 : using std::wstring;
      43                 : 
      44                 : /************************************************************************/
      45                 : /*                              FGdbLayer()                               */
      46                 : /************************************************************************/
      47              81 : 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              81 :     m_supressColumnMappingError(false), m_forceMulti(false)
      52                 : {
      53              81 :     m_pEnumRows = new EnumRows;
      54              81 : }
      55                 : 
      56                 : /************************************************************************/
      57                 : /*                            ~FGdbLayer()                              */
      58                 : /************************************************************************/
      59                 : 
      60              81 : FGdbLayer::~FGdbLayer()
      61                 : {
      62              81 :     if (m_pFeatureDefn)
      63                 :     {
      64              81 :         m_pFeatureDefn->Release();
      65              81 :         m_pFeatureDefn = NULL;
      66                 :     }
      67                 : 
      68              81 :     if (m_pSRS)
      69                 :     {
      70              79 :         m_pSRS->Release();
      71              79 :         m_pSRS = NULL;
      72                 :     }
      73                 : 
      74              81 :     if (m_pEnumRows)
      75                 :     {
      76              81 :         delete m_pEnumRows;
      77              81 :         m_pEnumRows = NULL;
      78                 :     }
      79                 : 
      80                 :     // NOTE: never delete m_pDS - the memory doesn't belong to us
      81                 :     // TODO: check if we need to close the table or if the destructor 
      82                 :     // takes care of closing as it should
      83              81 :     if (m_pTable)
      84                 :     {
      85              81 :         delete m_pTable;
      86              81 :         m_pTable = NULL;
      87                 :     }
      88                 : 
      89              81 :     if (m_pOGRFilterGeometry)
      90                 :     {
      91               0 :         OGRGeometryFactory::destroyGeometry(m_pOGRFilterGeometry);
      92               0 :         m_pOGRFilterGeometry = NULL;
      93                 :     }
      94                 :     
      95              81 : }
      96                 : 
      97                 : 
      98                 : /************************************************************************/
      99                 : /*                            CreateFeature()                           */
     100                 : /* Create an FGDB Row and populate it from an OGRFeature.               */
     101                 : /*                                                                      */
     102                 : /************************************************************************/
     103                 : 
     104              22 : OGRErr FGdbLayer::CreateFeature( OGRFeature *poFeature )
     105                 : {
     106              22 :     Table *fgdb_table = m_pTable;
     107              22 :     Row fgdb_row;
     108                 :     fgdbError hr;
     109              22 :     ShapeBuffer shape;
     110                 : 
     111              22 :     hr = fgdb_table->CreateRowObject(fgdb_row);
     112                 : 
     113                 :     /* Check the status of the Row create */
     114              22 :     if (FAILED(hr))
     115               0 :         return GDBErr(hr, "Failed at creating Row in CreateFeature.");
     116                 : 
     117              22 :     OGRFeatureDefn* poFeatureDefn = m_pFeatureDefn;
     118              22 :     int nFieldCount = poFeatureDefn->GetFieldCount();
     119                 : 
     120                 :     /* Copy the OGR visible fields (everything except geometry and FID) */
     121              22 :     for( int i = 0; i < nFieldCount; i++ )
     122                 :     {
     123              66 :         std::string field_name = poFeatureDefn->GetFieldDefn(i)->GetNameRef();
     124              66 :         std::wstring wfield_name = StringToWString(field_name);
     125                 : 
     126                 :         /* Set empty fields to NULL */
     127              66 :         if( !poFeature->IsFieldSet( i ) )
     128                 :         {
     129               0 :             if (FAILED(hr = fgdb_row.SetNull(wfield_name)))
     130               0 :                 return GDBErr(hr, "Failed setting field to NULL.");
     131               0 :             continue;
     132                 :         }
     133                 : 
     134                 :         /* Set the information using the appropriate FGDB function */
     135              66 :         int nOGRFieldType = poFeatureDefn->GetFieldDefn(i)->GetType();
     136                 : 
     137              66 :         if ( nOGRFieldType == OFTInteger )
     138                 :         {
     139                 :             /* Integers (we don't do FGDB Shorts) */
     140              12 :             int fldvalue = poFeature->GetFieldAsInteger(i);
     141              12 :             hr = fgdb_row.SetInteger(wfield_name, fldvalue);
     142                 :         }
     143              54 :         else if ( nOGRFieldType == OFTReal )
     144                 :         {
     145                 :             /* Doubles (we don't handle FGDB Floats) */
     146              32 :             double fldvalue = poFeature->GetFieldAsDouble(i);
     147              32 :             hr = fgdb_row.SetDouble(wfield_name, fldvalue);
     148                 :         }
     149              22 :         else if ( nOGRFieldType == OFTString )
     150                 :         {
     151                 :             /* Strings we convert to wstring */
     152              22 :             std::string fldvalue = poFeature->GetFieldAsString(i);
     153              22 :             std::wstring wfldvalue = StringToWString(fldvalue);
     154              22 :             hr = fgdb_row.SetString(wfield_name, wfldvalue);
     155                 :         }
     156               0 :         else if ( nOGRFieldType == OFTDateTime || nOGRFieldType == OFTDate )
     157                 :         {
     158                 :             /* Dates we need to coerce a little */
     159                 :             struct tm val;
     160                 :             poFeature->GetFieldAsDateTime(i, &(val.tm_year), &(val.tm_mon), &(val.tm_mday),
     161               0 :                                           &(val.tm_hour), &(val.tm_min), &(val.tm_sec), NULL);
     162               0 :             val.tm_year -= 1900;
     163               0 :             val.tm_mon = val.tm_mon - 1; /* OGR months go 1-12, FGDB go 0-11 */
     164               0 :             hr = fgdb_row.SetDate(wfield_name, val);
     165                 :         }
     166               0 :         else if ( nOGRFieldType == OFTBinary )
     167                 :         {
     168                 :             /* Binary data */
     169               0 :             ByteArray fgdb_bytearray;
     170                 :             int bytesize;
     171               0 :             GByte *bytes = poFeature->GetFieldAsBinary(i, &bytesize);
     172               0 :             if ( bytesize )
     173                 :             {
     174               0 :                 fgdb_bytearray.Allocate(bytesize);
     175               0 :                 memcpy(fgdb_bytearray.byteArray, bytes, bytesize);
     176               0 :                 fgdb_bytearray.inUseLength = bytesize;
     177               0 :                 hr = fgdb_row.SetBinary(wfield_name, fgdb_bytearray);
     178                 :             }
     179                 :             else
     180                 :             {
     181               0 :                 hr = fgdb_row.SetNull(wfield_name);
     182               0 :             }
     183                 :         }
     184                 :         else
     185                 :         {
     186                 :             /* We can't handle this type */
     187                 :             CPLError( CE_Failure, CPLE_AppDefined,
     188               0 :                 "FGDB driver does not support OGR type." );
     189               0 :             return OGRERR_FAILURE;
     190                 :         }
     191                 :     }
     192                 : 
     193                 :     /* Done with attribute fields, now do geometry */
     194              22 :     OGRGeometry *poGeom = poFeature->GetGeometryRef();
     195                 : 
     196                 :     /* Write geometry to a buffer */
     197              22 :     GByte *pabyShape = NULL;
     198              22 :     int nShapeSize = 0;
     199              22 :     OGRErr err = OGRWriteToShapeBin( poGeom, &pabyShape, &nShapeSize );
     200              22 :     if ( err != OGRERR_NONE )
     201               0 :         return err;
     202                 : 
     203                 :     /* Copy it into a ShapeBuffer */
     204              22 :     if ( nShapeSize > 0 )
     205                 :     {
     206              22 :         shape.Allocate(nShapeSize);
     207              22 :         memcpy(shape.shapeBuffer, pabyShape, nShapeSize);
     208              22 :         shape.inUseLength = nShapeSize;
     209                 :     }
     210                 : 
     211                 :     /* Free the shape buffer */
     212              22 :     CPLFree(pabyShape);
     213                 : 
     214                 :     /* Write ShapeBuffer into the Row */
     215              22 :     hr = fgdb_row.SetGeometry(shape);
     216              22 :     if (FAILED(hr))
     217               0 :         return GDBErr(hr, "Failed at writing Geometry to Row in CreateFeature.");
     218                 : 
     219                 :     /* Cannot write to FID field - it is managed by GDB*/
     220                 :     //std::wstring wfield_name = StringToWString(m_strOIDFieldName);
     221                 :     //hr = fgdb_row.SetInteger(wfield_name, poFeature->GetFID());
     222                 : 
     223                 :     /* Write the row to the table */
     224              22 :     hr = fgdb_table->Insert(fgdb_row);
     225              22 :     if (FAILED(hr))
     226               0 :         return GDBErr(hr, "Failed at writing Row to Table in CreateFeature.");
     227                 : 
     228              22 :     return OGRERR_NONE;
     229                 : 
     230                 : }
     231                 : 
     232                 : /************************************************************************/
     233                 : /*                            CreateField()                             */
     234                 : /*  Build up an FGDB XML field definition and use it to create a Field  */
     235                 : /*  Update the OGRFeatureDefn to reflect the new field.                 */
     236                 : /*                                                                      */
     237                 : /************************************************************************/
     238                 : 
     239              41 : OGRErr FGdbLayer::CreateField(OGRFieldDefn* poField, int bApproxOK)
     240                 : {
     241              41 :     OGRFieldDefn oField(poField);
     242              41 :     std::string fieldname = oField.GetNameRef();
     243              41 :     std::wstring wfieldname = StringToWString(fieldname);
     244              41 :     std::string fidname = std::string(GetFIDColumn());
     245              82 :     std::string nullable = "true";
     246              41 :     Table *fgdb_table = m_pTable;
     247                 : 
     248                 :     /* Try to map the OGR type to an ESRI type */
     249              82 :     OGRFieldType fldtype = oField.GetType();
     250              41 :     std::string gdbFieldType;
     251              41 :     if ( ! OGRToGDBFieldType(fldtype, &gdbFieldType) )
     252                 :     {
     253               0 :         GDBErr(-1, "Failed converting field type.");
     254               0 :         return OGRERR_FAILURE;
     255                 :     }
     256                 : 
     257                 :     /* If we don't have our FGDB Table pointer intialized, we can quit now. */
     258              41 :     if ( ! m_pTable )
     259                 :     {
     260               0 :         GDBErr(-1, "FGDB Table has not been initialized.");
     261               0 :         return OGRERR_FAILURE;
     262                 :     }
     263                 : 
     264                 :     /* Then the Field definition */
     265              41 :     CPLXMLNode *defn_xml = CPLCreateXMLNode(NULL, CXT_Element, "esri:Field");
     266                 : 
     267                 :     /* Add the XML attributes to the Field node */
     268              41 :     FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
     269              41 :     FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:xs", "http://www.w3.org/2001/XMLSchema");
     270              41 :     FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:esri", "http://www.esri.com/schemas/ArcGIS/10.1");
     271              41 :     FGDB_CPLAddXMLAttribute(defn_xml, "xsi:type", "esri:Field");
     272                 : 
     273                 :     /* Basic field information */
     274              41 :     CPLCreateXMLElementAndValue(defn_xml, "Name", fieldname.c_str());
     275              41 :     CPLCreateXMLElementAndValue(defn_xml, "Type", gdbFieldType.c_str());
     276              41 :     CPLCreateXMLElementAndValue(defn_xml, "IsNullable", nullable.c_str());
     277                 : 
     278                 :     /* Get the Width and Precision if we know them */
     279              41 :     int width = oField.GetWidth();
     280              41 :     int precision = oField.GetPrecision();
     281              41 :     if ( width <= 0 )
     282              38 :         GDBFieldTypeToWidthPrecision(gdbFieldType, &width, &precision);
     283                 : 
     284                 :     /* Write out the Width and Precision */
     285                 :     char buf[100];
     286              41 :     snprintf(buf, 100, "%d", width);
     287              41 :     CPLCreateXMLElementAndValue(defn_xml,"Length", buf);
     288              41 :     snprintf(buf, 100, "%d", precision);
     289              41 :     CPLCreateXMLElementAndValue(defn_xml,"Precision", buf);
     290                 : 
     291                 :     /* We know nothing about Scale, so zero it out */
     292              41 :     CPLCreateXMLElementAndValue(defn_xml,"Scale", "0");
     293                 : 
     294                 :     /* Default values are discouraged in OGR API docs */
     295                 :     /* <DefaultValue xsi:type="xs:string">afternoon</DefaultValue> */
     296                 : 
     297                 :     /* Convert our XML tree into a string for FGDB */
     298              41 :     char *defn_str = CPLSerializeXMLTree(defn_xml);
     299              41 :     CPLDebug("FGDB", "CreateField() generated XML for FGDB\n%s", defn_str);
     300                 : 
     301                 :     /* Add the FGDB Field to the FGDB Table. */
     302              41 :     fgdbError hr = fgdb_table->AddField(defn_str);
     303                 : 
     304                 :     /* Free the XML */
     305              41 :     CPLFree(defn_str);
     306              41 :     CPLDestroyXMLNode(defn_xml);
     307                 : 
     308                 :     /* Check the status of the Field add */
     309              41 :     if (FAILED(hr))
     310               0 :         return GDBErr(hr, "Failed at creating Field for " + fieldname);
     311                 : 
     312                 :     /* Now add the OGRFieldDefn to the OGRFeatureDefn */
     313              41 :     m_pFeatureDefn->AddFieldDefn(&oField);
     314                 : 
     315              41 :     m_vOGRFieldToESRIField.push_back(StringToWString(fieldname));
     316              41 :     m_vOGRFieldToESRIFieldType.push_back( gdbFieldType );
     317                 : 
     318                 :     /* All done and happy */
     319              41 :     return OGRERR_NONE;
     320                 : 
     321                 : }
     322                 : 
     323                 : /************************************************************************/
     324                 : /*                      XMLSpatialReference()                           */
     325                 : /*  Build up an XML representation of an OGRSpatialReference.           */
     326                 : /*  Used in layer creation.                                             */
     327                 : /*                                                                      */
     328                 : /************************************************************************/
     329                 : 
     330              35 : CPLXMLNode* XMLSpatialReference(OGRSpatialReference* poSRS, char** papszOptions)
     331                 : {
     332                 :     /* We always need a SpatialReference */
     333              35 :     CPLXMLNode *srs_xml = CPLCreateXMLNode(NULL, CXT_Element, "SpatialReference");
     334                 : 
     335                 :     /* Extract the WKID before morphing */
     336              35 :     char *wkid = NULL;
     337              35 :     if ( poSRS && poSRS->GetAuthorityCode(NULL) )
     338                 :     {
     339              33 :         wkid = CPLStrdup(poSRS->GetAuthorityCode(NULL));
     340                 :     }
     341                 : 
     342                 :     /* NULL poSRS => UnknownCoordinateSystem */
     343              35 :     if ( ! poSRS )
     344                 :     {
     345               2 :         FGDB_CPLAddXMLAttribute(srs_xml, "xsi:type", "esri:UnknownCoordinateSystem");
     346                 :     }
     347                 :     else
     348                 :     {
     349                 :         /* Make a clone so we can morph it without morphing the original */
     350              33 :         OGRSpatialReference* poSRSClone = poSRS->Clone();
     351                 : 
     352                 :         /* Flip the WKT to ESRI form, return UnknownCoordinateSystem if we can't */
     353              33 :         if ( poSRSClone->morphToESRI() != OGRERR_NONE )
     354                 :         {
     355               0 :             delete poSRSClone;
     356               0 :             FGDB_CPLAddXMLAttribute(srs_xml, "xsi:type", "esri:UnknownCoordinateSystem");
     357               0 :             return srs_xml;
     358                 :         }
     359                 : 
     360                 :         /* Set the SpatialReference type attribute correctly for GEOGCS/PROJCS */
     361              33 :         if ( poSRSClone->IsProjected() )
     362               0 :             FGDB_CPLAddXMLAttribute(srs_xml, "xsi:type", "esri:ProjectedCoordinateSystem");
     363                 :         else
     364              33 :             FGDB_CPLAddXMLAttribute(srs_xml, "xsi:type", "esri:GeographicCoordinateSystem");
     365                 : 
     366                 :         /* Add the WKT to the XML */
     367              33 :         char *wkt = NULL;
     368              33 :         poSRSClone->exportToWkt(&wkt);
     369              33 :         if (wkt)
     370                 :         {
     371              33 :             CPLCreateXMLElementAndValue(srs_xml,"WKT", wkt);
     372              33 :             OGRFree(wkt);
     373                 :         }
     374                 : 
     375                 :         /* Dispose of our close */
     376              33 :         delete poSRSClone;
     377                 :     }
     378                 :     
     379                 :     /* Handle Origin/Scale/Tolerance */
     380                 :     const char* grid[7] = {
     381                 :       "XOrigin", "YOrigin", "XYScale",
     382                 :       "ZOrigin", "ZScale",
     383              35 :       "XYTolerance", "ZTolerance" };
     384                 :     const char* gridvalues[7] = {
     385                 :       "-2147483647", "-2147483647", "1000000000",
     386                 :       "-2147483647", "1000000000",
     387              35 :       "0.0001", "0.0001" };
     388                 : 
     389                 :     /* Convert any layer creation options available, use defaults otherwise */
     390             280 :     for( int i = 0; i < 7; i++ )
     391                 :     {
     392             245 :         if ( CSLFetchNameValue( papszOptions, grid[i] ) != NULL )
     393               0 :             gridvalues[i] = CSLFetchNameValue( papszOptions, grid[i] );
     394                 : 
     395             245 :         CPLCreateXMLElementAndValue(srs_xml, grid[i], gridvalues[i]);
     396                 :     }
     397                 : 
     398                 :     /* FGDB is always High Precision */
     399              35 :     CPLCreateXMLElementAndValue(srs_xml, "HighPrecision", "true");     
     400                 : 
     401                 :     /* Add the WKID to the XML */
     402              35 :     if ( wkid ) 
     403                 :     {
     404              33 :         CPLCreateXMLElementAndValue(srs_xml, "WKID", wkid);
     405              33 :         CPLFree(wkid);
     406                 :     }
     407                 : 
     408              35 :     return srs_xml;
     409                 : }
     410                 : 
     411                 : /************************************************************************/
     412                 : /*                    CreateFeatureDataset()                            */
     413                 : /************************************************************************/
     414                 : 
     415               1 : bool FGdbLayer::CreateFeatureDataset(FGdbDataSource* pParentDataSource, 
     416                 :                                      std::string feature_dataset_name,
     417                 :                                      OGRSpatialReference* poSRS,
     418                 :                                      char** papszOptions )
     419                 : {
     420                 :     /* XML node */
     421               1 :     CPLXMLNode *xml_xml = CPLCreateXMLNode(NULL, CXT_Element, "?xml");
     422               1 :     FGDB_CPLAddXMLAttribute(xml_xml, "version", "1.0");
     423               1 :     FGDB_CPLAddXMLAttribute(xml_xml, "encoding", "UTF-8");
     424                 : 
     425                 :     /* First build up a bare-bones feature definition */
     426               1 :     CPLXMLNode *defn_xml = CPLCreateXMLNode(NULL, CXT_Element, "esri:DataElement");
     427               1 :     CPLAddXMLSibling(xml_xml, defn_xml);
     428                 : 
     429                 :     /* Add the attributes to the DataElement */
     430               1 :     FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
     431               1 :     FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:xs", "http://www.w3.org/2001/XMLSchema");
     432               1 :     FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:esri", "http://www.esri.com/schemas/ArcGIS/10.1");
     433                 : 
     434                 :     /* Need to set this to esri:DEFeatureDataset or esri:DETable */
     435               1 :     FGDB_CPLAddXMLAttribute(defn_xml, "xsi:type", "esri:DEFeatureDataset");
     436                 : 
     437                 :     /* Add in more children */
     438               1 :     std::string catalog_page = "\\" + feature_dataset_name;
     439               1 :     CPLCreateXMLElementAndValue(defn_xml,"CatalogPath", catalog_page.c_str());
     440               1 :     CPLCreateXMLElementAndValue(defn_xml,"Name", feature_dataset_name.c_str());
     441               1 :     CPLCreateXMLElementAndValue(defn_xml,"ChildrenExpanded", "false");
     442               1 :     CPLCreateXMLElementAndValue(defn_xml,"DatasetType", "esriDTFeatureDataset");
     443               1 :     CPLCreateXMLElementAndValue(defn_xml,"Versioned", "false");
     444               1 :     CPLCreateXMLElementAndValue(defn_xml,"CanVersion", "false");
     445                 : 
     446                 :     /* Add in empty extent */
     447               1 :     CPLXMLNode *extent_xml = CPLCreateXMLNode(NULL, CXT_Element, "Extent");
     448               1 :     FGDB_CPLAddXMLAttribute(extent_xml, "xsi:nil", "true");
     449               1 :     CPLAddXMLChild(defn_xml, extent_xml);
     450                 : 
     451                 :     /* Add the SRS */
     452                 :     if( TRUE ) // TODO: conditional on existence of SRS
     453                 :     {
     454               1 :         CPLXMLNode *srs_xml = XMLSpatialReference(poSRS, papszOptions);
     455               1 :         if ( srs_xml )
     456               1 :             CPLAddXMLChild(defn_xml, srs_xml);
     457                 :     }
     458                 : 
     459                 :     /* Convert our XML tree into a string for FGDB */
     460               1 :     char *defn_str = CPLSerializeXMLTree(xml_xml);
     461               1 :     CPLDestroyXMLNode(xml_xml);
     462                 : 
     463                 :     /* TODO, tie this to debugging levels */
     464               1 :     CPLDebug("FGDB", "%s", defn_str);
     465                 : 
     466                 :     /* Create the FeatureDataset. */
     467               1 :     Geodatabase *gdb = pParentDataSource->GetGDB();
     468               1 :     fgdbError hr = gdb->CreateFeatureDataset(defn_str);
     469                 : 
     470                 :     /* Free the XML */
     471               1 :     CPLFree(defn_str);
     472                 : 
     473                 :     /* Check table create status */
     474               1 :     if (FAILED(hr))
     475                 :     {
     476               0 :         return GDBErr(hr, "Failed at creating FeatureDataset " + feature_dataset_name);
     477                 :     }
     478                 : 
     479               1 :     return true;
     480                 : }
     481                 : 
     482                 : /************************************************************************/
     483                 : /*                            Create()                                  */
     484                 : /* Build up an FGDB XML layer definition and use it to create a Table   */
     485                 : /* or Feature Class to work from.                                       */
     486                 : /*                                                                      */
     487                 : /* Layer creation options:                                              */
     488                 : /*   FEATURE_DATASET, nest layer inside a FeatureDataset folder         */
     489                 : /*   GEOMETRY_NAME, user-selected name for the geometry column          */
     490                 : /*   OID_NAME, user-selected name for the FID column                    */
     491                 : /*   XORIGIN, YORIGIN, ZORIGIN, origin of the snapping grid             */
     492                 : /*   XYSCALE, ZSCALE, inverse resolution of the snapping grid           */
     493                 : /*   XYTOLERANCE, ZTOLERANCE, snapping tolerance for topology/networks  */
     494                 : /*                                                                      */
     495                 : /************************************************************************/
     496                 : 
     497              17 : bool FGdbLayer::Create(FGdbDataSource* pParentDataSource, 
     498                 :                        const char* pszLayerName, 
     499                 :                        OGRSpatialReference* poSRS, 
     500                 :                        OGRwkbGeometryType eType, 
     501                 :                        char** papszOptions)
     502                 : {
     503              17 :     std::string table_path = "\\" + std::string(pszLayerName);
     504              34 :     std::string parent_path = "";
     505              34 :     std::wstring wtable_path, wparent_path;
     506              17 :     std::string geometry_name = FGDB_GEOMETRY_NAME;
     507              34 :     std::string fid_name = FGDB_OID_NAME;
     508              17 :     std::string esri_type;
     509              17 :     bool has_z = false;
     510                 : 
     511                 :     /* Handle the FEATURE_DATASET case */
     512              17 :     if (  CSLFetchNameValue( papszOptions, "FEATURE_DATASET") != NULL )
     513                 :     {
     514               2 :         std::string feature_dataset = CSLFetchNameValue( papszOptions, "FEATURE_DATASET");
     515                 : 
     516                 :         /* Check if FEATURE_DATASET exists. Otherwise create it */
     517               2 :         std::vector<wstring> featuredatasets;
     518               2 :         Geodatabase *gdb = pParentDataSource->GetGDB();
     519               2 :         int bFeatureDataSetExists = FALSE;
     520                 :         fgdbError hr;
     521               2 :         if ( !FAILED(hr = gdb->GetChildDatasets(L"\\", L"Feature Dataset", featuredatasets)) )
     522                 :         {
     523               2 :             std::wstring feature_dataset_with_slash = L"\\" + StringToWString(feature_dataset);
     524               3 :             for ( unsigned int i = 0; i < featuredatasets.size(); i++ )
     525                 :             {
     526               1 :                 if (featuredatasets[i] == feature_dataset_with_slash)
     527               1 :                     bFeatureDataSetExists = TRUE;
     528               2 :             }
     529                 :         }
     530                 : 
     531               2 :         if (!bFeatureDataSetExists)
     532                 :         {
     533               1 :             bool rv = CreateFeatureDataset(pParentDataSource, feature_dataset, poSRS, papszOptions);
     534               1 :             if ( ! rv )
     535               0 :                 return rv;
     536                 :         }
     537                 : 
     538               2 :         table_path = "\\" + feature_dataset + table_path;
     539               2 :         parent_path = "\\" + feature_dataset;
     540                 :     }
     541                 : 
     542                 :     /* Convert table_path into wstring */
     543              17 :     wtable_path = StringToWString(table_path);
     544              17 :     wparent_path = StringToWString(parent_path);
     545                 : 
     546                 :     /* Over-ride the geometry name if necessary */
     547              17 :     if ( CSLFetchNameValue( papszOptions, "GEOMETRY_NAME") != NULL )
     548               0 :         geometry_name = CSLFetchNameValue( papszOptions, "GEOMETRY_NAME");
     549                 : 
     550                 :     /* Over-ride the OID name if necessary */
     551              17 :     if ( CSLFetchNameValue( papszOptions, "OID_NAME") != NULL )
     552               0 :         fid_name = CSLFetchNameValue( papszOptions, "OID_NAME");
     553                 : 
     554                 :     /* Figure out our geometry type */
     555              17 :     if ( eType != wkbNone )
     556                 :     {
     557              17 :         if ( wkbFlatten(eType) == wkbUnknown )
     558                 :         {
     559               0 :             return GDBErr(-1, "FGDB layers cannot be created with a wkbUnknown layer geometry type.");
     560                 :         }
     561              17 :         if ( ! OGRGeometryToGDB(eType, &esri_type, &has_z) )
     562               0 :             return GDBErr(-1, "Unable to map OGR type to ESRI type");
     563                 :     }
     564                 : 
     565                 :     /* XML node */
     566              17 :     CPLXMLNode *xml_xml = CPLCreateXMLNode(NULL, CXT_Element, "?xml");
     567              17 :     FGDB_CPLAddXMLAttribute(xml_xml, "version", "1.0");
     568              17 :     FGDB_CPLAddXMLAttribute(xml_xml, "encoding", "UTF-8");
     569                 : 
     570                 :     /* First build up a bare-bones feature definition */
     571              17 :     CPLXMLNode *defn_xml = CPLCreateXMLNode(NULL, CXT_Element, "esri:DataElement");
     572              17 :     CPLAddXMLSibling(xml_xml, defn_xml);
     573                 : 
     574                 :     /* Add the attributes to the DataElement */
     575              17 :     FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
     576              17 :     FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:xs", "http://www.w3.org/2001/XMLSchema");
     577              17 :     FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:esri", "http://www.esri.com/schemas/ArcGIS/10.1");
     578                 : 
     579                 :     /* Need to set this to esri:DEFeatureDataset or esri:DETable */
     580              17 :     FGDB_CPLAddXMLAttribute(defn_xml, "xsi:type", (eType == wkbNone ? "esri:DETable" : "esri:DEFeatureClass"));
     581                 : 
     582                 :     /* Add in more children */
     583              17 :     CPLCreateXMLElementAndValue(defn_xml,"CatalogPath",table_path.c_str());
     584              17 :     CPLCreateXMLElementAndValue(defn_xml,"Name", pszLayerName);
     585              17 :     CPLCreateXMLElementAndValue(defn_xml,"ChildrenExpanded", "false");
     586                 : 
     587                 :     /* WKB type of none implies this is a 'Table' otherwise it's a 'Feature Class' */
     588              17 :     std::string datasettype = (eType == wkbNone ? "esriDTTable" : "esriDTFeatureClass");
     589              34 :     CPLCreateXMLElementAndValue(defn_xml,"DatasetType", datasettype.c_str() );
     590              17 :     CPLCreateXMLElementAndValue(defn_xml,"Versioned", "false");
     591              17 :     CPLCreateXMLElementAndValue(defn_xml,"CanVersion", "false");
     592                 : 
     593                 :     /* We might need to make OID optional later, but OGR likes to have a FID */
     594              17 :     CPLCreateXMLElementAndValue(defn_xml,"HasOID", "true");
     595              17 :     CPLCreateXMLElementAndValue(defn_xml,"OIDFieldName", fid_name.c_str());
     596                 : 
     597                 :     /* Add in empty Fields */
     598              17 :     CPLXMLNode *fields_xml = CPLCreateXMLNode(defn_xml, CXT_Element, "Fields");
     599              17 :     FGDB_CPLAddXMLAttribute(fields_xml, "xsi:type", "esri:Fields");
     600              17 :     CPLXMLNode *fieldarray_xml = CPLCreateXMLNode(fields_xml, CXT_Element, "FieldArray");
     601              17 :     FGDB_CPLAddXMLAttribute(fieldarray_xml, "xsi:type", "esri:ArrayOfField");
     602                 : 
     603                 :     /* Feature Classes have an implicit geometry column, so we'll add it at creation time */
     604              17 :     if ( eType != wkbNone )
     605                 :     {
     606              17 :         CPLXMLNode *shape_xml = CPLCreateXMLNode(fieldarray_xml, CXT_Element, "Field");
     607              17 :         FGDB_CPLAddXMLAttribute(shape_xml, "xsi:type", "esri:Field");
     608              17 :         CPLCreateXMLElementAndValue(shape_xml, "Name", geometry_name.c_str());
     609              17 :         CPLCreateXMLElementAndValue(shape_xml, "Type", "esriFieldTypeGeometry");
     610              17 :         CPLCreateXMLElementAndValue(shape_xml, "IsNullable", "false");
     611              17 :         CPLCreateXMLElementAndValue(shape_xml, "Length", "0");
     612              17 :         CPLCreateXMLElementAndValue(shape_xml, "Precision", "0");
     613              17 :         CPLCreateXMLElementAndValue(shape_xml, "Scale", "0");
     614              17 :         CPLCreateXMLElementAndValue(shape_xml, "Required", "true");
     615              17 :         CPLXMLNode *geom_xml = CPLCreateXMLNode(shape_xml, CXT_Element, "GeometryDef");
     616              17 :         FGDB_CPLAddXMLAttribute(geom_xml, "xsi:type", "esri:GeometryDef");
     617              17 :         CPLCreateXMLElementAndValue(geom_xml, "AvgNumPoints", "0");
     618              17 :         CPLCreateXMLElementAndValue(geom_xml, "GeometryType", esri_type.c_str());
     619              17 :         CPLCreateXMLElementAndValue(geom_xml,"HasM", "false");
     620              17 :         CPLCreateXMLElementAndValue(geom_xml,"HasZ", (has_z ? "true" : "false"));
     621                 : 
     622                 :         /* Add the SRS if we have one */
     623              17 :         CPLXMLNode *srs_xml = XMLSpatialReference(poSRS, papszOptions);
     624              17 :         if ( srs_xml )
     625              17 :             CPLAddXMLChild(geom_xml, srs_xml);
     626                 :     }
     627                 : 
     628                 :     /* All (?) Tables and Feature Classes will have an ObjectID */
     629              17 :     CPLXMLNode *oid_xml = CPLCreateXMLNode(fieldarray_xml, CXT_Element, "Field");
     630              17 :     FGDB_CPLAddXMLAttribute(oid_xml, "xsi:type", "esri:Field");
     631              17 :     CPLCreateXMLElementAndValue(oid_xml, "Name", fid_name.c_str());
     632              17 :     CPLCreateXMLElementAndValue(oid_xml, "Type", "esriFieldTypeOID");
     633              17 :     CPLCreateXMLElementAndValue(oid_xml, "IsNullable", "false");
     634              17 :     CPLCreateXMLElementAndValue(oid_xml, "Length", "12");
     635              17 :     CPLCreateXMLElementAndValue(oid_xml, "Precision", "0");
     636              17 :     CPLCreateXMLElementAndValue(oid_xml, "Scale", "0");
     637              17 :     CPLCreateXMLElementAndValue(oid_xml, "Required", "true");
     638                 : 
     639                 :     /* Add in empty Indexes */
     640              17 :     CPLXMLNode *indexes_xml = CPLCreateXMLNode(defn_xml, CXT_Element, "Indexes");
     641              17 :     FGDB_CPLAddXMLAttribute(indexes_xml, "xsi:type", "esri:Indexes");
     642              17 :     CPLXMLNode *indexarray_xml = CPLCreateXMLNode(indexes_xml, CXT_Element, "IndexArray");
     643              17 :     FGDB_CPLAddXMLAttribute(indexarray_xml, "xsi:type", "esri:ArrayOfIndex");
     644                 : 
     645                 :     /* Map from OGR WKB type to ESRI type */
     646              17 :     if ( eType != wkbNone )
     647                 :     {
     648                 :         /* Declare our feature type */
     649              17 :         CPLCreateXMLElementAndValue(defn_xml,"FeatureType", "esriFTSimple");
     650              17 :         CPLCreateXMLElementAndValue(defn_xml,"ShapeType", esri_type.c_str());
     651              17 :         CPLCreateXMLElementAndValue(defn_xml,"ShapeFieldName", geometry_name.c_str());
     652                 : 
     653                 :         /* Dimensionality */
     654              17 :         CPLCreateXMLElementAndValue(defn_xml,"HasM", "false");
     655              17 :         CPLCreateXMLElementAndValue(defn_xml,"HasZ", (has_z ? "true" : "false"));
     656                 : 
     657                 :         /* TODO: Handle spatial indexes (layer creation option?) */
     658              17 :         CPLCreateXMLElementAndValue(defn_xml,"HasSpatialIndex", "false");
     659                 : 
     660                 :         /* We can't know the extent at this point <Extent xsi:nil='true'/> */
     661              17 :         CPLXMLNode *extn_xml = CPLCreateXMLNode(defn_xml, CXT_Element, "Extent");
     662              17 :         FGDB_CPLAddXMLAttribute(extn_xml, "xsi:nil", "true");
     663                 :     }
     664                 : 
     665                 :     /* Feature Class with known SRS gets an SRS entry */
     666              17 :     if( eType != wkbNone )
     667                 :     {
     668              17 :         CPLXMLNode *srs_xml = XMLSpatialReference(poSRS, papszOptions);
     669              17 :         if ( srs_xml )
     670              17 :             CPLAddXMLChild(defn_xml, srs_xml);
     671                 :     }
     672                 : 
     673                 :     /* Convert our XML tree into a string for FGDB */
     674              17 :     char *defn_str = CPLSerializeXMLTree(xml_xml);
     675              17 :     CPLDestroyXMLNode(xml_xml);
     676                 : 
     677                 :     /* TODO, tie this to debugging levels */
     678              17 :     CPLDebug("FGDB", "%s", defn_str);
     679                 :     //std::cout << defn_str << std::endl;
     680                 : 
     681                 :     /* Create the table. */
     682              17 :     Table *table = new Table;
     683              34 :     Geodatabase *gdb = pParentDataSource->GetGDB();
     684              17 :     fgdbError hr = gdb->CreateTable(defn_str, wparent_path, *table);
     685                 : 
     686                 :     /* Free the XML */
     687              17 :     CPLFree(defn_str);
     688                 : 
     689                 :     /* Check table create status */
     690              17 :     if (FAILED(hr))
     691                 :     {
     692               0 :         delete table;
     693               0 :         return GDBErr(hr, "Failed at creating table for " + table_path);
     694                 :     }
     695                 : 
     696                 :     /* Store the new FGDB Table pointer and set up the OGRFeatureDefn */
     697              17 :     return FGdbLayer::Initialize(pParentDataSource, table, wtable_path, L"Table");
     698                 : }
     699                 : 
     700                 : /*************************************************************************/
     701                 : /*                            Initialize()                               */
     702                 : /* Has ownership of the table as soon as it is called.                   */
     703                 : /************************************************************************/
     704                 : 
     705              81 : bool FGdbLayer::Initialize(FGdbDataSource* pParentDataSource, Table* pTable,
     706                 :                            std::wstring wstrTablePath, std::wstring wstrType)
     707                 : {
     708                 :     long hr;
     709                 : 
     710              81 :     m_pDS = pParentDataSource; // we never assume ownership of the parent - so our destructor should not delete
     711                 : 
     712              81 :     m_pTable = pTable;
     713                 : 
     714              81 :     m_wstrTablePath = wstrTablePath;
     715              81 :     m_wstrType = wstrType;
     716                 : 
     717              81 :     wstring wstrQueryName;
     718              81 :     if (FAILED(hr = pParentDataSource->GetGDB()->GetQueryName(wstrTablePath, wstrQueryName)))
     719                 :         return GDBErr(hr, "Failed at getting underlying table name for " +
     720               0 :                       WStringToString(wstrTablePath));
     721                 : 
     722              81 :     m_strName = WStringToString(wstrQueryName);
     723                 : 
     724              81 :     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
     725                 :     //as long as we use the same compiler & settings in both the ogr build and this
     726                 :     //driver, we should be OK
     727              81 :     m_pFeatureDefn->Reference();
     728                 : 
     729              81 :     string tableDef;
     730              81 :     if (FAILED(hr = m_pTable->GetDefinition(tableDef)))
     731                 :         return GDBErr(hr, "Failed at getting table definition for " +
     732               0 :                       WStringToString(wstrTablePath));
     733                 : 
     734                 :     //xxx  printf("Table definition = %s", tableDef.c_str() );
     735                 : 
     736              81 :     bool abort = false;
     737                 : 
     738                 :     // extract schema information from table
     739              81 :     CPLXMLNode *psRoot = CPLParseXMLString( tableDef.c_str() );
     740                 : 
     741              81 :     if (psRoot == NULL)
     742                 :     {
     743                 :         CPLError( CE_Failure, CPLE_AppDefined, "%s",
     744               0 :                   ("Failed parsing GDB Table Schema XML for " + m_strName).c_str());
     745               0 :         return false;
     746                 :     }
     747                 : 
     748              81 :     CPLXMLNode *pDataElementNode = psRoot->psNext; // Move to next field which should be DataElement
     749                 : 
     750              81 :     if( pDataElementNode != NULL
     751                 :         && pDataElementNode->psChild != NULL
     752                 :         && pDataElementNode->eType == CXT_Element
     753                 :         && EQUAL(pDataElementNode->pszValue,"esri:DataElement") )
     754                 :     {
     755                 :         CPLXMLNode *psNode;
     756                 : 
     757            1944 :         for( psNode = pDataElementNode->psChild;
     758                 :         psNode != NULL;
     759                 :         psNode = psNode->psNext )
     760                 :         {
     761            1863 :             if( psNode->eType == CXT_Element && psNode->psChild != NULL )
     762                 :             {
     763            1539 :                 if (EQUAL(psNode->pszValue,"OIDFieldName") )
     764                 :                 {
     765                 :                     char* pszUnescaped = CPLUnescapeString(
     766              81 :                     psNode->psChild->pszValue, NULL, CPLES_XML);
     767              81 :                     m_strOIDFieldName = pszUnescaped;
     768              81 :                     CPLFree(pszUnescaped);
     769                 :                 }
     770            1458 :                 else if (EQUAL(psNode->pszValue,"ShapeFieldName") )
     771                 :                 {
     772                 :                     char* pszUnescaped = CPLUnescapeString(
     773              81 :                     psNode->psChild->pszValue, NULL, CPLES_XML);
     774              81 :                     m_strShapeFieldName = pszUnescaped;
     775              81 :                     CPLFree(pszUnescaped);
     776                 :                 }
     777            1377 :                 else if (EQUAL(psNode->pszValue,"Fields") )
     778                 :                 {
     779              81 :                     if (!GDBToOGRFields(psNode))
     780                 :                     {
     781               0 :                         abort = true;
     782               0 :                         break;
     783                 :                     }
     784                 :                 }
     785                 :             }
     786                 :         }
     787                 : 
     788              81 :         if (m_strShapeFieldName.size() == 0)
     789               0 :             m_pFeatureDefn->SetGeomType(wkbNone);
     790                 :     }
     791                 :     else
     792                 :     {
     793                 :         CPLError( CE_Failure, CPLE_AppDefined, "%s",
     794               0 :                 ("Failed parsing GDB Table Schema XML (DataElement) for " + m_strName).c_str());
     795               0 :         return false;
     796                 :     }
     797              81 :     CPLDestroyXMLNode( psRoot );
     798                 : 
     799              81 :     if (abort)
     800               0 :         return false;
     801                 : 
     802              81 :     return true; //AOToOGRFields(ipFields, m_pFeatureDefn, m_vOGRFieldToESRIField);
     803                 : }
     804                 : 
     805                 : /************************************************************************/
     806                 : /*                          ParseGeometryDef()                          */
     807                 : /************************************************************************/
     808                 : 
     809              81 : bool FGdbLayer::ParseGeometryDef(CPLXMLNode* psRoot)
     810                 : {
     811                 :     CPLXMLNode *psGeometryDefItem;
     812                 : 
     813              81 :     string geometryType;
     814              81 :     bool hasZ = false;
     815              81 :     string wkt, wkid;
     816                 : 
     817             648 :     for (psGeometryDefItem = psRoot->psChild;
     818                 :         psGeometryDefItem != NULL;
     819                 :         psGeometryDefItem = psGeometryDefItem->psNext )
     820                 :     {
     821                 :         //loop through all "GeometryDef" elements
     822                 :         //
     823                 : 
     824             567 :         if (psGeometryDefItem->eType == CXT_Element &&
     825                 :             psGeometryDefItem->psChild != NULL)
     826                 :         {
     827             486 :             if (EQUAL(psGeometryDefItem->pszValue,"GeometryType"))
     828                 :             {
     829                 :                 char* pszUnescaped = CPLUnescapeString(
     830              81 :                     psGeometryDefItem->psChild->pszValue, NULL, CPLES_XML);
     831                 : 
     832              81 :                 geometryType = pszUnescaped;
     833                 : 
     834              81 :                 CPLFree(pszUnescaped);
     835                 :             }
     836             405 :             else if (EQUAL(psGeometryDefItem->pszValue,"SpatialReference"))
     837                 :             {
     838              81 :                 ParseSpatialReference(psGeometryDefItem, &wkt, &wkid); // we don't check for success because it
     839                 :                                                                 // may not be there
     840                 :             }
     841                 :             /* No M support in OGR yet
     842                 :             else if (EQUAL(psFieldNode->pszValue,"HasM")
     843                 :             {
     844                 :                 char* pszUnescaped = CPLUnescapeString(psNode->psChild->pszValue, NULL, CPLES_XML);
     845                 : 
     846                 :                 if (!strcmp(szUnescaped, "true"))
     847                 :                 hasM = true;
     848                 : 
     849                 :                 CPLFree(pszUnescaped);
     850                 :             }
     851                 :             */
     852             324 :             else if (EQUAL(psGeometryDefItem->pszValue,"HasZ"))
     853                 :             {
     854                 :                 char* pszUnescaped = CPLUnescapeString(
     855              81 :                     psGeometryDefItem->psChild->pszValue, NULL, CPLES_XML);
     856                 : 
     857              81 :                 if (!strcmp(pszUnescaped, "true"))
     858              36 :                 hasZ = true;
     859                 : 
     860              81 :                 CPLFree(pszUnescaped);
     861                 :             }
     862                 :         }
     863                 : 
     864                 :     }
     865                 : 
     866                 :     OGRwkbGeometryType ogrGeoType;
     867              81 :     if (!GDBToOGRGeometry(geometryType, hasZ, &ogrGeoType))
     868               0 :         return false;
     869                 : 
     870              81 :     m_pFeatureDefn->SetGeomType(ogrGeoType);
     871                 : 
     872              81 :     if (wkbFlatten(ogrGeoType) == wkbMultiLineString ||
     873                 :         wkbFlatten(ogrGeoType) == wkbMultiPoint)
     874              36 :         m_forceMulti = true;
     875                 : 
     876              81 :     if (wkid.length() > 0)
     877                 :     {
     878              79 :         m_pSRS = new OGRSpatialReference();
     879             158 :         if (m_pSRS->importFromEPSG(atoi(wkid.c_str())) != OGRERR_NONE)
     880                 :         {
     881               0 :             delete m_pSRS;
     882               0 :             m_pSRS = NULL;
     883                 :         }
     884                 :         else
     885              79 :             return true;
     886                 :     }
     887                 : 
     888               2 :     if (wkt.length() > 0)
     889                 :     {
     890               0 :         if (!GDBToOGRSpatialReference(wkt, &m_pSRS))
     891                 :         {
     892                 :             //report error, but be passive about it
     893                 :             CPLError( CE_Warning, CPLE_AppDefined,
     894               0 :                       "Failed Mapping ESRI Spatial Reference");
     895                 :         }
     896                 :     }
     897                 :     else
     898                 :     {
     899                 :         //report error, but be passive about it
     900               2 :         CPLError( CE_Warning, CPLE_AppDefined, "Empty Spatial Reference");
     901                 :     }
     902                 : 
     903               2 :     return true;
     904                 : }
     905                 : 
     906                 : /************************************************************************/
     907                 : /*                        ParseSpatialReference()                       */
     908                 : /************************************************************************/
     909                 : 
     910              81 : bool FGdbLayer::ParseSpatialReference(CPLXMLNode* psSpatialRefNode,
     911                 :                                       string* pOutWkt, string* pOutWKID)
     912                 : {
     913              81 :     *pOutWkt = "";
     914              81 :     *pOutWKID = "";
     915                 : 
     916                 :     CPLXMLNode* psSRItemNode;
     917                 : 
     918                 :     /* Loop through all the SRS elements we want to store */
     919             968 :     for( psSRItemNode = psSpatialRefNode->psChild;
     920                 :          psSRItemNode != NULL;
     921                 :          psSRItemNode = psSRItemNode->psNext )
     922                 :     {
     923                 :         /* The WKID maps (mostly) to an EPSG code */
     924             966 :         if( psSRItemNode->eType == CXT_Element &&
     925                 :             psSRItemNode->psChild != NULL &&
     926                 :             EQUAL(psSRItemNode->pszValue,"WKID") )
     927                 :         {
     928              79 :             char* pszUnescaped = CPLUnescapeString(psSRItemNode->psChild->pszValue, NULL, CPLES_XML);
     929              79 :             *pOutWKID = pszUnescaped;
     930              79 :             CPLFree(pszUnescaped);
     931                 :         }
     932                 :         /* The WKT well-known text can be converted by OGR */
     933             808 :         else if( psSRItemNode->eType == CXT_Element &&
     934                 :                 psSRItemNode->psChild != NULL &&
     935                 :                 EQUAL(psSRItemNode->pszValue,"WKT") )
     936                 :         {
     937              79 :             char* pszUnescaped = CPLUnescapeString(psSRItemNode->psChild->pszValue, NULL, CPLES_XML);
     938              79 :             *pOutWkt = pszUnescaped;
     939              79 :             CPLFree(pszUnescaped);
     940                 :         }
     941                 : 
     942                 :     }
     943              81 :     return (*pOutWkt != "" || *pOutWKID != "");
     944                 : }
     945                 : 
     946                 : /************************************************************************/
     947                 : /*                          GDBToOGRFields()                           */
     948                 : /************************************************************************/
     949                 : 
     950              81 : bool FGdbLayer::GDBToOGRFields(CPLXMLNode* psRoot)
     951                 : {
     952              81 :     m_vOGRFieldToESRIField.clear();
     953                 : 
     954              81 :     if (psRoot->psChild == NULL || psRoot->psChild->psNext == NULL)
     955                 :     {
     956               0 :         CPLError( CE_Failure, CPLE_AppDefined, "Unrecognized GDB XML Schema");
     957                 : 
     958               0 :         return false;
     959                 :     }
     960                 : 
     961              81 :     psRoot = psRoot->psChild->psNext; //change root to "FieldArray"
     962                 : 
     963                 :     //CPLAssert(ogrToESRIFieldMapping.size() == pOGRFeatureDef->GetFieldCount());
     964                 : 
     965                 :     CPLXMLNode* psFieldNode;
     966                 : 
     967             508 :     for( psFieldNode = psRoot->psChild;
     968                 :         psFieldNode != NULL;
     969                 :         psFieldNode = psFieldNode->psNext )
     970                 :     {
     971                 :         //loop through all "Field" elements
     972                 :         //
     973                 : 
     974             427 :         if( psFieldNode->eType == CXT_Element && psFieldNode->psChild != NULL &&
     975                 :             EQUAL(psFieldNode->pszValue,"Field"))
     976                 :         {
     977                 : 
     978                 :             CPLXMLNode* psFieldItemNode;
     979             346 :             std::string fieldName;
     980             346 :             std::string fieldType;
     981             346 :             int nLength = 0;
     982             346 :             int nPrecision = 0;
     983                 : 
     984                 :             // loop through all items in Field element
     985                 :             //
     986                 : 
     987            3784 :             for( psFieldItemNode = psFieldNode->psChild;
     988                 :                 psFieldItemNode != NULL;
     989                 :                 psFieldItemNode = psFieldItemNode->psNext )
     990                 :             {
     991            3438 :                 if (psFieldItemNode->eType == CXT_Element)
     992                 :                 {
     993                 : 
     994            3092 :                 if (EQUAL(psFieldItemNode->pszValue,"Name"))
     995                 :                 {
     996                 :                     char* pszUnescaped = CPLUnescapeString(
     997             346 :                         psFieldItemNode->psChild->pszValue, NULL, CPLES_XML);
     998             346 :                     fieldName = pszUnescaped;
     999             346 :                     CPLFree(pszUnescaped);
    1000                 :                 }
    1001            2746 :                 else if (EQUAL(psFieldItemNode->pszValue,"Type") )
    1002                 :                 {
    1003                 :                     char* pszUnescaped = CPLUnescapeString(
    1004             346 :                         psFieldItemNode->psChild->pszValue, NULL, CPLES_XML);
    1005             346 :                     fieldType = pszUnescaped;
    1006             346 :                     CPLFree(pszUnescaped);
    1007                 :                 }
    1008            2400 :                 else if (EQUAL(psFieldItemNode->pszValue,"GeometryDef") )
    1009                 :                 {
    1010              81 :                     if (!ParseGeometryDef(psFieldItemNode))
    1011               0 :                         return false; // if we failed parsing the GeometryDef, we are done!
    1012                 :                 }
    1013            2319 :                 else if (EQUAL(psFieldItemNode->pszValue,"Length") )
    1014                 :                 {
    1015             346 :                     nLength = atoi(psFieldItemNode->psChild->pszValue);
    1016                 :                 }
    1017            1973 :                 else if (EQUAL(psFieldItemNode->pszValue,"Precision") )
    1018                 :                 {
    1019             346 :                     nPrecision = atoi(psFieldItemNode->psChild->pszValue);
    1020                 :                 }
    1021                 :                 }
    1022                 :             }
    1023                 : 
    1024                 : 
    1025                 :             ///////////////////////////////////////////////////////////////////
    1026                 :             // At this point we have parsed everything about the current field
    1027                 : 
    1028                 : 
    1029             346 :             if (fieldType == "esriFieldTypeGeometry")
    1030                 :             {
    1031              81 :                 m_strShapeFieldName = fieldName;
    1032                 : 
    1033              81 :                 continue; // finish here for special field - don't add as OGR fielddef
    1034                 :             }
    1035             265 :             else if (fieldType == "esriFieldTypeOID")
    1036                 :             {
    1037                 :                 //m_strOIDFieldName = fieldName; // already set by this point
    1038                 : 
    1039              81 :                 continue; // finish here for special field - don't add as OGR fielddef
    1040                 :             }
    1041                 : 
    1042                 :             OGRFieldType ogrType;
    1043                 :             //CPLDebug("FGDB", "name = %s, type = %s", fieldName.c_str(), fieldType.c_str() );
    1044             184 :             if (!GDBToOGRFieldType(fieldType, &ogrType))
    1045                 :             {
    1046                 :                 // field cannot be mapped, skipping further processing
    1047                 :                 CPLError( CE_Warning, CPLE_AppDefined, "Skipping field: [%s] type: [%s] ",
    1048               0 :                 fieldName.c_str(), fieldType.c_str() );
    1049               0 :                 continue;
    1050                 :             }
    1051                 : 
    1052                 : 
    1053                 :             //TODO: Optimization - modify m_wstrSubFields so it only fetches fields that are mapped
    1054                 : 
    1055             184 :             OGRFieldDefn fieldTemplate( fieldName.c_str(), ogrType);
    1056                 :             //fieldTemplate.SetWidth(nLength);
    1057                 :             //fieldTemplate.SetPrecision(nPrecision);
    1058             184 :             m_pFeatureDefn->AddFieldDefn( &fieldTemplate );
    1059                 : 
    1060             184 :             m_vOGRFieldToESRIField.push_back(StringToWString(fieldName));
    1061             184 :             m_vOGRFieldToESRIFieldType.push_back( fieldType );
    1062                 : 
    1063                 :         }
    1064                 :     }
    1065                 : 
    1066              81 :     return true;
    1067                 : }
    1068                 : 
    1069                 : 
    1070                 : /************************************************************************/
    1071                 : /*                            ResetReading()                            */
    1072                 : /************************************************************************/
    1073                 : 
    1074             273 : void FGdbLayer::ResetReading()
    1075                 : {
    1076                 :     long hr;
    1077                 : 
    1078             273 :     if (m_pOGRFilterGeometry && !m_pOGRFilterGeometry->IsEmpty())
    1079                 :     {
    1080                 :         // Search spatial
    1081                 :         // As of beta1, FileGDB only supports bbox searched, if we have GEOS installed,
    1082                 :         // we can do the rest ourselves.
    1083                 : 
    1084              78 :         OGREnvelope ogrEnv;
    1085                 : 
    1086              78 :         m_pOGRFilterGeometry->getEnvelope(&ogrEnv);
    1087                 : 
    1088                 :         //spatial query
    1089              78 :         FileGDBAPI::Envelope env(ogrEnv.MinX, ogrEnv.MaxX, ogrEnv.MinY, ogrEnv.MaxY);
    1090                 : 
    1091              78 :         if FAILED(hr = m_pTable->Search(m_wstrSubfields, m_wstrWhereClause, env, true, *m_pEnumRows))
    1092               0 :         GDBErr(hr, "Failed Searching");
    1093                 : 
    1094              78 :         m_bFilterDirty = false;
    1095                 : 
    1096              78 :         return;
    1097                 :     }
    1098                 : 
    1099                 :     // Search non-spatial
    1100                 : 
    1101             195 :     if FAILED(hr = m_pTable->Search(m_wstrSubfields, m_wstrWhereClause, true, *m_pEnumRows))
    1102               0 :         GDBErr(hr, "Failed Searching");
    1103                 : 
    1104             195 :     m_bFilterDirty = false;
    1105                 :   
    1106                 : }
    1107                 : 
    1108                 : /************************************************************************/
    1109                 : /*                         SetSpatialFilter()                           */
    1110                 : /************************************************************************/
    1111                 : 
    1112             104 : void FGdbLayer::SetSpatialFilter( OGRGeometry* pOGRGeom )
    1113                 : {
    1114             104 :     if (m_pOGRFilterGeometry)
    1115                 :     {
    1116              26 :         OGRGeometryFactory::destroyGeometry(m_pOGRFilterGeometry);
    1117              26 :         m_pOGRFilterGeometry = NULL;
    1118                 :     }
    1119                 : 
    1120             104 :     if (pOGRGeom == NULL || pOGRGeom->IsEmpty())
    1121                 :     {
    1122              78 :         m_bFilterDirty = true;
    1123                 : 
    1124              78 :         return;
    1125                 :     }
    1126                 : 
    1127              26 :     m_pOGRFilterGeometry = pOGRGeom->clone();
    1128                 : 
    1129              26 :     m_pOGRFilterGeometry->transformTo(m_pSRS);
    1130                 : 
    1131              26 :     m_bFilterDirty = true;
    1132                 : }
    1133                 : 
    1134                 : /************************************************************************/
    1135                 : /*                         SetSpatialFilterRect()                       */
    1136                 : /************************************************************************/
    1137                 : 
    1138               0 : void FGdbLayer::SetSpatialFilterRect (double dfMinX, double dfMinY, double dfMaxX, double dfMaxY)
    1139                 : {
    1140                 : 
    1141                 :     //TODO: can optimize this by changing how the filter gets generated -
    1142                 :     //this will work for now
    1143                 : 
    1144               0 :     OGRGeometry* pTemp = OGRGeometryFactory::createGeometry(wkbPolygon);
    1145                 : 
    1146               0 :     pTemp->assignSpatialReference(m_pSRS);
    1147                 : 
    1148               0 :     OGRLinearRing ring;
    1149                 : 
    1150               0 :     ring.addPoint( dfMinX, dfMinY );
    1151               0 :     ring.addPoint( dfMinX, dfMaxY );
    1152               0 :     ring.addPoint( dfMaxX, dfMaxY );
    1153               0 :     ring.addPoint( dfMaxX, dfMinY );
    1154               0 :     ring.addPoint( dfMinX, dfMinY );
    1155               0 :     ((OGRPolygon *) pTemp)->addRing( &ring );
    1156                 : 
    1157               0 :     SetSpatialFilter(pTemp);
    1158                 : 
    1159               0 :     OGRGeometryFactory::destroyGeometry(pTemp);
    1160               0 : }
    1161                 : 
    1162                 : 
    1163                 : /************************************************************************/
    1164                 : /*                         SetAttributeFilter()                         */
    1165                 : /************************************************************************/
    1166                 : 
    1167              91 : OGRErr FGdbLayer::SetAttributeFilter( const char* pszQuery )
    1168                 : {
    1169              91 :     m_wstrWhereClause = StringToWString( (pszQuery != NULL) ? pszQuery : "" );
    1170                 : 
    1171              91 :     m_bFilterDirty = true;
    1172                 : 
    1173              91 :     return OGRERR_NONE;
    1174                 : }
    1175                 : 
    1176                 : /************************************************************************/
    1177                 : /*                           OGRFeatureFromGdbRow()                      */
    1178                 : /************************************************************************/
    1179                 : 
    1180             192 : bool FGdbLayer::OGRFeatureFromGdbRow(Row* pRow, OGRFeature** ppFeature)
    1181                 : {
    1182                 :     long hr;
    1183                 : 
    1184             192 :     OGRFeature* pOutFeature = new OGRFeature(m_pFeatureDefn);
    1185                 : 
    1186                 :     /////////////////////////////////////////////////////////
    1187                 :     // Translate OID
    1188                 :     //
    1189                 : 
    1190             192 :     int32 oid = -1;
    1191             192 :     if (FAILED(hr = pRow->GetOID(oid)))
    1192                 :     {
    1193                 :         //this should never happen
    1194               0 :         delete pOutFeature;
    1195               0 :         return false;
    1196                 :     }
    1197             192 :     pOutFeature->SetFID(oid);
    1198                 : 
    1199                 : 
    1200                 :     /////////////////////////////////////////////////////////
    1201                 :     // Translate Geometry
    1202                 :     //
    1203                 : 
    1204             192 :     ShapeBuffer gdbGeometry;
    1205             192 :     if (!FAILED(hr = pRow->GetGeometry(gdbGeometry)))
    1206                 :     {
    1207             192 :         OGRGeometry* pOGRGeo = NULL;
    1208                 : 
    1209             192 :         if ((!GDBGeometryToOGRGeometry(m_forceMulti, &gdbGeometry, m_pSRS, &pOGRGeo)) || pOGRGeo == NULL)
    1210                 :         {
    1211               0 :             delete pOutFeature;
    1212               0 :             return GDBErr(hr, "Failed to translate FileGDB Geometry to OGR Geometry for row " + string(CPLSPrintf("%d", (int)oid)));
    1213                 :         }
    1214                 : 
    1215             192 :         pOutFeature->SetGeometryDirectly(pOGRGeo);
    1216                 :     }
    1217                 : 
    1218                 : 
    1219                 :     //////////////////////////////////////////////////////////
    1220                 :     // Map fields
    1221                 :     //
    1222                 : 
    1223                 : 
    1224             192 :     size_t mappedFieldCount = m_vOGRFieldToESRIField.size();
    1225                 : 
    1226             192 :     bool foundBadColumn = false;
    1227                 : 
    1228             768 :     for (size_t i = 0; i < mappedFieldCount; ++i)
    1229                 :     {
    1230             576 :         const wstring & wstrFieldName = m_vOGRFieldToESRIField[i];
    1231             576 :         const std::string & strFieldType = m_vOGRFieldToESRIFieldType[i];
    1232                 : 
    1233             576 :         bool isNull = false;
    1234                 : 
    1235             576 :         if (FAILED(hr = pRow->IsNull(wstrFieldName, isNull)))
    1236                 :         {
    1237                 :             GDBErr(hr, "Failed to determine NULL status from column " +
    1238               0 :                    WStringToString(wstrFieldName));
    1239               0 :             foundBadColumn = true;
    1240               0 :             continue;
    1241                 :         }
    1242                 : 
    1243             576 :         if (isNull)
    1244                 :         {
    1245               0 :             continue; //leave as unset
    1246                 :         }
    1247                 : 
    1248                 :         //
    1249                 :         // NOTE: This switch statement needs to be kept in sync with GDBToOGRFieldType utility function
    1250                 :         //       since we are only checking for types we mapped in that utility function
    1251                 : 
    1252             576 :         switch (m_pFeatureDefn->GetFieldDefn(i)->GetType())
    1253                 :         {
    1254                 : 
    1255                 :             case OFTInteger:
    1256                 :             {
    1257                 :                 int32 val;
    1258                 : 
    1259             120 :                 if (FAILED(hr = pRow->GetInteger(wstrFieldName, val)))
    1260                 :                 {
    1261                 :                     int16 shortval;
    1262               0 :                     if (FAILED(hr = pRow->GetShort(wstrFieldName, shortval)))
    1263                 :                     {
    1264                 :                         GDBErr(hr, "Failed to determine integer value for column " +
    1265               0 :                                WStringToString(wstrFieldName));
    1266               0 :                         foundBadColumn = true;
    1267               0 :                         continue;
    1268                 :                     }
    1269               0 :                     val = shortval;
    1270                 :                 }
    1271                 : 
    1272             120 :                 pOutFeature->SetField(i, (int)val);
    1273                 :             }
    1274             120 :             break;
    1275                 : 
    1276                 :             case OFTReal:
    1277                 :             {
    1278             264 :                 if (strFieldType == "esriFieldTypeSingle")
    1279                 :                 {
    1280                 :                     float val;
    1281                 : 
    1282               0 :                     if (FAILED(hr = pRow->GetFloat(wstrFieldName, val)))
    1283                 :                     {
    1284                 :                         GDBErr(hr, "Failed to determine float value for column " +
    1285               0 :                                WStringToString(wstrFieldName));
    1286               0 :                         foundBadColumn = true;
    1287               0 :                         continue;
    1288                 :                     }
    1289                 : 
    1290               0 :                     pOutFeature->SetField(i, val);
    1291                 :                 }
    1292                 :                 else
    1293                 :                 {
    1294                 :                     double val;
    1295                 : 
    1296             264 :                     if (FAILED(hr = pRow->GetDouble(wstrFieldName, val)))
    1297                 :                     {
    1298                 :                         GDBErr(hr, "Failed to determine real value for column " +
    1299               0 :                                WStringToString(wstrFieldName));
    1300               0 :                         foundBadColumn = true;
    1301               0 :                         continue;
    1302                 :                     }
    1303                 : 
    1304             264 :                     pOutFeature->SetField(i, val);
    1305                 :                 }
    1306                 :             }
    1307             264 :             break;
    1308                 :             case OFTString:
    1309                 :             {
    1310             192 :                 wstring val;
    1311                 : 
    1312             192 :                 if (FAILED(hr = pRow->GetString(wstrFieldName, val)))
    1313                 :                 {
    1314                 :                     GDBErr(hr, "Failed to determine string value for column " +
    1315               0 :                         WStringToString(wstrFieldName));
    1316               0 :                     foundBadColumn = true;
    1317               0 :                     continue;
    1318                 :                 }
    1319                 : 
    1320             192 :                 pOutFeature->SetField(i, WStringToString(val).c_str());
    1321                 :             }
    1322             192 :             break;
    1323                 : 
    1324                 :             /* TODO: Need to get test dataset to implement these leave it as NULL for now
    1325                 :             case OFTBinary:
    1326                 :             {
    1327                 :                 ByteArray binaryBuf;
    1328                 : 
    1329                 :                 if (FAILED(hr = pRow->GetBinary(wstrFieldName, binaryBuf)))
    1330                 :                 {
    1331                 :                 GDBErr(hr, "Failed to determine binary value for column " + WStringToString(wstrFieldName));
    1332                 :                 foundBadColumn = true;
    1333                 :                 continue;
    1334                 :                 }
    1335                 : 
    1336                 :                 pOutFeature->SetField(i, (int)binaryBuf.inUseLength, (GByte*)binaryBuf.byteArray);
    1337                 :             }
    1338                 :             break;
    1339                 :             */
    1340                 : 
    1341                 :             case OFTDateTime:
    1342                 :             {
    1343                 :                 struct tm val;
    1344                 : 
    1345               0 :                 if (FAILED(hr = pRow->GetDate(wstrFieldName, val)))
    1346                 :                 {
    1347                 :                     GDBErr(hr, "Failed to determine date value for column " +
    1348               0 :                            WStringToString(wstrFieldName));
    1349               0 :                     foundBadColumn = true;
    1350               0 :                     continue;
    1351                 :                 }
    1352                 : 
    1353                 :                 pOutFeature->SetField(i, val.tm_year + 1900, val.tm_mon + 1,
    1354               0 :                                       val.tm_mday, val.tm_hour, val.tm_min, val.tm_sec);
    1355                 :             // Examine test data to figure out how to extract that
    1356                 :             }
    1357               0 :             break;
    1358                 : 
    1359                 :             default:
    1360                 :             {
    1361               0 :                 if (!m_supressColumnMappingError)
    1362                 :                 {
    1363               0 :                     foundBadColumn = true;
    1364                 :                     CPLError( CE_Warning, CPLE_AppDefined,
    1365                 :                             "Row id: %d col:%d has unhandled col type (%d). Setting to NULL.",
    1366               0 :                             (int)oid, (int)i, m_pFeatureDefn->GetFieldDefn(i)->GetType());
    1367                 :                 }
    1368                 :             }
    1369                 :         }
    1370                 :     }
    1371                 : 
    1372             192 :     if (foundBadColumn)
    1373               0 :         m_supressColumnMappingError = true;
    1374                 : 
    1375                 : 
    1376             192 :     *ppFeature = pOutFeature;
    1377                 : 
    1378             192 :     return true;
    1379                 : }
    1380                 : 
    1381                 : 
    1382                 : /************************************************************************/
    1383                 : /*                           GetNextFeature()                           */
    1384                 : /************************************************************************/
    1385                 : 
    1386             307 : OGRFeature* FGdbLayer::GetNextFeature()
    1387                 : {
    1388             307 :     if (m_bFilterDirty)
    1389              12 :         ResetReading();
    1390                 : 
    1391                 : 
    1392               0 :     while (1) //want to skip errors
    1393                 :     {
    1394             307 :         if (m_pEnumRows == NULL)
    1395               0 :             return NULL;
    1396                 : 
    1397                 :         long hr;
    1398                 : 
    1399             307 :         Row row;
    1400                 : 
    1401             307 :         if (FAILED(hr = m_pEnumRows->Next(row)))
    1402                 :         {
    1403               0 :             GDBErr(hr, "Failed fetching features");
    1404               0 :             return NULL;
    1405                 :         }
    1406                 : 
    1407             307 :         if (hr != S_OK)
    1408                 :         {
    1409                 :         // It's OK, we are done fetching - failure is catched by FAILED macro
    1410             117 :             return NULL;
    1411                 :         }
    1412                 : 
    1413             190 :         OGRFeature* pOGRFeature = NULL;
    1414                 : 
    1415             190 :         if (!OGRFeatureFromGdbRow(&row,  &pOGRFeature))
    1416                 :         {
    1417               0 :             int32 oid = -1;
    1418               0 :             row.GetOID(oid);
    1419                 : 
    1420               0 :             GDBErr(hr, CPLSPrintf("Failed translating FGDB row [%d] to OGR Feature", oid));
    1421                 : 
    1422                 :             //return NULL;
    1423               0 :             continue; //skip feature
    1424                 :         }
    1425                 : 
    1426             190 :         return pOGRFeature;
    1427                 :     }
    1428                 : }
    1429                 : 
    1430                 : /************************************************************************/
    1431                 : /*                             GetFeature()                             */
    1432                 : /************************************************************************/
    1433                 : 
    1434               2 : OGRFeature *FGdbLayer::GetFeature( long oid )
    1435                 : {
    1436                 :     // do query to fetch individual row
    1437                 : 
    1438                 :     long           hr;
    1439               2 :     Row            row;
    1440               2 :     EnumRows       enumRows;
    1441               2 :     CPLString      osQuery;
    1442                 : 
    1443               2 :     osQuery.Printf("%s = %ld", m_strOIDFieldName.c_str(), oid);
    1444                 : 
    1445               2 :     if (FAILED(hr = m_pTable->Search(m_wstrSubfields, StringToWString(osQuery.c_str()), true, enumRows)))
    1446                 :     {
    1447               0 :         GDBErr(hr, "Failed fetching row ");
    1448               0 :         return NULL;
    1449                 :     }
    1450                 : 
    1451               2 :     if (FAILED(hr = enumRows.Next(row)))
    1452                 :     {
    1453               0 :         GDBErr(hr, "Failed fetching row ");
    1454               0 :         return NULL;
    1455                 :     }
    1456                 : 
    1457               2 :     if (hr != S_OK)
    1458               0 :         return NULL; //none found - but no failure
    1459                 : 
    1460                 : 
    1461               2 :     OGRFeature* pOGRFeature = NULL;
    1462                 : 
    1463               2 :     if (!OGRFeatureFromGdbRow(&row,  &pOGRFeature))
    1464                 :     {
    1465               0 :         GDBErr(hr, "Failed translating ArcObjects row to OGR Feature");
    1466               0 :         return NULL;
    1467                 :     }
    1468                 : 
    1469               2 :     return pOGRFeature;
    1470                 : }
    1471                 : 
    1472                 : 
    1473                 : /************************************************************************/
    1474                 : /*                          GetFeatureCount()                           */
    1475                 : /************************************************************************/
    1476                 : 
    1477             130 : int FGdbLayer::GetFeatureCount( int bForce )
    1478                 : {
    1479                 :     long           hr;
    1480             130 :     int32          rowCount = 0;
    1481                 : 
    1482             130 :     if (m_pOGRFilterGeometry != NULL || m_wstrWhereClause.size() != 0)
    1483              52 :         return OGRLayer::GetFeatureCount(bForce);
    1484                 : 
    1485              78 :     if (FAILED(hr = m_pTable->GetRowCount(rowCount)))
    1486                 :     {
    1487               0 :         GDBErr(hr, "Failed counting rows");
    1488               0 :         return 0;
    1489                 :     }
    1490                 : 
    1491                 : #if 0
    1492                 :   Row            row;
    1493                 :   EnumRows       enumRows;
    1494                 : 
    1495                 :   if (FAILED(hr = m_pTable->Search(StringToWString(m_strOIDFieldName), L"", true, enumRows)))
    1496                 :   {
    1497                 :     GDBErr(hr, "Failed counting rows");
    1498                 :     return -1;
    1499                 :   }
    1500                 : 
    1501                 :   while (S_OK == (hr = enumRows.Next(row)))
    1502                 :     ++rowCount;
    1503                 : 
    1504                 :   if (FAILED(hr))
    1505                 :   {
    1506                 :     GDBErr(hr, "Failed counting rows (during fetch)");
    1507                 :     return -1;
    1508                 :   }
    1509                 : #endif
    1510                 : 
    1511              78 :     return static_cast<int>(rowCount);
    1512                 : }
    1513                 : 
    1514                 : 
    1515                 : 
    1516                 : /************************************************************************/
    1517                 : /*                             GetExtent()                              */
    1518                 : /************************************************************************/
    1519                 : 
    1520              13 : OGRErr FGdbLayer::GetExtent (OGREnvelope* psExtent, int bForce)
    1521                 : {
    1522              13 :     if (m_pOGRFilterGeometry != NULL || m_wstrWhereClause.size() != 0 ||
    1523                 :         m_strShapeFieldName.size() == 0)
    1524               0 :         return OGRLayer::GetExtent(psExtent, bForce);
    1525                 : 
    1526                 :     long hr;
    1527              13 :     Envelope envelope;
    1528              13 :     if (FAILED(hr = m_pTable->GetExtent(envelope)))
    1529                 :     {
    1530               0 :         GDBErr(hr, "Failed fetching extent");
    1531               0 :         return OGRERR_FAILURE;
    1532                 :     }
    1533                 : 
    1534              13 :     psExtent->MinX = envelope.xMin;
    1535              13 :     psExtent->MinY = envelope.yMin;
    1536              13 :     psExtent->MaxX = envelope.xMax;
    1537              13 :     psExtent->MaxY = envelope.yMax;
    1538                 : 
    1539              13 :     if (CPLIsNan(psExtent->MinX) ||
    1540                 :         CPLIsNan(psExtent->MinY) ||
    1541                 :         CPLIsNan(psExtent->MaxX) ||
    1542                 :         CPLIsNan(psExtent->MaxY))
    1543               0 :         return OGRERR_FAILURE;
    1544                 : 
    1545              13 :     return OGRERR_NONE;
    1546                 : }
    1547                 : 
    1548                 : /* OGRErr FGdbLayer::StartTransaction ()
    1549                 : {
    1550                 :     if ( ! m_pTable ) 
    1551                 :         return OGRERR_FAILURE;
    1552                 :         
    1553                 :     m_pTable->LoadOnlyMode(true);
    1554                 :     m_pTable->SetWriteLock();
    1555                 :     return OGRERR_NONE;
    1556                 :     
    1557                 : } */
    1558                 : 
    1559                 : 
    1560                 : /* OGRErr FGdbLayer::CommitTransaction ()
    1561                 : {
    1562                 :     if ( ! m_pTable ) 
    1563                 :         return OGRERR_FAILURE;
    1564                 :     
    1565                 :     m_pTable->LoadOnlyMode(false);
    1566                 :     m_pTable->FreeWriteLock();
    1567                 :     return OGRERR_NONE;
    1568                 : } */
    1569                 : 
    1570                 : /* OGRErr FGdbLayer::RollbackTransaction ()
    1571                 : {
    1572                 :     if ( ! m_pTable ) 
    1573                 :         return OGRERR_FAILURE;
    1574                 :     
    1575                 :     m_pTable->LoadOnlyMode(false);
    1576                 :     m_pTable->FreeWriteLock();
    1577                 :     return OGRERR_NONE;
    1578                 : } */
    1579                 : 
    1580                 : 
    1581                 : /************************************************************************/
    1582                 : /*                           GetLayerXML()                              */
    1583                 : /* Return XML definition of the Layer as provided by FGDB. Caller must  */
    1584                 : /* free result.                                                         */
    1585                 : /* Not currently used by the driver, but can be used by external code   */
    1586                 : /* for specific purposes.                                               */
    1587                 : /************************************************************************/
    1588                 : 
    1589               0 : OGRErr FGdbLayer::GetLayerXML (char **ppXml)
    1590                 : {
    1591                 :     long hr;
    1592               0 :     std::string xml;
    1593                 : 
    1594               0 :     if ( FAILED(hr = m_pTable->GetDefinition(xml)) )
    1595                 :     {
    1596               0 :         GDBErr(hr, "Failed fetching XML table definition");
    1597               0 :         return OGRERR_FAILURE;
    1598                 :     }
    1599                 : 
    1600               0 :     *ppXml = CPLStrdup(xml.c_str());
    1601               0 :     return OGRERR_NONE;
    1602                 : }
    1603                 : 
    1604                 : /************************************************************************/
    1605                 : /*                           GetLayerMetadataXML()                      */
    1606                 : /* Return XML metadata for the Layer as provided by FGDB. Caller must  */
    1607                 : /* free result.                                                         */
    1608                 : /* Not currently used by the driver, but can be used by external code   */
    1609                 : /* for specific purposes.                                               */
    1610                 : /************************************************************************/
    1611                 : 
    1612               0 : OGRErr FGdbLayer::GetLayerMetadataXML (char **ppXml)
    1613                 : {
    1614                 :     long hr;
    1615               0 :     std::string xml;
    1616                 : 
    1617               0 :     if ( FAILED(hr = m_pTable->GetDocumentation(xml)) )
    1618                 :     {
    1619               0 :         GDBErr(hr, "Failed fetching XML table metadata");
    1620               0 :         return OGRERR_FAILURE;
    1621                 :     }
    1622                 : 
    1623               0 :     *ppXml = CPLStrdup(xml.c_str());
    1624               0 :     return OGRERR_NONE;
    1625                 : }
    1626                 : 
    1627                 : /************************************************************************/
    1628                 : /*                           TestCapability()                           */
    1629                 : /************************************************************************/
    1630                 : 
    1631              52 : int FGdbLayer::TestCapability( const char* pszCap )
    1632                 : {
    1633                 : 
    1634              52 :     if (EQUAL(pszCap,OLCRandomRead))
    1635              13 :         return TRUE;
    1636                 : 
    1637              39 :     else if (EQUAL(pszCap,OLCFastFeatureCount)) 
    1638               0 :         return m_pOGRFilterGeometry == NULL && m_wstrWhereClause.size() == 0;
    1639                 : 
    1640              39 :     else if (EQUAL(pszCap,OLCFastSpatialFilter))
    1641               0 :         return TRUE;
    1642                 : 
    1643              39 :     else if (EQUAL(pszCap,OLCFastGetExtent))
    1644               0 :         return m_pOGRFilterGeometry == NULL && m_wstrWhereClause.size() == 0;
    1645                 : 
    1646              39 :     else if (EQUAL(pszCap,OLCCreateField)) /* CreateField() */
    1647               0 :         return TRUE;
    1648                 : 
    1649              39 :     else if (EQUAL(pszCap,OLCSequentialWrite)) /* CreateFeature() */
    1650               0 :         return TRUE;
    1651                 : 
    1652              39 :     else if (EQUAL(pszCap,OLCStringsAsUTF8)) /* Native UTF16, converted to UTF8 */
    1653              13 :         return TRUE;
    1654                 : 
    1655              26 :     else if (EQUAL(pszCap,OLCReorderFields)) /* TBD ReorderFields() */
    1656               0 :         return FALSE;
    1657                 : 
    1658              26 :     else if (EQUAL(pszCap,OLCDeleteFeature)) /* TBD DeleteFeature() */
    1659               0 :         return FALSE;
    1660                 : 
    1661              26 :     else if (EQUAL(pszCap,OLCRandomWrite)) /* TBD SetFeature() */
    1662              13 :         return FALSE;
    1663                 : 
    1664              13 :     else if (EQUAL(pszCap,OLCDeleteField)) /* TBD DeleteField() */
    1665               0 :         return FALSE;
    1666                 :         
    1667              13 :     else if (EQUAL(pszCap,OLCFastSetNextByIndex)) /* TBD FastSetNextByIndex() */
    1668              13 :         return FALSE;
    1669                 : 
    1670               0 :     else if (EQUAL(pszCap,OLCTransactions)) /* TBD Start/End Transactions() */
    1671               0 :         return FALSE;
    1672                 :         
    1673                 :     else 
    1674               0 :         return FALSE;
    1675            1947 : }
    1676                 : 

Generated by: LCOV version 1.7