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.