LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/gmlutils - gmlutils.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 122 164 74.4 %
Date: 2025-01-18 12:42:00 Functions: 12 12 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GML Utils
       4             :  * Purpose:  GML reader
       5             :  * Author:   Even Rouault, <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2010-2013, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "cpl_port.h"
      14             : #include "gmlutils.h"
      15             : 
      16             : #include <cstdio>
      17             : #include <cstdlib>
      18             : #include <cstring>
      19             : #include <map>
      20             : #include <string>
      21             : #include <utility>
      22             : 
      23             : #include "cpl_conv.h"
      24             : #include "cpl_string.h"
      25             : #include "ogr_api.h"
      26             : #include "ogr_core.h"
      27             : #include "ogr_p.h"
      28             : #include "ogr_spatialref.h"
      29             : 
      30             : /************************************************************************/
      31             : /*                GML_ExtractSrsNameFromGeometry()                      */
      32             : /************************************************************************/
      33             : 
      34             : const char *
      35        1676 : GML_ExtractSrsNameFromGeometry(const CPLXMLNode *const *papsGeometry,
      36             :                                std::string &osWork, bool bConsiderEPSGAsURN)
      37             : {
      38        1676 :     if (papsGeometry[0] != nullptr && papsGeometry[1] == nullptr)
      39             :     {
      40             :         const char *pszSRSName =
      41        1676 :             CPLGetXMLValue(papsGeometry[0], "srsName", nullptr);
      42        1676 :         if (pszSRSName)
      43             :         {
      44         543 :             const int nLen = static_cast<int>(strlen(pszSRSName));
      45             : 
      46         543 :             if (STARTS_WITH(pszSRSName, "EPSG:") && bConsiderEPSGAsURN)
      47             :             {
      48          36 :                 osWork.reserve(22 + nLen - 5);
      49          36 :                 osWork.assign("urn:ogc:def:crs:EPSG::", 22);
      50          36 :                 osWork.append(pszSRSName + 5, nLen - 5);
      51          36 :                 return osWork.c_str();
      52             :             }
      53         507 :             else if (STARTS_WITH(pszSRSName,
      54             :                                  "http://www.opengis.net/gml/srs/epsg.xml#"))
      55             :             {
      56          47 :                 osWork.reserve(5 + nLen - 40);
      57          47 :                 osWork.assign("EPSG:", 5);
      58          47 :                 osWork.append(pszSRSName + 40, nLen - 40);
      59          47 :                 return osWork.c_str();
      60             :             }
      61             :             else
      62             :             {
      63         460 :                 return pszSRSName;
      64             :             }
      65             :         }
      66             :     }
      67        1133 :     return nullptr;
      68             : }
      69             : 
      70             : /************************************************************************/
      71             : /*                       GML_IsSRSLatLongOrder()                        */
      72             : /************************************************************************/
      73             : 
      74         461 : bool GML_IsSRSLatLongOrder(const char *pszSRSName)
      75             : {
      76         461 :     if (pszSRSName == nullptr)
      77           0 :         return false;
      78             : 
      79         461 :     if (STARTS_WITH(pszSRSName, "urn:") &&
      80         390 :         strstr(pszSRSName, ":4326") != nullptr)
      81             :     {
      82             :         // Shortcut.
      83         332 :         return true;
      84             :     }
      85             :     /* fguuid:jgd20??.bl (Japanese FGD GML v4) */
      86         129 :     else if (EQUALN(pszSRSName, "fguuid:jgd2011.bl", 17) ||
      87         127 :              EQUALN(pszSRSName, "fguuid:jgd2001.bl", 17))
      88             :     {
      89           2 :         return true;
      90             :     }
      91         127 :     else if (!EQUALN(pszSRSName, "EPSG:", 5))
      92             :     {
      93          75 :         OGRSpatialReference oSRS;
      94          75 :         if (oSRS.SetFromUserInput(
      95             :                 pszSRSName,
      96          75 :                 OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()) ==
      97             :             OGRERR_NONE)
      98             :         {
      99         137 :             if (oSRS.EPSGTreatsAsLatLong() ||
     100          62 :                 oSRS.EPSGTreatsAsNorthingEasting())
     101          15 :                 return true;
     102             :         }
     103             :     }
     104         112 :     return false;
     105             : }
     106             : 
     107             : /************************************************************************/
     108             : /*                GML_BuildOGRGeometryFromList_CreateCache()            */
     109             : /************************************************************************/
     110             : 
     111             : namespace
     112             : {
     113             : class SRSDesc
     114             : {
     115             :   public:
     116             :     std::string osSRSName{};
     117             :     bool bAxisInvert = false;
     118             :     OGRSpatialReference *poSRS = nullptr;
     119             : 
     120         440 :     SRSDesc() = default;
     121             : 
     122         220 :     SRSDesc &operator=(SRSDesc &&other)
     123             :     {
     124         220 :         osSRSName = std::move(other.osSRSName);
     125         220 :         bAxisInvert = other.bAxisInvert;
     126         220 :         if (poSRS)
     127           0 :             poSRS->Release();
     128         220 :         poSRS = other.poSRS;
     129         220 :         other.poSRS = nullptr;
     130         220 :         return *this;
     131             :     }
     132             : 
     133         440 :     ~SRSDesc()
     134         440 :     {
     135         440 :         if (poSRS)
     136         218 :             poSRS->Release();
     137         440 :     }
     138             : 
     139             :     CPL_DISALLOW_COPY_ASSIGN(SRSDesc)
     140             : };
     141             : 
     142             : class SRSCache
     143             : {
     144             :     std::map<std::string, SRSDesc> oMap{};
     145             :     SRSDesc *poLastDesc = nullptr;
     146             : 
     147             :     CPL_DISALLOW_COPY_ASSIGN(SRSCache)
     148             : 
     149             :   public:
     150         794 :     SRSCache() = default;
     151             : 
     152         422 :     const SRSDesc &Get(const std::string &osSRSName)
     153             :     {
     154         422 :         if (poLastDesc && osSRSName == poLastDesc->osSRSName)
     155         202 :             return *poLastDesc;
     156             : 
     157         220 :         std::map<std::string, SRSDesc>::iterator oIter = oMap.find(osSRSName);
     158         220 :         if (oIter != oMap.end())
     159             :         {
     160           0 :             poLastDesc = &(oIter->second);
     161           0 :             return *poLastDesc;
     162             :         }
     163             : 
     164         220 :         SRSDesc oDesc;
     165         220 :         oDesc.osSRSName = osSRSName;
     166         220 :         oDesc.bAxisInvert = GML_IsSRSLatLongOrder(osSRSName.c_str());
     167         220 :         oDesc.poSRS = new OGRSpatialReference();
     168         220 :         oDesc.poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     169         220 :         if (oDesc.poSRS->SetFromUserInput(
     170             :                 osSRSName.c_str(),
     171         220 :                 OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()) !=
     172             :             OGRERR_NONE)
     173             :         {
     174           2 :             delete oDesc.poSRS;
     175           2 :             oDesc.poSRS = nullptr;
     176             :         }
     177         220 :         oMap[osSRSName] = std::move(oDesc);
     178         220 :         poLastDesc = &(oMap[osSRSName]);
     179         220 :         return *poLastDesc;
     180             :     }
     181             : };
     182             : 
     183             : }  // namespace
     184             : 
     185         794 : void *GML_BuildOGRGeometryFromList_CreateCache()
     186             : {
     187         794 :     return new SRSCache();
     188             : }
     189             : 
     190             : /************************************************************************/
     191             : /*                 GML_BuildOGRGeometryFromList_DestroyCache()          */
     192             : /************************************************************************/
     193             : 
     194         794 : void GML_BuildOGRGeometryFromList_DestroyCache(void *hCacheSRS)
     195             : {
     196         794 :     delete static_cast<SRSCache *>(hCacheSRS);
     197         794 : }
     198             : 
     199             : /************************************************************************/
     200             : /*                 GML_BuildOGRGeometryFromList()                       */
     201             : /************************************************************************/
     202             : 
     203        1184 : OGRGeometry *GML_BuildOGRGeometryFromList(
     204             :     const CPLXMLNode *const *papsGeometry, bool bTryToMakeMultipolygons,
     205             :     bool bInvertAxisOrderIfLatLong, const char *pszDefaultSRSName,
     206             :     bool bConsiderEPSGAsURN, GMLSwapCoordinatesEnum eSwapCoordinates,
     207             :     int nPseudoBoolGetSecondaryGeometryOption, void *hCacheSRS,
     208             :     bool bFaceHoleNegative)
     209             : {
     210        1184 :     OGRGeometry *poGeom = nullptr;
     211        1184 :     OGRGeometryCollection *poCollection = nullptr;
     212             : #ifndef WITHOUT_CPLDEBUG
     213             :     static const bool bDebugGML =
     214        1184 :         EQUAL(CPLGetConfigOption("CPL_DEBUG", ""), "GML");
     215             : #endif
     216        2368 :     for (int i = 0; papsGeometry[i] != nullptr; i++)
     217             :     {
     218             : #ifndef WITHOUT_CPLDEBUG
     219        1184 :         if (bDebugGML)
     220             :         {
     221           0 :             char *pszXML = CPLSerializeXMLTree(papsGeometry[i]);
     222           0 :             CPLDebug("GML", "Parsing: %s", pszXML);
     223           0 :             CPLFree(pszXML);
     224             :         }
     225             : #endif
     226        2368 :         OGRGeometry *poSubGeom = GML2OGRGeometry_XMLNode(
     227        1184 :             papsGeometry[i], nPseudoBoolGetSecondaryGeometryOption, 0, 0, false,
     228             :             true, bFaceHoleNegative);
     229        1184 :         if (poSubGeom)
     230             :         {
     231        1182 :             if (poGeom == nullptr)
     232             :             {
     233        1182 :                 poGeom = poSubGeom;
     234             :             }
     235             :             else
     236             :             {
     237           0 :                 if (poCollection == nullptr)
     238             :                 {
     239           0 :                     if (bTryToMakeMultipolygons &&
     240           0 :                         wkbFlatten(poGeom->getGeometryType()) == wkbPolygon &&
     241           0 :                         wkbFlatten(poSubGeom->getGeometryType()) == wkbPolygon)
     242             :                     {
     243             :                         OGRGeometryCollection *poGeomColl =
     244           0 :                             new OGRMultiPolygon();
     245           0 :                         poGeomColl->addGeometryDirectly(poGeom);
     246           0 :                         poGeomColl->addGeometryDirectly(poSubGeom);
     247           0 :                         poGeom = poGeomColl;
     248             :                     }
     249           0 :                     else if (bTryToMakeMultipolygons &&
     250           0 :                              wkbFlatten(poGeom->getGeometryType()) ==
     251           0 :                                  wkbMultiPolygon &&
     252           0 :                              wkbFlatten(poSubGeom->getGeometryType()) ==
     253             :                                  wkbPolygon)
     254             :                     {
     255           0 :                         OGRMultiPolygon *poGeomColl = poGeom->toMultiPolygon();
     256           0 :                         poGeomColl->addGeometryDirectly(poSubGeom);
     257             :                     }
     258           0 :                     else if (bTryToMakeMultipolygons &&
     259           0 :                              wkbFlatten(poGeom->getGeometryType()) ==
     260           0 :                                  wkbMultiPolygon &&
     261           0 :                              wkbFlatten(poSubGeom->getGeometryType()) ==
     262             :                                  wkbMultiPolygon)
     263             :                     {
     264           0 :                         OGRMultiPolygon *poGeomColl = poGeom->toMultiPolygon();
     265           0 :                         for (auto &&poMember : poSubGeom->toMultiPolygon())
     266             :                         {
     267           0 :                             poGeomColl->addGeometry(poMember);
     268             :                         }
     269           0 :                         delete poSubGeom;
     270             :                     }
     271           0 :                     else if (bTryToMakeMultipolygons &&
     272           0 :                              wkbFlatten(poGeom->getGeometryType()) ==
     273             :                                  wkbMultiPolygon)
     274             :                     {
     275           0 :                         delete poGeom;
     276           0 :                         delete poSubGeom;
     277           0 :                         return GML_BuildOGRGeometryFromList(
     278             :                             papsGeometry, false, bInvertAxisOrderIfLatLong,
     279             :                             pszDefaultSRSName, bConsiderEPSGAsURN,
     280             :                             eSwapCoordinates,
     281           0 :                             nPseudoBoolGetSecondaryGeometryOption, hCacheSRS);
     282             :                     }
     283             :                     else
     284             :                     {
     285           0 :                         poCollection = new OGRGeometryCollection();
     286           0 :                         poCollection->addGeometryDirectly(poGeom);
     287           0 :                         poGeom = poCollection;
     288             :                     }
     289             :                 }
     290           0 :                 if (poCollection != nullptr)
     291             :                 {
     292           0 :                     poCollection->addGeometryDirectly(poSubGeom);
     293             :                 }
     294             :             }
     295             :         }
     296             :     }
     297             : 
     298        1184 :     if (poGeom == nullptr)
     299           2 :         return nullptr;
     300             : 
     301        1182 :     std::string osWork;
     302        1182 :     const char *pszSRSName = GML_ExtractSrsNameFromGeometry(
     303             :         papsGeometry, osWork, bConsiderEPSGAsURN);
     304        1182 :     const char *pszNameLookup = pszSRSName;
     305        1182 :     if (pszNameLookup == nullptr)
     306         781 :         pszNameLookup = pszDefaultSRSName;
     307             : 
     308        1182 :     if (pszNameLookup != nullptr)
     309             :     {
     310         422 :         SRSCache *poSRSCache = static_cast<SRSCache *>(hCacheSRS);
     311         422 :         const SRSDesc &oSRSDesc = poSRSCache->Get(pszNameLookup);
     312         422 :         poGeom->assignSpatialReference(oSRSDesc.poSRS);
     313         422 :         if ((eSwapCoordinates == GML_SWAP_AUTO && oSRSDesc.bAxisInvert &&
     314         156 :              bInvertAxisOrderIfLatLong) ||
     315             :             eSwapCoordinates == GML_SWAP_YES)
     316             :         {
     317         268 :             poGeom->swapXY();
     318             :         }
     319             :     }
     320         760 :     else if (eSwapCoordinates == GML_SWAP_YES)
     321             :     {
     322           1 :         poGeom->swapXY();
     323             :     }
     324             : 
     325        1182 :     return poGeom;
     326             : }
     327             : 
     328             : /************************************************************************/
     329             : /*                           GML_GetSRSName()                           */
     330             : /************************************************************************/
     331             : 
     332         220 : char *GML_GetSRSName(const OGRSpatialReference *poSRS,
     333             :                      OGRGMLSRSNameFormat eSRSNameFormat, bool *pbCoordSwap)
     334             : {
     335         220 :     *pbCoordSwap = false;
     336         220 :     if (poSRS == nullptr)
     337         116 :         return CPLStrdup("");
     338             : 
     339         104 :     const auto &map = poSRS->GetDataAxisToSRSAxisMapping();
     340         146 :     if (eSRSNameFormat != SRSNAME_SHORT && map.size() >= 2 && map[0] == 2 &&
     341          42 :         map[1] == 1)
     342             :     {
     343          42 :         *pbCoordSwap = true;
     344             :     }
     345             : 
     346         104 :     const char *pszAuthName = poSRS->GetAuthorityName(nullptr);
     347         104 :     const char *pszAuthCode = poSRS->GetAuthorityCode(nullptr);
     348         104 :     if (nullptr != pszAuthName && nullptr != pszAuthCode)
     349             :     {
     350         104 :         if (eSRSNameFormat == SRSNAME_SHORT)
     351             :         {
     352           9 :             return CPLStrdup(
     353           9 :                 CPLSPrintf(" srsName=\"%s:%s\"", pszAuthName, pszAuthCode));
     354             :         }
     355          95 :         else if (eSRSNameFormat == SRSNAME_OGC_URN)
     356             :         {
     357          74 :             return CPLStrdup(CPLSPrintf(" srsName=\"urn:ogc:def:crs:%s::%s\"",
     358          74 :                                         pszAuthName, pszAuthCode));
     359             :         }
     360          21 :         else if (eSRSNameFormat == SRSNAME_OGC_URL)
     361             :         {
     362          21 :             return CPLStrdup(CPLSPrintf(
     363             :                 " srsName=\"http://www.opengis.net/def/crs/%s/0/%s\"",
     364          21 :                 pszAuthName, pszAuthCode));
     365             :         }
     366             :     }
     367           0 :     return CPLStrdup("");
     368             : }
     369             : 
     370             : /************************************************************************/
     371             : /*                       GML_IsLegitSRSName()                           */
     372             : /************************************************************************/
     373             : 
     374          85 : bool GML_IsLegitSRSName(const char *pszSRSName)
     375             : {
     376             : 
     377          85 :     if (STARTS_WITH_CI(pszSRSName, "http"))
     378             :     {
     379           6 :         if (!(STARTS_WITH_CI(pszSRSName, "http://opengis.net/def/crs") ||
     380           6 :               STARTS_WITH_CI(pszSRSName, "http://www.opengis.net/def/crs")))
     381             :         {
     382           0 :             return false;
     383             :         }
     384             :     }
     385          85 :     return true;
     386             : }

Generated by: LCOV version 1.14