Tomtom Navigator POI.DAT File description

This page is about a description of the file POI.dat used by Tomtom Navigator (v2, v3 and v5) to store theirs POIs.

Important : POIs from Tomtom Navigator are copyrighted and you CAN'T used the informations in this page to exploit-modify-diffuse natives POIs or do anything that Tomtom licence prohibit.
However, it seems that licence permit to replace native POI.DAT with yout own file, based on private or free datas.

Note that if you think that this page is written in a strange english language, don't hesitate to correct it

I wrote a perl software (extract_ttpoi.pl) that implement these informations.

General structure

A POI is a simple association of 3 datas : Tomtom organize their POIs in categories. There are about 60-70 differents categories (Restaurants, Petrol Station, Parking...) that are hard-coded in the Tomtom navigator program (poitypes.??.dll in Tomtom Navigator 3). See Annexe A for a complete listing of categories.
Each category is encoded as : So the number and the list of categories are fixed, unless the dll file is modified.

The following description consider an integer as 4 bytes. Also, the file is encoded as little-endian

The POI.DAT file is composed of several section :

BytesDescription
4 bytesN : the number of categories in the file as an integer
N*4 bytesa table of N integer that contains the category ID (see Annexe A)
(N+1)*4 bytesa table of N integer that contains the offset in this file of the begining of the POIs for the category. There is one more integer because of the offset of the end of block of the last category.
So the the offsets of the category of index M can be computed as (C code) :
  • first offset = table[M]
  • last offset = table[M+1] - 1
? bytesN blocks of ? bytes for the POIs themself

We are now able to identify each block of data that contains POIs. POIs are compressed, but not encrypted. However, the originals POIs are copyright, as explain the licence of the product. So please refer to this licence before exploit the datas.

The reason why POIs are compressed is to save memory. There is also a special encoding that let Tomtom to fast search a POI according to the GPS position.
The block of data is composed of records. Each record will be describe after. New Records could be added by Tomtom with futures release (ex : records 0x0C in Tomtom Navigator 5).

There is 2 kinds of record :

Aera Records

The logic of Area Record is to divide the complete map (we say level 0) in more little square (level 1), each level 1's square is divide in more litle square (level 2) and so on. There is many sub level. The last Area Record contains several POI Records, but no more that 10 (this seems to be a rule). Also, the way the areas are cut is not fixed. Each Area record as a specific size, depending of the POI density in the Area.
I think that the way Area Record are created is to obtain a good repartiton of POIs. When you want to display POI in your Tomtom Navigator, only a part of them are display : the POIs that are not so far of your position. I think that Tomtom display POIs that are in the same square you are, and perhaps POIs of adjacent-square. So, the software have only a little number of POIs to analyse, saving a lot of CPU. However, I don't really know what is the exact algorithm.

Here is an exemple of Record structure :

 Record 01                  (compelete map)    1835757 km²
  Record 01                 (level 1)           931185 km²
   Record 01                (level 2)           629400 km²
    Record 01               (level 3)           440059 km²
     Record 01              (level 4)           305839 km²
      Record 01             (level 5)           262615 km²
       Record 01            (level 6)           191422 km²
        Record 01           (level 7)           167841 km²
         Record 01          (level 8)           112220 km²
          Record 01         (level 9)           101074 km²
           Record 01        (level 10)           97175 km²
            Record 12
            ...
            Record 12
           Record 01        (level 10)            3899 km²
            Record 12
            ...
            Record 12
          Record 01         (level 9)            11187 km²
           Record 01        (level 10)           10958 km²
            Record 12
            ...

This graph shows an example of map divisions :

POI Records

