LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/filegdb - FGdbUtils.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 74 202 36.6 %
Date: 2025-02-18 14:19:29 Functions: 6 14 42.9 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  OpenGIS Simple Features Reference Implementation
       4             :  * Purpose:  Different utility functions used in FileGDB OGR driver.
       5             :  * Author:   Ragi Yaser Burhum, ragi@burhum.com
       6             :  *           Paul Ramsey, pramsey at cleverelephant.ca
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 2010, Ragi Yaser Burhum
      10             :  * Copyright (c) 2011, Paul Ramsey <pramsey at cleverelephant.ca>
      11             :  * Copyright (c) 2011-2014, Even Rouault <even dot rouault at spatialys.com>
      12             :  *
      13             :  * SPDX-License-Identifier: MIT
      14             :  ****************************************************************************/
      15             : 
      16             : #include "FGdbUtils.h"
      17             : #include <algorithm>
      18             : 
      19             : #include "ogr_api.h"
      20             : #include "ogrpgeogeometry.h"
      21             : #include "filegdb_reserved_keywords.h"
      22             : 
      23             : using std::string;
      24             : 
      25             : /*************************************************************************/
      26             : /*                          StringToWString()                            */
      27             : /*************************************************************************/
      28             : 
      29        5153 : std::wstring StringToWString(const std::string &utf8string)
      30             : {
      31             :     wchar_t *pszUTF16 =
      32        5153 :         CPLRecodeToWChar(utf8string.c_str(), CPL_ENC_UTF8, CPL_ENC_UCS2);
      33        5153 :     std::wstring utf16string = pszUTF16;
      34        5153 :     CPLFree(pszUTF16);
      35        5153 :     return utf16string;
      36             : }
      37             : 
      38             : /*************************************************************************/
      39             : /*                          WStringToString()                            */
      40             : /*************************************************************************/
      41             : 
      42        3693 : std::string WStringToString(const std::wstring &utf16string)
      43             : {
      44             :     char *pszUTF8 =
      45        3693 :         CPLRecodeFromWChar(utf16string.c_str(), CPL_ENC_UCS2, CPL_ENC_UTF8);
      46        3693 :     std::string utf8string = pszUTF8;
      47        3693 :     CPLFree(pszUTF8);
      48        3693 :     return utf8string;
      49             : }
      50             : 
      51             : /*************************************************************************/
      52             : /*                                GDBErr()                               */
      53             : /*************************************************************************/
      54             : 
      55          20 : bool GDBErr(long int hr, const std::string &desc, CPLErr errType,
      56             :             const char *pszAddMsg)
      57             : {
      58          20 :     std::wstring fgdb_error_desc_w;
      59             :     fgdbError er;
      60          20 :     er = FileGDBAPI::ErrorInfo::GetErrorDescription(static_cast<fgdbError>(hr),
      61             :                                                     fgdb_error_desc_w);
      62          20 :     if (er == S_OK)
      63             :     {
      64          40 :         std::string fgdb_error_desc = WStringToString(fgdb_error_desc_w);
      65          20 :         CPLError(errType, CPLE_AppDefined, "%s (%s)%s", desc.c_str(),
      66             :                  fgdb_error_desc.c_str(), pszAddMsg);
      67             :     }
      68             :     else
      69             :     {
      70           0 :         CPLError(errType, CPLE_AppDefined, "Error (%ld): %s%s", hr,
      71             :                  desc.c_str(), pszAddMsg);
      72             :     }
      73             :     // FIXME? EvenR: not sure if ClearErrors() is really necessary, but as it,
      74             :     // it causes crashes in case of repeated errors
      75             :     // FileGDBAPI::ErrorInfo::ClearErrors();
      76             : 
      77          40 :     return false;
      78             : }
      79             : 
      80             : /*************************************************************************/
      81             : /*                            GDBDebug()                                 */
      82             : /*************************************************************************/
      83             : 
      84           0 : bool GDBDebug(long int hr, const std::string &desc)
      85             : {
      86           0 :     std::wstring fgdb_error_desc_w;
      87             :     fgdbError er;
      88           0 :     er = FileGDBAPI::ErrorInfo::GetErrorDescription(static_cast<fgdbError>(hr),
      89             :                                                     fgdb_error_desc_w);
      90           0 :     if (er == S_OK)
      91             :     {
      92           0 :         std::string fgdb_error_desc = WStringToString(fgdb_error_desc_w);
      93           0 :         CPLDebug("FGDB", "%s (%s)", desc.c_str(), fgdb_error_desc.c_str());
      94             :     }
      95             :     else
      96             :     {
      97           0 :         CPLDebug("FGDB", "%s", desc.c_str());
      98             :     }
      99             :     // FIXME? EvenR: not sure if ClearErrors() is really necessary, but as it,
     100             :     // it causes crashes in case of repeated errors
     101             :     // FileGDBAPI::ErrorInfo::ClearErrors();
     102             : 
     103           0 :     return false;
     104             : }
     105             : 
     106             : /*************************************************************************/
     107             : /*                            GDBToOGRGeometry()                         */
     108             : /*************************************************************************/
     109             : 
     110         118 : bool GDBToOGRGeometry(const std::string &geoType, bool hasZ, bool hasM,
     111             :                       OGRwkbGeometryType *pOut)
     112             : {
     113         118 :     if (geoType == "esriGeometryPoint")
     114             :     {
     115          19 :         *pOut = wkbPoint;
     116             :     }
     117          99 :     else if (geoType == "esriGeometryMultipoint")
     118             :     {
     119          10 :         *pOut = wkbMultiPoint;
     120             :     }
     121          89 :     else if (geoType == "esriGeometryLine")
     122             :     {
     123           0 :         *pOut = wkbLineString;
     124             :     }
     125          89 :     else if (geoType == "esriGeometryPolyline")
     126             :     {
     127          27 :         *pOut = wkbMultiLineString;
     128             :     }
     129          72 :     else if (geoType == "esriGeometryPolygon" ||
     130          10 :              geoType == "esriGeometryMultiPatch")
     131             :     {
     132          62 :         *pOut = wkbMultiPolygon;  // no mapping to single polygon
     133             :     }
     134             :     else
     135             :     {
     136           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     137             :                  "Cannot map esriGeometryType(%s) to OGRwkbGeometryType",
     138             :                  geoType.c_str());
     139           0 :         return false;
     140             :     }
     141         118 :     if (hasZ)
     142          47 :         *pOut = wkbSetZ(*pOut);
     143         118 :     if (hasM)
     144           5 :         *pOut = wkbSetM(*pOut);
     145             : 
     146         118 :     return true;
     147             : }
     148             : 
     149             : /*************************************************************************/
     150             : /*                            OGRGeometryToGDB()                         */
     151             : /*************************************************************************/
     152             : 
     153           0 : bool OGRGeometryToGDB(OGRwkbGeometryType ogrType, std::string *gdbType,
     154             :                       bool *hasZ, bool *hasM)
     155             : {
     156           0 :     *hasZ = wkbHasZ(ogrType);
     157           0 :     *hasM = wkbHasM(ogrType);
     158           0 :     switch (wkbFlatten(ogrType))
     159             :     {
     160           0 :         case wkbPoint:
     161             :         {
     162           0 :             *gdbType = "esriGeometryPoint";
     163           0 :             break;
     164             :         }
     165             : 
     166           0 :         case wkbMultiPoint:
     167             :         {
     168           0 :             *gdbType = "esriGeometryMultipoint";
     169           0 :             break;
     170             :         }
     171             : 
     172           0 :         case wkbLineString:
     173             :         case wkbMultiLineString:
     174             :         {
     175           0 :             *gdbType = "esriGeometryPolyline";
     176           0 :             break;
     177             :         }
     178             : 
     179           0 :         case wkbPolygon:
     180             :         case wkbMultiPolygon:
     181             :         {
     182           0 :             *gdbType = "esriGeometryPolygon";
     183           0 :             break;
     184             :         }
     185             : 
     186           0 :         case wkbTIN:
     187             :         case wkbPolyhedralSurface:
     188             :         {
     189           0 :             *gdbType = "esriGeometryMultiPatch";
     190           0 :             break;
     191             :         }
     192             : 
     193           0 :         default:
     194             :         {
     195           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     196             :                      "Cannot map OGRwkbGeometryType (%s) to ESRI type",
     197             :                      OGRGeometryTypeToName(ogrType));
     198           0 :             return false;
     199             :         }
     200             :     }
     201           0 :     return true;
     202             : }
     203             : 
     204             : /*************************************************************************/
     205             : /*                            OGRToGDBFieldType()                        */
     206             : /*************************************************************************/
     207             : 
     208           0 : bool OGRToGDBFieldType(OGRFieldType ogrType, OGRFieldSubType eSubType,
     209             :                        std::string *gdbType)
     210             : {
     211           0 :     switch (ogrType)
     212             :     {
     213           0 :         case OFTInteger:
     214             :         {
     215           0 :             if (eSubType == OFSTInt16)
     216           0 :                 *gdbType = "esriFieldTypeSmallInteger";
     217             :             else
     218           0 :                 *gdbType = "esriFieldTypeInteger";
     219           0 :             break;
     220             :         }
     221           0 :         case OFTReal:
     222             :         case OFTInteger64:
     223             :         {
     224           0 :             if (eSubType == OFSTFloat32)
     225           0 :                 *gdbType = "esriFieldTypeSingle";
     226             :             else
     227           0 :                 *gdbType = "esriFieldTypeDouble";
     228           0 :             break;
     229             :         }
     230           0 :         case OFTString:
     231             :         {
     232           0 :             *gdbType = "esriFieldTypeString";
     233           0 :             break;
     234             :         }
     235           0 :         case OFTBinary:
     236             :         {
     237           0 :             *gdbType = "esriFieldTypeBlob";
     238           0 :             break;
     239             :         }
     240           0 :         case OFTDate:
     241             :         case OFTDateTime:
     242             :         {
     243           0 :             *gdbType = "esriFieldTypeDate";
     244           0 :             break;
     245             :         }
     246           0 :         default:
     247             :         {
     248           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     249             :                      "Cannot map OGR field type (%s)",
     250             :                      OGR_GetFieldTypeName(ogrType));
     251           0 :             return false;
     252             :         }
     253             :     }
     254             : 
     255           0 :     return true;
     256             : }
     257             : 
     258             : /*************************************************************************/
     259             : /*                       GDBFieldTypeToLengthInBytes()                   */
     260             : /*************************************************************************/
     261             : 
     262           0 : bool GDBFieldTypeToLengthInBytes(const std::string &gdbType, int &lengthOut)
     263             : {
     264             :     /* Length based on
     265             :      * FileGDB_API/samples/XMLsamples/OneOfEachFieldType.xml */
     266             :     /* Length is in bytes per doc of FileGDB_API/xmlResources/FileGDBAPI.xsd */
     267           0 :     if (gdbType == "esriFieldTypeSmallInteger")
     268             :     {
     269           0 :         lengthOut = 2;
     270             :     }
     271           0 :     else if (gdbType == "esriFieldTypeInteger")
     272             :     {
     273           0 :         lengthOut = 4;
     274             :     }
     275           0 :     else if (gdbType == "esriFieldTypeSingle")
     276             :     {
     277           0 :         lengthOut = 4;
     278             :     }
     279           0 :     else if (gdbType == "esriFieldTypeDouble")
     280             :     {
     281           0 :         lengthOut = 8;
     282             :     }
     283           0 :     else if (gdbType == "esriFieldTypeString" || gdbType == "esriFieldTypeXML")
     284             :     {
     285           0 :         lengthOut = atoi(CPLGetConfigOption("FGDB_STRING_WIDTH", "65536"));
     286             :     }
     287           0 :     else if (gdbType == "esriFieldTypeDate")
     288             :     {
     289           0 :         lengthOut = 8;
     290             :     }
     291           0 :     else if (gdbType == "esriFieldTypeOID")
     292             :     {
     293           0 :         lengthOut = 4;
     294             :     }
     295           0 :     else if (gdbType == "esriFieldTypeGUID")
     296             :     {
     297           0 :         lengthOut = 16;
     298             :     }
     299           0 :     else if (gdbType == "esriFieldTypeBlob")
     300             :     {
     301           0 :         lengthOut = 0;
     302             :     }
     303           0 :     else if (gdbType == "esriFieldTypeGlobalID")
     304             :     {
     305           0 :         lengthOut = 38;
     306             :     }
     307             :     else
     308             :     {
     309           0 :         CPLError(CE_Warning, CPLE_AppDefined, "Cannot map ESRI field type (%s)",
     310             :                  gdbType.c_str());
     311           0 :         return false;
     312             :     }
     313             : 
     314           0 :     return true;
     315             : }
     316             : 
     317             : /*************************************************************************/
     318             : /*                        GDBGeometryToOGRGeometry()                     */
     319             : /*************************************************************************/
     320             : 
     321        1610 : bool GDBGeometryToOGRGeometry(bool forceMulti,
     322             :                               FileGDBAPI::ShapeBuffer *pGdbGeometry,
     323             :                               OGRSpatialReference *pOGRSR,
     324             :                               OGRGeometry **ppOutGeometry)
     325             : {
     326             : 
     327        1610 :     OGRGeometry *pOGRGeometry = nullptr;
     328             : 
     329             :     OGRErr eErr =
     330        3220 :         OGRCreateFromShapeBin(pGdbGeometry->shapeBuffer, &pOGRGeometry,
     331        1610 :                               static_cast<int>(pGdbGeometry->inUseLength));
     332             : 
     333             :     // OGRErr eErr =
     334             :     // OGRGeometryFactory::createFromWkb(pGdbGeometry->shapeBuffer, pOGRSR,
     335             :     // &pOGRGeometry, pGdbGeometry->inUseLength );
     336             : 
     337        1610 :     if (eErr != OGRERR_NONE)
     338             :     {
     339           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     340             :                  "Failed attempting to import GDB WKB Geometry. "
     341             :                  "OGRGeometryFactory err:%d",
     342             :                  eErr);
     343           0 :         return false;
     344             :     }
     345             : 
     346        1610 :     if (pOGRGeometry != nullptr)
     347             :     {
     348             :         // force geometries to multi if requested
     349             : 
     350             :         // If it is a polygon, force to MultiPolygon since we always produce
     351             :         // multipolygons
     352             :         OGRwkbGeometryType eFlattenType =
     353        1610 :             wkbFlatten(pOGRGeometry->getGeometryType());
     354        1610 :         if (eFlattenType == wkbPolygon)
     355             :         {
     356         344 :             pOGRGeometry =
     357         344 :                 OGRGeometryFactory::forceToMultiPolygon(pOGRGeometry);
     358             :         }
     359        1266 :         else if (eFlattenType == wkbCurvePolygon)
     360             :         {
     361          12 :             OGRMultiSurface *poMS = new OGRMultiSurface();
     362          12 :             poMS->addGeometryDirectly(pOGRGeometry);
     363          12 :             pOGRGeometry = poMS;
     364             :         }
     365        1254 :         else if (forceMulti)
     366             :         {
     367         688 :             if (eFlattenType == wkbLineString)
     368             :             {
     369         342 :                 pOGRGeometry =
     370         342 :                     OGRGeometryFactory::forceToMultiLineString(pOGRGeometry);
     371             :             }
     372         346 :             else if (eFlattenType == wkbCompoundCurve)
     373             :             {
     374           9 :                 OGRMultiCurve *poMC = new OGRMultiCurve();
     375           9 :                 poMC->addGeometryDirectly(pOGRGeometry);
     376           9 :                 pOGRGeometry = poMC;
     377             :             }
     378         337 :             else if (eFlattenType == wkbPoint)
     379             :             {
     380           0 :                 pOGRGeometry =
     381           0 :                     OGRGeometryFactory::forceToMultiPoint(pOGRGeometry);
     382             :             }
     383             :         }
     384             : 
     385        1610 :         if (pOGRGeometry)
     386        1610 :             pOGRGeometry->assignSpatialReference(pOGRSR);
     387             :     }
     388             : 
     389        1610 :     *ppOutGeometry = pOGRGeometry;
     390             : 
     391        1610 :     return true;
     392             : }
     393             : 
     394             : /*************************************************************************/
     395             : /*                         GDBToOGRSpatialReference()                    */
     396             : /*************************************************************************/
     397             : 
     398           1 : bool GDBToOGRSpatialReference(const string &wkt, OGRSpatialReference **ppSR)
     399             : {
     400           1 :     if (wkt.empty())
     401             :     {
     402           0 :         CPLError(CE_Warning, CPLE_AppDefined, "ESRI Spatial Reference is NULL");
     403           0 :         return false;
     404             :     }
     405             : 
     406           1 :     *ppSR = new OGRSpatialReference(wkt.c_str());
     407           1 :     (*ppSR)->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     408             : 
     409           1 :     OGRErr result = (*ppSR)->morphFromESRI();
     410             : 
     411           1 :     if (result == OGRERR_NONE)
     412             :     {
     413           1 :         if (CPLTestBool(CPLGetConfigOption("USE_OSR_FIND_MATCHES", "YES")))
     414             :         {
     415           1 :             int nEntries = 0;
     416           1 :             int *panConfidence = nullptr;
     417             :             OGRSpatialReferenceH *pahSRS =
     418           1 :                 (*ppSR)->FindMatches(nullptr, &nEntries, &panConfidence);
     419           1 :             if (nEntries == 1 && panConfidence[0] == 100)
     420             :             {
     421           0 :                 (*ppSR)->Release();
     422           0 :                 (*ppSR) = reinterpret_cast<OGRSpatialReference *>(pahSRS[0]);
     423           0 :                 (*ppSR)->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     424           0 :                 CPLFree(pahSRS);
     425             :             }
     426             :             else
     427             :             {
     428           1 :                 OSRFreeSRSArray(pahSRS);
     429             :             }
     430           1 :             CPLFree(panConfidence);
     431             :         }
     432             :         else
     433             :         {
     434           0 :             (*ppSR)->AutoIdentifyEPSG();
     435             :         }
     436             : 
     437           1 :         return true;
     438             :     }
     439             :     else
     440             :     {
     441           0 :         delete *ppSR;
     442           0 :         *ppSR = nullptr;
     443             : 
     444           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     445             :                  "Failed morphing from ESRI Geometry: %s", wkt.c_str());
     446             : 
     447           0 :         return false;
     448             :     }
     449             : }
     450             : 
     451             : /*************************************************************************/
     452             : /*                           FGDB_CPLAddXMLAttribute()                   */
     453             : /*************************************************************************/
     454             : 
     455             : /* Utility method for attributing nodes */
     456           0 : void FGDB_CPLAddXMLAttribute(CPLXMLNode *node, const char *attrname,
     457             :                              const char *attrvalue)
     458             : {
     459           0 :     if (!node)
     460           0 :         return;
     461           0 :     CPLCreateXMLNode(CPLCreateXMLNode(node, CXT_Attribute, attrname), CXT_Text,
     462             :                      attrvalue);
     463             : }
     464             : 
     465             : /*************************************************************************/
     466             : /*                          FGDBLaunderName()                            */
     467             : /*************************************************************************/
     468             : 
     469           0 : std::wstring FGDBLaunderName(const std::wstring &name)
     470             : {
     471           0 :     std::wstring newName = name;
     472             : 
     473             :     // https://support.esri.com/en/technical-article/000005588
     474             : 
     475             :     // "Do not start field or table names with an underscore or a number."
     476             :     // But we can see in the wild table names starting with underscore...
     477             :     // (cf https://github.com/OSGeo/gdal/issues/4112)
     478           0 :     if (!newName.empty() && newName[0] >= '0' && newName[0] <= '9')
     479             :     {
     480           0 :         newName = StringToWString("_") + newName;
     481             :     }
     482             : 
     483             :     // "Essentially, eliminate anything that is not alphanumeric or an
     484             :     // underscore." Note: alphanumeric unicode is supported
     485           0 :     for (size_t i = 0; i < newName.size(); i++)
     486             :     {
     487           0 :         if (!(newName[i] == '_' || (newName[i] >= '0' && newName[i] <= '9') ||
     488           0 :               (newName[i] >= 'a' && newName[i] <= 'z') ||
     489           0 :               (newName[i] >= 'A' && newName[i] <= 'Z') || newName[i] >= 128))
     490             :         {
     491           0 :             newName[i] = '_';
     492             :         }
     493             :     }
     494             : 
     495           0 :     return newName;
     496             : }
     497             : 
     498             : /*************************************************************************/
     499             : /*                     FGDBEscapeUnsupportedPrefixes()                   */
     500             : /*************************************************************************/
     501             : 
     502           0 : std::wstring FGDBEscapeUnsupportedPrefixes(const std::wstring &className)
     503             : {
     504           0 :     std::wstring newName = className;
     505             :     // From ESRI docs
     506             :     // Feature classes starting with these strings are unsupported.
     507             :     static const char *const UNSUPPORTED_PREFIXES[] = {"sde_", "gdb_", "delta_",
     508             :                                                        nullptr};
     509             : 
     510           0 :     for (int i = 0; UNSUPPORTED_PREFIXES[i] != nullptr; i++)
     511             :     {
     512             :         // cppcheck-suppress stlIfStrFind
     513           0 :         if (newName.find(StringToWString(UNSUPPORTED_PREFIXES[i])) == 0)
     514             :         {
     515             :             // Normally table names shouldn't start with underscore, but
     516             :             // there are such in the wild (cf
     517             :             // https://github.com/OSGeo/gdal/issues/4112)
     518           0 :             newName = StringToWString("_") + newName;
     519           0 :             break;
     520             :         }
     521             :     }
     522             : 
     523           0 :     return newName;
     524             : }
     525             : 
     526             : /*************************************************************************/
     527             : /*                        FGDBEscapeReservedKeywords()                   */
     528             : /*************************************************************************/
     529             : 
     530           0 : std::wstring FGDBEscapeReservedKeywords(const std::wstring &name)
     531             : {
     532           0 :     std::string newName = WStringToString(name);
     533           0 :     std::string upperName = CPLString(newName).toupper();
     534             : 
     535             :     // Append an underscore to any FGDB reserved words used as field names
     536             :     // This is the same behavior ArcCatalog follows.
     537           0 :     for (const char *pszKeyword : apszRESERVED_WORDS)
     538             :     {
     539           0 :         if (upperName == pszKeyword)
     540             :         {
     541           0 :             newName += '_';
     542           0 :             break;
     543             :         }
     544             :     }
     545             : 
     546           0 :     return StringToWString(newName);
     547             : }

Generated by: LCOV version 1.14