1 /******************************************************************************
2  *
3  * Project:  GDAL Core
4  * Purpose:  Read metadata from DigitalGlobe imagery.
5  * Author:   Alexander Lisovenko
6  * Author:   Dmitry Baryshnikov, polimax@mail.ru
7  *
8  ******************************************************************************
9  * Copyright (c) 2014-2015, NextGIS info@nextgis.ru
10  *
11  * Permission is hereby granted, free of charge, to any person obtaining a
12  * copy of this software and associated documentation files (the "Software"),
13  * to deal in the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15  * and/or sell copies of the Software, and to permit persons to whom the
16  * Software is furnished to do so, subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice shall be included
19  * in all copies or substantial portions of the Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27  * DEALINGS IN THE SOFTWARE.
28  ****************************************************************************/
29 
30 #include "cpl_port.h"
31 #include "reader_digital_globe.h"
32 
33 #include <ctime>
34 
35 #include "cpl_conv.h"
36 #include "cpl_error.h"
37 #include "cpl_minixml.h"
38 #include "cpl_string.h"
39 #include "gdal_priv.h"
40 
41 CPL_CVSID("$Id: reader_digital_globe.cpp d8114610ec3abbffbfce3dfbd353ea53ac81c013 2021-03-04 05:38:17 -0500 John Papadakis $")
42 
43 /**
44  * GDALMDReaderDigitalGlobe()
45  */
GDALMDReaderDigitalGlobe(const char * pszPath,char ** papszSiblingFiles)46 GDALMDReaderDigitalGlobe::GDALMDReaderDigitalGlobe(const char *pszPath,
47                                                    char **papszSiblingFiles) :
48     GDALMDReaderBase(pszPath, papszSiblingFiles),
49     m_osXMLSourceFilename ( GDALFindAssociatedFile( pszPath, "XML",
50                                                          papszSiblingFiles, 0 ) ),
51     m_osIMDSourceFilename ( GDALFindAssociatedFile( pszPath, "IMD",
52                                                          papszSiblingFiles, 0 ) ),
53     m_osRPBSourceFilename ( GDALFindAssociatedFile( pszPath, "RPB",
54                                                          papszSiblingFiles, 0 ) )
55 {
56     if( !m_osIMDSourceFilename.empty() )
57         CPLDebug( "MDReaderDigitalGlobe", "IMD Filename: %s",
58                   m_osIMDSourceFilename.c_str() );
59     if( !m_osRPBSourceFilename.empty() )
60         CPLDebug( "MDReaderDigitalGlobe", "RPB Filename: %s",
61                   m_osRPBSourceFilename.c_str() );
62     if( !m_osXMLSourceFilename.empty() )
63         CPLDebug( "MDReaderDigitalGlobe", "XML Filename: %s",
64                   m_osXMLSourceFilename.c_str() );
65 }
66 
67 /**
68  * ~GDALMDReaderDigitalGlobe()
69  */
~GDALMDReaderDigitalGlobe()70 GDALMDReaderDigitalGlobe::~GDALMDReaderDigitalGlobe() {}
71 
72 /**
73  * HasRequiredFiles()
74  */
HasRequiredFiles() const75 bool GDALMDReaderDigitalGlobe::HasRequiredFiles() const
76 {
77     if (!m_osIMDSourceFilename.empty())
78         return true;
79     if (!m_osRPBSourceFilename.empty())
80         return true;
81 
82     // check <isd>
83     if(!m_osXMLSourceFilename.empty() &&
84             GDALCheckFileHeader(m_osXMLSourceFilename, "<isd>"))
85         return true;
86 
87     return false;
88 }
89 
90 /**
91  * LoadMetadata()
92  */
LoadMetadata()93 void GDALMDReaderDigitalGlobe::LoadMetadata()
94 {
95     if(m_bIsMetadataLoad)
96         return;
97 
98     if (!m_osIMDSourceFilename.empty())
99     {
100         m_papszIMDMD = GDALLoadIMDFile( m_osIMDSourceFilename );
101     }
102 
103     if(!m_osRPBSourceFilename.empty())
104     {
105         m_papszRPCMD = GDALLoadRPBFile( m_osRPBSourceFilename );
106     }
107 
108     if((nullptr == m_papszIMDMD || nullptr == m_papszRPCMD) && !m_osXMLSourceFilename.empty())
109     {
110         CPLXMLNode* psNode = CPLParseXMLFile(m_osXMLSourceFilename);
111 
112         if(psNode != nullptr)
113         {
114             CPLXMLNode* psisdNode = psNode->psNext;
115             if(psisdNode != nullptr)
116             {
117                 if( m_papszIMDMD == nullptr )
118                     m_papszIMDMD = LoadIMDXmlNode( CPLSearchXMLNode(psisdNode,
119                                                                         "IMD") );
120                 if( m_papszRPCMD == nullptr )
121                     m_papszRPCMD = LoadRPBXmlNode( CPLSearchXMLNode(psisdNode,
122                                                                         "RPB") );
123             }
124             CPLDestroyXMLNode(psNode);
125         }
126     }
127 
128     m_papszDEFAULTMD = CSLAddNameValue(m_papszDEFAULTMD, MD_NAME_MDTYPE, "DG");
129 
130     m_bIsMetadataLoad = true;
131 
132     if(nullptr == m_papszIMDMD)
133     {
134         return;
135     }
136     //extract imagery metadata
137     const char* pszSatId = CSLFetchNameValue(m_papszIMDMD, "IMAGE.SATID");
138     if(nullptr != pszSatId)
139     {
140         m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD,
141                                            MD_NAME_SATELLITE,
142                                            CPLStripQuotes(pszSatId));
143     }
144     else
145     {
146         pszSatId = CSLFetchNameValue(m_papszIMDMD, "IMAGE_1.SATID");
147         if(nullptr != pszSatId)
148         {
149             m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD,
150                                                MD_NAME_SATELLITE,
151                                                CPLStripQuotes(pszSatId));
152         }
153     }
154 
155     const char* pszCloudCover = CSLFetchNameValue(m_papszIMDMD,
156                                                   "IMAGE.CLOUDCOVER");
157     if(nullptr != pszCloudCover)
158     {
159         double fCC = CPLAtofM(pszCloudCover);
160         if(fCC < 0)
161         {
162             m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, MD_NAME_CLOUDCOVER,
163                                                MD_CLOUDCOVER_NA);
164         }
165         else
166         {
167             m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD,
168                           MD_NAME_CLOUDCOVER, CPLSPrintf("%d", int(fCC * 100)));
169         }
170     }
171     else
172     {
173         pszCloudCover = CSLFetchNameValue(m_papszIMDMD, "IMAGE_1.cloudCover");
174         if(nullptr != pszCloudCover)
175         {
176             double fCC = CPLAtofM(pszCloudCover);
177             if(fCC < 0)
178             {
179                 m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, MD_NAME_CLOUDCOVER,
180                                                    MD_CLOUDCOVER_NA);
181             }
182             else
183             {
184                 m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD,
185                               MD_NAME_CLOUDCOVER, CPLSPrintf("%d", int(fCC * 100)));
186             }
187         }
188     }
189 
190     const char* pszDateTime = CSLFetchNameValue(m_papszIMDMD,
191                                        "IMAGE.FIRSTLINETIME");
192     if(nullptr != pszDateTime)
193     {
194         time_t timeStart = GetAcquisitionTimeFromString(pszDateTime);
195         char szMidDateTime[80];
196         strftime (szMidDateTime, 80, MD_DATETIMEFORMAT, localtime(&timeStart));
197 
198         m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD,
199                                            MD_NAME_ACQDATETIME,
200                                            szMidDateTime);
201     }
202     else
203     {
204         pszDateTime = CSLFetchNameValue(m_papszIMDMD, "IMAGE_1.firstLineTime");
205         if(nullptr != pszDateTime)
206         {
207             time_t timeStart = GetAcquisitionTimeFromString(pszDateTime);
208             char szMidDateTime[80];
209             strftime (szMidDateTime, 80, MD_DATETIMEFORMAT, localtime(&timeStart));
210 
211             m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD,
212                                                MD_NAME_ACQDATETIME,
213                                                szMidDateTime);
214         }
215     }
216 }
217 
218 /**
219  * GetMetadataFiles()
220  */
GetMetadataFiles() const221 char** GDALMDReaderDigitalGlobe::GetMetadataFiles() const
222 {
223     char **papszFileList = nullptr;
224     if(!m_osIMDSourceFilename.empty())
225         papszFileList = CSLAddString( papszFileList, m_osIMDSourceFilename );
226     if(!m_osRPBSourceFilename.empty())
227         papszFileList = CSLAddString( papszFileList, m_osRPBSourceFilename );
228     if(!m_osXMLSourceFilename.empty())
229         papszFileList = CSLAddString( papszFileList, m_osXMLSourceFilename );
230 
231     return papszFileList;
232 }
233 
234 /**
235  * GDALLoadIMDXmlNode()
236  */
LoadIMDXmlNode(CPLXMLNode * psNode)237 char** GDALMDReaderDigitalGlobe::LoadIMDXmlNode(CPLXMLNode* psNode)
238 {
239     if(nullptr == psNode)
240         return nullptr;
241     char** papszList = nullptr;
242     return ReadXMLToList(psNode->psChild, papszList);
243 }
244 
245 /**
246  * GDALLoadRPBXmlNode()
247  */
248 static const char * const apszRPBMap[] = {
249     RPC_ERR_BIAS,   "image.errBias",
250     RPC_ERR_RAND,   "image.errRand",
251     RPC_LINE_OFF,   "image.lineOffset",
252     RPC_SAMP_OFF,   "image.sampOffset",
253     RPC_LAT_OFF,    "image.latOffset",
254     RPC_LONG_OFF,   "image.longOffset",
255     RPC_HEIGHT_OFF, "image.heightOffset",
256     RPC_LINE_SCALE, "image.lineScale",
257     RPC_SAMP_SCALE, "image.sampScale",
258     RPC_LAT_SCALE,  "image.latScale",
259     RPC_LONG_SCALE, "image.longScale",
260     RPC_HEIGHT_SCALE,   "image.heightScale",
261     RPC_LINE_NUM_COEFF, "image.lineNumCoefList.lineNumCoef",
262     RPC_LINE_DEN_COEFF, "image.lineDenCoefList.lineDenCoef",
263     RPC_SAMP_NUM_COEFF, "image.sampNumCoefList.sampNumCoef",
264     RPC_SAMP_DEN_COEFF, "image.sampDenCoefList.sampDenCoef",
265     nullptr,             nullptr };
266 
LoadRPBXmlNode(CPLXMLNode * psNode)267 char** GDALMDReaderDigitalGlobe::LoadRPBXmlNode(CPLXMLNode* psNode)
268 {
269     if(nullptr == psNode)
270         return nullptr;
271     char** papszList = nullptr;
272     papszList = ReadXMLToList(psNode->psChild, papszList);
273 
274     if(nullptr == papszList)
275         return nullptr;
276 
277     char** papszRPB = nullptr;
278     for( int i = 0; apszRPBMap[i] != nullptr; i += 2 )
279     {
280         papszRPB = CSLAddNameValue(papszRPB, apszRPBMap[i],
281                                CSLFetchNameValue(papszList, apszRPBMap[i + 1]));
282     }
283 
284     CSLDestroy(papszList);
285 
286     return papszRPB;
287 }
288