There is multiple POI record, each one implementing a specific compession method. I suppose that the goal of this structure is to store a maximum of POI in a little piece of memory. So, an optimized POI.DAT generator have to test each of the Record algorithm to find the one which consume a minumum of memory.
Also, some algorithm can't encode all characters (ex : Record 12 can't encode letter Q). So a POI.DAT generator have to eliminate some Records algorithm depending of the text description.
A simple POI.DAT generator could simply implement one Record algorithm : the Reocrd 07 that simply implement plain test description, (as OV2 files). This generator of course will generate large size file POI.DAT, and then consume more memory in your PDA.

Record 01

This Record is an Area Record. His goal is to determine a square.
The Format is the following :

BytesDescription
1 byte = 0x01The Record Type
4 bytesN : the total size of this record (including the 21 bytes header)
4 bytesLongitude 1 (in decimal degrees). This value has to be divide by 100000
4 bytesLatitude 1 (in decimal degrees). This value has to be divide by 100000
4 bytesLongitude 2 (in decimal degrees). This value has to be divide by 100000
4 bytesLatitude 2 (in decimal degrees). This value has to be divide by 100000
N bytesBlock of datas containing other records

Record 02 & 15

This Record is a POI Record. This record is used in .OV2 file. It is a plain text record
The Format is the following :

BytesDescription
1 byte = 0x02The Record Type
4 bytesN : the total size of this record (including the 02 bytes header)
4 bytesLongitude (in decimal degrees). This value has to be divide by 100000
4 bytesLatitude (in decimal degrees). This value has to be divide by 100000
N bytesClear text POI description

Record 04 & 20

This Record is an POI Record. There is no POI description isn't compressed. It is plain text.
The Format is the following :

BytesDescription
1 byte = 0x04The Record Type
3 bytesEncoded Longitude
3 bytesEncoded Latitude

Record 05 & 21

This Record is an POI Record. The POI description is a short numeric value.
The Format is the following :

BytesDescription
1 byte = 0x05 or 0x15The Record Type
3 bytesEncoded Longitude
3 bytesEncoded Latitude
2 bytesunsigned 16 bits numeric value (little endian)

Record 06 & 22

This Record is an POI Record. The POI description is a long numeric value.
The Format is the following :

BytesDescription
1 byte = 0x06The Record Type
3 bytesEncoded Longitude
3 bytesEncoded Latitude
3 bytesunsigned 24 bits numeric value (little endian)

Record 07 & 23

This Record is an POI Record. The POI description isn't compressed. It is plain text.
The Format is the following :

BytesDescription
1 byte = 0x07The Record Type
1 bytesN : the size of this record, minus 8 (the size of the fixed part of the record)
3 bytesEncoded Longitude
3 bytesEncoded Latitude
N bytesClear text POI description

Record 08 & 24

This Record is an POI Record. The POI description is compressed (compression type 08)
The Format is the following :

BytesDescription
1 byte = 0x08 or 0x18The Record Type
1 bytesN : the size of this record, minus 8 (the size of the fixed part of the record)
3 bytesEncoded Longitude
3 bytesEncoded Latitude
N bytesencoded POI description

The compressed method is the following :

Record 09 & 25

This Record is an POI Record. The POI description is compressed (compression type 09)

The Format is the following :

BytesDescription
1 byte = 0x09 or 0x19The Record Type
1 bytesN : the size of this record, minus 8 (the size of the fixed part of the record)
3 bytesEncoded Longitude
3 bytesEncoded Latitude
N bytesencoded POI description
The compressed method is the following :

Each character of the POI description consume a variable number a bits, using a transposition table (see Annexe D for the transposition table). This seems to be a b-tree (binary tree). The Build of this table is made using frenquence letters analyze, so that "a", "e", "i" comsume more bits than "Q", "X", "Z".

The block of data has to be used as a serie of bits. There is a special sequence of bit that have a special meaning (End of String).
The way the bits are arranged in the block is a little special : each byte must reverse the position of his bits as the following :
The byte (binary format) 0bABCDEFGH will be transform in 0bHGFEDCBA
So 0b10010010 will be transform in 0b01001001

Then, the data can be decoded, using the transpotion table.
For example

