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