OSSIM - Open Source Software Image Map  Version 1.9.0 (20180803)
ossimXmpInfo.cpp
Go to the documentation of this file.
1 //----------------------------------------------------------------------------
2 //
3 // License: LGPL
4 //
5 // See LICENSE.txt file in the top level directory for more details.
6 //
7 // Author: David Burken
8 //
9 // Description: Extensible Metadata Platform (XMP) Info object.
10 //
11 //----------------------------------------------------------------------------
12 // $Id$
13 
15 #include <ossim/base/ossimCommon.h>
16 #include <ossim/base/ossimDrect.h>
17 #include <ossim/base/ossimEndian.h>
18 #include <ossim/base/ossimNotify.h>
19 #include <ossim/base/ossimTrace.h>
24 #include <fstream>
25 #include <iomanip>
26 #include <iostream>
27 #include <sstream>
28 #include <vector>
29 
30 // Static trace for debugging.
31 static const ossimTrace traceDebug( ossimString("ossimXmpInfo:debug") );
32 
34  : ossimInfoBase(),
35  m_file(),
36  m_xmpApp1XmlBlock()
37 {
38 }
39 
41 {
42 }
43 
45 {
46  //---
47  // NOTE:
48  // This parser was written for a specific aerial camera. If something more generic is
49  // needed please provide sample to the group. D. Burken, 17 January 2013.
50  //---
51 
52  bool result = false;
53 
54  //---
55  // Open the file.
56  //---
57  std::ifstream str(file.c_str(), std::ios_base::binary|std::ios_base::in);
58  if ( str.good() )
59  {
60  // Parse for XMP APP1 XML block. Currently only case.
61  std::string xmlString;
62  result = getXmpApp1XmlBlock( str, xmlString );
63  if ( result )
64  {
65  m_file = file;
66  m_xmpApp1XmlBlock = xmlString;
67  }
68  else
69  {
70  m_file.clear();
71  m_xmpApp1XmlBlock.clear();
72  }
73  }
74 
75  return result;
76 }
77 
78 bool ossimXmpInfo::getXmpApp1XmlBlock( std::ifstream& str, std::string& xmpApp1XmlBlock ) const
79 {
80  bool result = false;
81 
82  if ( str.good() )
83  {
84  ossim_uint8 c;
85 
86  // Look for Start Of Image (SOI) marker 0xFFD8:
87  str.read( (char*)&c, 1);
88  if ( c == 0xff )
89  {
90  str.read( (char*)&c, 1);
91  if ( c == 0xd8 )
92  {
93  // Found SOI marker.
94 
95  // Now find APP1 marker 0xffe1. Only do 24 iterations max:
96  for ( ossim_uint32 i = 0; i < 24; ++i )
97  {
98  str.read( (char*)&c, 1);
99  if ( c == 0xff )
100  {
101  str.read( (char*)&c, 1);
102  if ( c == 0xe1 )
103  {
104  // Found APP1 marker.
105 
106  //---
107  // Next two bytes are the length, MSB first (big endian) per jpeg spec.
108  // length = 2 + 29 + length_of_xmp_packet
109  //---
110  ossim_uint16 xpacketLength = 0;
111  str.read( (char*)&xpacketLength, 2 );
112 
113  // See if system is not big endian and swap if needed.
115  {
116  ossimEndian swapper;
117  swapper.swap( xpacketLength );
118  }
119 
120  if ( xpacketLength > 31 )
121  {
122  //---
123  // XMP namepsace URI, used as unique ID:
124  // Null terminated ascii string: "http://ns.adobe.com/xap/1.0/"
125  // (29 bytes)
126  // Not using getline until ID verified.
127  //---
128  const ossim_uint32 XMP_ID_SIZE = 29;
129  std::vector<char> v( XMP_ID_SIZE );
130 
131  // Read the next 29 bytes. (string + null)
132  str.read( &v.front(), XMP_ID_SIZE );
133 
134  // Copy.
135  v.push_back( '\0' ); // Just in case string is bad.
136  std::string s = &v.front();
137 
138  if ( s == "http://ns.adobe.com/xap/1.0/" )
139  {
140  //---
141  // Now read header to determine encoding:
142  // Note: Currently hard coded for UTF-8.
143  //---
144 
145  //---
146  // Look for "<?xpacket begin="
147  // Line has binary data in it or getline could be used.
148  //---
149  const ossim_uint32 HEADER_SIZE = 16;
150  v.resize( HEADER_SIZE );
151  str.read( &v.front(), HEADER_SIZE );
152 
153  v.push_back( '\0' ); // Null terminate.
154  s = &v.front();
155 
156  if ( s == "<?xpacket begin=" )
157  {
158  // Skip the quote which can be single or double.
159  str.seekg( 1, std::ios_base::cur );
160 
161  // Now look for 0xEF, 0xBB, 0xBF which is UTF-8
162  ossim_uint8 encode_bytes[3];
163  str.read( (char*)encode_bytes, 3 );
164 
165  if ( ( encode_bytes[0] == 0xef ) &&
166  ( encode_bytes[1] == 0xbb ) &&
167  ( encode_bytes[2] == 0xbf ) )
168  {
169  // Skip the next six bytes: "' id='"
170  str.seekg( 6, std::ios_base::cur );
171 
172  // Read the ID: W5M0MpCehiHzreSzNTczkc9d
173  const ossim_uint32 XPACKET_ID_SIZE = 24;
174  v.resize( XPACKET_ID_SIZE );
175  str.read ( &v.front(), XPACKET_ID_SIZE );
176 
177  v.push_back( '\0' ); // null terminate
178  s = &v.front();
179 
180  if ( s == "W5M0MpCehiHzreSzNTczkc9d" )
181  {
182  //---
183  // Gobble the rest of the line. No more binary characters
184  // so just use getline.
185  //---
186  std::getline( str, s );
187 
188  // Read in the xml string (single line):
189  std::getline( str, xmpApp1XmlBlock );
190 
191  if ( xmpApp1XmlBlock.size() )
192  {
193  result = true;
194  }
195  }
196  }
197 
198  } // Matches: if ( s == "<?xpacket begin=" )
199 
200  } // Matches: if ( s == "http://ns.adobe.com/xap/1.0/" )
201 
202  } // Matches: if ( xpacketLength > 31 )
203 
204  } // Matches: if ( c == 0xd8 )
205 
206  } // Matches: if ( c == 0xff )
207 
208  } // Matches: for ( ossim_uint32 i; i < 24; ++i )
209 
210  } // Matches: if ( c == 0xd8 )
211 
212  } // Matches: if ( c == 0xff )
213 
214  } // Matches: if ( str.good() )
215 
216  return result;
217 
218 } // End: ossimXmpInfo::getXmpApp1XmlBlock
219 
221 {
222  if ( getXmpApp1XmlBlock().size() )
223  {
224  std::string prefix = "";
225  std::string ext = m_file.ext().downcase().string();
226  if ( ( ext == "jpg" ) || ( ext == "jpeg" ) )
227  {
228  prefix = "jpeg.";
229  }
230 
231  std::string s;
232 
233  // Date:
234  getDate( s );
235  if ( s.size() )
236  {
237  out << prefix << "acquisitionDate: " << s << std::endl;
238  }
239 
240  // Mission ID:
241  getMissionId( s );
242  if ( s.size() )
243  {
244  out << prefix << "missionId: " << s << std::endl;
245  }
246 
247  // Sensor ID:
248  getSensorId( s );
249  if ( s.size() )
250  {
251  out << prefix << "sensorId: " << s << std::endl;
252  }
253 
254  out << prefix << "xmp.app1.xml: " << getXmpApp1XmlBlock() << std::endl;
255 
256  }
257  return out;
258 
259 } // End: std::ostream& ossimXmpInfo::print(std::ostream& out) const
260 
261 const std::string& ossimXmpInfo::getXmpApp1XmlBlock() const
262 {
263  return m_xmpApp1XmlBlock;
264 }
265 
267 {
268  ossimRefPtr<ossimProjection> result = 0;
269 
270  if ( getXmpApp1XmlBlock().size() && ( imageRect.hasNans() == false ) )
271  {
272  // Open a stream to the xml block.
274  is.str( m_xmpApp1XmlBlock );
275 
276  ossimXmlDocument xmlDoc;
277 
278  // Read the xml document:
279  if ( xmlDoc.read( is ) )
280  {
281  // Find the four corner ground points.
282  ossimString s;
283  ossimString path;
284  ossimGpt ulg;
285  ossimGpt urg;
286  ossimGpt lrg;
287  ossimGpt llg;
288  bool foundAll = false;
289 
290  while ( 1 )
291  {
292  // Upper left:
293  path = "/x:xmpmeta/rdf:RDF/rdf:Description/dc:UL_Latitude";
294  if ( getPath( path, xmlDoc, s ) )
295  {
296  ulg.lat = s.toFloat64();
297  }
298  else
299  {
300  break;
301  }
302  path = "/x:xmpmeta/rdf:RDF/rdf:Description/dc:UL_Longitude";
303  if ( getPath( path, xmlDoc, s ) )
304  {
305  ulg.lon = s.toFloat64();
306  }
307  else
308  {
309  break;
310  }
311 
312  // Upper right:
313  path = "/x:xmpmeta/rdf:RDF/rdf:Description/dc:UR_Latitude";
314  if ( getPath( path, xmlDoc, s ) )
315  {
316  urg.lat = s.toFloat64();
317  }
318  else
319  {
320  break;
321  }
322  path = "/x:xmpmeta/rdf:RDF/rdf:Description/dc:UR_Longitude";
323  if ( getPath( path, xmlDoc, s ) )
324  {
325  urg.lon = s.toFloat64();
326  }
327  else
328  {
329  break;
330  }
331 
332  // Lower right:
333  path = "/x:xmpmeta/rdf:RDF/rdf:Description/dc:LR_Latitude";
334  if ( getPath( path, xmlDoc, s ) )
335  {
336  lrg.lat = s.toFloat64();
337  }
338  else
339  {
340  break;
341  }
342  path = "/x:xmpmeta/rdf:RDF/rdf:Description/dc:LR_Longitude";
343  if ( getPath( path, xmlDoc, s ) )
344  {
345  lrg.lon = s.toFloat64();
346  }
347  else
348  {
349  break;
350  }
351 
352  // Lower left:
353  path = "/x:xmpmeta/rdf:RDF/rdf:Description/dc:LL_Latitude";
354  if ( getPath( path, xmlDoc, s ) )
355  {
356  llg.lat = s.toFloat64();
357  }
358  else
359  {
360  break;
361  }
362  path = "/x:xmpmeta/rdf:RDF/rdf:Description/dc:LL_Longitude";
363  if ( getPath( path, xmlDoc, s ) )
364  {
365  llg.lon = s.toFloat64();
366 
367  // If we get here mark the status good:
368  foundAll = true;
369  }
370  else
371  {
372  break;
373  }
374 
375  //---
376  // Final trailing break out of infinite loop.
377  // No code past this point if while loop.
378  //---
379  break;
380  }
381 
382  if ( foundAll )
383  {
384  result = new ossimBilinearProjection( imageRect.ul(),
385  imageRect.ur(),
386  imageRect.lr(),
387  imageRect.ll(),
388  ulg,
389  urg,
390  lrg,
391  llg );
392  }
393 
394  } // Matches: if ( xmlDoc.read( is ) )
395 
396  } // Matches: if ( getXmpApp1XmlBlock().size() )
397 
398  return result;
399 
400 } // End: ossimXmpInfo::getProjection
401 
402 void ossimXmpInfo::getDate( std::string& date ) const
403 {
404  date.clear();
405 
406  if ( getXmpApp1XmlBlock().size() )
407  {
408  // Open a stream to the xml block.
410  is.str( m_xmpApp1XmlBlock );
411 
412  ossimXmlDocument xmlDoc;
413 
414  // Read the xml document:
415  if ( xmlDoc.read( is ) )
416  {
417  //---
418  // ISO8601 date in the form of: YYYY-MM-DDThh:mm:ss.sssZ
419  // Find the four corner ground points.
420  //---
421  ossimString s;
422  ossimString path;
423  std::string year;
424  std::string month;
425  std::string dayOfMonth;
426  std::string hour;
427  std::string minute;
428  std::string second;
429  std::string milliSecond;
430 
431  bool foundAll = false;
432 
433  while ( 1 )
434  {
435  // year:
436  path = "/x:xmpmeta/rdf:RDF/rdf:Description/dc:IRIG_Year";
437  if ( getPath( path, xmlDoc, s ) )
438  {
439  year = s.string();
440  }
441  else
442  {
443  break;
444  }
445  // month:
446  path = "/x:xmpmeta/rdf:RDF/rdf:Description/dc:IRIG_Month";
447  if ( getPath( path, xmlDoc, s ) )
448  {
449  month = s.string();
450  }
451  else
452  {
453  break;
454  }
455 
456  // day of month:
457  path = "/x:xmpmeta/rdf:RDF/rdf:Description/dc:IRIG_Day_of_Month";
458  if ( getPath( path, xmlDoc, s ) )
459  {
460  dayOfMonth = s.string();
461  }
462  else
463  {
464  break;
465  }
466 
467  // hour:
468  path = "/x:xmpmeta/rdf:RDF/rdf:Description/dc:IRIG_Hour";
469  if ( getPath( path, xmlDoc, s ) )
470  {
471  hour = s.string();
472  }
473  else
474  {
475  break;
476  }
477 
478  // minute:
479  path = "/x:xmpmeta/rdf:RDF/rdf:Description/dc:IRIG_Min";
480  if ( getPath( path, xmlDoc, s ) )
481  {
482  minute = s.string();
483  }
484  else
485  {
486  break;
487  }
488 
489  // second:
490  path = "/x:xmpmeta/rdf:RDF/rdf:Description/dc:IRIG_Sec";
491  if ( getPath( path, xmlDoc, s ) )
492  {
493  second = s.string();
494  }
495  else
496  {
497  break;
498  }
499 
500  // millisecond:
501  path = "/x:xmpmeta/rdf:RDF/rdf:Description/dc:IRIG_Msec";
502  if ( getPath( path, xmlDoc, s ) )
503  {
504  milliSecond = s.string();
505  }
506  else
507  {
508  break;
509  }
510 
511  // If we get here mark the status good for downstream code.
512  foundAll = true;
513 
514  //---
515  // Final trailing break out of infinite loop.
516  // No code past this point if while loop.
517  //---
518  break;
519 
520  } // End: while ( 1 )
521 
522  if ( foundAll )
523  {
524  date = year + "-"; // Assuming year in 4 digit form, e.g. 2013.
525 
526  // Month two digit wide.
527  std::ostringstream os1;
528  os1 << std::setiosflags(std::ios_base::fixed|std::ios_base::right)
529  << std::setfill('0')
530  << std::setw(2)
531  << month;
532  date += os1.str() + "-";
533 
534  // Day two digit wide.
536  os2 << std::setiosflags(std::ios_base::fixed|std::ios_base::right)
537  << std::setfill('0')
538  << std::setw(2)
539  << dayOfMonth;
540  date += os2.str() + "T";
541 
542  // Hour two digit wide.
543  std::ostringstream os3;
544  os3 << std::setiosflags(std::ios_base::fixed|std::ios_base::right)
545  << std::setfill('0')
546  << std::setw(2)
547  << hour;
548  date += os3.str() + ":";
549 
550  // Minute two digit wide.
551  std::ostringstream os4;
552  os4 << std::setiosflags(std::ios_base::fixed|std::ios_base::right)
553  << std::setfill('0')
554  << std::setw(2)
555  << minute;
556  date += os4.str() + ":";
557 
558  // Second two digit wide.
559  std::ostringstream os5;
560  os5 << std::setiosflags(std::ios_base::fixed|std::ios_base::right)
561  << std::setfill('0')
562  << std::setw(2)
563  << second;
564  date += os5.str() + ".";
565 
566  // Milli second three digit wide.
567  std::ostringstream os6;
568  os6 << std::setiosflags(std::ios_base::fixed|std::ios_base::right)
569  << std::setfill('0')
570  << std::setw(3)
571  << milliSecond;
572  date += os6.str() + "Z"; // Assumption...
573  }
574 
575  } // Matches: if ( xmlDoc.read( is ) )
576 
577  } // Matches: if ( getXmpApp1XmlBlock().size() )
578 
579 } // End: void ossimXmpInfo::getDate( std::string& date ) const
580 
581 
582 void ossimXmpInfo::getMissionId( std::string& mission ) const
583 {
584  // Currently nothing in sample data to grab so using mission "x".
585  mission = "x";
586 }
587 
588 void ossimXmpInfo::getSensorId( std::string& sensor ) const
589 {
590  // Currently nothing in sample data to grab so using sensor "x".
591  sensor = "x";
592 }
593 
595  const ossimString& path, const ossimXmlDocument& xdoc, ossimString& s) const
596 {
597  bool result = false;
598 
599  std::vector<ossimRefPtr<ossimXmlNode> > xnodes;
600  xdoc.findNodes(path, xnodes);
601  if ( xnodes.size() == 1 ) // Error if more than one.
602  {
603  if ( xnodes[0].valid() )
604  {
605  s = xnodes[0]->getText();
606  result = true;
607  }
608  else if(traceDebug())
609  {
611  << "ossimXmpInfo::getPath ERROR:\n"
612  << "Node not found: " << path
613  << std::endl;
614  }
615  }
616  else if ( xnodes.size() == 0 )
617  {
618  if(traceDebug())
619  {
621  << "ossimXmpInfo::getPath ERROR:\n"
622  << "Node not found: " << path
623  << std::endl;
624  }
625  }
626  else
627  {
628  if(traceDebug())
629  {
631  << "ossimXmpInfo::getPath ERROR:\n"
632  << "Multiple nodes found: " << path
633  << std::endl;
634  }
635  }
636 
637  if (!result)
638  {
639  s.clear();
640  }
641  return result;
642 
643 } // bool ossimXmpInfo::getPath
644 
std::ostringstream os2
void clear()
Erases the entire container.
Definition: ossimString.h:432
std::basic_ostringstream< char > ostringstream
Class for char output memory streams.
Definition: ossimIosFwd.h:35
void getMissionId(std::string &mission) const
Gets the mission ID if available.
std::basic_ifstream< char > ifstream
Class for char input file streams.
Definition: ossimIosFwd.h:44
const ossimDpt & ul() const
Definition: ossimDrect.h:339
virtual std::ostream & print(std::ostream &out) const
Print method.
OSSIM_DLL ossimByteOrder byteOrder()
Definition: ossimCommon.cpp:54
std::istream & getline(std::istream &is, ossimString &str, char delim)
Definition: ossimString.h:916
unsigned short ossim_uint16
virtual bool open(const ossimFilename &file)
open method.
ossimXmpInfo()
default constructor
const std::string & getXmpApp1XmlBlock() const
bool getPath(const ossimString &path, const ossimXmlDocument &xdoc, ossimString &s) const
Gets path from doc and initializes string.
yy_size_t size
ossim_float64 lon
Definition: ossimGpt.h:266
unsigned int ossim_uint32
Info Base.
Definition: ossimInfoBase.h:32
ossim_float64 toFloat64() const
static ossimString downcase(const ossimString &aString)
Definition: ossimString.cpp:48
bool hasNans() const
Definition: ossimDrect.h:396
const ossimDpt & ur() const
Definition: ossimDrect.h:340
ossimFilename m_file
Definition: ossimXmpInfo.h:116
const char * c_str() const
Returns a pointer to a null-terminated array of characters representing the string&#39;s contents...
Definition: ossimString.h:396
bool read(std::istream &in)
ossimString ext() const
const ossimDpt & ll() const
Definition: ossimDrect.h:342
ossim_float64 lat
Definition: ossimGpt.h:265
void getSensorId(std::string &sensor) const
Gets the sensor ID if available.
void findNodes(const ossimString &xpath, std::vector< ossimRefPtr< ossimXmlNode > > &nodelist) const
Appends any matching nodes to the list supplied (should be empty):
std::basic_istringstream< char > istringstream
Class for char input memory streams.
Definition: ossimIosFwd.h:32
const ossimDpt & lr() const
Definition: ossimDrect.h:341
ossimRefPtr< ossimProjection > getProjection(const ossimDrect &imageRect) const
Method to get a projection if possible.
std::string m_xmpApp1XmlBlock
Definition: ossimXmpInfo.h:117
void swap(ossim_sint8 &)
Definition: ossimEndian.h:26
unsigned char ossim_uint8
void getDate(std::string &date) const
Gets the date if available in ISO8601 format: YYYY-MM-DDThh:mm:ss.sssZ.
OSSIMDLLEXPORT std::ostream & ossimNotify(ossimNotifyLevel level=ossimNotifyLevel_WARN)
std::basic_ostream< char > ostream
Base class for char output streams.
Definition: ossimIosFwd.h:23
virtual ~ossimXmpInfo()
virtual destructor
const std::string & string() const
Definition: ossimString.h:414