LCOV - code coverage report
Current view: top level - gcore - gdaljp2abstractdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 305 319 95.6 %
Date: 2025-07-01 22:47:05 Functions: 9 10 90.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  GDALGeorefPamDataset with helper to read georeferencing and other
       5             :  *           metadata from JP2Boxes
       6             :  * Author:   Even Rouault <even dot rouault at spatialys.com>
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 2002, Frank Warmerdam <warmerdam@pobox.com>
      10             :  * Copyright (c) 2013, Even Rouault <even dot rouault at spatialys.com>
      11             :  *
      12             :  * SPDX-License-Identifier: MIT
      13             :  ****************************************************************************/
      14             : 
      15             : #include "cpl_port.h"
      16             : #include "gdaljp2abstractdataset.h"
      17             : 
      18             : #include <cstring>
      19             : 
      20             : #include <string>
      21             : 
      22             : #include "cpl_conv.h"
      23             : #include "cpl_error.h"
      24             : #include "cpl_minixml.h"
      25             : #include "cpl_string.h"
      26             : #include "cpl_vsi.h"
      27             : #include "gdal.h"
      28             : #include "gdal_mdreader.h"
      29             : #include "gdal_priv.h"
      30             : #include "gdaljp2metadata.h"
      31             : #include "ogrsf_frmts.h"
      32             : #include "memdataset.h"
      33             : 
      34             : /*! @cond Doxygen_Suppress */
      35             : 
      36             : /************************************************************************/
      37             : /*                     GDALJP2AbstractDataset()                         */
      38             : /************************************************************************/
      39             : 
      40             : GDALJP2AbstractDataset::GDALJP2AbstractDataset() = default;
      41             : 
      42             : /************************************************************************/
      43             : /*                     ~GDALJP2AbstractDataset()                        */
      44             : /************************************************************************/
      45             : 
      46        2425 : GDALJP2AbstractDataset::~GDALJP2AbstractDataset()
      47             : {
      48        2425 :     CPLFree(pszWldFilename);
      49        2424 :     GDALJP2AbstractDataset::CloseDependentDatasets();
      50        2425 :     CSLDestroy(papszMetadataFiles);
      51        2425 : }
      52             : 
      53             : /************************************************************************/
      54             : /*                      CloseDependentDatasets()                        */
      55             : /************************************************************************/
      56             : 
      57        4346 : int GDALJP2AbstractDataset::CloseDependentDatasets()
      58             : {
      59             :     const bool bRet =
      60        4346 :         CPL_TO_BOOL(GDALGeorefPamDataset::CloseDependentDatasets());
      61        4346 :     if (poMemDS == nullptr)
      62        4343 :         return bRet;
      63             : 
      64           3 :     GDALClose(poMemDS);
      65           3 :     poMemDS = nullptr;
      66           3 :     return true;
      67             : }
      68             : 
      69             : /************************************************************************/
      70             : /*                          LoadJP2Metadata()                           */
      71             : /************************************************************************/
      72             : 
      73        1186 : void GDALJP2AbstractDataset::LoadJP2Metadata(GDALOpenInfo *poOpenInfo,
      74             :                                              const char *pszOverrideFilenameIn,
      75             :                                              VSILFILE *fpBox)
      76             : {
      77        1186 :     const char *pszOverrideFilename = pszOverrideFilenameIn;
      78        1186 :     if (pszOverrideFilename == nullptr)
      79        1097 :         pszOverrideFilename = poOpenInfo->pszFilename;
      80             : 
      81             :     /* -------------------------------------------------------------------- */
      82             :     /*      Identify authorized georeferencing sources                      */
      83             :     /* -------------------------------------------------------------------- */
      84             :     const char *pszGeorefSourcesOption =
      85        1186 :         CSLFetchNameValue(poOpenInfo->papszOpenOptions, "GEOREF_SOURCES");
      86        1185 :     bool bGeorefSourcesConfigOption = pszGeorefSourcesOption != nullptr;
      87             :     CPLString osGeorefSources =
      88             :         (pszGeorefSourcesOption) ? pszGeorefSourcesOption
      89        1161 :                                  : CPLGetConfigOption("GDAL_GEOREF_SOURCES",
      90        3533 :                                                       "PAM,INTERNAL,WORLDFILE");
      91        1185 :     size_t nInternalIdx = osGeorefSources.ifind("INTERNAL");
      92        1165 :     if (nInternalIdx != std::string::npos &&
      93        4660 :         (nInternalIdx == 0 || osGeorefSources[nInternalIdx - 1] == ',') &&
      94        1166 :         (nInternalIdx + strlen("INTERNAL") == osGeorefSources.size() ||
      95        1155 :          osGeorefSources[nInternalIdx + strlen("INTERNAL")] == ','))
      96             :     {
      97             :         osGeorefSources.replace(nInternalIdx, strlen("INTERNAL"),
      98        1165 :                                 "GEOJP2,GMLJP2,MSIG");
      99             :     }
     100        2372 :     const CPLStringList aosTokens(CSLTokenizeString2(osGeorefSources, ",", 0));
     101        1183 :     m_bGotPAMGeorefSrcIndex = true;
     102        1183 :     m_nPAMGeorefSrcIndex = aosTokens.FindString("PAM");
     103        1184 :     const int nGEOJP2Index = aosTokens.FindString("GEOJP2");
     104        1185 :     const int nGMLJP2Index = aosTokens.FindString("GMLJP2");
     105        1186 :     const int nMSIGIndex = aosTokens.FindString("MSIG");
     106        1185 :     m_nWORLDFILEIndex = aosTokens.FindString("WORLDFILE");
     107             : 
     108        1185 :     if (bGeorefSourcesConfigOption)
     109             :     {
     110          92 :         for (const char *pszToken : aosTokens)
     111             :         {
     112          68 :             if (!EQUAL(pszToken, "PAM") && !EQUAL(pszToken, "GEOJP2") &&
     113          42 :                 !EQUAL(pszToken, "GMLJP2") && !EQUAL(pszToken, "MSIG") &&
     114          14 :                 !EQUAL(pszToken, "WORLDFILE") && !EQUAL(pszToken, "NONE"))
     115             :             {
     116           1 :                 CPLError(CE_Warning, CPLE_AppDefined,
     117             :                          "Unhandled value %s in GEOREF_SOURCES", pszToken);
     118             :             }
     119             :         }
     120             :     }
     121             : 
     122             :     /* -------------------------------------------------------------------- */
     123             :     /*      Check for georeferencing information.                           */
     124             :     /* -------------------------------------------------------------------- */
     125        2371 :     GDALJP2Metadata oJP2Geo;
     126        1186 :     int nIndexUsed = -1;
     127        1186 :     if ((((fpBox != nullptr || poOpenInfo->fpL != nullptr) &&
     128        1096 :           pszOverrideFilenameIn == nullptr &&
     129        1097 :           oJP2Geo.ReadAndParse(fpBox ? fpBox : poOpenInfo->fpL, nGEOJP2Index,
     130         620 :                                nGMLJP2Index, nMSIGIndex, &nIndexUsed)) ||
     131         620 :          (!((fpBox != nullptr || poOpenInfo->fpL != nullptr) &&
     132          89 :             pszOverrideFilenameIn == nullptr) &&
     133          89 :           oJP2Geo.ReadAndParse(pszOverrideFilename, nGEOJP2Index, nGMLJP2Index,
     134        2372 :                                nMSIGIndex, m_nWORLDFILEIndex, &nIndexUsed))) &&
     135           1 :         (nGMLJP2Index >= 0 || nGEOJP2Index >= 0 || nMSIGIndex >= 0 ||
     136           0 :          m_nWORLDFILEIndex >= 0))
     137             :     {
     138         617 :         m_oSRS = oJP2Geo.m_oSRS;
     139         617 :         if (!m_oSRS.IsEmpty())
     140         614 :             m_nProjectionGeorefSrcIndex = nIndexUsed;
     141         617 :         bGeoTransformValid = oJP2Geo.m_bHaveGeoTransform;
     142         617 :         if (bGeoTransformValid)
     143         585 :             m_nGeoTransformGeorefSrcIndex = nIndexUsed;
     144         617 :         m_gt = oJP2Geo.m_gt;
     145         617 :         nGCPCount = oJP2Geo.nGCPCount;
     146         617 :         if (nGCPCount)
     147          13 :             m_nGCPGeorefSrcIndex = nIndexUsed;
     148         617 :         pasGCPList = GDALDuplicateGCPs(oJP2Geo.nGCPCount, oJP2Geo.pasGCPList);
     149             : 
     150         617 :         if (oJP2Geo.bPixelIsPoint)
     151             :         {
     152           5 :             m_bPixelIsPoint = true;
     153           5 :             m_nPixelIsPointGeorefSrcIndex = nIndexUsed;
     154             :         }
     155         617 :         if (oJP2Geo.papszRPCMD)
     156             :         {
     157           2 :             m_papszRPC = CSLDuplicate(oJP2Geo.papszRPCMD);
     158           2 :             m_nRPCGeorefSrcIndex = nIndexUsed;
     159             :         }
     160             :     }
     161             : 
     162             :     /* -------------------------------------------------------------------- */
     163             :     /*      Report XML UUID box in a dedicated metadata domain              */
     164             :     /* -------------------------------------------------------------------- */
     165        1185 :     if (oJP2Geo.pszXMPMetadata)
     166             :     {
     167           9 :         char *apszMDList[2] = {oJP2Geo.pszXMPMetadata, nullptr};
     168           9 :         GDALDataset::SetMetadata(apszMDList, "xml:XMP");
     169             :     }
     170             : 
     171             :     /* -------------------------------------------------------------------- */
     172             :     /*      Do we have any XML boxes we would like to treat as special      */
     173             :     /*      domain metadata? (Note: the GDAL multidomain metadata XML box   */
     174             :     /*      has been excluded and is dealt a few lines below.               */
     175             :     /* -------------------------------------------------------------------- */
     176             : 
     177        1477 :     for (int iBox = 0;
     178        1477 :          oJP2Geo.papszGMLMetadata && oJP2Geo.papszGMLMetadata[iBox] != nullptr;
     179             :          ++iBox)
     180             :     {
     181         292 :         char *pszName = nullptr;
     182             :         const char *pszXML =
     183         292 :             CPLParseNameValue(oJP2Geo.papszGMLMetadata[iBox], &pszName);
     184         584 :         CPLString osDomain;
     185         292 :         osDomain.Printf("xml:%s", pszName);
     186         292 :         char *apszMDList[2] = {const_cast<char *>(pszXML), nullptr};
     187             : 
     188         292 :         GDALDataset::SetMetadata(apszMDList, osDomain);
     189             : 
     190         292 :         CPLFree(pszName);
     191             :     }
     192             : 
     193             :     /* -------------------------------------------------------------------- */
     194             :     /*      Do we have GDAL metadata?                                       */
     195             :     /* -------------------------------------------------------------------- */
     196        1185 :     if (oJP2Geo.pszGDALMultiDomainMetadata != nullptr)
     197             :     {
     198          19 :         CPLErr eLastErr = CPLGetLastErrorType();
     199          19 :         int nLastErrNo = CPLGetLastErrorNo();
     200          38 :         CPLString osLastErrorMsg = CPLGetLastErrorMsg();
     201             :         CPLXMLNode *psXMLNode =
     202          19 :             CPLParseXMLString(oJP2Geo.pszGDALMultiDomainMetadata);
     203          19 :         if (CPLGetLastErrorType() == CE_None && eLastErr != CE_None)
     204           0 :             CPLErrorSetState(eLastErr, nLastErrNo, osLastErrorMsg.c_str());
     205             : 
     206          19 :         if (psXMLNode)
     207             :         {
     208          38 :             GDALMultiDomainMetadata oLocalMDMD;
     209          19 :             oLocalMDMD.XMLInit(psXMLNode, FALSE);
     210          19 :             GDALDataset::SetMetadata(oLocalMDMD.GetMetadata());
     211          19 :             for (const char *pszDomain : cpl::Iterate(
     212          57 :                      static_cast<CSLConstList>(oLocalMDMD.GetDomainList())))
     213             :             {
     214          19 :                 if (!EQUAL(pszDomain, "") &&
     215           8 :                     !EQUAL(pszDomain, "IMAGE_STRUCTURE"))
     216             :                 {
     217           8 :                     if (GDALDataset::GetMetadata(pszDomain) != nullptr)
     218             :                     {
     219           0 :                         CPLDebug(
     220             :                             "GDALJP2",
     221             :                             "GDAL metadata overrides metadata in %s domain "
     222             :                             "over metadata read from other boxes",
     223             :                             pszDomain);
     224             :                     }
     225           8 :                     GDALDataset::SetMetadata(oLocalMDMD.GetMetadata(pszDomain),
     226             :                                              pszDomain);
     227             :                 }
     228             :             }
     229          19 :             CPLDestroyXMLNode(psXMLNode);
     230             :         }
     231             :         else
     232             :         {
     233           0 :             CPLErrorReset();
     234             :         }
     235             :     }
     236             : 
     237             :     /* -------------------------------------------------------------------- */
     238             :     /*      Do we have other misc metadata (from resd box for now) ?        */
     239             :     /* -------------------------------------------------------------------- */
     240        1185 :     if (oJP2Geo.papszMetadata != nullptr)
     241             :     {
     242          16 :         char **papszMD = CSLDuplicate(GDALDataset::GetMetadata());
     243             : 
     244          16 :         papszMD = CSLMerge(papszMD, oJP2Geo.papszMetadata);
     245          16 :         GDALDataset::SetMetadata(papszMD);
     246             : 
     247          16 :         CSLDestroy(papszMD);
     248             :     }
     249             : 
     250             :     /* -------------------------------------------------------------------- */
     251             :     /*      Do we have XML IPR ?                                            */
     252             :     /* -------------------------------------------------------------------- */
     253        1185 :     if (oJP2Geo.pszXMLIPR != nullptr)
     254             :     {
     255           5 :         char *apszMD[2] = {oJP2Geo.pszXMLIPR, nullptr};
     256           5 :         GDALDataset::SetMetadata(apszMD, "xml:IPR");
     257             :     }
     258             : 
     259             :     /* -------------------------------------------------------------------- */
     260             :     /*      Check for world file.                                           */
     261             :     /* -------------------------------------------------------------------- */
     262        1185 :     if (m_nWORLDFILEIndex >= 0 &&
     263        1169 :         ((bGeoTransformValid &&
     264         577 :           m_nWORLDFILEIndex < m_nGeoTransformGeorefSrcIndex) ||
     265        1164 :          !bGeoTransformValid))
     266             :     {
     267         598 :         bGeoTransformValid |=
     268        1793 :             GDALReadWorldFile2(pszOverrideFilename, nullptr, m_gt,
     269         597 :                                poOpenInfo->GetSiblingFiles(),
     270        1177 :                                &pszWldFilename) ||
     271         579 :             GDALReadWorldFile2(pszOverrideFilename, ".wld", m_gt,
     272         579 :                                poOpenInfo->GetSiblingFiles(), &pszWldFilename);
     273         598 :         if (bGeoTransformValid)
     274             :         {
     275          21 :             m_nGeoTransformGeorefSrcIndex = m_nWORLDFILEIndex;
     276          21 :             m_bPixelIsPoint = false;
     277          21 :             m_nPixelIsPointGeorefSrcIndex = -1;
     278             :         }
     279             :     }
     280             : 
     281        2372 :     GDALMDReaderManager mdreadermanager;
     282        1186 :     GDALMDReaderBase *mdreader = mdreadermanager.GetReader(
     283        1186 :         poOpenInfo->pszFilename, poOpenInfo->GetSiblingFiles(), MDR_ANY);
     284        1186 :     if (nullptr != mdreader)
     285             :     {
     286           3 :         mdreader->FillMetadata(&(oMDMD));
     287           3 :         papszMetadataFiles = mdreader->GetMetadataFiles();
     288             :     }
     289        1186 : }
     290             : 
     291             : /************************************************************************/
     292             : /*                            GetFileList()                             */
     293             : /************************************************************************/
     294             : 
     295          69 : char **GDALJP2AbstractDataset::GetFileList()
     296             : 
     297             : {
     298          69 :     char **papszFileList = GDALGeorefPamDataset::GetFileList();
     299             : 
     300         139 :     if (pszWldFilename != nullptr &&
     301           1 :         m_nGeoTransformGeorefSrcIndex == m_nWORLDFILEIndex &&
     302          71 :         GDALCanReliablyUseSiblingFileList(pszWldFilename) &&
     303           1 :         CSLFindString(papszFileList, pszWldFilename) == -1)
     304             :     {
     305           1 :         GDALGeoTransform l_gt;
     306           1 :         GetGeoTransform(l_gt);
     307             :         // GetGeoTransform() can modify m_nGeoTransformGeorefSrcIndex
     308             :         // cppcheck-suppress knownConditionTrueFalse
     309           1 :         if (m_nGeoTransformGeorefSrcIndex == m_nWORLDFILEIndex)
     310             :         {
     311           0 :             papszFileList = CSLAddString(papszFileList, pszWldFilename);
     312             :         }
     313             :     }
     314          69 :     if (papszMetadataFiles != nullptr)
     315             :     {
     316           6 :         for (int i = 0; papszMetadataFiles[i] != nullptr; ++i)
     317             :         {
     318           4 :             papszFileList = CSLAddString(papszFileList, papszMetadataFiles[i]);
     319             :         }
     320             :     }
     321          69 :     return papszFileList;
     322             : }
     323             : 
     324             : /************************************************************************/
     325             : /*                        LoadVectorLayers()                            */
     326             : /************************************************************************/
     327             : 
     328          67 : void GDALJP2AbstractDataset::LoadVectorLayers(int bOpenRemoteResources)
     329             : {
     330          67 :     char **papszGMLJP2 = GetMetadata("xml:gml.root-instance");
     331          67 :     if (papszGMLJP2 == nullptr)
     332          63 :         return;
     333             : 
     334          40 :     CPLErr eLastErr = CPLGetLastErrorType();
     335          40 :     int nLastErrNo = CPLGetLastErrorNo();
     336          40 :     CPLString osLastErrorMsg = CPLGetLastErrorMsg();
     337          40 :     CPLXMLNode *const psRoot = CPLParseXMLString(papszGMLJP2[0]);
     338          40 :     if (CPLGetLastErrorType() == CE_None && eLastErr != CE_None)
     339           0 :         CPLErrorSetState(eLastErr, nLastErrNo, osLastErrorMsg.c_str());
     340             : 
     341          40 :     if (psRoot == nullptr)
     342           0 :         return;
     343             :     CPLXMLNode *const psCC =
     344          40 :         CPLGetXMLNode(psRoot, "=gmljp2:GMLJP2CoverageCollection");
     345          40 :     if (psCC == nullptr)
     346             :     {
     347          36 :         CPLDestroyXMLNode(psRoot);
     348          36 :         return;
     349             :     }
     350             : 
     351           8 :     const std::string osTmpDir = VSIMemGenerateHiddenFilename("gmljp2");
     352             : 
     353             :     // Find feature collections.
     354           4 :     int nLayersAtCC = 0;
     355           4 :     int nLayersAtGC = 0;
     356             :     // CPLXMLNode* psCCChildIter = psCC->psChild;
     357          66 :     for (CPLXMLNode *psCCChildIter = psCC->psChild; psCCChildIter != nullptr;
     358          62 :          psCCChildIter = psCCChildIter->psNext)
     359             :     {
     360          62 :         if (psCCChildIter->eType != CXT_Element ||
     361          31 :             strcmp(psCCChildIter->pszValue, "gmljp2:featureMember") != 0 ||
     362          10 :             psCCChildIter->psChild == nullptr ||
     363          10 :             psCCChildIter->psChild->eType != CXT_Element)
     364          52 :             continue;
     365             : 
     366          10 :         CPLXMLNode *const psGCorGMLJP2Features = psCCChildIter->psChild;
     367          10 :         bool bIsGC =
     368          10 :             strstr(psGCorGMLJP2Features->pszValue, "GridCoverage") != nullptr;
     369             : 
     370          10 :         for (CPLXMLNode *psGCorGMLJP2FeaturesChildIter =
     371             :                  psGCorGMLJP2Features->psChild;
     372          48 :              psGCorGMLJP2FeaturesChildIter != nullptr;
     373          38 :              psGCorGMLJP2FeaturesChildIter =
     374             :                  psGCorGMLJP2FeaturesChildIter->psNext)
     375             :         {
     376          38 :             if (psGCorGMLJP2FeaturesChildIter->eType != CXT_Element ||
     377          28 :                 strcmp(psGCorGMLJP2FeaturesChildIter->pszValue,
     378           8 :                        "gmljp2:feature") != 0 ||
     379           8 :                 psGCorGMLJP2FeaturesChildIter->psChild == nullptr)
     380          31 :                 continue;
     381             : 
     382           8 :             CPLXMLNode *psFC = nullptr;
     383           8 :             bool bFreeFC = false;
     384             : 
     385           8 :             CPLXMLNode *const psChild = psGCorGMLJP2FeaturesChildIter->psChild;
     386           8 :             if (psChild->eType == CXT_Attribute &&
     387           6 :                 strcmp(psChild->pszValue, "xlink:href") == 0 &&
     388           6 :                 STARTS_WITH(psChild->psChild->pszValue, "gmljp2://xml/"))
     389             :             {
     390           4 :                 const char *const pszBoxName =
     391           4 :                     psChild->psChild->pszValue + strlen("gmljp2://xml/");
     392             :                 char **papszBoxData =
     393           4 :                     GetMetadata(CPLSPrintf("xml:%s", pszBoxName));
     394           4 :                 if (papszBoxData != nullptr)
     395             :                 {
     396           4 :                     psFC = CPLParseXMLString(papszBoxData[0]);
     397           4 :                     bFreeFC = true;
     398             :                 }
     399             :                 else
     400             :                 {
     401           0 :                     CPLDebug("GMLJP2",
     402             :                              "gmljp2:feature references %s, "
     403             :                              "but no corresponding box found",
     404           0 :                              psChild->psChild->pszValue);
     405             :                 }
     406             :             }
     407             : 
     408           8 :             CPLString osGMLTmpFile;
     409           8 :             if (psChild->eType == CXT_Attribute &&
     410           6 :                 strcmp(psChild->pszValue, "xlink:href") == 0 &&
     411           6 :                 (STARTS_WITH(psChild->psChild->pszValue, "http://") ||
     412           6 :                  STARTS_WITH(psChild->psChild->pszValue, "https://")))
     413             :             {
     414           2 :                 if (!bOpenRemoteResources)
     415           1 :                     CPLDebug(
     416             :                         "GMLJP2",
     417             :                         "Remote feature collection %s mentioned in GMLJP2 box",
     418           1 :                         psChild->psChild->pszValue);
     419             :                 else
     420             :                     osGMLTmpFile =
     421           1 :                         "/vsicurl/" + CPLString(psChild->psChild->pszValue);
     422             :             }
     423           6 :             else if (psChild->eType == CXT_Element &&
     424           2 :                      strstr(psChild->pszValue, "FeatureCollection") != nullptr)
     425             :             {
     426           2 :                 psFC = psChild;
     427             :             }
     428             : 
     429           8 :             if (psFC == nullptr && osGMLTmpFile.empty())
     430             :             {
     431           1 :                 continue;
     432             :             }
     433             : 
     434           7 :             if (psFC != nullptr)
     435             :             {
     436             :                 osGMLTmpFile =
     437           6 :                     CPLFormFilenameSafe(osTmpDir.c_str(), "my.gml", nullptr);
     438             :                 // Create temporary .gml file.
     439           6 :                 CPLSerializeXMLTreeToFile(psFC, osGMLTmpFile);
     440             :             }
     441             : 
     442           7 :             CPLDebug("GMLJP2", "Found a FeatureCollection at %s level",
     443             :                      bIsGC ? "GridCoverage" : "CoverageCollection");
     444             : 
     445          14 :             CPLString osXSDTmpFile;
     446             : 
     447           7 :             if (psFC)
     448             :             {
     449             :                 // Try to localize its .xsd schema in a GMLJP2 auxiliary box
     450             :                 const char *const pszSchemaLocation =
     451           6 :                     CPLGetXMLValue(psFC, "xsi:schemaLocation", nullptr);
     452           6 :                 if (pszSchemaLocation)
     453             :                 {
     454           5 :                     char **papszTokens = CSLTokenizeString2(
     455             :                         pszSchemaLocation, " \t\n",
     456             :                         CSLT_HONOURSTRINGS | CSLT_STRIPLEADSPACES |
     457             :                             CSLT_STRIPENDSPACES);
     458             : 
     459           5 :                     if ((CSLCount(papszTokens) % 2) == 0)
     460             :                     {
     461           5 :                         for (char **papszIter = papszTokens;
     462           6 :                              *papszIter != nullptr; papszIter += 2)
     463             :                         {
     464           6 :                             if (STARTS_WITH(papszIter[1], "gmljp2://xml/"))
     465             :                             {
     466           5 :                                 const char *pszBoxName =
     467           5 :                                     papszIter[1] + strlen("gmljp2://xml/");
     468           5 :                                 char **papszBoxData = GetMetadata(
     469           5 :                                     CPLSPrintf("xml:%s", pszBoxName));
     470           5 :                                 if (papszBoxData != nullptr)
     471             :                                 {
     472           5 :                                     osXSDTmpFile = CPLFormFilenameSafe(
     473           5 :                                         osTmpDir.c_str(), "my.xsd", nullptr);
     474           5 :                                     CPL_IGNORE_RET_VAL(
     475           5 :                                         VSIFCloseL(VSIFileFromMemBuffer(
     476             :                                             osXSDTmpFile,
     477             :                                             reinterpret_cast<GByte *>(
     478             :                                                 papszBoxData[0]),
     479           5 :                                             strlen(papszBoxData[0]), FALSE)));
     480             :                                 }
     481             :                                 else
     482             :                                 {
     483           0 :                                     CPLDebug(
     484             :                                         "GMLJP2",
     485             :                                         "Feature collection references %s, "
     486             :                                         "but no corresponding box found",
     487           0 :                                         papszIter[1]);
     488             :                                 }
     489           5 :                                 break;
     490             :                             }
     491             :                         }
     492             :                     }
     493           5 :                     CSLDestroy(papszTokens);
     494             :                 }
     495           6 :                 if (bFreeFC)
     496             :                 {
     497           4 :                     CPLDestroyXMLNode(psFC);
     498           4 :                     psFC = nullptr;
     499             :                 }
     500             :             }
     501             : 
     502           7 :             GDALDriverH hDrv = GDALIdentifyDriver(osGMLTmpFile, nullptr);
     503           7 :             GDALDriverH hGMLDrv = GDALGetDriverByName("GML");
     504           7 :             if (hDrv != nullptr && hDrv == hGMLDrv)
     505             :             {
     506           7 :                 char *apszOpenOptions[2] = {
     507             :                     const_cast<char *>("FORCE_SRS_DETECTION=YES"), nullptr};
     508             :                 GDALDatasetUniquePtr poTmpDS(
     509             :                     GDALDataset::Open(osGMLTmpFile, GDAL_OF_VECTOR, nullptr,
     510          14 :                                       apszOpenOptions, nullptr));
     511           7 :                 if (poTmpDS)
     512             :                 {
     513           7 :                     int nLayers = poTmpDS->GetLayerCount();
     514          14 :                     for (int i = 0; i < nLayers; ++i)
     515             :                     {
     516           7 :                         if (poMemDS == nullptr)
     517           3 :                             poMemDS = MEMDataset::Create("", 0, 0, 0,
     518             :                                                          GDT_Unknown, nullptr);
     519           7 :                         OGRLayer *poSrcLyr = poTmpDS->GetLayer(i);
     520             :                         const char *const pszLayerName =
     521             :                             bIsGC
     522           9 :                                 ? CPLSPrintf("FC_GridCoverage_%d_%s",
     523           2 :                                              ++nLayersAtGC, poSrcLyr->GetName())
     524           5 :                                 : CPLSPrintf("FC_CoverageCollection_%d_%s",
     525             :                                              ++nLayersAtCC,
     526           5 :                                              poSrcLyr->GetName());
     527           7 :                         poMemDS->CopyLayer(poSrcLyr, pszLayerName, nullptr);
     528             :                     }
     529           7 :                 }
     530             :             }
     531             :             else
     532             :             {
     533           0 :                 CPLDebug("GMLJP2",
     534             :                          "No GML driver found to read feature collection");
     535             :             }
     536             : 
     537           7 :             VSIRmdirRecursive(osTmpDir.c_str());
     538             :         }
     539             :     }
     540             : 
     541             :     // Find annotations
     542           4 :     int nAnnotations = 0;
     543          66 :     for (CPLXMLNode *psCCChildIter = psCC->psChild; psCCChildIter != nullptr;
     544          62 :          psCCChildIter = psCCChildIter->psNext)
     545             :     {
     546          62 :         if (psCCChildIter->eType != CXT_Element ||
     547          31 :             strcmp(psCCChildIter->pszValue, "gmljp2:featureMember") != 0 ||
     548          10 :             psCCChildIter->psChild == nullptr ||
     549          10 :             psCCChildIter->psChild->eType != CXT_Element)
     550          52 :             continue;
     551          10 :         CPLXMLNode *const psGCorGMLJP2Features = psCCChildIter->psChild;
     552          10 :         bool bIsGC =
     553          10 :             strstr(psGCorGMLJP2Features->pszValue, "GridCoverage") != nullptr;
     554          10 :         if (!bIsGC)
     555           6 :             continue;
     556           4 :         for (CPLXMLNode *psGCorGMLJP2FeaturesChildIter =
     557             :                  psGCorGMLJP2Features->psChild;
     558          30 :              psGCorGMLJP2FeaturesChildIter != nullptr;
     559          26 :              psGCorGMLJP2FeaturesChildIter =
     560             :                  psGCorGMLJP2FeaturesChildIter->psNext)
     561             :         {
     562          26 :             if (psGCorGMLJP2FeaturesChildIter->eType != CXT_Element ||
     563          22 :                 strcmp(psGCorGMLJP2FeaturesChildIter->pszValue,
     564           2 :                        "gmljp2:annotation") != 0 ||
     565           2 :                 psGCorGMLJP2FeaturesChildIter->psChild == nullptr ||
     566           2 :                 psGCorGMLJP2FeaturesChildIter->psChild->eType != CXT_Element ||
     567           2 :                 strstr(psGCorGMLJP2FeaturesChildIter->psChild->pszValue,
     568             :                        "kml") == nullptr)
     569          24 :                 continue;
     570             : 
     571           2 :             CPLDebug("GMLJP2", "Found a KML annotation");
     572             : 
     573             :             // Create temporary .kml file.
     574           2 :             CPLXMLNode *const psKML = psGCorGMLJP2FeaturesChildIter->psChild;
     575             :             const CPLString osKMLTmpFile(
     576           4 :                 VSIMemGenerateHiddenFilename("my.kml"));
     577           2 :             CPLSerializeXMLTreeToFile(psKML, osKMLTmpFile);
     578             : 
     579             :             GDALDatasetUniquePtr poTmpDS(GDALDataset::Open(
     580           4 :                 osKMLTmpFile, GDAL_OF_VECTOR, nullptr, nullptr, nullptr));
     581           2 :             if (poTmpDS)
     582             :             {
     583           2 :                 int nLayers = poTmpDS->GetLayerCount();
     584           3 :                 for (int i = 0; i < nLayers; ++i)
     585             :                 {
     586           1 :                     if (poMemDS == nullptr)
     587           0 :                         poMemDS = MEMDataset::Create("", 0, 0, 0, GDT_Unknown,
     588             :                                                      nullptr);
     589           1 :                     OGRLayer *const poSrcLyr = poTmpDS->GetLayer(i);
     590             :                     const char *pszLayerName =
     591           3 :                         CPLSPrintf("Annotation_%d_%s", ++nAnnotations,
     592           1 :                                    poSrcLyr->GetName());
     593           1 :                     poMemDS->CopyLayer(poSrcLyr, pszLayerName, nullptr);
     594             :                 }
     595             :             }
     596             :             else
     597             :             {
     598           0 :                 CPLDebug("GMLJP2",
     599             :                          "No KML/LIBKML driver found to read annotation");
     600             :             }
     601             : 
     602           2 :             VSIUnlink(osKMLTmpFile);
     603             :         }
     604             :     }
     605             : 
     606           4 :     CPLDestroyXMLNode(psRoot);
     607             : }
     608             : 
     609             : /************************************************************************/
     610             : /*                           GetLayerCount()                            */
     611             : /************************************************************************/
     612             : 
     613        2123 : int GDALJP2AbstractDataset::GetLayerCount()
     614             : {
     615        2123 :     return poMemDS != nullptr ? poMemDS->GetLayerCount() : 0;
     616             : }
     617             : 
     618             : /************************************************************************/
     619             : /*                             GetLayer()                               */
     620             : /************************************************************************/
     621             : 
     622          16 : OGRLayer *GDALJP2AbstractDataset::GetLayer(int i)
     623             : {
     624          16 :     return poMemDS != nullptr ? poMemDS->GetLayer(i) : nullptr;
     625             : }
     626             : 
     627             : /************************************************************************/
     628             : /*                            GetMetadata()                             */
     629             : /************************************************************************/
     630             : 
     631         839 : char **GDALJP2AbstractDataset::GetMetadata(const char *pszDomain)
     632             : {
     633         839 :     if (pszDomain && EQUAL(pszDomain, "IMAGE_STRUCTURE"))
     634             :     {
     635          10 :         if (m_aosImageStructureMetadata.empty())
     636             :         {
     637           8 :             VSILFILE *fp = GetFileHandle();
     638             :             m_aosImageStructureMetadata.Assign(
     639          16 :                 CSLDuplicate(GDALGeorefPamDataset::GetMetadata(pszDomain)),
     640           8 :                 true);
     641          16 :             CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
     642             :             const char *pszReversibility =
     643           8 :                 GDALGetJPEG2000Reversibility(GetDescription(), fp);
     644           8 :             if (pszReversibility)
     645             :                 m_aosImageStructureMetadata.SetNameValue(
     646           8 :                     "COMPRESSION_REVERSIBILITY", pszReversibility);
     647             :         }
     648          10 :         return m_aosImageStructureMetadata.List();
     649             :     }
     650         829 :     return GDALGeorefPamDataset::GetMetadata(pszDomain);
     651             : }
     652             : 
     653             : /************************************************************************/
     654             : /*                        GetMetadataItem()                             */
     655             : /************************************************************************/
     656             : 
     657         446 : const char *GDALJP2AbstractDataset::GetMetadataItem(const char *pszName,
     658             :                                                     const char *pszDomain)
     659             : {
     660         446 :     if (pszDomain && EQUAL(pszDomain, "IMAGE_STRUCTURE") &&
     661          43 :         EQUAL(pszName, "COMPRESSION_REVERSIBILITY"))
     662             :     {
     663           8 :         char **papszMD = GetMetadata(pszDomain);
     664           8 :         return CSLFetchNameValue(papszMD, pszName);
     665             :     }
     666         438 :     return GDALGeorefPamDataset::GetMetadataItem(pszName, pszDomain);
     667             : }
     668             : 
     669             : /*! @endcond */

Generated by: LCOV version 1.14