NITF File

This section describes the overall software design. You can also NitfFile for descriptions of the class member functions.

We want to be able to read an write NITF files.

The NitfFile class structure is shown in Fig. 4.

class NitfFile {
   +NitfFile(file_name=None,\n         security = security_unclassified)
   +read(file_name)
   +write(file_name)
   +NitfFileHeader file_header
   +file_name
   +NitfImageSegment image_segment[]
   +NitfGraphicSegment graphic_segment[]
   +NitfTextSegment text_segment[]
   +NitfDesSegment des_segment[]
   +NitfResSegment res_segment[]
   +Tre tre_list[]
   +security
}
note top
   The NITF file class, used for reading
   and writing a NITF file
end note

abstract class NitfSegment {
   {static} sh_class
   {static} _update_file_header_field
   {static} _type_support_tre
   {static} _tre_field_list
   {property} nitf_file
   {property} subheader
   {property} user_subheader
   {property} security
   +summary()
   +read_from_file(fh, seg_index=None)
   +write_to_file(fh, seg_index)
   +Tre tre_list[]
   +data
   +header_size
   +data_size
}
note top
   Base class of NITF segments.

   Note not all segment types can have
   user_subheaders or TREs. Just to prevent needing
   special handling for each segment type, we
   include this in all segment types but set to
   None (for user_subheader) or an empty list (for
   TREs) if the particular segment type doesn't
   support that.
end note

class NitfImageSegment {
   {property} image
   {property} idlvl
   {property} iid1
}

class NitfGraphicSegment {
   {property} graphic
}

class NitfTextSegment {
   {property} text
}

class NitfDesSegment {
   {property} des
}

class NitfResSegment {
   {property} res
}

class Tre

NitfSegment <|.. NitfImageSegment
NitfSegment <|.. NitfGraphicSegment
NitfSegment <|.. NitfTextSegment
NitfSegment <|.. NitfDesSegment
NitfSegment <|.. NitfResSegment
NitfFile o-- "many" NitfImageSegment
NitfFile o-- "many" NitfGraphicSegment
NitfFile o-- "many" NitfTextSegment
NitfFile o-- "many" NitfDesSegment
NitfFile o-- "many" NitfResSegment
NitfFile "file level" o-- "many" Tre
NitfImageSegment o-- "many" Tre
NitfTextSegment o-- "many" Tre
NitfGraphicSegment o-- "many" Tre

Fig. 4 NitfFile class structure

Note that only NitfFile, NitfImageSegment, NitfTextSegment and NitfGraphicSegment can have TREs. NitfResSegment and NitfDesSegment can not. However, NitfResSegment and NitfDesSegment can have a “user_subheader” supplied. The particular fields in a user_subheader are determined by the desid or resid type identifier.

TRE Errors

It is not infrequent to run into a file that has a TRE that doesn’t conform to the documentation that we code from. A required field might be left blank for example, or a format might have changed (e.g. MATESA changed from version 0.1 to 1.0 of the SNIP).

Depending on what you are trying to do you may want:

  1. The difference to be completely ignored, and the TRE replaced with TreUnknown.

  2. The difference reported as a warning, and the TRE replaced with TreUnknown.

  3. The difference to be treated as an error and an exception thrown.

To accommodate this range of possibilities. the TRE reading code reports using python warnings module. We introduce a new warning “TreWarning” derived from the standard UserWarning exception.

You can use the standard warnings filterwarnings function to control the behavior. For example, to treat all TreWarnings as an error you can use the code snippet:

warnings.filterwarnings("error", category=pynitf.TreWarning)

To ignore just the MATESA format change, you can use:

warnings.filterwarnings("ignore", "Trouble reading TRE MATESA",
                        category=pynitf.TreWarning)

NitfFile Handles and Hooks

In addition to the NitfSegment, NitfFile contains several handle and hooks, shown in Fig. 5.

class NitfFile {
   +NitfSegmentHookSet segment_hook_set
   +NitfSegmentUserSubheaderHandleSet user_subheader_handle_set
   +NitfSegmentDataHandleSet data_handle_set
}

class NitfSegmentHookSet {
   +add_hook(h)
   +discard_hook(h)
   {static} add_default_hook(cls, h)
   {static} discard_default_hook(cls, h)
   {static} default_hook_set()
   }
note top
  Hook objects to extend the handling
  of various attributes of a segments
  (e.g., add higher level classes Rpc
  or RSM).
end note

class NitfSegmentUserSubheaderHandleSet {
   +user_subheader_cls(seg)
}
note bottom
   Handle reading and writing User
   Subheaders for various segments.
end note

class NitfSegmentDataHandleSet {
  +read_from_file(seg, fh, seg_index=None)
}
note top
   Handle reading and writing the
   data in a segment (e.g, a image)
end note

NitfFile o--  NitfSegmentHookSet
NitfFile o--  NitfSegmentUserSubheaderHandleSet
NitfFile o--  NitfSegmentDataHandleSet

