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