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

Generated by: LCOV version 1.14