Skip to content
Dec 31 / simonecampora

Reading .OV2 TomTom POI Files using Java or C#

I’m posting today some source code about the famous TomTom One .OV2 file formats. These files are the one used by TomTom to store Point of Interest information within their navigation systems. However such format is proprietary, the data specs can be found in the TomTom website (see this PDF at section 2.4). I wrote down a simple procedure that gathers record by record only the simple POI records. Actually the OV2 format is a little bit more complicated than a simple list of POIs and it can be subjected to changes and version incompatibilities. I tested it with POIs taken from my TomTom ONE XL and ti worked great, however if the OV2 files contains proprietary or special record types (not specified in TomTom data sheets) these procedures will simply stop since it is not possible for this format to distinguish between a corrupted file or non standard encoding.

I hope you’ll find it interesting.

Java version:

package readers;

import java.io.FileInputStream;
import java.io.IOException;

public class OV2RecordReader {

	public static String[] readOV2Record(FileInputStream inputStream){
		String[] record = null;
		int b = -1;
		try{
			if ((b = inputStream.read())> -1) {
				// if it is a simple POI record
				if (b == 2) {
					record = new String[3];
		    	    long total = readLong(inputStream);

		    	    double longitude = (double) readLong(inputStream) / 100000.0;
		    	    double latitude = (double) readLong(inputStream) / 100000.0;

		    	    byte[] r = new byte[(int) total - 13];
		    	    inputStream.read(r);

		    	    record[0] = new String(r);
		    	    record[0] = record[0].substring(0,record[0].length()-1);
		    	    record[1] = Double.toString(latitude);
		    	    record[2] = Double.toString(longitude);
		    	}
				//if it is a deleted record
				else if(b == 0){
					byte[] r = new byte[9];
		    	    inputStream.read(r);
				}
				//if it is a skipper record
				else if(b == 1){
					byte[] r = new byte[20];
		    	    inputStream.read(r);
				}
				else{
					throw new IOException("wrong record type");
				}
			}
			else{
				return null;
			}
		}
		catch(IOException e){
			e.printStackTrace();
		}
		return record;
	}

	private static long readLong(FileInputStream is){
		long res = 0;
		try{
			res = is.read();
			res += is.read() <<8;
			res += is.read() <<16;
			res += is.read() <<24;
		}
		catch(IOException e){
			e.printStackTrace();
		}
		return res;
	}
}

C# Version


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace OV2Reader
{
 class Program
 {
 static void Main(string[] args)
 {
 string filename = @"C:\test.ov2";
 FileStream infile = new FileStream(filename, FileMode.Open, FileAccess.Read);

 while (infile.Length != infile.Position)
 {
 string[] data = readOV2Record(infile);
 if (data != null)
 {
 for (int i = 0; i < data.Length; i++)
 {
 Console.WriteLine(data[i]);
 }
 }
 }

 Console.WriteLine("exiting....");
 Console.ReadKey();
 }

 static string[] readOV2Record(FileStream inputStream)
 {
 String[] record = null;
 int b = -1;

 if ((b = inputStream.ReadByte()) > -1)
 {
 // if it is a simple POI record
 if (b == 2)
 {

 long total = readLong(inputStream);

 double longitude = (double)readLong(inputStream) / 100000.0;
 double latitude = (double)readLong(inputStream) / 100000.0;

 byte[] r = new byte[(int)total - 13];
 inputStream.Read(r, 0, (int)total - 13);

 record = new String[3];
 string s = System.Text.ASCIIEncoding.ASCII.GetString(r);
 record[0] = s;
 record[0] = record[0].Substring(0, record[0].Length - 1);
 record[1] = latitude.ToString();
 record[2] = longitude.ToString();
 }
 //if it is a deleted record
 else if (b == 0)
 {
 byte[] r = new byte[9];
 inputStream.Read(r, 0, 9);
 }
 //if it is a skipper record
 else if (b == 1)
 {
 byte[] r = new byte[20];
 inputStream.Read(r, 0, 20);
 }
 else
 {
 //
 return null;
 }
 }
 else
 {
 return null;
 }

 return record;
 }

 private static long readLong(FileStream fis)
 {
 long res = 0;

 res = fis.ReadByte();
 res += fis.ReadByte() << 8;
 res += fis.ReadByte() << 16;
 res += fis.ReadByte() << 24;
 return res;
 }
 }
}

23 Comments

