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