OSM Map On Garmin/DEM Subfile Format
A reverse-engineering of Garmin's DEM Subfile format
The DEM Subfile contains the digital elevation model for a Garmin map. The data is used for hillshading and elevation profiles in MapSource and on Garmin devices.
DEM Header
Common Header
The structure of the common header is the same in all subfiles.
Byte Offset | Length (bytes) | Description |
---|---|---|
0x000 | 2 | Header length. So far only '0x29' (and '0x25') have been seen. |
0x002 | 10 | Type GARMIN DEM |
0x00C | 1 | 0x01 ? |
0x00D | 1 | 0x00 |
0x00E | 2 | Creation year |
0x010 | 1 | Creation month |
0x011 | 1 | Creation day |
0x012 | 1 | Creation hour |
0x013 | 1 | Creation minute |
0x014 | 1 | Creation second |
DEM-specific Header
Byte Offset | Length (bytes) | Description |
---|---|---|
0x015 | 4 | Seems to be flags for interpreting the DEM data. First bit defines whether elevation is given in meter (0) or feet (1). On most maps any other bits are zero. |
0x019 | 2 | number of zoom levels (can be different from the number of map levels) |
0x01B | 4 | 00 00 00 00 |
0x01F | 2 | size of record in data block 3 (always 0x3c ?) |
0x021 | 4 | points to the begin of data block 3 |
0x025 | 4 | Only present for header length 0x29. All zero so far. |
DEM-data
The following example is drawn from an "empty" DEM file generated by GMapTool for a map with five levels. Each zoom level contains 2 data blocks.
Data block 1
The records in this block seem to correspond to a rectangle of the map ("tile") and describe the records in data block 2. The record length varies according to values in data block 3. It can be 3 ("empty" DEM only?) up to 8 bytes.
Length (bytes) | Description |
---|---|
1, 2 or 3 | Offset in data block 2 relative to start of block; zero for the first and "empty" blocks. |
1 or 2 | base height |
1 or 2 | difference between maximum height and base height |
0 or 1 | unknown; 0x00 or 0x02 seen so far |
In the empty DEM file, the records are filled with
00 00 00 02
The "base height" seems to contain the elevation at least for "empty" blocks describing areas without data (e.g. sea): Sea areas are often shown with a height of -8888m in Basecamp/Mapsource. This corresponds to -29,160ft = 0x8e18, a value which is often found in this section.
The tiles are ordered row by row starting in north-western corner and ending in south-eastern corner of covered area. For a level with 4 tiles you would get the following indices:
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
Data block 2
There is a block for each map level. In the empty DEM file, however, these blocks are empty (length zero).
Otherwise it contains the real elevation data per tile. This data has to be compressed since the length of data per tile varies heavy. The data may also be normalized. Perhaps to the height difference given in data block 1?
Data block 3
This block describes the position and structure of data blocks 1 and 2. There is one record (60 bytes) per zoom level (see DEM header).
Byte Offset | Example bytes | Description |
---|---|---|
0x00 | 00 00 | index of the record (starting with zero) |
0x02 | 40 00 00 00 | number of pixel/tile (x-axis) |
0x06 | 40 00 00 00 | number of pixel/tile (y-axis) |
0x0A | 02 00 00 00 | unknown; values in latitude direction or information about resolution of elevation data? |
0x0E | 02 00 00 00 | unknown; values in longitude direction or information about resolution of elevation data? |
0x12 | 00 00 | ? |
0x14 | 00 00 00 00 | number of tiles in x direction - 1 (Columns) |
0x18 | 00 00 00 00 | number of tiles in y direction - 1 (Rows) |
0x1c | 10 00 | Describe the structure of records in data block 1.
The lowest two bits are the "offset size": 00 = 1 byte; 01 = 2 byte; 10 = 3 byte; 11 = 4 byte (last was not seen so far) The third bit defines size of "base height" field. If set this field is 2 bytes long, otherwise just 1. The forth bit defines size of "height difference" field. If set this field is 2 bytes long, otherwise just 1. If the fifth bit is set there will be an extra byte in the records. |
0x1e | 04 00 | Size of record in data block 1. Values up to 8 have been seen. |
0x20 | 25 00 00 00 | pointer to the start of corresponding data block 1 |
0x24 | 29 00 00 00 | pointer to the start of corresponding data block 2 |
0x28 | 00 2d b2 05 | western boundary (4 bytes!) |
0x2c | 00 e7 d5 24 | northern boundary (4 bytes!) |
0x30 | 00 d1 8f 00 | Distance between pixel (n-s direction)? |
0x34 | 00 05 a2 00 | Distance between pixel (w-e direction)? |
0x38 | 00 00 | minimum height. This is the minimum of all base heigth given in records of data block 1. |
0x3a | 00 00 | maximum height. There is at least one record where base height + height difference = maximum height. |
Remarks: The elevation data is formed from discrete points which are referenced as pixel in the above table.
Further findings about the DEM format,based on reverse engineering,can be found at DEM Explorer
Another step to exploring the DEM format, based on a lot of trial and error, can be found at Garmin-DEM-Build. There is my algorithm to build your on DEM-File-Encoder and a ready to use C#-program.