LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/filegdb - FGdbUtils.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 165 202 81.7 %
Date: 2024-11-25 13:07:18 Functions: 13 14 92.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       20814 : std::wstring StringToWString(const std::string &utf8string)
      30             : {
      31             :     wchar_t *pszUTF16 =
      32       20814 :         CPLRecodeToWChar(utf8string.c_str(), CPL_ENC_UTF8, CPL_ENC_UCS2);
      33       20814 :     std::wstring utf16string = pszUTF16;
      34       20814 :     CPLFree(pszUTF16);
      35       20814 :     return utf16string;
      36             : }
      37             : 
      38             : /*************************************************************************/
      39             : /*                          WStringToString()                            */
      40             : /*************************************************************************/
      41             : 
      42       10447 : std::string WStringToString(const std::wstring &utf16string)
      43             : {
      44             :     char *pszUTF8 =
      45       10447 :         CPLRecodeFromWChar(utf16string.c_str(), CPL_ENC_UCS2, CPL_ENC_UTF8);
      46       10447 :     std::string utf8string = pszUTF8;
      47       10447 :     CPLFree(pszUTF8);
      48       10447 :     return utf8string;
      49             : }
      50             : 
      51             : /*************************************************************************/
      52             : /*                                GDBErr()                               */
      53             : /*************************************************************************/
      54             : 
      55          26 : bool GDBErr(long int hr, const std::string &desc, CPLErr errType,
      56             :             const char *pszAddMsg)
      57             : {
      58          26 :     std::wstring fgdb_error_desc_w;
      59             :     fgdbError er;
      60          26 :     er = FileGDBAPI::ErrorInfo::GetErrorDescription(static_cast<fgdbError>(hr),
      61             :                                                     fgdb_error_desc_w);
      62          26 :     if (er == S_OK)
      63             :     {
      64          52 :         std::string fgdb_error_desc = WStringToString(fgdb_error_desc_w);
      65          26 :         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          52 :     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         370 : bool GDBToOGRGeometry(const std::string &geoType, bool hasZ, bool hasM,
     111             :                       OGRwkbGeometryType *pOut)
     112             : {
     113         370 :     if (geoType == "esriGeometryPoint")
     114             :     {
     115          88 :         *pOut = wkbPoint;
     116             :     }
     117         282 :     else if (geoType == "esriGeometryMultipoint")
     118             :     {
     119          35 :         *pOut = wkbMultiPoint;
     120             :     }
     121         247 :     else if (geoType == "esriGeometryLine")
     122             :     {
     123           0 :         *pOut = wkbLineString;
     124             :     }
     125         247 :     else if (geoType == "esriGeometryPolyline")
     126             :     {
     127          80 :         *pOut = wkbMultiLineString;
     128             :     }
     129         201 :     else if (geoType == "esriGeometryPolygon" ||
     130          34 :              geoType == "esriGeometryMultiPatch")
     131             :     {
     132         167 :         *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         370 :     if (hasZ)
     142         145 :         *pOut = wkbSetZ(*pOut);
     143         370 :     if (hasM)
     144           7 :         *pOut = wkbSetM(*pOut);
     145             : 
     146         370 :     return true;
     147             : }
     148             : 
     149             : /*************************************************************************/
     150             : /*                            OGRGeometryToGDB()                         */
     151             : /*************************************************************************/
     152             : 
     153         137 : bool OGRGeometryToGDB(OGRwkbGeometryType ogrType, std::string *gdbType,
     154             :                       bool *hasZ, bool *hasM)
     155             : {
     156         137 :     *hasZ = wkbHasZ(ogrType);
     157         137 :     *hasM = wkbHasM(ogrType);
     158         137 :     switch (wkbFlatten(ogrType))
     159             :     {
     160          42 :         case wkbPoint:
     161             :         {
     162          42 :             *gdbType = "esriGeometryPoint";
     163          42 :             break;
     164             :         }
     165             : 
     166          13 :         case wkbMultiPoint:
     167             :         {
     168          13 :             *gdbType = "esriGeometryMultipoint";
     169          13 :             break;
     170             :         }
     171             : 
     172          27 :         case wkbLineString:
     173             :         case wkbMultiLineString:
     174             :         {
     175          27 :             *gdbType = "esriGeometryPolyline";
     176          27 :             break;
     177             :         }
     178             : 
     179          49 :         case wkbPolygon:
     180             :         case wkbMultiPolygon:
     181             :         {
     182          49 :             *gdbType = "esriGeometryPolygon";
     183          49 :             break;
     184             :         }
     185             : 
     186           6 :         case wkbTIN:
     187             :         case wkbPolyhedralSurface:
     188             :         {
     189           6 :             *gdbType = "esriGeometryMultiPatch";
     190           6 :             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         137 :     return true;
     202             : }
     203             : 
     204             : /*************************************************************************/
     205             : /*                            OGRToGDBFieldType()                        */
     206             : /*************************************************************************/
     207             : 
     208        1375 : bool OGRToGDBFieldType(OGRFieldType ogrType, OGRFieldSubType eSubType,
     209             :                        std::string *gdbType)
     210             : {
     211        1375 :     switch (ogrType)
     212             :     {
     213         426 :         case OFTInteger:
     214             :         {
     215         426 :             if (eSubType == OFSTInt16)
     216         104 :                 *gdbType = "esriFieldTypeSmallInteger";
     217             :             else
     218         322 :                 *gdbType = "esriFieldTypeInteger";
     219         426 :             break;
     220             :         }
     221         321 :         case OFTReal:
     222             :         case OFTInteger64:
     223             :         {
     224         321 :             if (eSubType == OFSTFloat32)
     225         102 :                 *gdbType = "esriFieldTypeSingle";
     226             :             else
     227         219 :                 *gdbType = "esriFieldTypeDouble";
     228         321 :             break;
     229             :         }
     230         320 :         case OFTString:
     231             :         {
     232         320 :             *gdbType = "esriFieldTypeString";
     233         320 :             break;
     234             :         }
     235         204 :         case OFTBinary:
     236             :         {
     237         204 :             *gdbType = "esriFieldTypeBlob";
     238         204 :             break;
     239             :         }
     240         104 :         case OFTDate:
     241             :         case OFTDateTime:
     242             :         {
     243         104 :             *gdbType = "esriFieldTypeDate";
     244         104 :             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        1375 :     return true;
     256             : }
     257             : 
     258             : /*************************************************************************/
     259             : /*                       GDBFieldTypeToLengthInBytes()                   */
     260             : /*************************************************************************/
     261             : 
     262        1375 : 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        1375 :     if (gdbType == "esriFieldTypeSmallInteger")
     268             :     {
     269         206 :         lengthOut = 2;
     270             :     }
     271        1169 :     else if (gdbType == "esriFieldTypeInteger")
     272             :     {
     273         220 :         lengthOut = 4;
     274             :     }
     275         949 :     else if (gdbType == "esriFieldTypeSingle")
     276             :     {
     277         204 :         lengthOut = 4;
     278             :     }
     279         745 :     else if (gdbType == "esriFieldTypeDouble")
     280             :     {
     281         117 :         lengthOut = 8;
     282             :     }
     283         628 :     else if (gdbType == "esriFieldTypeString" || gdbType == "esriFieldTypeXML")
     284             :     {
     285         217 :         lengthOut = atoi(CPLGetConfigOption("FGDB_STRING_WIDTH", "65536"));
     286             :     }
     287         411 :     else if (gdbType == "esriFieldTypeDate")
     288             :     {
     289         104 :         lengthOut = 8;
     290             :     }
     291         307 :     else if (gdbType == "esriFieldTypeOID")
     292             :     {
     293           0 :         lengthOut = 4;
     294             :     }
     295         307 :     else if (gdbType == "esriFieldTypeGUID")
     296             :     {
     297         102 :         lengthOut = 16;
     298             :     }
     299         205 :     else if (gdbType == "esriFieldTypeBlob")
     300             :     {
     301         204 :         lengthOut = 0;
     302             :     }
     303           1 :     else if (gdbType == "esriFieldTypeGlobalID")
     304             :     {
     305           1 :         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        1375 :     return true;
     315             : }
     316             : 
     317             : /*************************************************************************/
     318             : /*                        GDBGeometryToOGRGeometry()                     */
     319             : /*************************************************************************/
     320             : 
     321        1841 : bool GDBGeometryToOGRGeometry(bool forceMulti,
     322             :                               FileGDBAPI::ShapeBuffer *pGdbGeometry,
     323             :                               OGRSpatialReference *pOGRSR,
     324             :                               OGRGeometry **ppOutGeometry)
     325             : {
     326             : 
     327        1841 :     OGRGeometry *pOGRGeometry = nullptr;
     328             : 
     329             :     OGRErr eErr =
     330        3682 :         OGRCreateFromShapeBin(pGdbGeometry->shapeBuffer, &pOGRGeometry,
     331        1841 :                               static_cast<int>(pGdbGeometry->inUseLength));
     332             : 
     333             :     // OGRErr eErr =
     334             :     // OGRGeometryFactory::createFromWkb(pGdbGeometry->shapeBuffer, pOGRSR,
     335             :     // &pOGRGeometry, pGdbGeometry->inUseLength );
     336             : 
     337        1841 :     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        1841 :     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        1841 :             wkbFlatten(pOGRGeometry->getGeometryType());
     354        1841 :         if (eFlattenType == wkbPolygon)
     355             :         {
     356         570 :             pOGRGeometry =
     357         570 :                 OGRGeometryFactory::forceToMultiPolygon(pOGRGeometry);
     358             :         }
     359        1271 :         else if (eFlattenType == wkbCurvePolygon)
     360             :         {
     361          12 :             OGRMultiSurface *poMS = new OGRMultiSurface();
     362          12 :             poMS->addGeometryDirectly(pOGRGeometry);
     363          12 :             pOGRGeometry = poMS;
     364             :         }
     365        1259 :         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        1841 :         if (pOGRGeometry)
     386        1841 :             pOGRGeometry->assignSpatialReference(pOGRSR);
     387             :     }
     388             : 
     389        1841 :     *ppOutGeometry = pOGRGeometry;
     390             : 
     391        1841 :     return true;
     392             : }
     393             : 
     394             : /*************************************************************************/
     395             : /*                         GDBToOGRSpatialReference()                    */
     396             : /*************************************************************************/
     397             : 
     398           7 : bool GDBToOGRSpatialReference(const string &wkt, OGRSpatialReference **ppSR)
     399             : {
     400           7 :     if (wkt.empty())
     401             :     {
     402           0 :         CPLError(CE_Warning, CPLE_AppDefined, "ESRI Spatial Reference is NULL");
     403           0 :         return false;
     404             :     }
     405             : 
     406           7 :     *ppSR = new OGRSpatialReference(wkt.c_str());
     407           7 :     (*ppSR)->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     408             : 
     409           7 :     OGRErr result = (*ppSR)->morphFromESRI();
     410             : 
     411           7 :     if (result == OGRERR_NONE)
     412             :     {
     413           7 :         if (CPLTestBool(CPLGetConfigOption("USE_OSR_FIND_MATCHES", "YES")))
     414             :         {
     415           7 :             int nEntries = 0;
     416           7 :             int *panConfidence = nullptr;
     417             :             OGRSpatialReferenceH *pahSRS =
     418           7 :                 (*ppSR)->FindMatches(nullptr, &nEntries, &panConfidence);
     419           7 :             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           7 :                 OSRFreeSRSArray(pahSRS);
     429             :             }
     430           7 :             CPLFree(panConfidence);
     431             :         }
     432             :         else
     433             :         {
     434           0 :             (*ppSR)->AutoIdentifyEPSG();
     435             :         }
     436             : 
     437           7 :         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        7718 : void FGDB_CPLAddXMLAttribute(CPLXMLNode *node, const char *attrname,
     457             :                              const char *attrvalue)
     458             : {
     459        7718 :     if (!node)
     460           0 :         return;
     461        7718 :     CPLCreateXMLNode(CPLCreateXMLNode(node, CXT_Attribute, attrname), CXT_Text,
     462             :                      attrvalue);
     463             : }
     464             : 
     465             : /*************************************************************************/
     466             : /*                          FGDBLaunderName()                            */
     467             : /*************************************************************************/
     468             : 
     469        1525 : std::wstring FGDBLaunderName(const std::wstring &name)
     470             : {
     471        1525 :     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        1525 :     if (!newName.empty() && newName[0] >= '0' && newName[0] <= '9')
     479             :     {
     480           2 :         newName = StringToWString("_") + newName;
     481             :     }
     482             : 
     483             :     // "Essentially, eliminate anything that is not alphanumeric or an
     484             :     // underscore." Note: alphanumeric unicode is supported
     485       10735 :     for (size_t i = 0; i < newName.size(); i++)
     486             :     {
     487       17323 :         if (!(newName[i] == '_' || (newName[i] >= '0' && newName[i] <= '9') ||
     488        8113 :               (newName[i] >= 'a' && newName[i] <= 'z') ||
     489         312 :               (newName[i] >= 'A' && newName[i] <= 'Z') || newName[i] >= 128))
     490             :         {
     491          20 :             newName[i] = '_';
     492             :         }
     493             :     }
     494             : 
     495        1525 :     return newName;
     496             : }
     497             : 
     498             : /*************************************************************************/
     499             : /*                     FGDBEscapeUnsupportedPrefixes()                   */
     500             : /*************************************************************************/
     501             : 
     502         150 : std::wstring FGDBEscapeUnsupportedPrefixes(const std::wstring &className)
     503             : {
     504         150 :     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         597 :     for (int i = 0; UNSUPPORTED_PREFIXES[i] != nullptr; i++)
     511             :     {
     512             :         // cppcheck-suppress stlIfStrFind
     513         448 :         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           1 :             newName = StringToWString("_") + newName;
     519           1 :             break;
     520             :         }
     521             :     }
     522             : 
     523         150 :     return newName;
     524             : }
     525             : 
     526             : /*************************************************************************/
     527             : /*                        FGDBEscapeReservedKeywords()                   */
     528             : /*************************************************************************/
     529             : 
     530        1525 : std::wstring FGDBEscapeReservedKeywords(const std::wstring &name)
     531             : {
     532        3050 :     std::string newName = WStringToString(name);
     533        3050 :     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       44191 :     for (const char *pszKeyword : apszRESERVED_WORDS)
     538             :     {
     539       42668 :         if (upperName == pszKeyword)
     540             :         {
     541           2 :             newName += '_';
     542           2 :             break;
     543             :         }
     544             :     }
     545             : 
     546        3050 :     return StringToWString(newName);
     547             : }

Generated by: LCOV version 1.14