leave a comment
  1. Specter / Apr 6 2010

    Hello, how could I translate this code to C#??

    Thanks a lot.

  2. Specter / Apr 6 2010

    Specter :
    Hello, how could I translate this code to C#??
    Thanks a lot.

    And thanks for your work.

  3. admin / Apr 6 2010

    @Specter
    hi Specter, actually I’m not a C# expert but I would suggest you to try to use the same algorithm with small substitutions of InputStream against C# Stream object and its .read function like it is shown in those links

    http://www.yoda.arachsys.com/csharp/readbinary.html

    and using a bit converter to get the long from byte array like it is done here

    http://msdn.microsoft.com/en-us/library/bb384066.aspx

    I will try to double check it as well in the meanwhile but I’m pretty sure that will do the job

  4. Specter / Apr 7 2010

    @admin

    Thanks a lot for your help, I think I have translated the code to C#. The code in Visual Studio 2008 gives no errors but I need to test it with some ov2 file.

    I have one last question, this function reads only a record or the whole file?

  5. admin / Apr 7 2010

    hi! actually this function translates one record but it can be iterated until the .OV file is completely parsed. yes you should try with a real OV2 file to be sure on the correctness and beware that on the internet there are a lot of OV2 files that are not complying with TomTom specifications, (if for example they have more record types) and therefore they will not considered by this function (and if no information is given for a specific record type it is impossible to know its byte size, causing the thread to terminate with an Exception)

  6. Specter / Apr 9 2010

    Hello again. I tried to read an ov2 file with only 1 record inside, and it didn’t read it.

    This is the ov2 file:
    http://www.multiupload.com/VK74VHH0C7

    I think it doesn’t read it because the first byte of this ov2 file is 01, and it’s skipped by the algorythm, or maybe I’m doing something wrong.

    Thanks.
    Could you help me, please?

  7. admin / Apr 9 2010

    hi! actually I used the same procedure and the result was:

    SEM VTS Sevilla Carlos III CRUCE Fco. Montesinos DIR CÛrdoba CL 37.39609 -6.01247

    are you using your own C# algorithm or the same java version? in the last case you can try the TestOV2Reader java class of this library (st-toolkit.sourceforge.net)

    in order to get more insights on the OV2 data extraction (click here to see the class source)

  8. Specter / Apr 10 2010

    Hello!! Yes, that’s what that file contents, but it seems mine doesn’t work.

    I think the problem is here:

    11 try{
    12 if ((b = inputStream.read())> -1) {
    13 // if it is a simple POI record

    In C# I’m using a FileStream object to read the file, but this object doesn’t have any read method that accepts 0 parameters.

    This object has two read methods. The first one is:

    http://msdn.microsoft.com/en-gb/library/system.io.filestream.read.aspx

    and the second is:

    http://msdn.microsoft.com/en-gb/library/system.io.filestream.readbyte.aspx

    And I suppose I have to use the second, am I right?

  9. Simone / Apr 12 2010

    I think your guess is right you should follow the example shown in the second link, however I will set up a working C# Example for you so that you can have a clearer idea on your issue: give me a couple of days (nights to be more accurate :) ) and I will add it to the post!

    bests
    Simone

  10. Specter / Apr 27 2010

    Hello again, any tips about the c# code? Will this code work if I use a BinaryReader instead of the FileStream object that I have been using??

  11. Simone / Apr 30 2010

    hi sorry for the late reply ;) I’ve posted the correct C# translation of the java code in the main topic. Hope you’ll find the answers you were looking for! actually you needed to use both Read and ReadByte.

    thanks for your interest in my blog
    Simone

  12. Specter / May 1 2010

    Hello, have you tried the C# code? It throws me a Null Reference Exception because the returned value from readOV2Record function is null.

  13. Simone / May 1 2010

    yes of course I tried it but with a TomTom strictly compliant .OV2 file, and not the one your provided: is it that one that was throwing the exception?

  14. Specter / May 1 2010

    Yes, it is. I tried to read it with this code but it throws that exception. I basically had the same code that you have posted in C#, but I always have the same problem, the ov2 file starts with 01, the readOV2Record function doesn’t read anything and it returns a null.

  15. Simone / May 2 2010

    I’ve updated the main executing function in my C# code: basically the problem that you were encountering was due to the return values of the read function. The function I’ve provided is only returning dat structures for POI content, reading them one by one (you have to iterate on the FileStream to get all of them). It is only looping inside the OV2 records and if it does not contain a proper POI data field it returns simply a null pointer (to be handled and filtered when the function is called). If you keep looping until the end of the file, skipping the null records, you can retrieve all the POIs of your OV2 File. I’ve tested with the OV2 file you gave me and it works with it as well.

    cheers
    simone

  16. Specter / May 3 2010

    Thanks a lot Simone, now it works.

  17. Specter / May 7 2010

    Hello, it’s me again. Do you know how to read/write upi files for Sygic?

  18. Simone / May 10 2010

    hi there! unfortunately I’ve never tried reading those files… do you need the C# procedure or just a standalone readeR? otherwise you could make use of POI Converter…

    bests
    Simone

  19. Specter / May 10 2010

    Hello, I’m using now the POI Converter, but I’d like to know how to read/write this kind of files.

    Greetings

  20. daroal / May 25 2010

    Hi, i’m interested too in read/write upi files in java :) . In version 7 of sygic, just renaming the ov2 to upi files works fine, but in the new version 8.06 of sygic doesn’t seems to works.
    I’ve found this site on sygic http://www.sygic.com/partners/pois.html but not suficient :(

  21. Specter / Jun 16 2010

    Hello, I have the structure of the upi format, but I’d need some help. ;-)

  22. simonecampora / Jul 12 2010

    hi unfortunately I’ve never worked with such files, but I’m investigating on it! Can you give me the link of the file organisation? I think I can provide another reader for UPIs…. in the next days stay tuned!

  23. Specter / Apr 16 2011

    Hello, this is the structure of the upi files:

    Header File UPI
    —————————–
    1 byte – L – name length
    L bytes – name with 3 00 at the end (i.e. null terminated)
    1 byte – Z – licence length (02 if null)
    Z bytes – licence name with 3 00 at the end (i.e. null terminated) (00 00 if null)
    2 Bytes – notification (00 00 if null)
    5 bytes – expiration date with 00 at the end (1 byte day, 1 byte month, 2 bytes year) (00 00 00 00 00 if null)
    1 byte – B – name length of bmp file
    B bytes – bmp file name with 3 00 at the end (i.e. null terminated)

    Record – Area
    —————————–
    1 byte – type = 01
    4 bytes – length (included type and POI records)
    4 bytes – lon vertex upper/right area
    4 bytes – lat vertex upper/right area
    4 bytes – lon vertex bottom/left area
    4 bytes – lat vertex bottom/left area

    Records – POI
    —————————–
    1 byte – record type = 03
    4 bytes – R – record length (included record type)
    4 bytes – lon POI
    4 bytes – lat POI
    (R-13) bytes – poi description with 3 00 at the end (i.e. null terminated)

Leave a Comment

Switch to our mobile site