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 */
|