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:

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:

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

Useful values

Scaling values

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

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)