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