The DIRSIG4 Interactive Mode provides a highly configurable interface for querying simulations. Users supply arbitrary rays to be traced and then extract the result. The output can be set to human-friendly plaintext, parser-friendly XML, or high-performance binary. During the interactive simulation, the specific information to be pulled is specified through a configuration file. This is a simple XML file for which a a Document Type Definition (DTD) is available.
The purpose of Interactive Mode is to abstract DIRSIG down to a scene radiometry prediction model that can be queried by external tools. The most common application is where a user develops their own sensor model rather than using the built-in platform and sensor model. To compute the flux onto each pixel in their model, they can ask DIRSIG for the radiances along various rays that sample the pixel.
Although the tutorial at the end of this guide describes reading a fixed set of queries from an input file and writing the results of those queries to an output, the point of interactive mode is to be interactive. Hence, the expected usage is to start the DIRSIG model in this mode and have an external piece of software send queries to the model and receive the results as part of a continuous, interactive exchange.
Collector File
This file defines how the interactive session will operate by defining what data will be generated and the format of this data (ASCII/Text, XML or binary). An example of the collector XML is provided below and is then followed by a description:
<xml version="1.0" standalone="no"?>
<!DOCTYPE interactive SYSTEM "http://www.dirsig.org/dtds/interactive.dtd">
<interactive format="plain" pausepolicy="never">
<simulationcollectors>
<simscenefilename/>
<simbandpasses/>
<simscenebbox/>
</simulationcollectors>
<solutioncollectors>
<pathviewray/>
<pathsolidangle/>
<pathsurfacecount/>
</solutioncollectors>
<surfacecollectors>
<surfacematid/>
<surfacetemperature/>
<surfaceentrypoint/>
<surfaceexitpoint/>
<surfacepathlength/>
<surfacenormal/>
<surfacetransmission/>
<surfaceradiance/>
</surfacecollectors>
</interactive>
The interactive section has four attributes which may be set:
-
The
format
attribute may be set toplain
,xml
, orbinary
. It controls the output formatting of the Interactive Session. -
The
pausepolicy
is an experimental feature which may be set tonever
,percollector
, orpersurface
. Rather than immediately output all the results, the interactive session will halt at the specified points, waiting for the user to type an arbitrary character (or characters) followed by a new line. -
The
startupoutputredirect
may specify a filename to which all the DIRSIG startup text gets redirected. This does not include the output portion of the interactive session. If this attribute is not specified, then all output will go to either stdout or stderr, as in a normal DIRSIG simulation. Note that input must still be provided via stdin. -
The
interactiveoutputredirect
may specify a filename to which all the DIRSIG interactive results are redirected. If this attribute is not specified, then all output will go to stdout. Note that input must still be provided via stdin.
In the collector configuration file, the <simulationcollectors>
corresponds to the Simulation Level Queries. Likewise,
<solutioncollectors>
go with Solutions and
<surfacecollectors>
are applied to each surface in a given solution.
A list of the available collectors are provided in the following tables.
The Document Type Definition (DTD)
associated with this input file also lists the available options and
provides a means for syntax validation using external tools.
Simulation Collectors
The following table lists the available collectors to gather simulation level information.
Name | Description |
---|---|
simatmmodelname |
String describing atmospheric model being used. |
simscenefilename |
Name of the top-level ODB filename for the simulation. |
simmatfilename |
Name of the simulation material filename. |
simbandpasses |
Outputs the simulation bandpass list. This is indicates the discretization of electromagnetic spectrum which DIRSIG is using in its radiometric calculations. |
simscenebbox |
Provides extent points of an axis-aligned bounding box encapsulating all the non-moving geometry in the scene. |
Solution Collectors
The following table lists the available collectors to gather solution level information.
Collector Tag | Description |
---|---|
pathviewray |
Origin and direction of the ray used to intersect geometry. (Matches what is supplied by user). |
pathsolidangle |
Solid angle used for radiometric calculations with view ray. (Matches what is supplied by user). |
pathsurfacecount |
Number of surfaces intersected by the ray before the path transmission fell below DIRSIG’s internal threshold. (Normally the maximum value in the transmission spectral vector is no higher than 0.5%). |
pathradiance |
The sensor reaching radiance. |
pathtransmission |
Transmission along the entire path. Note that this will be zero if the ray strikes any opaque surfaces. |
Surface Collectors
The following table lists the available collectors to gather surface level information.
Collector Tag | Description |
---|---|
surfacetransmission |
A spectral vector of Mueller matrices indicating the surface transmission. |
surfaceradiance |
A spectral vector of stokes vector indicating the surface radiance in W/(cm^2 * omega * micron). |
surfaceirradiance |
A spectral vector of stokes vectors indicating the surface irradiance in W/(cm^2). |
surfacetemperature |
The temperature of the surface in Kelvin. |
surfaceentrypoint |
World space coordinate (in meters) where the view ray hit the surface. |
surfaceexitpoint |
World space coordinate (in meters) where the view ray left the surface. Relevant for volume geometry. |
surfaceentrydistance |
Distance along ray to surface entry. |
surfaceexitdistance |
Distance along ray to surface exit. |
surfaceentryangle |
Entry angle of the ray relative to the surface normal. Given in radians. |
surfaceexitangle |
Exit angle of the ray relative to the surface normal. Given in radians. |
surfacepathlength |
Length of the ray segment passing through the surface. Relevant for volume geometry and equals the Euclidean distance between the entry and exit point. |
surfacenormal |
A normalized vector describing the surface normal. |
surfacematid |
The Material Id associated with the surface. This corresponds to an entry in the materials database (.mat file). |
surfacethermalthickness |
Non-volume geometry can be given a thermal thickness for the purposes of running DIRSIG’s temperature (Therm) model. This value is normally specified in the materials database (.mat file), but can be overridden on a per facet basis in a GDB file. |
surfacegasconcentration |
Volume geometry may be used to represent a gas, in which case this value represents the gas concentration of the associated material in PPM. |
Polarized Mueller Matrices are stored in row major order. |
Surfaces are output in ascending order sorted by entry distance. |
Bandpass File
DIRSIG defines spectral regions using a minimum, maximum, and a uniform delta. These spectral regions are termed a "bandpass". Multiple bandpasses can be assembled in to a "bandpass list". A bandpass with a minimum wavelength of 0.3 microns, a maximum wavelength of 0.5 microns, and a sampling delta of 0.1 microns tells DIRSIG to simulate for the wavelengths 0.3, 0.4, and 0.5 microns. This means that internally, all the radiances, transmissions, irradiances, etc are computed for these three wavelengths. An example of the bandpass XML follows:
<bandpasslist>
<bandpass spectralunits="microns">
<minimum>0.3</minimum>
<maximum>0.5</maximum>
<delta>0.1</delta>
</bandpass>
</bandpasslist>
Spectral units of "microns", "nanometers", and "wavenumbers" are currently supported. An example of a bandpass XML specifying two bandpasses follows. This example will cause DIRSIG to simulate at 0.3, 0.31, 0.32, 0.33, 0.4, and 0.5 microns.
<bandpasslist>
<bandpass spectralunits="microns">
<minimum>0.3</minimum>
<maximum>0.5</maximum>
<delta>0.1</delta>
</bandpass>
<bandpass spectralunits="nanometers">
<minimum>310</minimum>
<maximum>330</maximum>
<delta>10</delta>
</bandpass>
</bandpasslist>
Input Description
With a collector and bandpass configuration file in place, DIRSIG4 can be started in Interactive Mode as follows:
$ dirsig --mode=interactive --scene_filename=example.scene \
--atm_filename=example.atm --collector_filename=collectors.xml \
--bandpass_filename=bandpass.xml --datetime=2008-04-01T14:00:00.0000-05:00
In this case, collector.xml
is the name of the
collector file and bandpass.xml
is the name of the
bandpass file.
When DIRSIG has finished loading the inputs, the following prompt will
appear:
Interactive Mode Ready <!-- [time] [omega] [orX] [orY] [orZ] [dirX] [dirY] [dirZ] -->
The XML style comment indicates the format for an input query that Interactive Mode expects (summarized below).
This informational prompt does not appear when operating in "binary" output mode. |
Parameter | Description |
---|---|
time |
Offset in seconds from reference simulation time (fractional and negative values allowed). |
omega |
The solid angle represented by the ray. |
orX, orY, orZ |
Ray origin: X, Y, and Z coordinates. |
dirX, dirY, dirZ |
Ray direction vector: X, Y, and Z values. |
Both ray origin and ray direction components are specified in the Scene ENU coordinate system. |
The ray direction vector cannot be zero length, but it does not need to be unit length. |
The solid angle (omega) must be greater than zero. The solid angle is utilized by only a handful of radiometry solvers and optical properties in DIRSIG. For example, the Fresnel radiometry solver uses a "solid angle in equals solid angle out" assumption when determining if the Sun overlaps the delta function ray reflected by a surface. |
Output Description
Interactive Mode is capable of outputting plain text, XML, or binary.
This is controlled by setting the format
attribute in the
collector file.
ASCII/Text Output
The ASCII/Text output format is intended to be human friendly. The following example contains the verbatim results of an interactive session.
Interactive Mode Ready # Simulation Parameters # Scene Bounding Box: minimum: (-521.713,-543.484,-953.792) maximum: (1654.42,1632.64,1222.34) # Solution Results # View Ray: origin: (800,800,1000) direction: (0,0,-1) Solid Angle: 1 Surface Count: 1 Path Radiance: [0.47 microns] 0.00279327 [0.48 microns] 0.00281209 [0.49 microns] 0.00275102 [0.5 microns] 0.0028744 # Surface 1 # Entry Distance: 859.254 Entry Angle: 0.485803 Material ID: 25 Interactive Mode Finished
XML Output
The XML output format allows users to easily parse interactive mode output using a DOM or SAX parser. The output is a completely valid XML document, including a reference to a DTD which can be used for basic syntax validation. Note that the spacing in the XML demonstration has been modified to make it more human-friendly. Likewise, some XML-style comments have been omitted for brevity.
<?xml version="1.0" standalone="no"?>
<!DOCTYPE interactivesession
SYSTEM "http://www.dirsig.org/dtds/interactive_out.dtd">
<interactivesession>
<simulationinfo>
<simscenebbox>
<box>
<lowerextent>
<point><x>-521.713</x><y>-543.484</y><z>-953.792</z></point>
</lowerextent>
<upperextent>
<point><x>1654.42</x><y>1632.64</y><z>1222.34</z></point>
</upperextent>
</box>
</simscenebbox>
</simulationinfo>
<solutioninfo>
<pathviewray>
<ray>
<origin><point><x>800</x><y>800</y><z>1000</z></point></origin>
<direction><vector><x>0</x><y>0</y><z>-1</z></vector></direction>
</ray>
</pathviewray>
<pathsolidangle>1</pathsolidangle>
<pathsurfacecount>1</pathsurfacecount>
<pathradiance>
<spectralvector>
<stokesvector location="0.47"> 0.00279327 </stokesvector>
<stokesvector location="0.48"> 0.00281209 </stokesvector>
<stokesvector location="0.49"> 0.00275102 </stokesvector>
<stokesvector location="0.5"> 0.0028744 </stokesvector>
</spectralvector>
</pathradiance>
<surfaceinfo>
<surfaceentrydistance>859.254</surfaceentrydistance>
<surfaceentryangle>0.485803</surfaceentryangle>
<surfacematid>25</surfacematid>
</surfaceinfo>
</solutioninfo>
</interactivesession>
Binary Output
Interactive Mode also supports binary output. This section describes the format for that output. The table below shows the assumptions about the size and formatting of the basic types discussed in this section.
All data is output as big endian, regardless of the CPU architecture on which DIRSIG is run. |
Basic Data Types
Type | Description |
---|---|
byte |
8 bits, signed via two’s complement, -128 to 127 |
int |
4 bytes, signed via two’s complement, big endian, -2,147,483,648 to 2,147,483,647 |
double |
8 bytes, IEEE754 format, big endian, 4.94065645841246544e-324 to 1.79769313486231570e+308 |
DIRSIG builds on these basic types to build more complex types for things like rays, stokes vectors, strings, etc. The following collection of tables shows how these DIRSIG types are structured. They list the elements of each type, in order, and explain what that element represents.
String
A string is returned when the user requests a name (for example, the material filename used in a sim).
Type | Description |
---|---|
int |
Number of characters in string. |
N bytes |
All N ASCII characters in string. |
Mueller Matrix
A Mueller Matrix is used to represent a polarized or unpolarized reflectance, emissivity, transmission, extinction, etc. at a given wavelength.
Type | Description |
---|---|
byte |
Polarization status. 0=unpolarized, 1=polarized. |
1 or 16 doubles |
Mueller matrix elements in row major order. If unpolarized, there is only a single double (M_00) otherwise the full 4x4 matrix is presented. |
Stokes Vector
A Stokes Vector is used to represent a polarized or unpolarized radiance, irradiance, radiant intensity, etc. at a given wavelength.
Type | Description |
---|---|
byte |
Polarization status. 0=unpolarized, 1=polarized. |
1 or 4 doubles |
Stokes vector elements. If unpolarized, there is only a single double (S_0) otherwise the full 4 element vector is presented. |
Spectral Vector of Mueller Matrices
A Spectral Vector is used to store a spectral reflectance, emissivity, transmission, extinction, etc. It is composed of a value indicating how many wavelengths are in the vector and then a sequence of Mueller Matrix data elements.
Type | Description |
---|---|
int |
Number of Mueller matrices in spectral vector. |
N (double, Mueller Matrix) |
All N matrices in the vector are printed out. Each is preceded by a double indicating the wavelength associated with the matrix in microns. |
Spectral Vector of Stokes Vectors
A Spectral Vector is used to store a spectral radiance, irradiance, radiant intensity, etc. It is composed of a value indicating how many wavelengths are in the vector and then a sequence of Stokes Vector data elements.
Type | Description |
---|---|
int |
Number of stokes vectors in spectral vector. |
N (double, Stokes Vector) |
All N stokes vectors in the spectral vector are printed out. Each is preceded by a double indicating the wavelength associated with the stokes vector in microns. |
Point
A Point is used to store a 3D point in space.
Type | Description |
---|---|
double |
X component. |
double |
Y component. |
double |
Z component. |
The X,Y and Y components of a point are in Scene ENU coordinates. |
Vector
A Vector is used to store a 3D vector in space.
Type | Description |
---|---|
double |
X component. |
double |
Y component. |
double |
Z component. |
The X,Y and Y components of a vector are in Scene ENU coordinates. |
Ray
A Ray is used to to store a direction (Vector) leaving an origin (Point).
Type | Description |
---|---|
Point |
Ray origin. |
Vector |
Ray direction. |
Box
A Box is used to store an axis-aligned box defined by the minimum XYZ corner (Point) and maximum XYZ corner (Point).
Type | Description |
---|---|
Point |
Minimum extent of axis-aligned box. |
Point |
Maximum extent of axis-aligned box. |
Bandpass
A Bandpass defines a spectral bandpass, including the spectral units, the miniumum, the maximum and the sampling between the extents.
Type | Description |
---|---|
double |
Minimum extent of bandpass. Units defined byte "spectral units" byte. |
double |
Maximum extent of bandpass. Units defined byte "spectral units" byte. |
double |
Sampling delta for bandpass. |
byte |
Spectral units. 1 = wave numbers, 2 = microns, 3 = nanometers. |
int |
Total number of sampling points in bandpass. Should be same as computing via min/max extents and delta. |
Bandpass List
A Bandpass List is a simple list of Bandpass objects.
Type | Description |
---|---|
int |
Number of "Bandpass" items in list. |
int |
Total number of sampling points for all the bandpasses in list. |
N Bandpass |
All N bandpasses in the list, printed out sequentially. Number of bandpasses printed is given by "bandpass count" above. |
With the list of basic types and DIRSIG types established above, it is now possible to parse the collector output when in binary mode.
Collector | Output Type |
---|---|
simatmmodelname |
string |
simscenefilename |
string |
simmatfilename |
string |
simbandpasses |
Bandpass List |
simscenebbox |
Box |
pathviewray |
Ray |
pathsolidangle |
double |
pathsurfacecount |
int |
pathtransmission |
Spectral Vector of Mueller Matrices |
pathradiance |
Spectral Vector of Stokes Vectors |
surfacetransmission |
Spectral Vector of Mueller Matrices |
surfaceradiance |
Spectral Vector of Stokes Vectors |
surfacetemperature |
double |
surfaceentrypoint |
Point |
surfaceexitpoint |
Point |
surfaceentrydistance |
double |
surfaceexitdistance |
double |
surfaceentryangle |
double |
surfaceexitangle |
double |
surfacepathlength |
double |
surfacenormal |
Vector |
surfacematid |
int |
surfacethermalthickness |
double |
surfacegasconcentration |
double |
Tutorial
This tutorial walks through the process of creating a simple parser for the binary output functionality of Interactive Mode. This tutorial includes example C++ and Matlab source code to parse the path radiance for a known number of queries. This tutorial requires some knowledge of both programming and binary data representation.
When using Interactive Mode with binary output, it is the user’s
responsibility to craft a valid collector file to
be parsed. For instance, the number of surfaces intersected by a ray
can vary depending on the scene location. In order to know how many
surfaces are in the binary output, the collector file
would need to include the pathsurfacecount
collector. Without this
solution-level collector’s information, the parser would not know how
much of the following binary data represents surface collections.
Input Collector File
This tutorial will simply ask DIRSIG for the path radiance using a fixed number of queries. The collector file looks as follows:
<?xml version="1.0" standalone="no"?>
<!DOCTYPE interactive SYSTEM "http://www.dirsig.org/dtds/interactive.dtd">
<interactive format="binary" pausepolicy="never"
startupoutputredirect="startup.log">
<simulationcollectors>
</simulationcollectors>
<solutioncollectors>
<pathradiance/>
</solutioncollectors>
<surfacecollectors>
</surfacecollectors>
</interactive>
Note that the format has been specified as "binary". Also, the startup output from DIRSIG which gives diagnostic information as a scene loads has been redirected to a file called "startup.log". Only the "pathradiance" solution-level collector is in use.
Input Bandpass File
For the tutorial, the following simple bandpass XML file is used:
<bandpasslist>
<bandpass spectralunits="microns">
<minimum>0.3</minimum>
<maximum>0.5</maximum>
<delta>0.1</delta>
</bandpass>
</bandpasslist>
With this bandpass, DIRSIG will compute radiance values for 0.3, 0.4, and 0.5 microns. The output from the "pathradiance" collector is a Stokes Vector (which may or may not be polarized).
Input Query
The interactive mode queries will be supplied via a text file that looks as follows:
0 1E-6 0 0 800 0 0 -1 0 1E-6 0 1 800 0 0 -1
Here we have two queries. Both have a time offset of zero relative to the simulation base time. Both specify a solid angle of 1E-6 steradians. Both queries specify a ray shooting straight down from an altitude of 800 meters. The rays are separated by 1 meter along the y-axis.
Running in Interactive Mode
Given an appropriate DIRSIG scene and atmosphere, the pieces are put together as follows:
$ dirsig --mode=interactive \
--scene_filename=demo.scene \
--atm_filename=uniform.atm \
--collector_filename=collectors.xml \
--bandpass_filename=bandpasses.xml \
--datetime=2008-07-01T12:00:00.0000-05:00 < query_input.txt > query_output.txt
This example uses shell redirection to read the queries from
query_input.txt
and feed them to DIRSIG via stdin. It takes the output
from DIRSIG and redirects it to "query_output.txt". The contents of this
queryoutput file will be binary data. Remember, there is a "startup.log"
file available with plaintext output of the startup process. The next
step in the tutorial is to write a simple program to parse this binary
output. First the pseudo code:
-
For each query …
-
Parse the radiance spectral vector in to memory</para>
-
Print the radiance spectral vector to the console
-
-
To parse a spectral vector:
-
Parse the number of Stokes Vectors. This is a four-byte, big-endian integer.
-
Attempt to parse the number of specified stokes vectors. Each stokes vector is preceded by an eight-byte, big-endian double describing the spectral location in microns. After the spectral location comes the actual stokes vector.
-
-
To parse a stokes vector:
-
Parse the polarization status byte:
-
A value of
0
indicates an unpolarized stokes vector, meaning just one double of data will follow. A value of1
indicates polarized data, meaning four doubles of data will follow.
-
-
Parse either one or four eight-byte, big endian doubles, as specified by the polarization status byte.
-
C++ Binary Reader Example
Note that Interactive Mode will always output data in big-endian format,
regardless of the native architecture’s endianness. For simplicity reasons
the following example code does not perform any endian checking or
conversions. This means that the as-is version of the following code will
only work on big-endian processors such as SPARC. The x86 processors for
Intel and AMD and little-endian, and the following code will not work as-is
on these platforms. The following code also assumes that a native double
is 8 bytes and a native int
is 4 bytes.
#include <fstream>
#include <iostream>
#include <istream>
#include <utility>
#include <vector>
typedef std::vector<double> StokesVector;
typedef std::pair<double,StokesVector> SpectralData;
typedef std::vector<SpectralData> SpectralVector;
/* Parse a stokes vector. */
void parseSV( std::istream& is, StokesVector& stokes ) {
// read polarization status
char polStatus = -1;
is.read( &polStatus, 1 );
// number of data elements in stokes depends on polarization state
const int numElements = ( polStatus == 1 ) ? 4 : 1;
// read all stokes elements
for ( int i = 0; i < numElements; i++ ) {
// read current stokes vector element
double stokesData = -1.0;
is.read( reinterpret_cast<char*>( &stokesData ), sizeof( double ) );
// stash parsed data value
stokes.push_back( stokesData );
}
}
/* Parse spectral vector of stokes vectors */
void parseSpecVecSV( std::istream& is, SpectralVector& specvec ) {
// read number of stokes vectors in spectral vector
int vectorCount = -1;
is.read( reinterpret_cast<char*>( &vectorCount ), sizeof( int ) );
// read each wavelength / stokes vector pair
for ( int i = 0; i < vectorCount; i++ ) {
double wavelength = -1.0;
is.read( reinterpret_cast<char*>( &wavelength ), sizeof( double ) );
StokesVector stokes;
parseSV( is, stokes );
specvec.push_back( SpectralData( wavelength, stokes ) );
}
}
int main( int argc, char* argv ) {
// Name of query output data file
std::string dataFilename = "queryoutput";
// Number of queries we are parsing data for
const int numQueries = 2;
// Open binary data file
std::ifstream is( dataFilename.c_str(), std::ios::in | std::ios::binary );
if ( !is ) {
std::cerr << "Error: unable to open query result file" << std::endl;
return 1;
}
// Drive through each query result
for ( int i = 0; i < numQueries; i++ ) {
// Parse query info
SpectralVector pathRadiance;
parseSpecVecSV( is, pathRadiance );
// Pretty print query info
std::cout << "Query # " << (i+1) << std::endl;
for ( int l = 0; l < pathRadiance.size(); l++ ) {
// dump wavelength
std::cout << " [" << pathRadiance.at(l).first << "] ";
// dump stokes
for ( int stokesIdx = 0;
stokesIdx < pathRadiance.at(l).second.size();
stokesIdx++ ) {
std::cout << pathRadiance.at(l).second.at(stokesIdx) << " ";
}
std::cout << std::endl;
}
std::cout << std::endl;
}
return 0;
}
Assuming that the assumptions about endianness and type sizes are correct for the platform this program is compiled on, the following output will result:
Query # 1 [0.3] 0.0236427 [0.4] 0.0235044 [0.5] 0.00549655 Query # 2 [0.3] 0.0236427 [0.4] 0.0234982 [0.5] 0.0051655
It is possible to verify these results by rerunning the queries using the "plain" output format in the collector configuration file.
Matlab Binary Reader Example
The following example Matlab code will read a radiance Spectral Vector.
Note that the fopen()
call supplies the 'ieee-be'
value for the
optional machinefmt
parameter, which says to assume the input file
is in Big Endian. This will allows the values to be automatically
converted to the native endian during the read.
% open the input file as read-only and big endian order
fid = fopen(fname,'r','ieee-be');
% read the number of bands (values) in the spectral radiance vector
numBands = fread(fid,1,'int32');
% create storage for the spectral locations (wavelengths) and data values
specVec = zeros(numBands,1);
dataVec = zeros(numBands,1);
% read in each spectral value one at a time
for f = 1:numBands
% read the spectral location (wavelength)
specVec(f) = fread(fid,1,'double');
% read the polarization flag
polarInfo = fread(fid,1,'int8');
% we are expecting unpolarized data, so make sure that is true
if(polarInfo~=0)
error('Polarized Vector')
else
% read in the spectral value
dataVec(f) = fread(fid,1,'double');
end
end
% close the file
fclose(fid);
This tutorial has demonstrated that it is possible to bypass much of the traditional DIRSIG "front-end" by using Interactive Mode. The binary output allows for high performance interaction with the model. For many cases the XML output of Interactive Mode is preferable because it is easier to debug and robust XML parsers are available for almost all major programming languages.