1 /******************************************************************************
2 * $Id: gdaljp2abstractdataset.cpp 29131 2015-05-03 14:47:58Z rouault $
3 *
4 * Project: GDAL
5 * Purpose: GDALGeorefPamDataset with helper to read georeferencing and other
6 * metadata from JP2Boxes
7 * Author: Even Rouault <even dot rouault at mines-paris dot org>
8 *
9 ******************************************************************************
10 * Copyright (c) 2002, Frank Warmerdam <warmerdam@pobox.com>
11 * Copyright (c) 2013, Even Rouault <even dot rouault at mines-paris dot org>
12 *
13 * Permission is hereby granted, free of charge, to any person obtaining a
14 * copy of this software and associated documentation files (the "Software"),
15 * to deal in the Software without restriction, including without limitation
16 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
17 * and/or sell copies of the Software, and to permit persons to whom the
18 * Software is furnished to do so, subject to the following conditions:
19 *
20 * The above copyright notice and this permission notice shall be included
21 * in all copies or substantial portions of the Software.
22 *
23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29 * DEALINGS IN THE SOFTWARE.
30 ****************************************************************************/
31
32 #include "gdaljp2abstractdataset.h"
33 #include "gdaljp2metadata.h"
34 #include "ogrsf_frmts.h"
35 #include "gdal_mdreader.h"
36
37 /************************************************************************/
38 /* GDALJP2AbstractDataset() */
39 /************************************************************************/
40
GDALJP2AbstractDataset()41 GDALJP2AbstractDataset::GDALJP2AbstractDataset()
42 {
43 pszWldFilename = NULL;
44 poMemDS = NULL;
45 papszMetadataFiles = NULL;
46 }
47
48 /************************************************************************/
49 /* ~GDALJP2AbstractDataset() */
50 /************************************************************************/
51
~GDALJP2AbstractDataset()52 GDALJP2AbstractDataset::~GDALJP2AbstractDataset()
53 {
54 CPLFree(pszWldFilename);
55 CloseDependentDatasets();
56 CSLDestroy(papszMetadataFiles);
57 }
58
59 /************************************************************************/
60 /* CloseDependentDatasets() */
61 /************************************************************************/
62
CloseDependentDatasets()63 int GDALJP2AbstractDataset::CloseDependentDatasets()
64 {
65 int bRet = GDALGeorefPamDataset::CloseDependentDatasets();
66 if( poMemDS )
67 {
68 GDALClose(poMemDS);
69 poMemDS = NULL;
70 bRet = TRUE;
71 }
72 return bRet;
73 }
74
75 /************************************************************************/
76 /* LoadJP2Metadata() */
77 /************************************************************************/
78
LoadJP2Metadata(GDALOpenInfo * poOpenInfo,const char * pszOverideFilenameIn)79 void GDALJP2AbstractDataset::LoadJP2Metadata(GDALOpenInfo* poOpenInfo,
80 const char* pszOverideFilenameIn)
81 {
82 const char* pszOverideFilename = pszOverideFilenameIn;
83 if( pszOverideFilename == NULL )
84 pszOverideFilename = poOpenInfo->pszFilename;
85
86 /* -------------------------------------------------------------------- */
87 /* Check for georeferencing information. */
88 /* -------------------------------------------------------------------- */
89 GDALJP2Metadata oJP2Geo;
90
91 if( (poOpenInfo->fpL != NULL && pszOverideFilenameIn == NULL &&
92 oJP2Geo.ReadAndParse(poOpenInfo->fpL) ) ||
93 (!(poOpenInfo->fpL != NULL && pszOverideFilenameIn == NULL) &&
94 oJP2Geo.ReadAndParse( pszOverideFilename )) )
95 {
96 CPLFree(pszProjection);
97 pszProjection = CPLStrdup(oJP2Geo.pszProjection);
98 bGeoTransformValid = oJP2Geo.bHaveGeoTransform;
99 memcpy( adfGeoTransform, oJP2Geo.adfGeoTransform,
100 sizeof(double) * 6 );
101 nGCPCount = oJP2Geo.nGCPCount;
102 pasGCPList =
103 GDALDuplicateGCPs( oJP2Geo.nGCPCount, oJP2Geo.pasGCPList );
104
105 if( oJP2Geo.bPixelIsPoint )
106 GDALDataset::SetMetadataItem(GDALMD_AREA_OR_POINT, GDALMD_AOP_POINT);
107 if( oJP2Geo.papszRPCMD )
108 GDALDataset::SetMetadata( oJP2Geo.papszRPCMD, "RPC" );
109 }
110
111 /* -------------------------------------------------------------------- */
112 /* Report XML UUID box in a dedicated metadata domain */
113 /* -------------------------------------------------------------------- */
114 if (oJP2Geo.pszXMPMetadata)
115 {
116 char *apszMDList[2];
117 apszMDList[0] = (char *) oJP2Geo.pszXMPMetadata;
118 apszMDList[1] = NULL;
119 GDALDataset::SetMetadata(apszMDList, "xml:XMP");
120 }
121
122 /* -------------------------------------------------------------------- */
123 /* Do we have any XML boxes we would like to treat as special */
124 /* domain metadata? (Note: the GDAL multidomain metadata XML box */
125 /* has been excluded and is dealt a few lines below. */
126 /* -------------------------------------------------------------------- */
127 int iBox;
128
129 for( iBox = 0;
130 oJP2Geo.papszGMLMetadata
131 && oJP2Geo.papszGMLMetadata[iBox] != NULL;
132 iBox++ )
133 {
134 char *pszName = NULL;
135 const char *pszXML =
136 CPLParseNameValue( oJP2Geo.papszGMLMetadata[iBox],
137 &pszName );
138 CPLString osDomain;
139 char *apszMDList[2];
140
141 osDomain.Printf( "xml:%s", pszName );
142 apszMDList[0] = (char *) pszXML;
143 apszMDList[1] = NULL;
144
145 GDALDataset::SetMetadata( apszMDList, osDomain );
146
147 CPLFree( pszName );
148 }
149
150 /* -------------------------------------------------------------------- */
151 /* Do we have GDAL metadata? */
152 /* -------------------------------------------------------------------- */
153 if( oJP2Geo.pszGDALMultiDomainMetadata != NULL )
154 {
155 CPLXMLNode* psXMLNode = CPLParseXMLString(oJP2Geo.pszGDALMultiDomainMetadata);
156 if( psXMLNode )
157 {
158 GDALMultiDomainMetadata oLocalMDMD;
159 oLocalMDMD.XMLInit(psXMLNode, FALSE);
160 char** papszDomainList = oLocalMDMD.GetDomainList();
161 char** papszIter = papszDomainList;
162 GDALDataset::SetMetadata(oLocalMDMD.GetMetadata());
163 while( papszIter && *papszIter )
164 {
165 if( !EQUAL(*papszIter, "") && !EQUAL(*papszIter, "IMAGE_STRUCTURE") )
166 {
167 if( GDALDataset::GetMetadata(*papszIter) != NULL )
168 {
169 CPLDebug("GDALJP2",
170 "GDAL metadata overrides metadata in %s domain over metadata read from other boxes",
171 *papszIter);
172 }
173 GDALDataset::SetMetadata(oLocalMDMD.GetMetadata(*papszIter), *papszIter);
174 }
175 papszIter ++;
176 }
177 CPLDestroyXMLNode(psXMLNode);
178 }
179 else
180 CPLErrorReset();
181 }
182
183 /* -------------------------------------------------------------------- */
184 /* Do we have other misc metadata (from resd box for now) ? */
185 /* -------------------------------------------------------------------- */
186 if( oJP2Geo.papszMetadata != NULL )
187 {
188 char **papszMD = CSLDuplicate(GDALDataset::GetMetadata());
189
190 papszMD = CSLMerge( papszMD, oJP2Geo.papszMetadata );
191 GDALDataset::SetMetadata( papszMD );
192
193 CSLDestroy( papszMD );
194 }
195
196 /* -------------------------------------------------------------------- */
197 /* Do we have XML IPR ? */
198 /* -------------------------------------------------------------------- */
199 if( oJP2Geo.pszXMLIPR != NULL )
200 {
201 char* apszMD[2] = { NULL, NULL };
202 apszMD[0] = oJP2Geo.pszXMLIPR;
203 GDALDataset::SetMetadata( apszMD, "xml:IPR" );
204 }
205
206 /* -------------------------------------------------------------------- */
207 /* Check for world file. */
208 /* -------------------------------------------------------------------- */
209 if( !bGeoTransformValid )
210 {
211 bGeoTransformValid |=
212 GDALReadWorldFile2( pszOverideFilename, NULL,
213 adfGeoTransform,
214 poOpenInfo->GetSiblingFiles(), &pszWldFilename )
215 || GDALReadWorldFile2( pszOverideFilename, ".wld",
216 adfGeoTransform,
217 poOpenInfo->GetSiblingFiles(), &pszWldFilename );
218 }
219
220 GDALMDReaderManager mdreadermanager;
221 GDALMDReaderBase* mdreader = mdreadermanager.GetReader(poOpenInfo->pszFilename,
222 poOpenInfo->GetSiblingFiles(), MDR_ANY);
223 if(NULL != mdreader)
224 {
225 mdreader->FillMetadata(&(oMDMD));
226 papszMetadataFiles = mdreader->GetMetadataFiles();
227 }
228 }
229
230 /************************************************************************/
231 /* GetFileList() */
232 /************************************************************************/
233
GetFileList()234 char **GDALJP2AbstractDataset::GetFileList()
235
236 {
237 char **papszFileList = GDALGeorefPamDataset::GetFileList();
238
239 if( pszWldFilename != NULL &&
240 CSLFindString( papszFileList, pszWldFilename ) == -1 )
241 {
242 papszFileList = CSLAddString( papszFileList, pszWldFilename );
243 }
244 if(NULL != papszMetadataFiles)
245 {
246 for( int i = 0; papszMetadataFiles[i] != NULL; i++ )
247 {
248 papszFileList = CSLAddString( papszFileList, papszMetadataFiles[i] );
249 }
250 }
251 return papszFileList;
252 }
253
254 /************************************************************************/
255 /* LoadVectorLayers() */
256 /************************************************************************/
257
LoadVectorLayers(int bOpenRemoteResources)258 void GDALJP2AbstractDataset::LoadVectorLayers(int bOpenRemoteResources)
259 {
260 char** papszGMLJP2 = GetMetadata("xml:gml.root-instance");
261 if( papszGMLJP2 == NULL )
262 return;
263 GDALDriver* poMemDriver = (GDALDriver*)GDALGetDriverByName("Memory");
264 if( poMemDriver == NULL )
265 return;
266 CPLXMLNode* psRoot = CPLParseXMLString(papszGMLJP2[0]);
267 if( psRoot == NULL )
268 return;
269 CPLXMLNode* psCC = CPLGetXMLNode(psRoot, "=gmljp2:GMLJP2CoverageCollection");
270 if( psCC == NULL )
271 {
272 CPLDestroyXMLNode(psRoot);
273 return;
274 }
275
276 // Find feature collections
277 CPLXMLNode* psCCChildIter = psCC->psChild;
278 int nLayersAtCC = 0;
279 int nLayersAtGC = 0;
280 for( ; psCCChildIter != NULL; psCCChildIter = psCCChildIter->psNext )
281 {
282 if( psCCChildIter->eType != CXT_Element ||
283 strcmp(psCCChildIter->pszValue, "gmljp2:featureMember") != 0 ||
284 psCCChildIter->psChild == NULL ||
285 psCCChildIter->psChild->eType != CXT_Element )
286 continue;
287
288 CPLXMLNode* psGCorGMLJP2Features = psCCChildIter->psChild;
289 int bIsGC = ( strstr(psGCorGMLJP2Features->pszValue, "GridCoverage") != NULL );
290
291 CPLXMLNode* psGCorGMLJP2FeaturesChildIter = psGCorGMLJP2Features->psChild;
292 for( ; psGCorGMLJP2FeaturesChildIter != NULL;
293 psGCorGMLJP2FeaturesChildIter = psGCorGMLJP2FeaturesChildIter->psNext )
294 {
295 if( psGCorGMLJP2FeaturesChildIter->eType != CXT_Element ||
296 strcmp(psGCorGMLJP2FeaturesChildIter->pszValue, "gmljp2:feature") != 0 ||
297 psGCorGMLJP2FeaturesChildIter->psChild == NULL )
298 continue;
299
300 CPLXMLNode* psFC = NULL;
301 int bFreeFC = FALSE;
302 CPLString osGMLTmpFile;
303
304 CPLXMLNode* psChild = psGCorGMLJP2FeaturesChildIter->psChild;
305 if( psChild->eType == CXT_Attribute &&
306 strcmp(psChild->pszValue, "xlink:href") == 0 &&
307 strncmp(psChild->psChild->pszValue,
308 "gmljp2://xml/", strlen("gmljp2://xml/")) == 0 )
309 {
310 const char* pszBoxName = psChild->psChild->pszValue + strlen("gmljp2://xml/");
311 char** papszBoxData = GetMetadata(CPLSPrintf("xml:%s", pszBoxName));
312 if( papszBoxData != NULL )
313 {
314 psFC = CPLParseXMLString(papszBoxData[0]);
315 bFreeFC = TRUE;
316 }
317 else
318 {
319 CPLDebug("GMLJP2",
320 "gmljp2:feature references %s, but no corresponding box found",
321 psChild->psChild->pszValue);
322 }
323 }
324 if( psChild->eType == CXT_Attribute &&
325 strcmp(psChild->pszValue, "xlink:href") == 0 &&
326 (strncmp(psChild->psChild->pszValue, "http://", strlen("http://")) == 0 ||
327 strncmp(psChild->psChild->pszValue, "https://", strlen("https://")) == 0) )
328 {
329 if( !bOpenRemoteResources )
330 CPLDebug("GMLJP2", "Remote feature collection %s mentionned in GMLJP2 box",
331 psChild->psChild->pszValue);
332 else
333 osGMLTmpFile = "/vsicurl/" + CPLString(psChild->psChild->pszValue);
334 }
335 else if( psChild->eType == CXT_Element &&
336 strstr(psChild->pszValue, "FeatureCollection") != NULL )
337 {
338 psFC = psChild;
339 }
340 if( psFC == NULL && osGMLTmpFile.size() == 0 )
341 continue;
342
343 if( psFC != NULL )
344 {
345 osGMLTmpFile = CPLSPrintf("/vsimem/gmljp2/%p/my.gml", this);
346 // Create temporary .gml file
347 CPLSerializeXMLTreeToFile(psFC, osGMLTmpFile);
348 }
349
350 CPLDebug("GMLJP2", "Found a FeatureCollection at %s level",
351 (bIsGC) ? "GridCoverage" : "CoverageCollection");
352
353 CPLString osXSDTmpFile;
354
355 if( psFC )
356 {
357 // Try to localize its .xsd schema in a GMLJP2 auxiliary box
358 const char* pszSchemaLocation = CPLGetXMLValue(psFC, "xsi:schemaLocation", NULL);
359 if( pszSchemaLocation )
360 {
361 char **papszTokens = CSLTokenizeString2(
362 pszSchemaLocation, " \t\n",
363 CSLT_HONOURSTRINGS | CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES);
364
365 if( (CSLCount(papszTokens) % 2) == 0 )
366 {
367 for(char** papszIter = papszTokens; *papszIter; papszIter += 2 )
368 {
369 if( strncmp(papszIter[1], "gmljp2://xml/", strlen("gmljp2://xml/")) == 0 )
370 {
371 const char* pszBoxName = papszIter[1] + strlen("gmljp2://xml/");
372 char** papszBoxData = GetMetadata(CPLSPrintf("xml:%s", pszBoxName));
373 if( papszBoxData != NULL )
374 {
375 osXSDTmpFile = CPLSPrintf("/vsimem/gmljp2/%p/my.xsd", this);
376 VSIFCloseL(VSIFileFromMemBuffer(osXSDTmpFile,
377 (GByte*)papszBoxData[0],
378 strlen(papszBoxData[0]),
379 FALSE));
380 }
381 else
382 {
383 CPLDebug("GMLJP2",
384 "Feature collection references %s, but no corresponding box found",
385 papszIter[1]);
386 }
387 break;
388 }
389 }
390 }
391 CSLDestroy(papszTokens);
392 }
393 if( bFreeFC )
394 {
395 CPLDestroyXMLNode(psFC);
396 psFC = NULL;
397 }
398 }
399
400 GDALDriverH hDrv = GDALIdentifyDriver(osGMLTmpFile, NULL);
401 GDALDriverH hGMLDrv = GDALGetDriverByName("GML");
402 if( hDrv != NULL && hDrv == hGMLDrv )
403 {
404 char* apszOpenOptions[2];
405 apszOpenOptions[0] = (char*) "FORCE_SRS_DETECTION=YES";
406 apszOpenOptions[1] = NULL;
407 GDALDataset* poTmpDS = (GDALDataset*)GDALOpenEx( osGMLTmpFile,
408 GDAL_OF_VECTOR, NULL, apszOpenOptions, NULL );
409 if( poTmpDS )
410 {
411 int nLayers = poTmpDS->GetLayerCount();
412 for(int i=0;i<nLayers;i++)
413 {
414 if( poMemDS == NULL )
415 poMemDS = poMemDriver->Create("", 0, 0, 0, GDT_Unknown, NULL);
416 OGRLayer* poSrcLyr = poTmpDS->GetLayer(i);
417 const char* pszLayerName;
418 if( bIsGC )
419 pszLayerName = CPLSPrintf("FC_GridCoverage_%d_%s",
420 ++nLayersAtGC, poSrcLyr->GetName());
421 else
422 pszLayerName = CPLSPrintf("FC_CoverageCollection_%d_%s",
423 ++nLayersAtCC, poSrcLyr->GetName());
424 poMemDS->CopyLayer(poSrcLyr, pszLayerName, NULL);
425 }
426 GDALClose(poTmpDS);
427
428 // In case we don't have a schema, a .gfs might have been generated
429 VSIUnlink(CPLSPrintf("/vsimem/gmljp2/%p/my.gfs", this));
430 }
431 }
432 else
433 {
434 CPLDebug("GMLJP2", "No GML driver found to read feature collection");
435 }
436
437 if( strncmp(osGMLTmpFile, "/vsicurl/", strlen("/vsicurl/")) != 0 )
438 VSIUnlink(osGMLTmpFile);
439 if( osXSDTmpFile.size() )
440 VSIUnlink(osXSDTmpFile);
441 }
442 }
443
444 // Find annotations
445 psCCChildIter = psCC->psChild;
446 int nAnnotations = 0;
447 for( ; psCCChildIter != NULL; psCCChildIter = psCCChildIter->psNext )
448 {
449 if( psCCChildIter->eType != CXT_Element ||
450 strcmp(psCCChildIter->pszValue, "gmljp2:featureMember") != 0 ||
451 psCCChildIter->psChild == NULL ||
452 psCCChildIter->psChild->eType != CXT_Element )
453 continue;
454 CPLXMLNode* psGCorGMLJP2Features = psCCChildIter->psChild;
455 int bIsGC = ( strstr(psGCorGMLJP2Features->pszValue, "GridCoverage") != NULL );
456 if( !bIsGC )
457 continue;
458 CPLXMLNode* psGCorGMLJP2FeaturesChildIter = psGCorGMLJP2Features->psChild;
459 for( ; psGCorGMLJP2FeaturesChildIter != NULL;
460 psGCorGMLJP2FeaturesChildIter = psGCorGMLJP2FeaturesChildIter->psNext )
461 {
462 if( psGCorGMLJP2FeaturesChildIter->eType != CXT_Element ||
463 strcmp(psGCorGMLJP2FeaturesChildIter->pszValue, "gmljp2:annotation") != 0 ||
464 psGCorGMLJP2FeaturesChildIter->psChild == NULL ||
465 psGCorGMLJP2FeaturesChildIter->psChild->eType != CXT_Element ||
466 strstr(psGCorGMLJP2FeaturesChildIter->psChild->pszValue, "kml") == NULL )
467 continue;
468 CPLDebug("GMLJP2", "Found a KML annotation");
469
470 // Create temporary .kml file
471 CPLXMLNode* psKML = psGCorGMLJP2FeaturesChildIter->psChild;
472 CPLString osKMLTmpFile(CPLSPrintf("/vsimem/gmljp2/%p/my.kml", this));
473 CPLSerializeXMLTreeToFile(psKML, osKMLTmpFile);
474
475 GDALDataset* poTmpDS = (GDALDataset*)GDALOpenEx( osKMLTmpFile,
476 GDAL_OF_VECTOR, NULL, NULL, NULL );
477 if( poTmpDS )
478 {
479 int nLayers = poTmpDS->GetLayerCount();
480 for(int i=0;i<nLayers;i++)
481 {
482 if( poMemDS == NULL )
483 poMemDS = poMemDriver->Create("", 0, 0, 0, GDT_Unknown, NULL);
484 OGRLayer* poSrcLyr = poTmpDS->GetLayer(i);
485 const char* pszLayerName;
486 pszLayerName = CPLSPrintf("Annotation_%d_%s",
487 ++nAnnotations, poSrcLyr->GetName());
488 poMemDS->CopyLayer(poSrcLyr, pszLayerName, NULL);
489 }
490 GDALClose(poTmpDS);
491 }
492 else
493 {
494 CPLDebug("GMLJP2", "No KML/LIBKML driver found to read annotation");
495 }
496
497 VSIUnlink(osKMLTmpFile);
498 }
499 }
500
501 CPLDestroyXMLNode(psRoot);
502 }
503
504 /************************************************************************/
505 /* GetLayerCount() */
506 /************************************************************************/
507
GetLayerCount()508 int GDALJP2AbstractDataset::GetLayerCount()
509 {
510 return (poMemDS != NULL) ? poMemDS->GetLayerCount() : 0;
511 }
512
513 /************************************************************************/
514 /* GetLayer() */
515 /************************************************************************/
516
GetLayer(int i)517 OGRLayer* GDALJP2AbstractDataset::GetLayer(int i)
518 {
519 return (poMemDS != NULL) ? poMemDS->GetLayer(i) : NULL;
520 }
521