vil_nitf2_tagged_record.cxx
Go to the documentation of this file.
1 // vil_nitf2: Written by Harry Voorhees (hlv@) and Rob Radtke (rob@) of
2 // Stellar Science Ltd. Co. (stellarscience.com) for
3 // Air Force Research Laboratory, 2005.
4 
6 #include <iomanip>
7 #include <iostream>
8 #include <sstream>
9 #include <string>
10 #include <utility>
11 
12 #ifdef _MSC_VER
13 # include <vcl_msvc_warnings.h>
14 #endif
15 
16 #include <vil/vil_stream_core.h>
17 #include <vil/vil_stream_section.h>
18 
22 #include "vil_nitf2_index_vector.h"
24 #include "vil_nitf2_scalar_field.h"
25 
27 {
28  static vil_nitf2_field_definition length_definition(
29  "CEL", "Extension Length", new vil_nitf2_integer_formatter(5));
30  return length_definition;
31 }
32 
34 {
35  static vil_nitf2_field_definition tag_definition (
36  "CETAG", "Extension Tag", new vil_nitf2_string_formatter(6));
37  return tag_definition;
38 }
39 
41 {
42  static vil_nitf2_integer_formatter length_formatter(5);
43  return length_formatter;
44 }
45 
47 {
48  static vil_nitf2_string_formatter tag_formatter(6);
49  return tag_formatter;
50 }
51 
52 std::string vil_nitf2_tagged_record::name() const
53 {
54  std::string cetag;
55  if ( m_tag_field->value(cetag) ) return cetag;
56  else return "<Unknown>";
57 }
58 
60 {
62  else return "<unknown>";
63 }
64 
66 {
67  auto* record = new vil_nitf2_tagged_record();
68  if (record->read(input)) {
69  return record;
70  }
71  else {
72  delete record;
73  return nullptr;
74  }
75 }
76 
78 {
79  // Read TRE tag
81  if (!m_tag_field) {
82  std::cerr << "Error reading extension tag at offset " << input.tell() << ".\n";
83  // Can't continue reading file
84  return false;
85  }
86  std::string cetag;
87  m_tag_field->value(cetag);
88 
89  // Read TRE data length
91  if (!m_length_field) {
92  std::cerr << "Error reading extension length for tag " << cetag << ".\n";
93  // Can't continue reading file
94  return false;
95  }
97 
98  // See if this record is defined ...
99  vil_nitf2_tagged_record_definition* record_definition =
101 
102  // ... if not, skip ahead to next record ...
103  if (record_definition == nullptr) {
104  VIL_NITF2_LOG(log_info) << "Skipping unknown record " << cetag << ".\n";
105  // Return whether I've found end of record
106  //input.seekg(ceLength, std::ios::cur);
107  input.seek(input.tell()+m_length);
108  return input.ok();
109  }
110  // ... otherwise, populate this record's fields.
111  // First save the position to check later that we read entire record
112  vil_streampos record_data_start_pos = input.tell();
113  m_definition = record_definition;
114  m_field_sequence = new vil_nitf2_field_sequence(record_definition->field_definitions());
115  m_field_sequence->read(input);
116 
117  // Check that the expected amount of data was read
118  vil_streampos expected_pos = record_data_start_pos;
119  expected_pos += m_length;
120  if (input.tell() != expected_pos) {
121  std::cerr << "vil_nitf2_tagged_record::read(): Read " << input.tell() - record_data_start_pos
122  << " bytes instead of " << m_length << " as expected in "<<cetag<<".\n";
123  // Attempt to reposition input stream to end of record; return whether
124  // successful
125  input.seek(expected_pos);
126  return input.ok();
127  }
128  // At end of record, as expected
129  return true;
130 }
131 
132 bool vil_nitf2_tagged_record::get_value(std::string tag, int& out_value) const
133 { return m_field_sequence->get_value(std::move(tag), out_value); }
134 
135 bool vil_nitf2_tagged_record::get_value(std::string tag, double& out_value) const
136 { return m_field_sequence->get_value(std::move(tag), out_value); }
137 
138 bool vil_nitf2_tagged_record::get_value(std::string tag, char& out_value) const
139 { return m_field_sequence->get_value(std::move(tag), out_value); }
140 bool vil_nitf2_tagged_record::get_value(std::string tag, void*& out_value) const
141 { return m_field_sequence->get_value(std::move(tag), out_value); }
142 
143 bool vil_nitf2_tagged_record::get_value(std::string tag, std::string& out_value) const
144 { return m_field_sequence->get_value(std::move(tag), out_value); }
145 
146 bool vil_nitf2_tagged_record::get_value(std::string tag, vil_nitf2_location*& out_value) const
147 { return m_field_sequence->get_value(std::move(tag), out_value); }
148 
149 bool vil_nitf2_tagged_record::get_value(std::string tag, vil_nitf2_date_time& out_value) const
150 { return m_field_sequence->get_value(std::move(tag), out_value); }
151 
152 #if VXL_HAS_INT_64
153 // if not VXL_HAS_INT_64 isn't defined the vil_nitf2_long is the same as just plain 'int'
154 // and this function will be a duplicate of that get_value
155 bool vil_nitf2_tagged_record::get_value(std::string tag, vil_nitf2_long& out_value) const
156 { return m_field_sequence->get_value(std::move(tag), out_value); }
157 #endif
158 
159 bool vil_nitf2_tagged_record::get_value(std::string tag, const vil_nitf2_index_vector& indexes, int& out_value) const
160 { return m_field_sequence->get_value(std::move(tag), indexes, out_value); }
161 
162 bool vil_nitf2_tagged_record::get_value(std::string tag, const vil_nitf2_index_vector& indexes, double& out_value) const
163 { return m_field_sequence->get_value(std::move(tag), indexes, out_value); }
164 
165 bool vil_nitf2_tagged_record::get_value(std::string tag, const vil_nitf2_index_vector& indexes, char& out_value) const
166 { return m_field_sequence->get_value(std::move(tag), indexes, out_value); }
167 
168 bool vil_nitf2_tagged_record::get_value(std::string tag, const vil_nitf2_index_vector& indexes, void*& out_value) const
169 { return m_field_sequence->get_value(std::move(tag), indexes, out_value); }
170 
171 bool vil_nitf2_tagged_record::get_value(std::string tag, const vil_nitf2_index_vector& indexes, std::string& out_value) const
172 { return m_field_sequence->get_value(std::move(tag), indexes, out_value); }
173 
174 bool vil_nitf2_tagged_record::get_value(std::string tag, const vil_nitf2_index_vector& indexes, vil_nitf2_location*& out_value) const
175 { return m_field_sequence->get_value(std::move(tag), indexes, out_value); }
176 
177 bool vil_nitf2_tagged_record::get_value(std::string tag, const vil_nitf2_index_vector& indexes, vil_nitf2_date_time& out_value) const
178 { return m_field_sequence->get_value(std::move(tag), indexes, out_value); }
179 
180 #if VXL_HAS_INT_64
181 // if not VXL_HAS_INT_64 isn't defined the vil_nitf2_long is the same as just plain 'int'
182 // and this function will be a duplicate of that get_value
183 bool vil_nitf2_tagged_record::get_value(std::string tag, const vil_nitf2_index_vector& indexes, vil_nitf2_long& out_value) const
184 { return m_field_sequence->get_value(std::move(tag), indexes, out_value); }
185 #endif
186 
187 // Macro to define both overloads of get_values()
188 #define VIL_NITF2_TAGGED_RECORD_GET_VALUES(T) \
189 bool vil_nitf2_tagged_record::get_values(std::string tag, const vil_nitf2_index_vector& indexes, \
190  std::vector<T>& out_values, bool clear_out_values) const \
191 { return m_field_sequence->get_values(tag, indexes, out_values, clear_out_values); } \
192 bool vil_nitf2_tagged_record::get_values(std::string tag, std::vector<T>& out_values) const \
193 { return m_field_sequence->get_values(tag, out_values); }
194 
202 #if VXL_HAS_INT_64
204 #endif
205 
206 
208  : m_length_field(nullptr), m_tag_field(nullptr), m_length(0), m_definition(nullptr), m_field_sequence(nullptr)
209 {}
210 
211 // TO DO: rewrite this method a sequence of unit tests!
212 //
214 {
215  bool error = false;
216  const char* test_tre_tag = "@TEST@";
217  // Example Tagged Record Extension definition
218  vil_nitf2_tagged_record_definition::define(test_tre_tag, "Test Definition" )
219  .field("MTI_DP", "Destination Point", NITF_INT(2))
220  .field("MTI_PACKET_ID", "MTI Packed ID Number", NITF_INT(3))
221  .field("DATIME", "Scan Date & Time", NITF_DAT(14), true)
222  .field("ACFT_LOC", "Aircraft Position", NITF_LOC(21))
223  .field("ACFT_LOC2", "Aircraft Position 2", NITF_LOC(21))
224  .field("SQUINT_ANGLE", "Squint Angle", NITF_DBL(6, 2, true), true)
225  .field("NO_VALID_TARGETS", "Number of Targets", NITF_INT(3))
226  .field("TGT_CAT", "Target Classification Category",
228  .value("H", "Helicopter")
229  .value("T", "Tracked")
230  .value("U", "Unknown")
231  .value("W", "Wheeled")
232  .value("TOO LONG", "Too long value test")
233  .value("T", "Duplicate value test")))
234  .repeat(new vil_nitf2_field_value<int>("NO_VALID_TARGETS"),
236  .field("TGT_n_SPEED", "Target Estimated Ground Speed", NITF_INT(4), true)
237  .field("TGT_n_CAT", "Target Classification Category",
239  .value("H", "Helicopter")
240  .value("T", "Tracked")
241  .value("U", "Unknown")
242  .value("W", "Wheeled")),
243  true))
244  .field("TEST_NEG_COND", "Test False Condition", NITF_STR_BCSA(14), false,
245  nullptr, new vil_nitf2_field_value_greater_than<int>("MTI_DP", 5))
246  .field("TEST_POS_COND", "Test True Condition", NITF_STR_BCSA(14), false,
247  nullptr, new vil_nitf2_field_value_greater_than<int>("MTI_DP", 1))
248  .field("CLASS", "Security Classification",
250  .value("T", "Top Secret")
251  .value("S", "Secret")
252  .value("C", "Confindential")
253  .value("R", "Restricted")
254  .value("U", "Unclassified")),
255  true, nullptr, nullptr)
256  .field("CODEW", "Code Words", NITF_STR_BCSA(15), false,
257  nullptr, new vil_nitf2_field_value_one_of<std::string>( "CLASS", "T" ) )
258  .field("CWTEST", "Another Code Word Test", NITF_STR_BCSA(15), false,
259  nullptr, new vil_nitf2_field_value_one_of<std::string>( "CLASS", "U" ) )
260  .field("NBANDS", "Number of bands", NITF_INT(1), false,
261  nullptr, nullptr )
262  .field("XBANDS", "Large number of bands", NITF_INT(2), false,
263  nullptr, new vil_nitf2_field_value_one_of<int>("NBANDS",0))
264  .repeat(new vil_nitf2_choose_field_value<int>("NBANDS", "XBANDS",
265  new vil_nitf2_field_value_greater_than<int>("NBANDS", 0)),
267  .field("BAND_LTR", "Band Description", NITF_CHAR(), true,
268  nullptr)
269  )
270  .field( "EXP_TEST", "Exponential format test", NITF_EXP(6,1))
271  // test nested repeats and functor references to tags within and
272  // outside repeat loops
273  .field( "N", "Test repeat N", NITF_INT(1))
275  .field("A", "Test repeat A", NITF_INT(1))
277  .field("S", "Test repeat S", NITF_STR(3)))
279  .field("B", "Test repeat B", NITF_STR_BCSA(3))
281  .field("C", "Test repeat C", NITF_STR_BCSA(4)))))
282  // test fixed repeat count
284  .field("D", "Test fixed repeat", NITF_INT(1))
285  )
286  .end();
287  // Create a test input std::string
288  std::string testFieldsStr =
289  "02" // MTI_DP
290  "003" // MTI_PACKET_ID
291  //"19990908070605" // DATIME
292  " " // DATIME
293  "+89.111111-159.222222" // ACFT_LOC
294  "890102.33N0091122.00W" // ACFT_LOC2
295  "-60.00" // SQUINT_ANGLE
296  "003" // NO_VALID_TARGETS
297  " " // TGT_CAT
298  "2222" "H" // TGT_1_SPEED2, TGT_1_CAT2
299  " " " " // TGT_2_SPEED2, TGT_2_CAT2
300  "4444" "T" // TGT_3_SPEED2, TGT_3_CAT2
301  "" // TEST_NEG_COND not present
302  "True Condition" // TEST_POS_COND
303  "T" // CLASS
304  "RandomCodeWords" // CODEW (only present if CLASS=T)
305  "" // CWTEST (not present because CLASS!=U
306  "0" // NBANDS (0, so use XBANDS instead)
307  "12" // XBANDS (present because NBANDS=0)
308  "abcdefghijkl" // 12 BAND_LTRs (XBAND=12)
309  "+1.234567E-8" // Exponential format test
310  // test nested repeats
311  "2" // N
312  // for i=0...N-1: i=0
313  "1" // A[0]
314  "S00" // S[0,0]
315  "S01" // S[0,1]
316  // for j=0...A[i]-1: j=0
317  "B00" // B[0,0]
318  // for k=0..A[i]-1: k=0
319  "C000" // C[0,0,0]
320  // i=1:
321  "2" // A[1]
322  "S10" // S[1,0]
323  "S11" // S[1,1]
324  // for j=0..A[i]: j=0
325  "B10" // B[1,0]
326  // for k=0..A[i]
327  "C100" // C[1,0,0]
328  "C101" // C[1,0,1]
329  "B11" // B[1,1]
330  "C110" // C[1,1,0]
331  "C111" // C[1,1,1]
332  // test fixed repeat
333  "7890"
334  ;
335  std::stringstream test_stream;
336  test_stream << test_tre_tag; // CETAG
337  test_stream << std::setw(5) << std::setfill('0') << testFieldsStr.length(); // CELENGTH
338  test_stream << testFieldsStr; // rest of fields
339  std::string read_string = test_stream.str();
340  // Write the test input std::string to a vil_stream
341  auto* vs = new vil_stream_core();
342  vs->write(read_string.c_str(), read_string.length());
343  vs->seek(0);
344  auto* vss = new vil_stream_section(vs, 0, int(read_string.length()));
345  // Record from the vil_stream
347  if (record)
348  {
349  std::cerr << *record << '\n';
350  // Now write the record, and compare the output to the test input
351  std::cerr << "\nOriginal string:\n" << read_string
352  << "\nWrite() output:\n";
353  auto* vs2 = new vil_stream_core();
354  record->write(*(vil_stream*)vs2);
355  vil_streampos bufsize = vs2->file_size();
356  char* buf = new char[(unsigned int)bufsize + 1];
357  vs2->seek(0);
358  vs2->read(buf, bufsize);
359  buf[bufsize]='\0';
360  std::string write_string = std::string(buf);
361  std::cerr << write_string << '\n';
362  if (read_string != write_string) {
363  std::cerr << "\nWrite failed!\n";
364  error = true;
365  }
366  delete[] buf;
367  std::cerr << "Testing get_value:\n";
368  int mti_dp;
369  if (!record->get_value("MTI_DP", mti_dp) || mti_dp!=2) {
370  std::cerr << "Get value failed!\n";
371  error = true;
372  }
373  else {
374  std::cerr << "MTI_DP = " << mti_dp << '\n';
375  }
376  int tgt_speed[4];
377  if (!record->get_value("TGT_n_SPEED", vil_nitf2_index_vector(0), tgt_speed[0]) ||
378  tgt_speed[0] != 2222 ||
379  record->get_value("TGT_n_SPEED", vil_nitf2_index_vector(1), tgt_speed[1]) /*should be null*/ ||
380  !record->get_value("TGT_n_SPEED", vil_nitf2_index_vector(2), tgt_speed[2]) ||
381  tgt_speed[2] != 4444) {
382  std::cerr << "Get vector value test failed!\n";
383  error = true;
384  }
385  else {
386  std::cerr << "TGT_2_SPEED = " << tgt_speed[2] << '\n';
387  }
388  int d2;
389  if (!record->get_value("D", vil_nitf2_index_vector(2), d2) || d2 != 9) {
390  std::cerr << "Get fixed repeat count test failed!\n";
391  error = true;
392  }
393  // fetch C[*]
394  std::cerr << "Testing get_values (all values)...\n";
395  std::vector<std::string> c_values;
396  if (!record->get_values("C", c_values) ||
397  c_values.size() != 5 ||
398  c_values[0]!="C000" ||
399  c_values[1]!="C100" ||
400  c_values[2]!="C101" ||
401  c_values[3]!="C110" ||
402  c_values[4]!="C111") {
403  std::cerr << "failed!\n\n";
404  error = true;
405  }
406  // Fetch A[1,*]
407  std::cerr << "Get values (partial index)...\n";
408  vil_nitf2_index_vector indexes;
409  std::vector<int> a_values;
410  indexes.push_back(1);
411  if (!record->get_values("A", indexes, a_values) ||
412  a_values.size() != 1 ||
413  a_values[0] != 2) {
414  std::cerr << "failed!\n\n";
415  error = true;
416  }
417  // Fetch C[1,*]
418  if (!record->get_values("C", indexes, c_values) ||
419  c_values.size() != 4 ||
420  c_values[0]!="C100" ||
421  c_values[1]!="C101" ||
422  c_values[2]!="C110" ||
423  c_values[3]!="C111")
424  {
425  std::cerr << "failed!\n\n";
426  }
427  }
428  else {
429  std::cerr << "Didn't create record!\n";
430  error = true;
431  }
432  // Try output of vector field
433  std::cerr << "Output of vector field C:\n"
434  << *(record->get_field("C"));
435  // Clean up test definition and test cleanup
437  std::cerr << "Error undefining TRE.\n";
438  error = true;
439  }
440  return !error;
441 }
442 
443 std::ostream& vil_nitf2_tagged_record::output(std::ostream& os) const
444 {
445  os << "CETAG: " << name() << '\n'
446  << "CELEN: " << length() << std::endl;
447  for (auto & m_field_definition : *m_definition->m_field_definitions)
448  {
449  vil_nitf2_field_definition* field_def = m_field_definition->field_definition();
450  // to do: handle other nodes
451  if (!field_def) break;
452  vil_nitf2_field* field = get_field(field_def->tag);
453  os << field_def->tag << ": ";
454  if (field) {
455  os << *field << std::endl;
456  }
457  else {
458  os << "(undefined)" << std::endl;
459  }
460  }
461  return os;
462 }
463 
465 {
466  // To track of how much is written
467  vil_streampos start = output.tell();
468  // Write tag and length fields
469  if (m_tag_field && m_length_field) {
472  }
473  else return false;
474  // Write data fields
476  // Check whether the std::right amount was written
477  vil_streampos end = output.tell();
478  vil_streampos length_written = end - start;
479  int expected_length = s_tag_formatter().field_width + s_length_formatter().field_width
480  + length();
481  return length_written == expected_length;
482 }
483 
485 {
486  delete m_field_sequence;
487 }
488 
490 {
491  for (auto m_field_definition : *m_field_definitions)
492  {
493  vil_nitf2_field_definition* field_def = m_field_definition->field_definition();
494  // to do: search other nodes
495  if (!field_def) break;
496 
497  if (field_def->tag == tag) {
498  return field_def;
499  }
500  }
501  // tag definition not found
502  return nullptr;
503 }
504 
506 {
507  //create our tree
508  //we add the field definitions if the TRE was recognized, or we note that we
509  //skipped it otherwise
511  if ( m_field_sequence ) {
512  tr = m_field_sequence->get_tree();
513  }
514  else {
516  auto* skipped_node = new vil_nitf2_field::field_tree;
517  skipped_node->columns.emplace_back("CEDATA" );
518  skipped_node->columns.emplace_back("<skipped unknown TRE>" );
519  tr->children.push_back( skipped_node );
520  }
521 
522  //add the columns describing the name of the TRE
523  tr->columns.push_back( name() );
524  tr->columns.push_back( pretty_name() );
525  //add the CEL (length) field to the front
526  auto* first_child = new vil_nitf2_field::field_tree;
527  first_child->columns.emplace_back("CEL" );
528  first_child->columns.emplace_back("Extension Length" );
529  std::stringstream len_stream;
530  len_stream << length();
531  first_child->columns.push_back( len_stream.str() );
532  tr->children.insert( tr->children.begin(), first_child );
533  return tr;
534 }
535 
536 std::ostream& operator << (std::ostream& os, const vil_nitf2_tagged_record& record)
537 {
538  return record.output(os);
539 }
540 
541 // vil_nitf2_tagged_record_sequence
542 
543 std::ostream& operator << (std::ostream& os, const vil_nitf2_tagged_record_sequence& seq)
544 {
545  os << seq.size() << " TRE's:" << std::endl;
546  vil_nitf2_tagged_record_sequence::const_iterator it;
547  for (it = seq.begin(); it != seq.end(); ++it) {
548  os << *it << std::endl;
549  }
550  return os;
551 }
Functor vil_nitf2_field_value_one_of defines a predicate that sets its out parameter to true iff the ...
vxl_int_32 vil_nitf2_long
Definition: vil_nitf2.h:26
Functor vil_nitf2_field_value defines a function that sets its out parameter to a value of a field fr...
#define VIL_NITF2_TAGGED_RECORD_GET_VALUES(T)
virtual vil_streampos tell() const =0
Return file pointer.
bool read(vil_nitf2_istream &input, const vil_nitf2_field_definitions *field_defs=nullptr, const vil_nitf2_index_vector &indexes=vil_nitf2_index_vector())
#define NITF_INT
std::vector< std::string > columns
#define NITF_STR_BCSA(LEN)
std::ostream & output(std::ostream &) const
void end()
Declares that definition is finished, preventing further invocations of field() or repeat().
vil_nitf2_field * get_field(std::string tag) const
#define NITF_STR
vil_nitf2_tagged_record_definition & field(std::string field_name, std::string pretty_name, vil_nitf2_field_formatter *formatter, bool blanks_ok=false, vil_nitf2_field_functor< int > *width_functor=nullptr, vil_nitf2_field_functor< bool > *condition_functor=nullptr, std::string units="", std::string description="")
Define a field. Assumes ownership of pointer arguments.
virtual void seek(vil_streampos position)=0
Goto file pointer.
#define VIL_NITF2_LOG(LEVEL)
Definition: vil_nitf2.h:63
virtual vil_streampos read(void *buf, vil_streampos n)=0
Read n bytes into buf. Returns number of bytes read.
#define NITF_DAT
static vil_nitf2_field_definition & s_length_definition()
vil_nitf2_field_definition * find_field_definition(const std::string &tag)
vil_nitf2_tagged_record_definition * m_definition
#define NITF_EXP
bool write(vil_nitf2_ostream &output, int variable_width=-1) const
static vil_nitf2_tagged_record_definition & define(std::string name, std::string pretty_name)
Factory method. Assumes ownership of optional pointer argument.
#define NITF_DBL
virtual bool write(vil_nitf2_ostream &, const vil_nitf2_field_definitions *field_defs=nullptr, vil_nitf2_index_vector indexes=vil_nitf2_index_vector())
virtual bool value(int &) const
Stream interface for VIL image loaders.
Definition: vil_stream.h:21
const vil_nitf2_field_definitions & field_definitions() const
Return field definitions.
static vil_nitf2_field_definition & s_tag_definition()
vil_nitf2_scalar_field * m_tag_field
vil_nitf2_field_sequence * m_field_sequence
make a section of a vil_stream behave like a vil_stream.
static vil_nitf2_integer_formatter & s_length_formatter()
vil_nitf2: Written by Harry Voorhees (hlv@) and Rob Radtke (rob@) of Stellar Science Ltd.
bool get_value(std::string tag, int &out_value) const
vil_nitf2_scalar_field * m_length_field
bool read(vil_nitf2_istream &input)
vil_nitf2_tagged_record_definition defines a particular tagged record extension (TRE).
static bool undefine(const std::string &name)
Undefines a TRE. Returns whether TRE with specified name was found.
An in-core vil_stream implementation.
#define NITF_CHAR
virtual bool ok() const =0
Return false if the stream is broken.
const vil_nitf2_field_definitions * m_field_definitions
virtual vil_nitf2_field::field_tree * get_tree() const
Functor vil_nitf2_choose_field_value defines a function that sets its out parameter to a value of one...
std::vector< field_tree * > children
vxl_int_32 vil_streampos
Definition: vil_stream.h:16
virtual vil_streampos file_size() const =0
Amount of data in the stream.
make a section of a vil_stream behave like a vil_stream
static vil_nitf2_tagged_record_definition * find(const std::string &name)
Look up a record definition.
std::ostream & operator<<(std::ostream &os, const vil_nitf2_tagged_record &record)
bool get_value(std::string tag, int &out_value) const
vil_nitf2_tagged_record_definition & repeat(vil_nitf2_field_functor< int > *repeat_functor, vil_nitf2_field_definitions &field_definitions)
Define a repeat node. Assumes ownership of pointer argument.
virtual vil_nitf2_field::field_tree * get_tree(vil_nitf2_field::field_tree *tr=nullptr) const
Functor vil_nitf2_field_value_greater_than defines a comparison predicate that sets its out parameter...
#define NITF_LOC
vil_nitf2_field_definition * field_definition()
static vil_nitf2_scalar_field * read(vil_nitf2_istream &input, vil_nitf2_field_definition *definition, int variable_width=-1, bool *error=nullptr)
virtual bool write(vil_nitf2_ostream &)
An in-core vil_stream implementation.
static vil_nitf2_string_formatter & s_tag_formatter()
static vil_nitf2_tagged_record * create(vil_nitf2_istream &input)
#define NITF_ENUM