Fig. 5 NitfFile class Handles and Hooks

NitfSegmentHookSet

The NitfSegmentHookSet is used to extend the handling of various attributes of a segment. The hooks are pretty general, and can be used for whatever is desired. But the original use case was adding higher level objects to NitfSegments such as RPC and RSM (done in the separate GeoCal library).

The current set of higher level objects are:

Table 1 Higher level objects handled by various NitfSegmentHook

Segment Attribute

Description

rpc

In GeoCal (not pynitf). This is a RPC (Rational Polynomial Coefficient. This is a common technique, and there are numerous references. One reference is Fraser, CS, Dial, G, Grodecki, J “Sensor orientation via RPCs” ISPRS J PHOTOGRAMM 60 (3): 182-194 MAY 2006.

rsm

In GeoCal (not pynitf). This is a RSM (Replacement Sensor Model), see Dolloff, J.T., M.M. Iiyama, and C.R. Taylor, 2008. The Replacement Sensor Model (RSM): Overview, Status, and Performance Summary, ASPRS 2008 Annual Conference, April 28 - May 2, 2008

See Fig. 6.

class NitfFile {
   +NitfSegmentHookSet segment_hook_set
}

class NitfSegmentHookSet {
   +after_init_hook(seg, nitf_file)
   +after_append_hook(seg, nitf_file)
   +before_write_hook(seg, nitf_file)
   +after_read_hook(seg, nitf_file)
   +before_str_hook(seg, nitf_file, fh)
   +before_str_tre_hook(seg, tre, nitf_file, fh)
   +add_hook(h)
   +discard_hook(h)
   {static} add_default_hook(cls, h)
   {static} discard_default_hook(cls, h)
   {static} default_hook_set()
   }
note top
  Set of all the hook objects we use
  for a NitfFile.
end note

class NitfSegmentHook {
   +after_init_hook(seg, nitf_file)
   +after_append_hook(seg, nitf_file)
   +before_write_hook(seg, nitf_file)
   +after_read_hook(seg, nitf_file)
   +before_str_hook(seg, nitf_file, fh)
   +before_str_tre_hook(seg, tre, nitf_file, fh)
   +remove_for_report_raw()
}
note bottom
   Hook object to extend handling of
   various attributes of a NitfSegment.
end note

NitfFile o--  NitfSegmentHookSet
NitfSegmentHookSet o-- "many" NitfSegmentHook

Fig. 6 NitfSegmentHookSet

We call all the NitfSegmentHook objects at several points in the processing:

  • After NitfSegment.__init__ is called for a segment. This might do something like add a new attribute to the newly created segment (e.g., add “rpc”)

  • Before writing a NitfSegment to a file. This might translate a higher level object into TREs (e.g., for a RSM object).

  • After reading a NitfSegment. This might create a object based on TREs (e.g., RSM based on various RSM Tres). Note this actually gets called after the entire file has been read, so if the objects depend on other later segments they are available (e.g., the orbit DESs for a GLAS/GFM object on a image segment)

  • Before calling __str__ on a NitfSegment. This can be used to write out a higher level object (e.g., RPC, RSM).

  • Before calling __str__ on a TRE. This can write a replacement text. Should return “True” if the TRE printing has been done by this function, “False” if the normal TRE printing should be done instead.

Note that when printing out a NitfSegment, most of the time we want the higher level objects printed. However, there may be instances where we want the “raw” data (e.g., nitfinfofull reporting raw TRE data). NitfSegmentHookSet will skip calling before_str_hook and before_tre_str_hook if “remove_for_report_raw” is True for the NitfSegmentHook.

NitfSegmentUserSubheaderHandleSet

The NitfSegmentUserSubheaderHandleSet is used to handle reading and writing the user subheaders found in the NitfDesSegment and NitfResSegment. This is a Priority Handle Set for handling each of these segment types. The handle returns the user subheader class type, which is then used by NitfSegment for reading and writing the user subheader. See Fig. 7.

class NitfFile {
   +NitfSegmentUserSubheaderHandleSet user_subheader_handle_set
}

class NitfSegmentUserSubheaderHandleSet {
   +user_subheader_cls(seg)
}
note bottom
  Return the Class to use for
  the user subheader for the
  given segment (or None for
  no user subheader)
end note

abstract class PriorityHandleSet {
   +add_handle(h, priority_order=0)
   +discard_handle(h)
   {static} add_default_handle(cls, h, priority_order=0)
   {static} discard_default_handle(cls, h, priority_order=0)
   {static} default_handle_set()
   +handle(*args, **keywords)
}

abstract class UserSubheaderHandle {
   {static} seg_class
   +user_subheader_cls(seg)
}

class DesIdToUSHHandle {
   +add_des_user_subheader(desid, des_user_subheader_cls)
}
note bottom
   Often we just need the DES ID to
   map to the class for the DES User Subheader.
   This class is a simple dict going from
   the id to the class that handles the
   user subheader.
end note

class ResIdToUSHHandle {
   +add_res_user_subheader(resid, res_user_subheader_cls)
}

NitfFile o--  NitfSegmentUserSubheaderHandleSet
NitfSegmentUserSubheaderHandleSet o-- "many" UserSubheaderHandle
UserSubheaderHandle <|-- DesIdToUSHHandle
UserSubheaderHandle <|-- ResIdToUSHHandle
PriorityHandleSet <|-- NitfSegmentUserSubheaderHandleSet

Fig. 7 NitfSegmentUserSubheaderHandleSet

NitfSegmentDataHandleSet

The NitfSegmentDataHandleSet is used to handle reading and writing the data field of each of the NitfSegment types. This is a Priority Handle Set for handling each of these segment types. See Fig. 8.

class NitfFile {
   +NitfSegmentDataHandleSet data_handle_set
}

class NitfSegmentDataHandleSet {
  +read_from_file(seg, fh, seg_index=None)
  +handle_h(cls, seg, fh, seg_index)
}
note top
   Handle reading the data in
   a segment (e.g, a image)
end note

abstract class PriorityHandleSet {
   +add_handle(h, priority_order=0)
   +discard_handle(h)
   {static} add_default_handle(cls, h, priority_order=0)
   {static} discard_default_handle(cls, h, priority_order=0)
   {static} default_handle_set()
   +handle(*args, **keywords)
}

abstract class NitfData {
   {static} seg_class
   {static} sh_class
   {static} uh_class
   {property} subheader
   {property} user_subheader
   {property} user_subheader_size
   +__init__(seg=None)
   {abstract} read_from_file(fh, seg_index = None)
   {abstract} write_to_file(fh):
   {property} security
}
note left
   Handle reading and writing
   the data in a segment (e.g,
   a image). read_from_file should
   return True if this class can
   handle the type, and False
   otherwise.
end note

abstract class NitfImage {
   {property} shape
   {property} dtype
   {property} idlvl
   {property} iid1
}
abstract class NitfDes
abstract class NitfText
abstract class NitfGraphic
abstract class NitfRes

class NitfDataPlaceHolder {
   +read_from_file(fh, seg_index = None)
   +write_to_file(fh):
}
note top
  Implementation that doesn't actually
  read data, instead it skips it.
  Useful as a final place holder of none
  of our other NitfData classes can
  handle a particular segment.
end note

NitfFile o--  NitfSegmentDataHandleSet
NitfSegmentDataHandleSet o-- "many" NitfData
PriorityHandleSet <|-- NitfSegmentDataHandleSet
NitfData <|-- NitfImage
NitfData <|-- NitfDes
NitfData <|-- NitfText
NitfData <|-- NitfGraphic
NitfData <|-- NitfRes
NitfData <|-- NitfDataPlaceHolder
NitfImage -[hidden]down- NitfDes
NitfImage -[hidden]down- NitfText
NitfImage -[hidden]down- NitfGraphic
NitfImage -[hidden]down- NitfRes

Fig. 8 NitfSegmentDataHandleSet

NitfFile convenience functions

While the individual lists can be filters/searched using normal python functions, there are a set of things done frequently enough that it is useful to add convenience functions to do them. These are shown in in Fig. 9.

class NitfFile {
   +engrda
   +find_tre(tre_tag)
   +find_one_tre(tre_tag)
   +find_exactly_one_tre(tre_tag)
   +iseg_by_idlv(idlvl)
   +iseg_by_iid1(iid1)
   +iseg_by_iid1_single(iid1)
}

note left of NitfFile::engrda
   ENGRDA data returned as a
   dict like interface (e.g.,
   f.engrda["My_sensor 1"]["TEMP1"])

   Both reading and setting values
   supported
end note
note right of NitfFile::find_tre
   Return list of TREs of the
   given tag. Possibly empty
end note
note right of NitfFile::find_one_tre
   Find at most one TRE of the
   given tag. Return None if not
   found, error if multiple found
end note
note right of NitfFile::find_exactly_one_tre
   Like find_one_tre, but not finding
   TRE is treated as an error.
end note

class NitfImageSegment {
   +engrda
   +find_tre(tre_tag)
   +find_one_tre(tre_tag)
   +find_exactly_one_tre(tre_tag)
}

class NitfTextSegment {
   +engrda
   +find_tre(tre_tag)
   +find_one_tre(tre_tag)
   +find_exactly_one_tre(tre_tag)
}

class NitfGraphicSegment {
   +engrda
   +find_tre(tre_tag)
   +find_one_tre(tre_tag)
   +find_exactly_one_tre(tre_tag)
}
NitfFile o-- "many" NitfImageSegment
NitfFile o-- "many" NitfGraphicSegment
NitfFile o-- "many" NitfTextSegment

Fig. 9 NitfFile Convenience Functions