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 156 75.0 %
Date: 2025-12-11 01:36:05 Functions: 9 9 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_mem_cache.h"
      25             : #include "cpl_string.h"
      26             : #include "ogr_api.h"
      27             : #include "ogr_core.h"
      28             : #include "ogr_p.h"
      29             : #include "ogr_spatialref.h"
      30             : 
      31             : /************************************************************************/
      32             : /*                GML_ExtractSrsNameFromGeometry()                      */
      33             : /************************************************************************/
      34             : 
      35             : const char *
      36        2090 : GML_ExtractSrsNameFromGeometry(const CPLXMLNode *const *papsGeometry,
      37             :                                std::string &osWork, bool bConsiderEPSGAsURN)
      38             : {
      39        2090 :     if (papsGeometry[0] != nullptr && papsGeometry[1] == nullptr)
      40             :     {
      41             :         const char *pszSRSName =
      42        2090 :             CPLGetXMLValue(papsGeometry[0], "srsName", nullptr);
      43        2090 :         if (pszSRSName)
      44             :         {
      45         553 :             const int nLen = static_cast<int>(strlen(pszSRSName));
      46             : 
      47         553 :             if (STARTS_WITH(pszSRSName, "EPSG:") && bConsiderEPSGAsURN)
      48             :             {
      49          36 :                 osWork.reserve(22 + nLen - 5);
      50          36 :                 osWork.assign("urn:ogc:def:crs:EPSG::", 22);
      51          36 :                 osWork.append(pszSRSName + 5, nLen - 5);
      52          36 :                 return osWork.c_str();
      53             :             }
      54         517 :             else if (STARTS_WITH(pszSRSName,
      55             :                                  "http://www.opengis.net/gml/srs/epsg.xml#"))
      56             :             {
      57          47 :                 osWork.reserve(5 + nLen - 40);
      58          47 :                 osWork.assign("EPSG:", 5);
      59          47 :                 osWork.append(pszSRSName + 40, nLen - 40);
      60          47 :                 return osWork.c_str();
      61             :             }
      62             :             else
      63             :             {
      64         470 :                 return pszSRSName;
      65             :             }
      66             :         }
      67             :     }
      68        1537 :     return nullptr;
      69             : }
      70             : 
      71             : /************************************************************************/
      72             : /*                       GML_IsSRSLatLongOrder()                        */
      73             : /************************************************************************/
      74             : 
      75         248 : bool GML_IsSRSLatLongOrder(const char *pszSRSName)
      76             : {
      77         248 :     if (pszSRSName == nullptr)
      78           0 :         return false;
      79             : 
      80         248 :     if (STARTS_WITH(pszSRSName, "urn:") &&
      81         227 :         strstr(pszSRSName, ":4326") != nullptr)
      82             :     {
      83             :         // Shortcut.
      84         190 :         return true;
      85             :     }
      86          58 :     else if (!EQUALN(pszSRSName, "EPSG:", 5))
      87             :     {
      88          40 :         OGRSpatialReference oSRS;
      89          40 :         if (oSRS.SetFromUserInput(
      90             :                 pszSRSName,
      91          40 :                 OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()) ==
      92             :             OGRERR_NONE)
      93             :         {
      94          77 :             if (oSRS.EPSGTreatsAsLatLong() ||
      95          37 :                 oSRS.EPSGTreatsAsNorthingEasting())
      96           5 :                 return true;
      97             :         }
      98             :     }
      99          53 :     return false;
     100             : }
     101             : 
     102             : /************************************************************************/
     103             : /*              OGRGML_SRSCacheEntry::~OGRGML_SRSCacheEntry()           */
     104             : /************************************************************************/
     105             : 
     106         724 : OGRGML_SRSCacheEntry::~OGRGML_SRSCacheEntry()
     107             : {
     108         362 :     if (poSRS)
     109         362 :         poSRS->Release();
     110         362 : }
     111             : 
     112             : /************************************************************************/
     113             : /*                             OGRGML_SRSCache                          */
     114             : /************************************************************************/
     115             : 
     116             : class OGRGML_SRSCache
     117             : {
     118             :   public:
     119             :     lru11::Cache<std::string, std::shared_ptr<OGRGML_SRSCacheEntry>>
     120             :         oSRSCache{};
     121             : };
     122             : 
     123             : /************************************************************************/
     124             : /*                        OGRGML_SRSCache_Create()                      */
     125             : /************************************************************************/
     126             : 
     127        1951 : OGRGML_SRSCache *OGRGML_SRSCache_Create()
     128             : {
     129        1951 :     return new OGRGML_SRSCache();
     130             : }
     131             : 
     132             : /************************************************************************/
     133             : /*                        OGRGML_SRSCache_Destroy()                     */
     134             : /************************************************************************/
     135             : 
     136        1951 : void OGRGML_SRSCache_Destroy(OGRGML_SRSCache *hSRSCache)
     137             : {
     138        1951 :     delete hSRSCache;
     139        1951 : }
     140             : 
     141             : /************************************************************************/
     142             : /*                         OGRGML_SRSCache_GetInfo()                    */
     143             : /************************************************************************/
     144             : 
     145             : std::shared_ptr<OGRGML_SRSCacheEntry>
     146        2099 : OGRGML_SRSCache_GetInfo(OGRGML_SRSCache *hSRSCache, const char *pszSRSName)
     147             : {
     148        2099 :     std::shared_ptr<OGRGML_SRSCacheEntry> entry;
     149        2099 :     if (!hSRSCache->oSRSCache.tryGet(pszSRSName, entry))
     150             :     {
     151         362 :         entry = std::make_shared<OGRGML_SRSCacheEntry>();
     152         362 :         auto poSRS = new OGRSpatialReference();
     153         362 :         entry->poSRS = poSRS;
     154         362 :         poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     155         362 :         if (poSRS->SetFromUserInput(
     156             :                 pszSRSName,
     157         362 :                 OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()) !=
     158             :             OGRERR_NONE)
     159             :         {
     160          35 :             return nullptr;
     161             :         }
     162         327 :         entry->nAxisCount = poSRS->GetAxesCount();
     163         327 :         entry->bIsGeographic = poSRS->IsGeographic();
     164         327 :         entry->bIsProjected = poSRS->IsProjected();
     165         607 :         entry->bInvertedAxisOrder = !STARTS_WITH(pszSRSName, "EPSG:") &&
     166         280 :                                     (poSRS->EPSGTreatsAsLatLong() ||
     167          84 :                                      poSRS->EPSGTreatsAsNorthingEasting());
     168         327 :         entry->dfSemiMajor = poSRS->GetSemiMajor();
     169         327 :         if (entry->bIsProjected)
     170         104 :             entry->dfLinearUnits = poSRS->GetLinearUnits(nullptr);
     171         327 :         if (entry->bIsGeographic)
     172             :         {
     173         223 :             entry->bAngularUnitIsDegree =
     174         223 :                 fabs(poSRS->GetAngularUnits(nullptr) -
     175         223 :                      CPLAtof(SRS_UA_DEGREE_CONV)) < 1e-8;
     176             :         }
     177         327 :         hSRSCache->oSRSCache.insert(pszSRSName, entry);
     178             :     }
     179        2064 :     return entry;
     180             : }
     181             : 
     182             : /************************************************************************/
     183             : /*                 GML_BuildOGRGeometryFromList()                       */
     184             : /************************************************************************/
     185             : 
     186        1408 : OGRGeometry *GML_BuildOGRGeometryFromList(
     187             :     const CPLXMLNode *const *papsGeometry, bool bTryToMakeMultipolygons,
     188             :     bool bInvertAxisOrderIfLatLong, const char *pszDefaultSRSName,
     189             :     bool bConsiderEPSGAsURN, GMLSwapCoordinatesEnum eSwapCoordinates,
     190             :     int nPseudoBoolGetSecondaryGeometryOption, OGRGML_SRSCache *hSRSCache,
     191             :     bool bFaceHoleNegative)
     192             : {
     193        1408 :     OGRGeometry *poGeom = nullptr;
     194        1408 :     OGRGeometryCollection *poCollection = nullptr;
     195             : #ifndef WITHOUT_CPLDEBUG
     196             :     static const bool bDebugGML =
     197        1408 :         EQUAL(CPLGetConfigOption("CPL_DEBUG", ""), "GML");
     198             : #endif
     199        2816 :     for (int i = 0; papsGeometry[i] != nullptr; i++)
     200             :     {
     201             : #ifndef WITHOUT_CPLDEBUG
     202        1408 :         if (bDebugGML)
     203             :         {
     204           0 :             char *pszXML = CPLSerializeXMLTree(papsGeometry[i]);
     205           0 :             CPLDebug("GML", "Parsing: %s", pszXML);
     206           0 :             CPLFree(pszXML);
     207             :         }
     208             : #endif
     209        2816 :         OGRGeometry *poSubGeom = GML2OGRGeometry_XMLNode(
     210        1408 :             papsGeometry[i], nPseudoBoolGetSecondaryGeometryOption, hSRSCache,
     211             :             0, 0, false, true, bFaceHoleNegative);
     212        1408 :         if (poSubGeom)
     213             :         {
     214        1401 :             if (poGeom == nullptr)
     215             :             {
     216        1401 :                 poGeom = poSubGeom;
     217             :             }
     218             :             else
     219             :             {
     220           0 :                 if (poCollection == nullptr)
     221             :                 {
     222           0 :                     if (bTryToMakeMultipolygons &&
     223           0 :                         wkbFlatten(poGeom->getGeometryType()) == wkbPolygon &&
     224           0 :                         wkbFlatten(poSubGeom->getGeometryType()) == wkbPolygon)
     225             :                     {
     226             :                         OGRGeometryCollection *poGeomColl =
     227           0 :                             new OGRMultiPolygon();
     228           0 :                         poGeomColl->addGeometryDirectly(poGeom);
     229           0 :                         poGeomColl->addGeometryDirectly(poSubGeom);
     230           0 :                         poGeom = poGeomColl;
     231             :                     }
     232           0 :                     else if (bTryToMakeMultipolygons &&
     233           0 :                              wkbFlatten(poGeom->getGeometryType()) ==
     234           0 :                                  wkbMultiPolygon &&
     235           0 :                              wkbFlatten(poSubGeom->getGeometryType()) ==
     236             :                                  wkbPolygon)
     237             :                     {
     238           0 :                         OGRMultiPolygon *poGeomColl = poGeom->toMultiPolygon();
     239           0 :                         poGeomColl->addGeometryDirectly(poSubGeom);
     240             :                     }
     241           0 :                     else if (bTryToMakeMultipolygons &&
     242           0 :                              wkbFlatten(poGeom->getGeometryType()) ==
     243           0 :                                  wkbMultiPolygon &&
     244           0 :                              wkbFlatten(poSubGeom->getGeometryType()) ==
     245             :                                  wkbMultiPolygon)
     246             :                     {
     247           0 :                         OGRMultiPolygon *poGeomColl = poGeom->toMultiPolygon();
     248           0 :                         for (auto &&poMember : poSubGeom->toMultiPolygon())
     249             :                         {
     250           0 :                             poGeomColl->addGeometry(poMember);
     251             :                         }
     252           0 :                         delete poSubGeom;
     253             :                     }
     254           0 :                     else if (bTryToMakeMultipolygons &&
     255           0 :                              wkbFlatten(poGeom->getGeometryType()) ==
     256             :                                  wkbMultiPolygon)
     257             :                     {
     258           0 :                         delete poGeom;
     259           0 :                         delete poSubGeom;
     260           0 :                         return GML_BuildOGRGeometryFromList(
     261             :                             papsGeometry, false, bInvertAxisOrderIfLatLong,
     262             :                             pszDefaultSRSName, bConsiderEPSGAsURN,
     263             :                             eSwapCoordinates,
     264           0 :                             nPseudoBoolGetSecondaryGeometryOption, hSRSCache);
     265             :                     }
     266             :                     else
     267             :                     {
     268           0 :                         poCollection = new OGRGeometryCollection();
     269           0 :                         poCollection->addGeometryDirectly(poGeom);
     270           0 :                         poGeom = poCollection;
     271             :                     }
     272             :                 }
     273           0 :                 if (poCollection != nullptr)
     274             :                 {
     275           0 :                     poCollection->addGeometryDirectly(poSubGeom);
     276             :                 }
     277             :             }
     278             :         }
     279             :     }
     280             : 
     281        1408 :     if (poGeom == nullptr)
     282           7 :         return nullptr;
     283             : 
     284        1401 :     std::string osWork;
     285        1401 :     const char *pszSRSName = GML_ExtractSrsNameFromGeometry(
     286             :         papsGeometry, osWork, bConsiderEPSGAsURN);
     287        1401 :     const char *pszNameLookup = pszSRSName;
     288        1401 :     if (pszNameLookup == nullptr)
     289         993 :         pszNameLookup = pszDefaultSRSName;
     290             : 
     291        1401 :     if (pszNameLookup != nullptr)
     292             :     {
     293         870 :         auto entry = OGRGML_SRSCache_GetInfo(hSRSCache, pszNameLookup);
     294         435 :         if (entry)
     295             :         {
     296         435 :             poGeom->assignSpatialReference(entry->poSRS);
     297         866 :             if ((eSwapCoordinates == GML_SWAP_AUTO &&
     298         435 :                  entry->bInvertedAxisOrder && bInvertAxisOrderIfLatLong) ||
     299             :                 eSwapCoordinates == GML_SWAP_YES)
     300             :             {
     301         275 :                 poGeom->swapXY();
     302             :             }
     303             :         }
     304             :     }
     305         966 :     else if (eSwapCoordinates == GML_SWAP_YES)
     306             :     {
     307           1 :         poGeom->swapXY();
     308             :     }
     309             : 
     310        1401 :     return poGeom;
     311             : }
     312             : 
     313             : /************************************************************************/
     314             : /*                           GML_GetSRSName()                           */
     315             : /************************************************************************/
     316             : 
     317         220 : char *GML_GetSRSName(const OGRSpatialReference *poSRS,
     318             :                      OGRGMLSRSNameFormat eSRSNameFormat, bool *pbCoordSwap)
     319             : {
     320         220 :     *pbCoordSwap = false;
     321         220 :     if (poSRS == nullptr)
     322         116 :         return CPLStrdup("");
     323             : 
     324         104 :     const auto &map = poSRS->GetDataAxisToSRSAxisMapping();
     325         146 :     if (eSRSNameFormat != SRSNAME_SHORT && map.size() >= 2 && map[0] == 2 &&
     326          42 :         map[1] == 1)
     327             :     {
     328          42 :         *pbCoordSwap = true;
     329             :     }
     330             : 
     331         104 :     const char *pszAuthName = poSRS->GetAuthorityName(nullptr);
     332         104 :     const char *pszAuthCode = poSRS->GetAuthorityCode(nullptr);
     333         104 :     if (nullptr != pszAuthName && nullptr != pszAuthCode)
     334             :     {
     335         104 :         if (eSRSNameFormat == SRSNAME_SHORT)
     336             :         {
     337           9 :             return CPLStrdup(
     338           9 :                 CPLSPrintf(" srsName=\"%s:%s\"", pszAuthName, pszAuthCode));
     339             :         }
     340          95 :         else if (eSRSNameFormat == SRSNAME_OGC_URN)
     341             :         {
     342          74 :             return CPLStrdup(CPLSPrintf(" srsName=\"urn:ogc:def:crs:%s::%s\"",
     343          74 :                                         pszAuthName, pszAuthCode));
     344             :         }
     345          21 :         else if (eSRSNameFormat == SRSNAME_OGC_URL)
     346             :         {
     347          21 :             return CPLStrdup(CPLSPrintf(
     348             :                 " srsName=\"http://www.opengis.net/def/crs/%s/0/%s\"",
     349          21 :                 pszAuthName, pszAuthCode));
     350             :         }
     351             :     }
     352           0 :     return CPLStrdup("");
     353             : }
     354             : 
     355             : /************************************************************************/
     356             : /*                       GML_IsLegitSRSName()                           */
     357             : /************************************************************************/
     358             : 
     359          98 : bool GML_IsLegitSRSName(const char *pszSRSName)
     360             : {
     361             : 
     362          98 :     if (STARTS_WITH_CI(pszSRSName, "http"))
     363             :     {
     364           6 :         if (!(STARTS_WITH_CI(pszSRSName, "http://opengis.net/def/crs") ||
     365           6 :               STARTS_WITH_CI(pszSRSName, "http://www.opengis.net/def/crs")))
     366             :         {
     367           0 :             return false;
     368             :         }
     369             :     }
     370          98 :     return true;
     371             : }

Generated by: LCOV version 1.14