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

Generated by: LCOV version 1.14