Triangle Map Format

This document is an editable version of the met.no Map Data File type=triangles Specification, 2006-10-25 document.

Intro:

This describes the structure of a binary file format for map data - polygons describing geographic element and precalculated triangles that tesselates these polygons.

The data is ordered in tiles and tilegroups. The world is divided into equal sized rectangular lat/lon-tiles (described by bounding box), and to accomodate larger tile-sets (larger than MAX_SHORT_INT), tiles are ordered into groups of tiles (with its own super bounding box).

Each tile is populated with the set of polygons p, belonging to a possibly larger closed and simple polygon P that describes a geographic element (continent, island, lake, island_in_lake, etc.) that is strictly within a tiles bounding box. In addition you find the subset of triangles t, from the full tessallation T of P, that falls within the tiles bounding box.

Binary format:

Contents of file interpreted as short integer (2 byte integers). Each record 1024 short integers

Some groups of data must not be split across record boundaries, see the specification below for these.

Values that are bigger than MAX_SHORT_INT are represented as 4 byte integers by storing the first word (lsw) and the second word (msw) in separate short integers. Look out for lsw/msw in desc. below.

These seem to be reversed in the files supplied with diana.

The file has a variable-sized header and a data-section. All data is ordered in tile groups and tiles.

The header has lookup-tables for tile-data. You will typically check the bounding boxes for each tile, decide which tiles you will need data from, and use the tables to look up which file-records and offsets you need to access.

Data consists of:

pure polygon-vertices, sorted by size and polygon-type (0=coastline, 1=lakes, 2=islands_in_lakes, etc)

triangle-vertices.

Useful values

nwrec = 1024 (record size in 2-byte integers)

delta_lat = latitude_size_of_tile_bounding box (degrees)

delta_lon = longitude_size_of_tile_bounding box (degrees)

Scaling values

All floating point data is scaled to fit into a short integer: scale= 64000 / max( delta_lat,delta_lon )

iscale2= max ( ceil(log10( scale / 32000 )), 0 )

iscale1= scale * (10**-iscale2)

itscale= 2

tscale= 10**itscale

When reading file, scale is calculated with: scale = iscale1 * (10**iscale2)

The actual file content

Data separated by // must not be split over record boundary.

What this means is that groups of related data, such as the general description of a tile containing the numbers of constituent parts and the bounding box, must be stored together. If the group of data would cross a record boundary, the data is written at the start of the next record. The bytes from the offset in the file where the data should have started and the start of the next record are undefined.

Heading:

rec[ 0]= (('p' << 8) | 'm'); // identifier
rec[ 1]= 4; // version number
rec[ 2]= 2*nwrec // record length in unit bytes
rec[ 3]= iscale1 // scaling value 1
rec[ 4]= iscale2 // scaling value 2
rec[ 5]= itscale // scaling value 3
rec[ 6]= numgroups // number of tilegroups

n=7
# Starts at rec[n] with heading data for first tilegroup
(tilegrouploop)
rec[n+0]= number_of_tiles_in_group
rec[n+1]= group_boundingbox_west * tscale
rec[n+2]= group_boundingbox_east * tscale
rec[n+3]= group_boundingbox_south * tscale
rec[n+4]= group_boundingbox_north * tscale
//
n+=5
(tile loop - tiles in current group)
rec[n+0]= record number, start of data
rec[n+1]= offset in record, start of data
rec[n+2]= tile_boundingbox_west * tscale
rec[n+3]= tile_boundingbox_east * tscale
rec[n+4]= tile_boundingbox_north * tscale
rec[n+5]= tile_boundingbox_south * tscale
//
n+=6
(end of tile loop)
(end of group loop)

Data:

# Starts at rec[n] with data for first tilegroup
(tilegrouploop)
(tile loop - tiles in current group)
rec[n+ 0]= number_of_polygons lsw
rec[n+ 1]= number_of_polygons msw
rec[n+ 2]= total number_of_polygon_vertices lsw
rec[n+ 3]= total number_of_polygon_vertices msw
rec[n+ 4]= total number_of_triangle_vertices lsw
rec[n+ 5]= total number_of_triangle_vertices msw
rec[n+ 6]= number_of_polygon_types (max 10)
rec[n+ 7]= record number, start of data for polygon type 0
rec[n+ 8]= offset in record, start of data for polygon type 0
rec[n+ 9]= record number, start of data for polygon type 1
rec[n+10]= offset in record, start of data for polygon type 1
..
rec[n+25]= record number, start of data for polygon type 9
rec[n+26]= offset in record, start of data for polygon type 9
//
n+=27
(polygon type loop)
rec[n+ 0]= number_of_polygons_this_type
//
n+=1
(polygon loop [P])
rec[n+ 0]= polygon_boundingbox_west * scale
rec[n+ 1]= polygon_boundingbox_east * scale
rec[n+ 2]= polygon_boundingbox_south * scale
rec[n+ 3]= polygon_boundingbox_north * scale
rec[n+ 4]= number_of_polygons [p] // 1 or # subparts of P
rec[n+ 5]= number_of_triangles lsw
rec[n+ 6]= number_of_triangles msw
//
n+=6
(polygon loop [p])
rec[n+ 0]= number_of_vertices lsw
rec[n+ 1]= number_of_vertices msw
//
n+=2
(polygon vertices loop)
rec[n+ 0]= x_offset_from_tile_midpoint * scale
rec[n+ 1]= y_offset_from_tile_midpoint * scale
//
n+=2
(end of polygon vertices loop)
(end of polygon loop [p])
(triangle loop)
rec[n+ 0]= x1_offset_from_tile_midpoint * scale
rec[n+ 1]= y1_offset_from_tile_midpoint * scale
rec[n+ 2]= x2_offset_from_tile_midpoint * scale
rec[n+ 3]= y2_offset_from_tile_midpoint * scale
rec[n+ 4]= x3_offset_from_tile_midpoint * scale
rec[n+ 5]= y3_offset_from_tile_midpoint * scale
//
n+=6
(end of triangle loop)
(end of polygon loop [P])
(end of polygon type loop)
(end of tile loop)
(end of group loop)