starting with the bytes : 0x68 0x78 0x3c 0xb2 0x01
This can be transform in binary : 01101000 01111000 00111100 10010010 00000001
and then revert                 : 00010110 00011110 00111100 01001101 10000000
and then, using b-tree table    : 00010 1100 0011 1100 0111 1000 1001 1011 (0000000)
and then                        : station

Record 10 & 26

This Record is an POI Record. The POI description is compressed (compression type 10)

The Format is the following :

BytesDescription
1 byte = 0x0A or 0x1AThe Record Type
1 bytesN : the size of this record, minus 8 (the size of the fixed part of the record)
3 bytesEncoded Longitude
3 bytesEncoded Latitude
N bytesencoded POI description

The compressed method is the following :

Each pairs of 2 bytes (little endian encoding) can be decompressed in 3 character. The optional last byte give one character.
The characters can be obtained by calculate the modulus-40 three times on the 2 bytes, and 1 time on the 1 byte. This give 3 indexes on a transposition table :

  abcdefghijklmnopqrstuvwxyz0123456789 .-

if modulus is 0, this is consider as the end of string.

For example

starting with the bytes : 0x59 0x20, this give the value 0x2059 (8281)
get the modulus 1 : 8281 % 40 = 1           : A
get the modulus 2 : (8281/40) % 40 = 7      : G
get the modulus 3 : ((8281/40)/40) % 40 = 5 : E

Note : Thanks to "Didier71" for his work

Record 12 & 28

This Record is an POI Record. The POI description is compressed (compression type 12)

The Format is the following :

BytesDescription
1 byte = 0x0CThe Record Type
1 bytesN : the size of this record, minus 8 (the size of the fixed part of the record)
3 bytesEncoded Longitude
3 bytesEncoded Latitude
N bytesencoded POI description

This Record is a new record from TomTom v5.

These records have 2 blocks of data

Each block have a special 5-bit code that is an end of string. After this code, the 2nd block directly begin, until the special 4-bit code that is the end of the phone number.

The way the bits are arranged in the block is a little special : the coded string data have to be first reverse. Then, the group of 5 bits (and then 4 bits) have to be parse from the end of the reversed data, as the following :
The bytes (binary format) 0bABCDEFGH 0bIJKLMNOP 0bQRSTUVWX will be reversed in 0bQRSTUVWX 0bIJKLMNOP 0bABCDEFGH, and the the groups of 5-bits will be (QRST) UVWXI JKLMN OPABC DEFGH

Then, the data can be decoded, using the transpotion table, and the resulting clear text have to be reverse.

For example

starting with the bytes : 0x51 0x02 0x89 0x5c 0xd3 0x21 0x03
This can be transform in binary : 01010001 00000010 10001001 01011100 11010011 00100001 00000011
and the revert string           : 00000011 00100001 11010011 01011100 10001001 00000010 01010001
and then, using the tables      : 0000 0011 0010 0001 11010 01101 01110 01000 10010 00000 10010 10001
and then                        : "210" & "noitats
and then, using b-tree table    : Description:"station" & Phone Number:"012"

The way Latitude is encoded

Latitudes in POI Records are encoded on 3 bytes unsigned value (little endian) (in decimal degrees).
The final value is obtain by this formula (considering X as the 3 bytes value) :

Latitude = (X/100000 - 80)

The way longitude is encoded

Longitudes in POI Records are encoded on 3 bytes unsigned value (little endian) (in decimal degrees).
Longitude could be obtain with the same formula as latitude, but there is some corrective value : 3 bytes only let store 16777216 values (so nearly 167° on 360°). Depending on the aera zone the POI exists (refer to his parent Aera Record), an offset should be applied.

The primary value is obtain by this formula (considering X as the 3 bytes value) :

Note : let compute integer value to minimize floating approximations

Let applie this formula
  Longitude = (X - 8000000)
