LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/gmlutils - gmlutils.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 117 161 72.7 %
Date: 2025-09-10 17:48:50 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        2073 : GML_ExtractSrsNameFromGeometry(const CPLXMLNode *const *papsGeometry,
      36             :                                std::string &osWork, bool bConsiderEPSGAsURN)
      37             : {
      38        2073 :     if (papsGeometry[0] != nullptr && papsGeometry[1] == nullptr)
      39             :     {
      40             :         const char *pszSRSName =
      41        2073 :             CPLGetXMLValue(papsGeometry[0], "srsName", nullptr);
      42        2073 :         if (pszSRSName)
      43             :         {
      44         548 :             const int nLen = static_cast<int>(strlen(pszSRSName));
      45             : 
      46         548 :             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         512 :             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         465 :                 return pszSRSName;
      64             :             }
      65             :         }
      66             :     }
      67        1525 :     return nullptr;
      68             : }
      69             : 
      70             : /************************************************************************/
      71             : /*                       GML_IsSRSLatLongOrder()                        */
      72             : /************************************************************************/
      73             : 
      74         476 : bool GML_IsSRSLatLongOrder(const char *pszSRSName)
      75             : {
      76         476 :     if (pszSRSName == nullptr)
      77           0 :         return false;
      78             : 
      79         476 :     if (STARTS_WITH(pszSRSName, "urn:") &&
      80         404 :         strstr(pszSRSName, ":4326") != nullptr)
      81             :     {
      82             :         // Shortcut.
      83         337 :         return true;
      84             :     }
      85         139 :     else if (!EQUALN(pszSRSName, "EPSG:", 5))
      86             :     {
      87          87 :         OGRSpatialReference oSRS;
      88          87 :         if (oSRS.SetFromUserInput(
      89             :                 pszSRSName,
      90          87 :                 OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()) ==
      91             :             OGRERR_NONE)
      92             :         {
      93         159 :             if (oSRS.EPSGTreatsAsLatLong() ||
      94          72 :                 oSRS.EPSGTreatsAsNorthingEasting())
      95          17 :                 return true;
      96             :         }
      97             :     }
      98         122 :     return false;
      99             : }
     100             : 
     101             : /************************************************************************/
     102             : /*                GML_BuildOGRGeometryFromList_CreateCache()            */
     103             : /************************************************************************/
     104             : 
     105             : namespace
     106             : {
     107             : class SRSDesc
     108             : {
     109             :   public:
     110             :     std::string osSRSName{};
     111             :     bool bAxisInvert = false;
     112             :     OGRSpatialReference *poSRS = nullptr;
     113             : 
     114         458 :     SRSDesc() = default;
     115             : 
     116         229 :     SRSDesc &operator=(SRSDesc &&other)
     117             :     {
     118         229 :         osSRSName = std::move(other.osSRSName);
     119         229 :         bAxisInvert = other.bAxisInvert;
     120         229 :         if (poSRS)
     121           0 :             poSRS->Release();
     122         229 :         poSRS = other.poSRS;
     123         229 :         other.poSRS = nullptr;
     124         229 :         return *this;
     125             :     }
     126             : 
     127         458 :     ~SRSDesc()
     128         458 :     {
     129         458 :         if (poSRS)
     130         229 :             poSRS->Release();
     131         458 :     }
     132             : 
     133             :     CPL_DISALLOW_COPY_ASSIGN(SRSDesc)
     134             : };
     135             : 
     136             : class SRSCache
     137             : {
     138             :     std::map<std::string, SRSDesc> oMap{};
     139             :     SRSDesc *poLastDesc = nullptr;
     140             : 
     141             :     CPL_DISALLOW_COPY_ASSIGN(SRSCache)
     142             : 
     143             :   public:
     144         827 :     SRSCache() = default;
     145             : 
     146         432 :     const SRSDesc &Get(const std::string &osSRSName)
     147             :     {
     148         432 :         if (poLastDesc && osSRSName == poLastDesc->osSRSName)
     149         203 :             return *poLastDesc;
     150             : 
     151         229 :         std::map<std::string, SRSDesc>::iterator oIter = oMap.find(osSRSName);
     152         229 :         if (oIter != oMap.end())
     153             :         {
     154           0 :             poLastDesc = &(oIter->second);
     155           0 :             return *poLastDesc;
     156             :         }
     157             : 
     158         229 :         SRSDesc oDesc;
     159         229 :         oDesc.osSRSName = osSRSName;
     160         229 :         oDesc.bAxisInvert = GML_IsSRSLatLongOrder(osSRSName.c_str());
     161         229 :         oDesc.poSRS = new OGRSpatialReference();
     162         229 :         oDesc.poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     163         229 :         if (oDesc.poSRS->SetFromUserInput(
     164             :                 osSRSName.c_str(),
     165         229 :                 OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()) !=
     166             :             OGRERR_NONE)
     167             :         {
     168           0 :             delete oDesc.poSRS;
     169           0 :             oDesc.poSRS = nullptr;
     170             :         }
     171         229 :         oMap[osSRSName] = std::move(oDesc);
     172         229 :         poLastDesc = &(oMap[osSRSName]);
     173         229 :         return *poLastDesc;
     174             :     }
     175             : };
     176             : 
     177             : }  // namespace
     178             : 
     179         827 : void *GML_BuildOGRGeometryFromList_CreateCache()
     180             : {
     181         827 :     return new SRSCache();
     182             : }
     183             : 
     184             : /************************************************************************/
     185             : /*                 GML_BuildOGRGeometryFromList_DestroyCache()          */
     186             : /************************************************************************/
     187             : 
     188         827 : void GML_BuildOGRGeometryFromList_DestroyCache(void *hCacheSRS)
     189             : {
     190         827 :     delete static_cast<SRSCache *>(hCacheSRS);
     191         827 : }
     192             : 
     193             : /************************************************************************/
     194             : /*                 GML_BuildOGRGeometryFromList()                       */
     195             : /************************************************************************/
     196             : 
     197        1397 : OGRGeometry *GML_BuildOGRGeometryFromList(
     198             :     const CPLXMLNode *const *papsGeometry, bool bTryToMakeMultipolygons,
     199             :     bool bInvertAxisOrderIfLatLong, const char *pszDefaultSRSName,
     200             :     bool bConsiderEPSGAsURN, GMLSwapCoordinatesEnum eSwapCoordinates,
     201             :     int nPseudoBoolGetSecondaryGeometryOption, void *hCacheSRS,
     202             :     bool bFaceHoleNegative)
     203             : {
     204        1397 :     OGRGeometry *poGeom = nullptr;
     205        1397 :     OGRGeometryCollection *poCollection = nullptr;
     206             : #ifndef WITHOUT_CPLDEBUG
     207             :     static const bool bDebugGML =
     208        1397 :         EQUAL(CPLGetConfigOption("CPL_DEBUG", ""), "GML");
     209             : #endif
     210        2794 :     for (int i = 0; papsGeometry[i] != nullptr; i++)
     211             :     {
     212             : #ifndef WITHOUT_CPLDEBUG
     213        1397 :         if (bDebugGML)
     214             :         {
     215           0 :             char *pszXML = CPLSerializeXMLTree(papsGeometry[i]);
     216           0 :             CPLDebug("GML", "Parsing: %s", pszXML);
     217           0 :             CPLFree(pszXML);
     218             :         }
     219             : #endif
     220        2794 :         OGRGeometry *poSubGeom = GML2OGRGeometry_XMLNode(
     221        1397 :             papsGeometry[i], nPseudoBoolGetSecondaryGeometryOption, 0, 0, false,
     222             :             true, bFaceHoleNegative);
     223        1397 :         if (poSubGeom)
     224             :         {
     225        1390 :             if (poGeom == nullptr)
     226             :             {
     227        1390 :                 poGeom = poSubGeom;
     228             :             }
     229             :             else
     230             :             {
     231           0 :                 if (poCollection == nullptr)
     232             :                 {
     233           0 :                     if (bTryToMakeMultipolygons &&
     234           0 :                         wkbFlatten(poGeom->getGeometryType()) == wkbPolygon &&
     235           0 :                         wkbFlatten(poSubGeom->getGeometryType()) == wkbPolygon)
     236             :                     {
     237             :                         OGRGeometryCollection *poGeomColl =
     238           0 :                             new OGRMultiPolygon();
     239           0 :                         poGeomColl->addGeometryDirectly(poGeom);
     240           0 :                         poGeomColl->addGeometryDirectly(poSubGeom);
     241           0 :                         poGeom = poGeomColl;
     242             :                     }
     243           0 :                     else if (bTryToMakeMultipolygons &&
     244           0 :                              wkbFlatten(poGeom->getGeometryType()) ==
     245           0 :                                  wkbMultiPolygon &&
     246           0 :                              wkbFlatten(poSubGeom->getGeometryType()) ==
     247             :                                  wkbPolygon)
     248             :                     {
     249           0 :                         OGRMultiPolygon *poGeomColl = poGeom->toMultiPolygon();
     250           0 :                         poGeomColl->addGeometryDirectly(poSubGeom);
     251             :                     }
     252           0 :                     else if (bTryToMakeMultipolygons &&
     253           0 :                              wkbFlatten(poGeom->getGeometryType()) ==
     254           0 :                                  wkbMultiPolygon &&
     255           0 :                              wkbFlatten(poSubGeom->getGeometryType()) ==
     256             :                                  wkbMultiPolygon)
     257             :                     {
     258           0 :                         OGRMultiPolygon *poGeomColl = poGeom->toMultiPolygon();
     259           0 :                         for (auto &&poMember : poSubGeom->toMultiPolygon())
     260             :                         {
     261           0 :                             poGeomColl->addGeometry(poMember);
     262             :                         }
     263           0 :                         delete poSubGeom;
     264             :                     }
     265           0 :                     else if (bTryToMakeMultipolygons &&
     266           0 :                              wkbFlatten(poGeom->getGeometryType()) ==
     267             :                                  wkbMultiPolygon)
     268             :                     {
     269           0 :                         delete poGeom;
     270           0 :                         delete poSubGeom;
     271           0 :                         return GML_BuildOGRGeometryFromList(
     272             :                             papsGeometry, false, bInvertAxisOrderIfLatLong,
     273             :                             pszDefaultSRSName, bConsiderEPSGAsURN,
     274             :                             eSwapCoordinates,
     275           0 :                             nPseudoBoolGetSecondaryGeometryOption, hCacheSRS);
     276             :                     }
     277             :                     else
     278             :                     {
     279           0 :                         poCollection = new OGRGeometryCollection();
     280           0 :                         poCollection->addGeometryDirectly(poGeom);
     281           0 :                         poGeom = poCollection;
     282             :                     }
     283             :                 }
     284           0 :                 if (poCollection != nullptr)
     285             :                 {
     286           0 :                     poCollection->addGeometryDirectly(poSubGeom);
     287             :                 }
     288             :             }
     289             :         }
     290             :     }
     291             : 
     292        1397 :     if (poGeom == nullptr)
     293           7 :         return nullptr;
     294             : 
     295        1390 :     std::string osWork;
     296        1390 :     const char *pszSRSName = GML_ExtractSrsNameFromGeometry(
     297             :         papsGeometry, osWork, bConsiderEPSGAsURN);
     298        1390 :     const char *pszNameLookup = pszSRSName;
     299        1390 :     if (pszNameLookup == nullptr)
     300         985 :         pszNameLookup = pszDefaultSRSName;
     301             : 
     302        1390 :     if (pszNameLookup != nullptr)
     303             :     {
     304         432 :         SRSCache *poSRSCache = static_cast<SRSCache *>(hCacheSRS);
     305         432 :         const SRSDesc &oSRSDesc = poSRSCache->Get(pszNameLookup);
     306         432 :         poGeom->assignSpatialReference(oSRSDesc.poSRS);
     307         432 :         if ((eSwapCoordinates == GML_SWAP_AUTO && oSRSDesc.bAxisInvert &&
     308         162 :              bInvertAxisOrderIfLatLong) ||
     309             :             eSwapCoordinates == GML_SWAP_YES)
     310             :         {
     311         272 :             poGeom->swapXY();
     312             :         }
     313             :     }
     314         958 :     else if (eSwapCoordinates == GML_SWAP_YES)
     315             :     {
     316           1 :         poGeom->swapXY();
     317             :     }
     318             : 
     319        1390 :     return poGeom;
     320             : }
     321             : 
     322             : /************************************************************************/
     323             : /*                           GML_GetSRSName()                           */
     324             : /************************************************************************/
     325             : 
     326         220 : char *GML_GetSRSName(const OGRSpatialReference *poSRS,
     327             :                      OGRGMLSRSNameFormat eSRSNameFormat, bool *pbCoordSwap)
     328             : {
     329         220 :     *pbCoordSwap = false;
     330         220 :     if (poSRS == nullptr)
     331         116 :         return CPLStrdup("");
     332             : 
     333         104 :     const auto &map = poSRS->GetDataAxisToSRSAxisMapping();
     334         146 :     if (eSRSNameFormat != SRSNAME_SHORT && map.size() >= 2 && map[0] == 2 &&
     335          42 :         map[1] == 1)
     336             :     {
     337          42 :         *pbCoordSwap = true;
     338             :     }
     339             : 
     340         104 :     const char *pszAuthName = poSRS->GetAuthorityName(nullptr);
     341         104 :     const char *pszAuthCode = poSRS->GetAuthorityCode(nullptr);
     342         104 :     if (nullptr != pszAuthName && nullptr != pszAuthCode)
     343             :     {
     344         104 :         if (eSRSNameFormat == SRSNAME_SHORT)
     345             :         {
     346           9 :             return CPLStrdup(
     347           9 :                 CPLSPrintf(" srsName=\"%s:%s\"", pszAuthName, pszAuthCode));
     348             :         }
     349          95 :         else if (eSRSNameFormat == SRSNAME_OGC_URN)
     350             :         {
     351          74 :             return CPLStrdup(CPLSPrintf(" srsName=\"urn:ogc:def:crs:%s::%s\"",
     352          74 :                                         pszAuthName, pszAuthCode));
     353             :         }
     354          21 :         else if (eSRSNameFormat == SRSNAME_OGC_URL)
     355             :         {
     356          21 :             return CPLStrdup(CPLSPrintf(
     357             :                 " srsName=\"http://www.opengis.net/def/crs/%s/0/%s\"",
     358          21 :                 pszAuthName, pszAuthCode));
     359             :         }
     360             :     }
     361           0 :     return CPLStrdup("");
     362             : }
     363             : 
     364             : /************************************************************************/
     365             : /*                       GML_IsLegitSRSName()                           */
     366             : /************************************************************************/
     367             : 
     368          98 : bool GML_IsLegitSRSName(const char *pszSRSName)
     369             : {
     370             : 
     371          98 :     if (STARTS_WITH_CI(pszSRSName, "http"))
     372             :     {
     373           6 :         if (!(STARTS_WITH_CI(pszSRSName, "http://opengis.net/def/crs") ||
     374           6 :               STARTS_WITH_CI(pszSRSName, "http://www.opengis.net/def/crs")))
     375             :         {
     376           0 :             return false;
     377             :         }
     378             :     }
     379          98 :     return true;
     380             : }

Generated by: LCOV version 1.14