ITF stands for "Intermediate TIN Format." It is a simple, efficient, geospatially aware standard file format for Triangular Irregular Networks (TINs).
Before ITF, there was no standard, documented file format for geospatial TINs. The format was proposed and implemented by Ben Discoe via the Virtual Terrain Project in 2003.
In the CAD world, an AutoCAD DXF could be used to store a TIN, but DXF is very inefficient, seldom georeferenced, and does not support a clean topology of shared edges. In the GIS world, ArcGIS has no TIN format, and ArcInfo has a TIN format which is proprietary (tnxy.adf, tnz.adf, tnod.adf), undocumented, and not widely used. (As a side note, when a reverse-engineered description of ArcInfo TIN came to light in 2007, its structure was found to be extremely similar to ITF, except spread out in a directory of files.)
The file begins with a header which contains the following:
Field Length
in BytesDescription Identifier 5 A marker which indicates that this is an ITF file.
For version 2.0 of the format, it contains the characters "tin02".Num_Vertices 4 (int) Number of vertices. Num_Triangles 4 (int) Number of triangles. Data_Start 4 (int) File offset where the data starts. A parser should seek to this position to read it. CRS_Length 4 (int) Length in bytes of the ASCII string which contains the CRS. CRS CRS_Length Description of a coordinate reference system (CRS) in the OpenGIS Well-Known Text (WKT) format. Extents 8 (double) * 4 Horizontal extents: left, top, right, bottom 4 (float) * 2 Vertical extents: minimum height, maximum height Next is the data itself, vertices then triangles. Each vertex is encoded this way:
Field Length Description X 8 (double) X coordinate (easting) Y 8 (double) Y coordinate (northing) Z 4 (float) Z coordinate (elevation) Next is the triangles, which each contain the indices of their three vertices:
Field Length Description Vertex 0 4 (int) Index of the first corner. Vertex 1 4 (int) Index of the second corner. Vertex 2 4 (int) Index of the third corner. All numeric values use little-endian encoding (Intel, not Motorola).
The ASCII string for the CRS does not include a NULL at the end.
Note that the use of Data_Start in the header allows for the possibility of fields being added to the header in the future, retaining full backward compatibility. Parsers expecting an older format will still find the header fields and data exactly as expected.
For version 1.0 of the format, the Identifier contains the characters "tin01". The 'Extents' in the header are not present.
Here is an excerpt from the vtdata C/C++ open-source library which shows how simple it is to read and write ITF 1.0:
bool vtTin::Read(const
char *fname)
{
FILE *fp = fopen(fname, "rb");
if (!fp)
return false;
int i, verts, tris, data_start, proj_len;
char marker[5];
fread(marker, 5, 1, fp);
fread(&verts, 4, 1, fp);
fread(&tris, 4, 1, fp);
fread(&data_start, 4, 1, fp);
fread(&proj_len, 4, 1, fp);
if (proj_len > 4000)
return false;
char wkt_buf[4000], *wkt = wkt_buf;
fread(wkt, proj_len, 1, fp);
wkt_buf[proj_len] = 0;
OGRErr err = m_proj.importFromWkt((char **)
&wkt);
if (err != OGRERR_NONE)
return false;
fseek(fp, data_start, SEEK_SET);
// read verts
DPoint2 p;
float z;
for (i = 0; i < verts; i++)
{
fread(&p.x, 8, 2, fp); // 2 doubles
fread(&z, 4, 1, fp);
// 1 float
AddVert(p, z);
}
// read tris
int tribuf[3];
for (i = 0; i < tris; i++)
{
fread(tribuf, 4, 3, fp); // 3 ints
AddTri(tribuf[0], tribuf[1], tribuf[2]);
}
fclose(fp);
return true;
}
bool vtTin::Write(const
char *fname) const
{
FILE *fp = fopen(fname, "wb");
if (!fp)
return false;
char *wkt;
OGRErr err = m_proj.exportToWkt(&wkt);
if (err != OGRERR_NONE)
{
fclose(fp);
return false;
}
int proj_len = strlen(wkt);
int data_start = 5 + 4 + 4 + 4 + + 4 + proj_len;
int i;
int verts = NumVerts();
int tris = NumTris();
fwrite("tin01",
5, 1, fp);
fwrite(&verts, 4, 1, fp);
fwrite(&tris, 4, 1, fp);
fwrite(&data_start, 4, 1, fp);
fwrite(&proj_len, 4, 1, fp);
fwrite(wkt, proj_len, 1, fp);
OGRFree(wkt);
// write verts
for (i = 0; i < verts; i++)
{
fwrite(&m_vert[i].x, 8, 2, fp);
// 2 doubles
fwrite(&m_z[i], 4, 1, fp);
// 1 float
}
// write tris
for (i = 0; i < tris*3; i++)
{
fwrite(&m_tri[i], 4, 1, fp);
// 1 int
}
fclose(fp);
return true;
}