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