until Longitude inside the 2 longitudes values stored in his parent Aera Record.
If Longitude if under the -180°, then add 360°.

Europe don't have to applied these offset, but it's the case for USA.

For example :

  For an Area Record of (-165.12345, 55.12345) - (-151.12345, 58.12345)  (Alaska)
  The POI Longitude is : 0x6C9785  that is 8755052 in decimal
     8755052 - 8000000 =    755052
      755052 - 8000000 =  -7244948
    -7244948 - 8000000 = -15244948   it's this value !!!
Note : Thanks to Lutz Bendlin for his work

Annexe A : Category ID

Here is the categories ID that exists in Tomtom Navigator. This list is as complete as possible.

Category IDEnglishFrench
7367Government OfficeBureau gouvernemental
9364mountain PeakSommet montagneux
7369Open ParkingParking ouvert
7313Parking GarageParking couvert
7311Petrol StationStation-essence
7380Railway StationGare ferroviaire
7395Rest AreaAire de repos
7383AirportAéroport
9910Car DealerConcessionnaire automobile
7341CasinoCasino
9906ChurchEglise
7342CinemaCinéma
7379City CenterCentre-ville
9352CompanySociété
9367Concert HallSalle de concerts
9363CourthousePalais de justice
7319Cultural CenterCentre culturel
7385Exhibition CenterCentre des expositions
7352Ferry TerminalTerminal de car-ferry
7366Frontier CrossingFrontière
9911Golf CourseTerrain de golf
7321Hospital PolyclinicHôpital/clinique
7314Hotel/MotelHôtel/motel
7376Tourist AttractionSite touristique
9935mountain PassCol montagneux
7317museumMusée
9365OperaOpéra
7339Place of WorshipLieu de recueil
7324Post OfficeBureau de poste
7312Rent Car FacilityCentre de location de véhicules
9930Rent Car ParkingParking pour véhicules de location
7315RestaurantRestaurant
9361shopMagasin
7373shopping CenterCentre commercial
7374stadiumStade
7318TheatreThéâtre
7316Tourist Information OfficeSyndicat d'initiative
9927ZooZoo
7320sports CentreComplexe sportif
7322Police StationCommissariat de police
7365EmbassyAmbassade
7377College UniversityLycée/université
7397Cash DispenserBilletterie
9357BeachPlage
9360Ice Skating RingPatinoire
9369Tennis CourtCourt de tennis
9371Water SportCentre de sports aquatiques
9373DoctorDocteur
9374DentistDentiste
9375VeterinarianVétérinaire
9379NightlifeActivités nocturnes
9902Amusement ParkParc d'attractions
9913LibraryBibliothèque
7310Car Repair FacilityRéparations automobiles
7326PharmacyPharmacie
7337scenic/Panoramic ViewVue panoramique
7338swimming PoolPiscine
7349WineryCave à vins
7360Camping GroundTerrain de camping
9362Park and Recreation AreaParc et aire de jeux
9377Convention CentreCentre de conventions
9378Leisure CentreCentre de loisirs
9380yacht BasiMarina
9980 Code postal
9800 Légal/Mandataires
9801 Légal/Autre

Annexe B : Record 8 transposition table

This table refers to Record 8

5 bits valueTrasposition letter
00000not used
00001.
00010<space>
00011S
00100a
00101e
00110r
00111i
01000o
01001n
01010s
01011t
01100l
01101d
01110c
01111h
10000u
10001m
10010g
10011p
10100b
10101k
10110f
10111z
11000v
11001A
11010C
11011B
11100M
11101P
11110G
11111-

Annexe C : Record 8 transposition table

This table refers to Record 8

quartet sourcequartet destination
10000010
10010011
10100100
10110101
11000110
11010111
11101110
11111111

Annexe D : Record 9 transposition table

This table refers to Record 9.
Note : this table is not complete, and must be complete if others sequences are discovered

