LCOV - code coverage report
Current view: top level - gcore - gdaljp2abstractdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 305 320 95.3 %
Date: 2024-11-21 22:18:42 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             : 
      33             : /*! @cond Doxygen_Suppress */
      34             : 
      35             : /************************************************************************/
      36             : /*                     GDALJP2AbstractDataset()                         */
      37             : /************************************************************************/
      38             : 
      39             : GDALJP2AbstractDataset::GDALJP2AbstractDataset() = default;
      40             : 
      41             : /************************************************************************/
      42             : /*                     ~GDALJP2AbstractDataset()                        */
      43             : /************************************************************************/
      44             : 
      45        2282 : GDALJP2AbstractDataset::~GDALJP2AbstractDataset()
      46             : {
      47        2282 :     CPLFree(pszWldFilename);
      48        2282 :     GDALJP2AbstractDataset::CloseDependentDatasets();
      49        2282 :     CSLDestroy(papszMetadataFiles);
      50        2282 : }
      51             : 
      52             : /************************************************************************/
      53             : /*                      CloseDependentDatasets()                        */
      54             : /************************************************************************/
      55             : 
      56        4209 : int GDALJP2AbstractDataset::CloseDependentDatasets()
      57             : {
      58             :     const bool bRet =
      59        4209 :         CPL_TO_BOOL(GDALGeorefPamDataset::CloseDependentDatasets());
      60        4209 :     if (poMemDS == nullptr)
      61        4206 :         return bRet;
      62             : 
      63           3 :     GDALClose(poMemDS);
      64           3 :     poMemDS = nullptr;
      65           3 :     return true;
      66             : }
      67             : 
      68             : /************************************************************************/
      69             : /*                          LoadJP2Metadata()                           */
      70             : /************************************************************************/
      71             : 
      72        1045 : void GDALJP2AbstractDataset::LoadJP2Metadata(GDALOpenInfo *poOpenInfo,
      73             :                                              const char *pszOverrideFilenameIn,
      74             :                                              VSILFILE *fpBox)
      75             : {
      76        1045 :     const char *pszOverrideFilename = pszOverrideFilenameIn;
      77        1045 :     if (pszOverrideFilename == nullptr)
      78         956 :         pszOverrideFilename = poOpenInfo->pszFilename;
      79             : 
      80             :     /* -------------------------------------------------------------------- */
      81             :     /*      Identify authorized georeferencing sources                      */
      82             :     /* -------------------------------------------------------------------- */
      83             :     const char *pszGeorefSourcesOption =
      84        1045 :         CSLFetchNameValue(poOpenInfo->papszOpenOptions, "GEOREF_SOURCES");
      85        1045 :     bool bGeorefSourcesConfigOption = pszGeorefSourcesOption != nullptr;
      86             :     CPLString osGeorefSources =
      87             :         (pszGeorefSourcesOption) ? pszGeorefSourcesOption
      88        1021 :                                  : CPLGetConfigOption("GDAL_GEOREF_SOURCES",
      89        3111 :                                                       "PAM,INTERNAL,WORLDFILE");
      90        1045 :     size_t nInternalIdx = osGeorefSources.ifind("INTERNAL");
      91             :     // coverity[tainted_data]
      92        1026 :     if (nInternalIdx != std::string::npos &&
      93        4103 :         (nInternalIdx == 0 || osGeorefSources[nInternalIdx - 1] == ',') &&
      94        1026 :         (nInternalIdx + strlen("INTERNAL") == osGeorefSources.size() ||
      95        1016 :          osGeorefSources[nInternalIdx + strlen("INTERNAL")] == ','))
      96             :     {
      97             :         osGeorefSources.replace(nInternalIdx, strlen("INTERNAL"),
      98        1026 :                                 "GEOJP2,GMLJP2,MSIG");
      99             :     }
     100        2090 :     const CPLStringList aosTokens(CSLTokenizeString2(osGeorefSources, ",", 0));
     101        1045 :     m_bGotPAMGeorefSrcIndex = true;
     102        1045 :     m_nPAMGeorefSrcIndex = aosTokens.FindString("PAM");
     103        1045 :     const int nGEOJP2Index = aosTokens.FindString("GEOJP2");
     104        1045 :     const int nGMLJP2Index = aosTokens.FindString("GMLJP2");
     105        1045 :     const int nMSIGIndex = aosTokens.FindString("MSIG");
     106        1045 :     m_nWORLDFILEIndex = aosTokens.FindString("WORLDFILE");
     107             : 
     108        1045 :     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        2090 :     GDALJP2Metadata oJP2Geo;
     126        1045 :     int nIndexUsed = -1;
     127        1045 :     if ((((fpBox != nullptr || poOpenInfo->fpL != nullptr) &&
     128         956 :           pszOverrideFilenameIn == nullptr &&
     129         956 :           oJP2Geo.ReadAndParse(fpBox ? fpBox : poOpenInfo->fpL, nGEOJP2Index,
     130         474 :                                nGMLJP2Index, nMSIGIndex, &nIndexUsed)) ||
     131         474 :          (!((fpBox != nullptr || poOpenInfo->fpL != nullptr) &&
     132          89 :             pszOverrideFilenameIn == nullptr) &&
     133          89 :           oJP2Geo.ReadAndParse(pszOverrideFilename, nGEOJP2Index, nGMLJP2Index,
     134        2091 :                                nMSIGIndex, m_nWORLDFILEIndex, &nIndexUsed))) &&
     135           1 :         (nGMLJP2Index >= 0 || nGEOJP2Index >= 0 || nMSIGIndex >= 0 ||
     136           0 :          m_nWORLDFILEIndex >= 0))
     137             :     {
     138         623 :         m_oSRS = oJP2Geo.m_oSRS;
     139         623 :         if (!m_oSRS.IsEmpty())
     140         620 :             m_nProjectionGeorefSrcIndex = nIndexUsed;
     141         623 :         bGeoTransformValid = CPL_TO_BOOL(oJP2Geo.bHaveGeoTransform);
     142         623 :         if (bGeoTransformValid)
     143         591 :             m_nGeoTransformGeorefSrcIndex = nIndexUsed;
     144         623 :         memcpy(adfGeoTransform, oJP2Geo.adfGeoTransform, sizeof(double) * 6);
     145         623 :         nGCPCount = oJP2Geo.nGCPCount;
     146         623 :         if (nGCPCount)
     147          13 :             m_nGCPGeorefSrcIndex = nIndexUsed;
     148         623 :         pasGCPList = GDALDuplicateGCPs(oJP2Geo.nGCPCount, oJP2Geo.pasGCPList);
     149             : 
     150         623 :         if (oJP2Geo.bPixelIsPoint)
     151             :         {
     152           5 :             m_bPixelIsPoint = true;
     153           5 :             m_nPixelIsPointGeorefSrcIndex = nIndexUsed;
     154             :         }
     155         623 :         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        1045 :     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        1337 :     for (int iBox = 0;
     178        1337 :          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        1045 :     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        1045 :     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        1045 :     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        1045 :     if (m_nWORLDFILEIndex >= 0 &&
     263        1029 :         ((bGeoTransformValid &&
     264         583 :           m_nWORLDFILEIndex < m_nGeoTransformGeorefSrcIndex) ||
     265        1024 :          !bGeoTransformValid))
     266             :     {
     267         451 :         bGeoTransformValid |=
     268        1353 :             GDALReadWorldFile2(pszOverrideFilename, nullptr, adfGeoTransform,
     269         451 :                                poOpenInfo->GetSiblingFiles(),
     270         883 :                                &pszWldFilename) ||
     271         432 :             GDALReadWorldFile2(pszOverrideFilename, ".wld", adfGeoTransform,
     272         432 :                                poOpenInfo->GetSiblingFiles(), &pszWldFilename);
     273         451 :         if (bGeoTransformValid)
     274             :         {
     275          21 :             m_nGeoTransformGeorefSrcIndex = m_nWORLDFILEIndex;
     276          21 :             m_bPixelIsPoint = false;
     277          21 :             m_nPixelIsPointGeorefSrcIndex = -1;
     278             :         }
     279             :     }
     280             : 
     281        2090 :     GDALMDReaderManager mdreadermanager;
     282        1045 :     GDALMDReaderBase *mdreader = mdreadermanager.GetReader(
     283        1045 :         poOpenInfo->pszFilename, poOpenInfo->GetSiblingFiles(), MDR_ANY);
     284        1045 :     if (nullptr != mdreader)
     285             :     {
     286           3 :         mdreader->FillMetadata(&(oMDMD));
     287           3 :         papszMetadataFiles = mdreader->GetMetadataFiles();
     288             :     }
     289        1045 : }
     290             : 
     291             : /************************************************************************/
     292             : /*                            GetFileList()                             */
     293             : /************************************************************************/
     294             : 
     295          87 : char **GDALJP2AbstractDataset::GetFileList()
     296             : 
     297             : {
     298          87 :     char **papszFileList = GDALGeorefPamDataset::GetFileList();
     299             : 
     300         175 :     if (pszWldFilename != nullptr &&
     301           1 :         m_nGeoTransformGeorefSrcIndex == m_nWORLDFILEIndex &&
     302          89 :         GDALCanReliablyUseSiblingFileList(pszWldFilename) &&
     303           1 :         CSLFindString(papszFileList, pszWldFilename) == -1)
     304             :     {
     305             :         double l_adfGeoTransform[6];
     306           1 :         GetGeoTransform(l_adfGeoTransform);
     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          87 :     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          87 :     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             :     GDALDriver *const poMemDriver =
     334          40 :         static_cast<GDALDriver *>(GDALGetDriverByName("Memory"));
     335          40 :     if (poMemDriver == nullptr)
     336           0 :         return;
     337             : 
     338          40 :     CPLErr eLastErr = CPLGetLastErrorType();
     339          40 :     int nLastErrNo = CPLGetLastErrorNo();
     340          40 :     CPLString osLastErrorMsg = CPLGetLastErrorMsg();
     341          40 :     CPLXMLNode *const psRoot = CPLParseXMLString(papszGMLJP2[0]);
     342          40 :     if (CPLGetLastErrorType() == CE_None && eLastErr != CE_None)
     343           0 :         CPLErrorSetState(eLastErr, nLastErrNo, osLastErrorMsg.c_str());
     344             : 
     345          40 :     if (psRoot == nullptr)
     346           0 :         return;
     347             :     CPLXMLNode *const psCC =
     348          40 :         CPLGetXMLNode(psRoot, "=gmljp2:GMLJP2CoverageCollection");
     349          40 :     if (psCC == nullptr)
     350             :     {
     351          36 :         CPLDestroyXMLNode(psRoot);
     352          36 :         return;
     353             :     }
     354             : 
     355           8 :     const std::string osTmpDir = VSIMemGenerateHiddenFilename("gmljp2");
     356             : 
     357             :     // Find feature collections.
     358           4 :     int nLayersAtCC = 0;
     359           4 :     int nLayersAtGC = 0;
     360             :     // CPLXMLNode* psCCChildIter = psCC->psChild;
     361          66 :     for (CPLXMLNode *psCCChildIter = psCC->psChild; psCCChildIter != nullptr;
     362          62 :          psCCChildIter = psCCChildIter->psNext)
     363             :     {
     364          62 :         if (psCCChildIter->eType != CXT_Element ||
     365          31 :             strcmp(psCCChildIter->pszValue, "gmljp2:featureMember") != 0 ||
     366          10 :             psCCChildIter->psChild == nullptr ||
     367          10 :             psCCChildIter->psChild->eType != CXT_Element)
     368          52 :             continue;
     369             : 
     370          10 :         CPLXMLNode *const psGCorGMLJP2Features = psCCChildIter->psChild;
     371          10 :         bool bIsGC =
     372          10 :             strstr(psGCorGMLJP2Features->pszValue, "GridCoverage") != nullptr;
     373             : 
     374          10 :         for (CPLXMLNode *psGCorGMLJP2FeaturesChildIter =
     375             :                  psGCorGMLJP2Features->psChild;
     376          48 :              psGCorGMLJP2FeaturesChildIter != nullptr;
     377          38 :              psGCorGMLJP2FeaturesChildIter =
     378             :                  psGCorGMLJP2FeaturesChildIter->psNext)
     379             :         {
     380          38 :             if (psGCorGMLJP2FeaturesChildIter->eType != CXT_Element ||
     381          28 :                 strcmp(psGCorGMLJP2FeaturesChildIter->pszValue,
     382           8 :                        "gmljp2:feature") != 0 ||
     383           8 :                 psGCorGMLJP2FeaturesChildIter->psChild == nullptr)
     384          31 :                 continue;
     385             : 
     386           8 :             CPLXMLNode *psFC = nullptr;
     387           8 :             bool bFreeFC = false;
     388             : 
     389           8 :             CPLXMLNode *const psChild = psGCorGMLJP2FeaturesChildIter->psChild;
     390           8 :             if (psChild->eType == CXT_Attribute &&
     391           6 :                 strcmp(psChild->pszValue, "xlink:href") == 0 &&
     392           6 :                 STARTS_WITH(psChild->psChild->pszValue, "gmljp2://xml/"))
     393             :             {
     394           4 :                 const char *const pszBoxName =
     395           4 :                     psChild->psChild->pszValue + strlen("gmljp2://xml/");
     396             :                 char **papszBoxData =
     397           4 :                     GetMetadata(CPLSPrintf("xml:%s", pszBoxName));
     398           4 :                 if (papszBoxData != nullptr)
     399             :                 {
     400           4 :                     psFC = CPLParseXMLString(papszBoxData[0]);
     401           4 :                     bFreeFC = true;
     402             :                 }
     403             :                 else
     404             :                 {
     405           0 :                     CPLDebug("GMLJP2",
     406             :                              "gmljp2:feature references %s, "
     407             :                              "but no corresponding box found",
     408           0 :                              psChild->psChild->pszValue);
     409             :                 }
     410             :             }
     411             : 
     412           8 :             CPLString osGMLTmpFile;
     413           8 :             if (psChild->eType == CXT_Attribute &&
     414           6 :                 strcmp(psChild->pszValue, "xlink:href") == 0 &&
     415           6 :                 (STARTS_WITH(psChild->psChild->pszValue, "http://") ||
     416           6 :                  STARTS_WITH(psChild->psChild->pszValue, "https://")))
     417             :             {
     418           2 :                 if (!bOpenRemoteResources)
     419           1 :                     CPLDebug(
     420             :                         "GMLJP2",
     421             :                         "Remote feature collection %s mentioned in GMLJP2 box",
     422           1 :                         psChild->psChild->pszValue);
     423             :                 else
     424             :                     osGMLTmpFile =
     425           1 :                         "/vsicurl/" + CPLString(psChild->psChild->pszValue);
     426             :             }
     427           6 :             else if (psChild->eType == CXT_Element &&
     428           2 :                      strstr(psChild->pszValue, "FeatureCollection") != nullptr)
     429             :             {
     430           2 :                 psFC = psChild;
     431             :             }
     432             : 
     433           8 :             if (psFC == nullptr && osGMLTmpFile.empty())
     434             :             {
     435           1 :                 continue;
     436             :             }
     437             : 
     438           7 :             if (psFC != nullptr)
     439             :             {
     440             :                 osGMLTmpFile =
     441           6 :                     CPLFormFilename(osTmpDir.c_str(), "my.gml", nullptr);
     442             :                 // Create temporary .gml file.
     443           6 :                 CPLSerializeXMLTreeToFile(psFC, osGMLTmpFile);
     444             :             }
     445             : 
     446           7 :             CPLDebug("GMLJP2", "Found a FeatureCollection at %s level",
     447             :                      bIsGC ? "GridCoverage" : "CoverageCollection");
     448             : 
     449          14 :             CPLString osXSDTmpFile;
     450             : 
     451           7 :             if (psFC)
     452             :             {
     453             :                 // Try to localize its .xsd schema in a GMLJP2 auxiliary box
     454             :                 const char *const pszSchemaLocation =
     455           6 :                     CPLGetXMLValue(psFC, "xsi:schemaLocation", nullptr);
     456           6 :                 if (pszSchemaLocation)
     457             :                 {
     458           5 :                     char **papszTokens = CSLTokenizeString2(
     459             :                         pszSchemaLocation, " \t\n",
     460             :                         CSLT_HONOURSTRINGS | CSLT_STRIPLEADSPACES |
     461             :                             CSLT_STRIPENDSPACES);
     462             : 
     463           5 :                     if ((CSLCount(papszTokens) % 2) == 0)
     464             :                     {
     465           5 :                         for (char **papszIter = papszTokens;
     466           6 :                              *papszIter != nullptr; papszIter += 2)
     467             :                         {
     468           6 :                             if (STARTS_WITH(papszIter[1], "gmljp2://xml/"))
     469             :                             {
     470           5 :                                 const char *pszBoxName =
     471           5 :                                     papszIter[1] + strlen("gmljp2://xml/");
     472           5 :                                 char **papszBoxData = GetMetadata(
     473           5 :                                     CPLSPrintf("xml:%s", pszBoxName));
     474           5 :                                 if (papszBoxData != nullptr)
     475             :                                 {
     476             :                                     osXSDTmpFile = CPLFormFilename(
     477           5 :                                         osTmpDir.c_str(), "my.xsd", nullptr);
     478           5 :                                     CPL_IGNORE_RET_VAL(
     479           5 :                                         VSIFCloseL(VSIFileFromMemBuffer(
     480             :                                             osXSDTmpFile,
     481             :                                             reinterpret_cast<GByte *>(
     482             :                                                 papszBoxData[0]),
     483           5 :                                             strlen(papszBoxData[0]), FALSE)));
     484             :                                 }
     485             :                                 else
     486             :                                 {
     487           0 :                                     CPLDebug(
     488             :                                         "GMLJP2",
     489             :                                         "Feature collection references %s, "
     490             :                                         "but no corresponding box found",
     491           0 :                                         papszIter[1]);
     492             :                                 }
     493           5 :                                 break;
     494             :                             }
     495             :                         }
     496             :                     }
     497           5 :                     CSLDestroy(papszTokens);
     498             :                 }
     499           6 :                 if (bFreeFC)
     500             :                 {
     501           4 :                     CPLDestroyXMLNode(psFC);
     502           4 :                     psFC = nullptr;
     503             :                 }
     504             :             }
     505             : 
     506           7 :             GDALDriverH hDrv = GDALIdentifyDriver(osGMLTmpFile, nullptr);
     507           7 :             GDALDriverH hGMLDrv = GDALGetDriverByName("GML");
     508           7 :             if (hDrv != nullptr && hDrv == hGMLDrv)
     509             :             {
     510           7 :                 char *apszOpenOptions[2] = {
     511             :                     const_cast<char *>("FORCE_SRS_DETECTION=YES"), nullptr};
     512             :                 GDALDatasetUniquePtr poTmpDS(
     513             :                     GDALDataset::Open(osGMLTmpFile, GDAL_OF_VECTOR, nullptr,
     514          14 :                                       apszOpenOptions, nullptr));
     515           7 :                 if (poTmpDS)
     516             :                 {
     517           7 :                     int nLayers = poTmpDS->GetLayerCount();
     518          14 :                     for (int i = 0; i < nLayers; ++i)
     519             :                     {
     520           7 :                         if (poMemDS == nullptr)
     521           3 :                             poMemDS = poMemDriver->Create("", 0, 0, 0,
     522             :                                                           GDT_Unknown, nullptr);
     523           7 :                         OGRLayer *poSrcLyr = poTmpDS->GetLayer(i);
     524             :                         const char *const pszLayerName =
     525             :                             bIsGC
     526           9 :                                 ? CPLSPrintf("FC_GridCoverage_%d_%s",
     527           2 :                                              ++nLayersAtGC, poSrcLyr->GetName())
     528           5 :                                 : CPLSPrintf("FC_CoverageCollection_%d_%s",
     529             :                                              ++nLayersAtCC,
     530           5 :                                              poSrcLyr->GetName());
     531           7 :                         poMemDS->CopyLayer(poSrcLyr, pszLayerName, nullptr);
     532             :                     }
     533           7 :                 }
     534             :             }
     535             :             else
     536             :             {
     537           0 :                 CPLDebug("GMLJP2",
     538             :                          "No GML driver found to read feature collection");
     539             :             }
     540             : 
     541           7 :             VSIRmdirRecursive(osTmpDir.c_str());
     542             :         }
     543             :     }
     544             : 
     545             :     // Find annotations
     546           4 :     int nAnnotations = 0;
     547          66 :     for (CPLXMLNode *psCCChildIter = psCC->psChild; psCCChildIter != nullptr;
     548          62 :          psCCChildIter = psCCChildIter->psNext)
     549             :     {
     550          62 :         if (psCCChildIter->eType != CXT_Element ||
     551          31 :             strcmp(psCCChildIter->pszValue, "gmljp2:featureMember") != 0 ||
     552          10 :             psCCChildIter->psChild == nullptr ||
     553          10 :             psCCChildIter->psChild->eType != CXT_Element)
     554          52 :             continue;
     555          10 :         CPLXMLNode *const psGCorGMLJP2Features = psCCChildIter->psChild;
     556          10 :         bool bIsGC =
     557          10 :             strstr(psGCorGMLJP2Features->pszValue, "GridCoverage") != nullptr;
     558          10 :         if (!bIsGC)
     559           6 :             continue;
     560           4 :         for (CPLXMLNode *psGCorGMLJP2FeaturesChildIter =
     561             :                  psGCorGMLJP2Features->psChild;
     562          30 :              psGCorGMLJP2FeaturesChildIter != nullptr;
     563          26 :              psGCorGMLJP2FeaturesChildIter =
     564             :                  psGCorGMLJP2FeaturesChildIter->psNext)
     565             :         {
     566          26 :             if (psGCorGMLJP2FeaturesChildIter->eType != CXT_Element ||
     567          22 :                 strcmp(psGCorGMLJP2FeaturesChildIter->pszValue,
     568           2 :                        "gmljp2:annotation") != 0 ||
     569           2 :                 psGCorGMLJP2FeaturesChildIter->psChild == nullptr ||
     570           2 :                 psGCorGMLJP2FeaturesChildIter->psChild->eType != CXT_Element ||
     571           2 :                 strstr(psGCorGMLJP2FeaturesChildIter->psChild->pszValue,
     572             :                        "kml") == nullptr)
     573          24 :                 continue;
     574             : 
     575           2 :             CPLDebug("GMLJP2", "Found a KML annotation");
     576             : 
     577             :             // Create temporary .kml file.
     578           2 :             CPLXMLNode *const psKML = psGCorGMLJP2FeaturesChildIter->psChild;
     579             :             const CPLString osKMLTmpFile(
     580           4 :                 VSIMemGenerateHiddenFilename("my.kml"));
     581           2 :             CPLSerializeXMLTreeToFile(psKML, osKMLTmpFile);
     582             : 
     583             :             GDALDatasetUniquePtr poTmpDS(GDALDataset::Open(
     584           4 :                 osKMLTmpFile, GDAL_OF_VECTOR, nullptr, nullptr, nullptr));
     585           2 :             if (poTmpDS)
     586             :             {
     587           2 :                 int nLayers = poTmpDS->GetLayerCount();
     588           3 :                 for (int i = 0; i < nLayers; ++i)
     589             :                 {
     590           1 :                     if (poMemDS == nullptr)
     591           0 :                         poMemDS = poMemDriver->Create("", 0, 0, 0, GDT_Unknown,
     592             :                                                       nullptr);
     593           1 :                     OGRLayer *const poSrcLyr = poTmpDS->GetLayer(i);
     594             :                     const char *pszLayerName =
     595           3 :                         CPLSPrintf("Annotation_%d_%s", ++nAnnotations,
     596           1 :                                    poSrcLyr->GetName());
     597           1 :                     poMemDS->CopyLayer(poSrcLyr, pszLayerName, nullptr);
     598             :                 }
     599             :             }
     600             :             else
     601             :             {
     602           0 :                 CPLDebug("GMLJP2",
     603             :                          "No KML/LIBKML driver found to read annotation");
     604             :             }
     605             : 
     606           2 :             VSIUnlink(osKMLTmpFile);
     607             :         }
     608             :     }
     609             : 
     610           4 :     CPLDestroyXMLNode(psRoot);
     611             : }
     612             : 
     613             : /************************************************************************/
     614             : /*                           GetLayerCount()                            */
     615             : /************************************************************************/
     616             : 
     617        2120 : int GDALJP2AbstractDataset::GetLayerCount()
     618             : {
     619        2120 :     return poMemDS != nullptr ? poMemDS->GetLayerCount() : 0;
     620             : }
     621             : 
     622             : /************************************************************************/
     623             : /*                             GetLayer()                               */
     624             : /************************************************************************/
     625             : 
     626          16 : OGRLayer *GDALJP2AbstractDataset::GetLayer(int i)
     627             : {
     628          16 :     return poMemDS != nullptr ? poMemDS->GetLayer(i) : nullptr;
     629             : }
     630             : 
     631             : /************************************************************************/
     632             : /*                            GetMetadata()                             */
     633             : /************************************************************************/
     634             : 
     635         839 : char **GDALJP2AbstractDataset::GetMetadata(const char *pszDomain)
     636             : {
     637         839 :     if (pszDomain && EQUAL(pszDomain, "IMAGE_STRUCTURE"))
     638             :     {
     639          10 :         if (m_aosImageStructureMetadata.empty())
     640             :         {
     641           8 :             VSILFILE *fp = GetFileHandle();
     642             :             m_aosImageStructureMetadata.Assign(
     643          16 :                 CSLDuplicate(GDALGeorefPamDataset::GetMetadata(pszDomain)),
     644           8 :                 true);
     645          16 :             CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
     646             :             const char *pszReversibility =
     647           8 :                 GDALGetJPEG2000Reversibility(GetDescription(), fp);
     648           8 :             if (pszReversibility)
     649             :                 m_aosImageStructureMetadata.SetNameValue(
     650           8 :                     "COMPRESSION_REVERSIBILITY", pszReversibility);
     651             :         }
     652          10 :         return m_aosImageStructureMetadata.List();
     653             :     }
     654         829 :     return GDALGeorefPamDataset::GetMetadata(pszDomain);
     655             : }
     656             : 
     657             : /************************************************************************/
     658             : /*                        GetMetadataItem()                             */
     659             : /************************************************************************/
     660             : 
     661         446 : const char *GDALJP2AbstractDataset::GetMetadataItem(const char *pszName,
     662             :                                                     const char *pszDomain)
     663             : {
     664         446 :     if (pszDomain && EQUAL(pszDomain, "IMAGE_STRUCTURE") &&
     665          43 :         EQUAL(pszName, "COMPRESSION_REVERSIBILITY"))
     666             :     {
     667           8 :         char **papszMD = GetMetadata(pszDomain);
     668           8 :         return CSLFetchNameValue(papszMD, pszName);
     669             :     }
     670         438 :     return GDALGeorefPamDataset::GetMetadataItem(pszName, pszDomain);
     671             : }
     672             : 
     673             : /*! @endcond */

Generated by: LCOV version 1.14