b-tree sequence of bitscharacter
0010space
0011a
1010110A
101010b
00001010B
11010c
00000010C
01101d
01100011D
111e
010010101E
0100100f
010001000F
010011g
01000111G
000001h
10100000H
0111i
0000101100I
000010111j
0100010011J
0000000k
000000111K
00011l
10100001L
010000m
00001110M
1001n
0000001100N
1000o
0100011001O
011001p
01000101P
1010111001q
1010111000000Q
0101r
01100000R
00010s
0000100S
1100t
000011111T
11011u
01100001101U
1010011v
000011110V
10100010w
011000010W
1010001100x
01001010010101X
01100010y
1010111111001Y
1010010z
01000110000Z
1010001111é
101000110111è
10101111110001ë
0000101101110110ê
00001011011110ô
1010001110ö
01100001111ó
10101111110111ò
000010110111010100õ
00001011011101111î
01100001110111011ï
10101110100í
01001010010110ì
010001101101001â
0110000111010à
1010111110ä
1010001101011å
010001101100á
01000110110101ã
010001100010æ
1010111000001ç
0100101000ü
101011111100001011û
010001101101000ù
01001010010111ú
000010110111010101011ÿ
000010110111011101010Â
01100001110110Å
1010001101010Ä
000010110111010110À
101011111101101Á
101011111100001001101Ã
1010111111000010100Æ
011000011101110010001Ç
1010111111011000É
0100101001010001È
10101111110000100000110Ê
00001011011101110100100Ë
01100001110111010Í
00001011011101110100101Î
101011111100001000010Ï
101011111100001010111Ô
0100011011011Ö
000010110111011101000Ò
00001011011101110110Ó
1010111111000001011011Û
00001011011100Ü
011000011101110001Ú
10101111110000101010Ñ
1010111111010ñ
101011110ß
011000011100ø
011000011101111Ø
1010111111011001ª
0100101001010000ý (U+00FD)
0000101101110100l (U+0142)
101011111100001000110111L (U+0141)
01100001110111001010xba
0100011010'
1010111111000011
011000011101110011`
101011111100001001100$
010001100011"
011000011101110010000\
101011111100001010110?
01001011-
0000101101110101011_
10101111110000011:
00001011011101010100;
0000110.
1010111011,
0100010010&
10101111110000100001110#
00001011011111+
101011111100000100*
01100001110111001001!
10101111110000100001101>
0000101101110111011110@
000010110111010101010°
0000001101/
101011100010
000010110101
010001101112
101011101013
101011111114
0100101001005
1010001101106
1010001101007
0000101101108
1010111000019
01001010011(
01100001100)
000010110111010111[
000010110111011100]
101011111100000101000010{
101011111100000101000000}
1010111111000001011010space ???
1011End of string

Annexe E : Record 12 transposition table (text table)

This table refers to Record 12

Caution : this table DOES NOT contain letter Q !

5-bit codetext character
00000a
00001b
00010c
00011d
00100e
00101f
00110g
00111h
01000i
01001j
01010k
01011l
01100m
01101n
01110o
01111p
10000r
10001s
10010t
10011u
10100v
10101w
10110x
10111y
11000z
11001space
11010End of string
11011(
11100)
11101&
11110'
11111-

Annexe F : Record 12 transposition table (numeric table)

This table refers to Record 12

4-bit codeNumeric character
0000End of String
00010
00101
00112
01003
01014
01105
01116
10007
10018
10109
1011-
1100(
1101)
1110+
1111#


Updates :
Aug 09 2005 : Original
Aug 11 2005 : Added Record 10 (thanks to "Didier71")
Aug 12 2005 : Fix Longitude decoding (thanks to Lutz Bendlin)
Sep 03 2005 : Fix Length Record 01
Sep 09 2005 : Add record 02
Sep 16 2005 : Add new record, update Record 9 table

Dernière mise à jour : 16-Septembre-2005
Laurent Licour - laurent at licour.com