Difference between revisions of "BGL File Format"

From FSDeveloper Wiki
Jump to: navigation, search
(OffsetThreshold)
(Vasi)
Line 742: Line 742:
 
|}
 
|}
  
=== Vasi===
+
=== Vasi ===
 
{| class="wikitable"
 
{| class="wikitable"
 
|-
 
|-
Line 802: Line 802:
 
| FLOAT
 
| FLOAT
 
| Pitch
 
| Pitch
 +
|}
 +
=== Approach Lights ===
 +
{| class="wikitable"
 +
|-
 +
! Offset
 +
! Number<br/>of bytes
 +
! Format
 +
! Description
 +
|-
 +
|- valign="top"
 +
| 0x00
 +
| <center>2</center>
 +
| WORD
 +
| Id Primary Left (<b><span style="color:#0000FF">0x000B </span></b>)<br/>
 +
Id Primary Right (<b><span style="color:#0000FF">0x000C </span></b>)<br/>
 +
Id Secondary Left (<b><span style="color:#0000FF">0x000D </span></b>)<br/>
 +
Id Secondary Right (<b><span style="color:#0000FF">0x000E </span></b>)
 +
|-
 +
| 0x02
 +
| <center>4</center>
 +
| DWORD
 +
| Size of subrecord (0x18)
 
|}
 
|}
 
<br/>
 
<br/>

Revision as of 09:33, 28 August 2014

Introduction

The information contained in this wiki comes from different sources:

  • "FSX File structure" by Winfried Orthmann
This is the first document about BGL file format. It describes the generic format of a BGL file and the content of some important sections like Airport, Scenery Objects,etc.
  • Some reverse engineering work on the TmfViewer and BglComp applications.



BGL Common Format

All BGL files share the same generic format. A BGL file is made of a header, sections, subsections and subsection data. Subsections are children of sections. The sections are here to help us locate the subsections in the file. Data specific information is contained in the subsections data and their format is dependent on the section type.
A BGL file is really a big container where all kind of information can be stored (in the subsection).The meaning of the data contained in the subsections is known only by the application using it. Th only contraint is for the file to comply to the generic format described below.

A BGL file always start with a header (Size = 0x38 bytes), followed by a list of section pointers. The number of section pointers is defined in the header.
Cvx BGLFormat.png

File Header

The header consists of 0x38 (56) bytes. It contains the number of sections defined in the file as well as the bounding geographical coordinates of the covered squared area.

Offset Number of bytes Description
0x00 4 - DWORD Magic Number #1 – Must be 0x01, 0x02, 0x92, 0x19
0x04 4 - DWORD Header size : 0x38
0x08 4 - DWORD dwLowDateTime of the FILETIME structure.
Date and Time the file was created

The FILETIME structure represents the number of 100-nanosecond intervals since January 1, 1601
See http://support.microsoft.com/kb/188768

0x0C 4 - DWORD dwHighDateTime of the FILETIME structure
0x10 4 - DWORD Magic Number #2 – Must be 0x03, 0x18, 0x05, 0x08
Maybe to identify the FS version
0x14 4 - DWORD The number of sections following this header.
0x18 32 Array[8] of unsigned integers (DWORD).
Each value describes the bounding coordinates of a squared subarea.

Even if 8 slots are provided, it is not necessary to have all 8 values filled. The list stops at the first null (0x00000000) value.
I don’t know what these subareas are used for.
To compute the bounding coordinates, please see Computing the bounding coordinates from a DWORD value.
To get the bounding coordinates of the square area covered by the file, just keep the minimal and maximal values of each bounding coordinates.



Example

For example, in CVX2815.bgl :

Offset Values Description
0x00
01 02 92 19
Magic Number #1
0x04
38 00 00 00
Header size
0x08
EF 82 DF E2
Low = 3806298863
0x0C
E8 C7 C6 01
High = 29804520
=> February 27, 2007
0x10
03 18 15 08
Magic Number #2
0x14
01 00 00 00
1 section following this header
0x18
E8 07 02 00
MinLatitude(Deg) = 46.40625
MaxLatitude(Deg) = 47.8125
MinLongitude(Deg) = -75.0
MaxLongitude(Deg) = -73.125
0x1C
E9 07 02 00
MinLatitude(Deg) = 46.40625
MaxLatitude(Deg) = 47.8125
MinLongitude(Deg) = - 73.125
MaxLongitude(Deg) = -71.25
0x20
EA 07 02 00
MinLatitude(Deg) = 45.0
MaxLatitude(Deg) = 46.40625
MinLongitude(Deg) = -75.0
MaxLongitude(Deg) = -73.125
0x24
EB 07 02 00
MinLatitude(Deg) = 45.0
MaxLatitude(Deg) = 46.40625
MinLongitude(Deg) = -73.124
MaxLongitude(Deg) = -71.25
0x28
00 00 00 00
0x2C
00 00 00 00
0x30
00 00 00 00
0x34
00 00 00 00

You’ll notice that only 4 subareas are defined (on a possibility of 8) and the last 4 available slots are empty (Value = 0)
So the bounding coordinates of the area covered by cvx2815.bgl are:

MinLatitude(Deg) = 45.0
MaxLatitude(Deg) = 47.8125
MinLongitude(Deg) = -75.0
MaxLongitude(Deg) = -71.25

Sections

Following the header, at offset 0x38, there are as many sections as defined at offset 0x14 of the header. A same file may contain sections of different type. Each section has a size of 20 bytes and has the following structure:

Relative Offset Number of bytes Description
0x00 4 - DWORD Section type (as defined by Microsoft Flight Simulator): one of the following values:
  • None = 0x0
  • Copyright = 0x1
  • Guid = 0x2
  • Airport = 0x3
  • Nav = 0x13
  • Ndb = 0x17
  • Marker = 0x18
  • Boundary = 0x20
  • Waypoint = 0x22
  • Geopol = 0x23
  • SceneryObject = 0x25
  • AirportNameIndex = 0x27
  • VorIcaoIndex = 0x28
  • NdbIcaoIndex = 0x29
  • WaypointIcaoIndex = 0x2A
  • ModelData = 02B
  • AirportSummary = 0x2C
  • Exclusion = 0x2E
  • TimeZone = 0x2F
  • TerrainVectorDb = 0x65
  • TerrainElevation = 0x67
  • TerrainLandClass = 0x68
  • TerrainWaterClass = 0x69
  • TerrainRegion = 0x6A
  • PopulationDensity = 0x6C
  • AutogenAnnotation = 0x6D
  • TerrainIndex = 0x6E (Note: According to George (Golf-HotelDelta), non-seasonal photo scenery compiled with resample also has this type.
  • TerrainTextureLookup = 0x6F
  • TerrainSeasonJan = 0x78
  • TerrainSeasonFeb = 0x79
  • TerrainSeasonMar = 0x7A
  • TerrainSeasonApr = 0x7B
  • TerrainSeasonMay = 0x7C
  • TerrainSeasonJun = 0x7D
  • TerrainSeasonJul = 0x7E
  • TerrainSeasonAug = 0x7F
  • TerrainSeasonSep = 0x80
  • TerrainSeasonOct = 0x81
  • TerrainSeasonNov = 0x82
  • TerrainSeasonDec = 0x83
  • TerrainPhotoJan = 0x8C
  • TerrainPhotoFeb = 0x8D
  • TerrainPhotoMar = 0x8E
  • TerrainPhotoApr = 0x8F
  • TerrainPhotoMay = 0x90
  • TerrainPhotoJun = 0x91
  • TerrainPhotoJul = 0x92
  • TerrainPhotoAug = 0x93
  • TerrainPhotoSep = 0x94
  • TerrainPhotoOct = 0x95
  • TerrainPhotoNov = 0x96
  • TerrainPhotoDec = 0x97
  • TerrainPhotoNight = 0x98
  • FakeTypes = 0x2710
  • IcaoRunway = 0x2711
0x04 4 - DWORD Unknown
0x08 4 - DWORD Number of subsections in the section.
0x0C 4 - DWORD File offset = position in the file where the first subsection starts.
0x10 4 - DWORD Total Size (in bytes) of all the subsections. This value should be equal to : 16 * nbSubSections


Example

For example, in CVX2815.bgl, the only one section, at offset 0x38, has this:

Offset Values Description
0x38
65 00 00 00
TerrainVectorDb = 101 = 0x65
0x3C
01 00 00 00
Unknown
0x40
8D 07 00 00
0x078D = 1933 subsections in the section
0x44
01 CD 1F 00
0x1FCD01 = File offset = position in the file where the first subsection starts.
0x48
D0 78 00 00
Size (in bytes) =0x78D0 = 30928 bytes

Subsections

A subsection contains information about the geographical area it covers. If also contains the file offset of the subsection’s data. All subsections of a same section are contiguous meaning that they are following each other in the file.
A subsection has the following structure:

Relative Offset Number of bytes Description
0x00 4 - DWORD Bounding coordinates of a squared area covered by this subsection. See Computing the bounding coordinates from a DWORD value.
0x04 4 - DWORD Number of records contained in the subsection's data.
It is 0 for TerrainVectorDb (which actually means only 1 record).
0x08 4 - DWORD File Offset = Position in the file of the subsection’s data
0x0C 4 - DWORD Size of the subsection’s data


The interpretation of the section data depends on the section type.

Example

In CVX2815.bgl:

Offset Values Description
0x1FCD01
00 FA 81 00
Bounding coordinates:
  • MinLatitude(Deg) = 47.63671875
  • MaxLatitude(Deg) = 47.8125
  • MinLongitude(Deg) =- 75.0
  • MaxLongitude(Deg) = -74.765625
0x1FCD05
00 00 00 00
Number of records = 0
0x1FCD09
4C 00 00 00
0x4C = File Offset = Position in the file of the subsection’s data.
0x1FCD0D
DD 00 00 00
Size of the subsection’s data = 0xDD = 221 bytes.



AIRPORT Record

Each airport record consists of a fixed part with the length of 0x38 bytes, followed by a variable part with 0..n subrecords of different types. The structure of the fixed part is as follows

Offset Number
of bytes
Format Description
0x00
2
WORD Id (0x003C)
0x02
4
DWORD Size of the airport record
0x06
1
BYTE Number of RUNWAY subrecords
0x07
1
BYTE Number of COM subrecords
0x08
1
BYTE Number of START subrecords
0x09
1
BYTE Number of APPROACH subrecords (?)
0x0A
1
BYTE Bit 0-6 : Number of aprons (?)
Bit 7: flag for deleteAirport record
0x0B
1
BYTE Number of HELIPAD subrecords
0x0C
4
DWORD Longitude (See Computing Longitude and Latitude from a DWORD value)
0x10
4
DWORD Latitude(See Computing Longitude and Latitude from a DWORD value)
0x14
4
DWORD Altitude (in meters)
0x18
4
DWORD Tower Longitude (if present)
0x1C
4
DWORD Tower Latitude (if present)
0x20
4
DWORD Tower Altitude (in meters)
0x24
4
Float Magnetic Variation (Deg)
0x28
4
DWORD ICAO Ident (Special Format)
0x30
4
DWORD Region Ident (Special Format)
0x34
1
BYTE Unknown
0x35
1
BYTE Traffic Scalar
0x36
2
WORD Unknown

The following subrecords can be present after the main airport record:

Name

This subrecord seems to be present in every airport record, and it is always the first one immediately after the fixed part.

Offset Number
of bytes
Format Description
0x00
2
WORD Id (0x0019 )
0x02
4
DWORD Size of the Name subrecord
0x06 STRING AirportName (padded with NUL (0x000 bytes)

Included Tower Scenery Object

Offset Number
of bytes
Format Description
0x00
2
WORD Id (0x0066 )
0x02
4
DWORD Size of the subrecord
0x06
4
DWORD Size of the included scenery object

After this record we find an included scenery object with an internal structure identical to that of other scenery objects (see below) and including possible attachments. The BglComp compiler allows only one scenery object to be included at this point, but in some FS X scenery files we find more than one objects included here. If present, the subrecords of this type appear immediately after the Name subrecord.

Runway

The runway subrecord consists of a fixed part with a length of 0x34 bytes and a variable number of sub-subrecords. The fixed part has the following structure:

Offset Number
of bytes
Format Description
0x00
2
WORD Id (0x0004 )
0x02
4
DWORD Size of the runway subrecord
0x06
2
WORD Type of surface:
  • 0x0002 WATER
  • 0x0004 ASPHALT
  • 0x0007 CLAY
  • 0x0008 SNOW
  • 0x0009 ICE
  • 0x000C DIRT
  • 0x000D CORAL
  • 0x000E GRAVEL
  • 0x000F OIL_TREATED
  • 0x0010 STEEL_MATS
  • 0x0011 BITUMINOUS
  • 0x0012 BRICK
  • 0x0013 MACADAM
  • 0x0014 PLANKS
  • 0x0015 SAND
  • 0x0016 SHALE
  • 0x0017 TARMAC
  • 0x00FE UNKNOWN
0x08
1
BYTE Primary runway number (01 - 36)
0x09
1
BYTE Primary runway designator:
  • 0 = NONE
  • 1 = LEFT
  • 2 = RIGHT
  • 3 = CENTER
  • 4 = WATER
  • 5 = A
  • 6 = B
0x0A
1
BYTE Secondary runway number
0x0B
1
BYTE Secondary runway designator
0x0C
4
DWORD ICAO Ident. for primary ILS (special format), 0x0000 if none.
0x10
4
DWORD ICAO Ident. for seconday ILS (special format)
0x14
4
DWORD Longitude (See Computing Longitude and Latitude from a DWORD value)
0x18
4
DWORD Latitude(See Computing Longitude and Latitude from a DWORD value)
0x1C
4
LONG Elevation (in meters)
0x20
4
FLOAT Length (in meters)
0x24
4
FLOAT Width (in meters)
0x28
4
FLOAT Heading (Deg)
0x2C
4
FLOAT Pattern Altitude
0x30
2
WORD Marking flags:

BIT 0: edges
BIT 1: threshold
BIT 2: fixedDistance
BIT 3: touchdown
BIT 4: dashes
BIT 5: ident
BIT 6: precision
BIT 7: edgePavement
BIT 8: singleEnd
BIT 9: primaryClosed
BIT 10: second.Closed
BIT 11: primaryStol
BIT 12: secondaryStol
BIT 13-15 unused (?)

0x32
1
BYTE Light flags:

BIT 0-1: edge (00 none, 01 low, 10 medium, 11 high)
BIT 2-3: center (as with edge)
BIT 5: flag for centerRed
BIT 5-7: unused (?)

0x33
1
BYTE Pattern flags:

BIT 0: primaryTakeoff (0 = YES)
BIT 1: primaryLanding (0 = YES)
BIT 2: primaryPattern (0 = LEFT)
BIT 3: secondaryTakeoff
BIT 4: secondaryLanding
BIT 5: secondaryPattern
BIT 6-7: unused (?)

The following sub-subreports can be present within a runway subrecord:

OffsetThreshold

Offset Number
of bytes
Format Description
0x00
2
WORD Id Primary (0x0005 )

Id Secondary (0x0006 )

0x02
4
DWORD Size of subrecord (0x10)
0x06
2
WORD Surface (same as in runway)
0x08
4
FLOAT Length in meters
0x0C
4
FLOAT Width in meters

BlastPad

Offset Number
of bytes
Format Description
0x00
2
WORD Id Primary (0x0007 )

Id Secondary (0x0008 )

0x02
4
DWORD Size of subrecord (0x10)
0x06
2
WORD Surface (same as in runway)
0x08
4
FLOAT Length in meters
0x0C
4
FLOAT Width in meters

Overrun

Offset Number
of bytes
Format Description
0x00
2
WORD Id Primary (0x0009 )

Id Secondary (0x000A )

0x02
4
DWORD Size of subrecord (0x10)
0x06
2
WORD Surface (same as in runway)
0x08
4
FLOAT Length in meters
0x0C
4
FLOAT Width in meters

Vasi

Offset Number
of bytes
Format Description
0x00
2
WORD Id Primary Left (0x000B )

Id Primary Right (0x000C )
Id Secondary Left (0x000D )
Id Secondary Right (0x000E )

0x02
4
DWORD Size of subrecord (0x18)
0x06
2
WORD Type:
  • 0x01 = VASI21
  • 0x02 = VASI31
  • 0x03 = VASI22
  • 0x04 = VASI32
  • 0x05 = VASI23
  • 0x06 = VASI33
  • 0x07 = PAPI2
  • 0x08 = PAPI4
  • 0x09 = TRICOLOR
  • 0x0a = PVASI
  • 0x0b = TVASI
  • 0x0c = BALL
  • 0x0d = APAP/PANELS
0x08
4
FLOAT BiasX
0x0C
4
FLOAT BiasZ
0x10
4
FLOAT Spacing
0x14
4
FLOAT Pitch

Approach Lights

Offset Number
of bytes
Format Description
0x00
2
WORD Id Primary Left (0x000B )

Id Primary Right (0x000C )
Id Secondary Left (0x000D )
Id Secondary Right (0x000E )

0x02
4
DWORD Size of subrecord (0x18)


TERRAIN_VECTOR_DB Record

This type of subsection contains values to retrieve geographical coordinates, organized as segments. These coordinates are used to draw lines (roads, railways, etc.) or polygons (lakes, airport bounds, etc.).

The section does not contain geographical coordinates per se but offsets to the lower left corner of the covered area (minimum latitude / longitude). The covered area is also defined in the subsection.

The data is organized in lists of pair values (One pair per geographical coordinate). Each pair can then be used to compute the final geographical coordinates. There are 3 ways to retrieve these lists of pairs.

The first value in a pair is longitude-related. The second value is latitude-related.

The algorithm to compute the geographical coordinates from a pair is:


void convertToCoordinates (double longitude_related_Value, double latitude_related_Value)
{
    var deltaLongFactor = (MaxLongitudeDeg - MinLongitudeDeg) * 0.000030517578125;
    var deltaLatFactor = (MaxLatitudeDeg - MinLatitudeDeg) * 0.000030517578125;

    var LongitudeDeg = MinLongitudeDeg + (longitude_related_Value * deltaLongFactor);
    var LatitudeDeg = MinLongitudeDeg + (latitude_related_Value * deltaLatFactor);
}

Subsection Header

Each subsection starts with the following structure:

Relative Offset Number of bytes Description
0x00 4 - DWORD 6
I did not find a cvx file with a value other than 6.
0x04 4 - DWORD Bounding coordinates of a squared area covered by this subsection. See Computing the bounding coordinates from a DWORD value.
0x08 4 - DWORD 0
I did not find a cvx file with a value other than 0.
0x0C 4 - DWORD Number of entities.
An entity is a list of segments. (a segment is a list of points).
0x10 4 - DWORD Number of bytes in the signatures buffer.
The signatures buffer contains some GUIDs identifying the segments of this subsection. Some GUID values (like the Texture) are not used by the TmfViewer application.


It also contains some extra bytes the use of it is unknown.
The GUID values are defined at: http://msdn.microsoft.com/en-us/library/cc707102.aspx - Terrain and Scenery
(See Vector Attributes for the Shp2Vec Tool)
The usable GUID values are:

{359C73E8-06BE-4FB2-ABCB-EC942F7761D0} Airport Bounds
{91CB4A9B-9398-48E6-81DA-70AEA3295914} Parks
{EA0C44F7-01DE-4D10-97EB-FB5510EB7B72} Water Polygons (GPS)
{956A42AD-EC8A-41BE-B7CB-C68B5FF1727E} Water Polygons
{AC39CDCB-DB78-4628-9A7C-051DA7AC864A} Exclusions
{0CBC8FAD-DF73-40A1-AD2B-FE62F8004F6F} Shorelines
{714BF912-F9DF-467E-80AE-28EB27374DBD} Streams
{C7ACE4AE-871D-4938-8BDC-BB29C4BBF4E3} Utilities
{33239EB4-D2B8-46F5-98AB-47B3D0922E2A} Railways
{560FA8E6-723D-407D-B730-AE08039102A5} Roads
{54B91ED8-BC02-41B7-8C3B-2B8449FF85EC} Freeway Traffic Roads
0x14 4 - DWORD Maximum allowed number of signature offsets.
Signature offsets (see Entity Structure below) are offset into the signature buffer and point to the GUID identifying the corresponding segment.
0x18 4 - DWORD Maximum number of pair values per segment for this subsection.
= 2 * maximum number of points per segment
0x1C 4 - DWORD 0
I did not find a cvx file with a value other than 0.
0x20 N Signatures buffer containing <N> bytes, where N is defined at relative offset 0x10

Entity Structure

Then for each entity (the number of entities is defined at relative offset 0x0C of the subsection header (see Subsection Header above), we have the following structure:

Relative Offset Number of bytes Description
0x00 4 - DWORD Number of segments in the entity.
0x04 4 - DWORD Segment type:
  • 1 – Points only
  • 2 – Lines
  • 3 - Polygons
0x08 2 - WORD Number of signatures offsets (Must be < 0x64).
0x0A 4 - DWORD N = Number of signatures offsets (see above).
Will contain the offsets into the signature buffer.

Segment Structure

Then for each segment (the number of segments is defined at relative offset 0x00 of the entity structure (see Entity Structure above), we have the following structure:

Relative Offset Number of bytes Description
0x00 4 - DWORD Number of points (geographical coordinates) in this segment.
0x04 1 - BYTE AddFlag : Determines how many additional bytes to read after the data buffer has been read.
0x05 1 - BYTE Method used to build the pairs list from the data buffer.
Possible values are 1,2 or 3
  • 1 – Method1
  • 2 – Method2
  • 3 – Method3


Note: I did not find a cvx file with a value of 3 but it looks like Method3 read the pairs list directly from the data buffer without any extra processing.
Methods 1 and 2 need some extra processing. See below.

0x06 Data buffer – Size may vary depending on the method used. See below.
- N N depends on the AddFlag defined at relative offset 0x04.
  • If AddFlag = 0 then N = 0
  • If AddFlag = 1 then N = 4 x NumberOfPoints (as defined at relative offset 0x00)
  • If AddFlag = 2 then N = 4


I don’t know what the use of these extra bytes is.

Cvx SectionStructure.png

Method 1

By far the most complex method. I have some code for it. But I still have to figure out the big picture.

The data buffer for this method has the following structure:

Relative offset Number of bytes Description
0x00 4 - DWORD First Longitude Data - First value in the list of pairs.
0x04 4 - DWORD First Latitude Data - First value in the list of pairs.
0x08 4 - DWORD LongitudeData Increment
0x0C 4 - DWORD LatitudeData Increment
0x10 4 - DWORD Number of bytes
0x14 N N is the number of bytes defined above at relative offset 0x10.
These N bytes are the raw data used to build the final list of pairs.

Method 2

The data buffer for this method has the following structure:

Relative offset Number of bytes Description
0x00 1 - BYTE Root Mask
0x01 N N is computed using the value of the Root Mask.
N = (RootMask * NbPoints * 2 + 7) >> 3
where
  • RootMask is defined at relative offset 0x00 of this structure.
  • NbPoints is the number of points as defined at relative offset 0x00 of the Segment Structure.


These N bytes are the raw data used to build the final list of pairs.

Algorithm

The algorithm used by Method2 to build the list of pairs is:


/*
Fills an array listOfValues of DWORD 
*/

PositionInPair = 0;   /* 0 = first value in pair, 1 = second value in pair */
PairIndex = 0 ;
ShiftValue = 0 ;
NbBytesLeftToRead = ((rootMask * nbPoints) * 2 + 7) >> 3;
Mask = 1 << rootMask ;    /* 2 ^ rootMask */

/* Read first value */
if (NbBytesLeftToRead > 3)
{
    valueFromFile = read (4 bytes)
    NbBytesLeftToRead -= 4 ;
}
else
{
    valueFromFile = read (NbBytesLeftToRead bytes)
    NbBytesLeftToRead = 0 ;
}

while (PairIndex < nbPoints)
{
    if (ShiftValue < 0)
    {
        /* Shift Left */
        result = (uint)((valueFromFile << (-ShiftValue)) & Mask);

        /* Add to existing pair value */
        result += listOfValues[PairIndex * 2 + PositionInPair];        

        listOfValues[PairIndex * 2 + PositionInPair] = result;
    }
    else
    {
        /* Shift Right */
        result = (uint)((valueFromFile >> ShiftValue) & Mask);
        listOfValues[PairIndex * 2 + PositionInPair] = result;
    }

    if (rootMask + ShiftValue >= 32)
    {
        if (NbBytesLeftToRead > 3)
        {
            valueFromFile = read (4 bytes)
            NbBytesLeftToRead -= 4 ;
        }
        else
        {
            valueFromFile = read (NbBytesLeftToRead bytes)
            NbBytesLeftToRead = 0 ;
        }
        ShiftValue -= 32;    /* now negative */
    }
    else
    {
        /* continue processing value from file */
        ShiftValue += rootMask;
        PositionInPair++;
    }

    if (PositionInPair == 2)
    {
        PairIndex++;    /* Next entry in the list of pair values */
        PositionInPair = 0;
    }
}

Example

Still with the same example as in Subsections, we know that the first subsection data starts at offset 0x4C (as described in the first subsection starting at offset 0x1FCD01) . So at this file offset, we have the subsection header of the first subsection data.

Offset Value Description
0x4C
06 00 00 00
6
0x50
00 FA 81 00
Bounding coordinates:
  • MinLatitude(Deg) = 47.63671875
  • MaxLatitude(Deg) = 47.8125
  • MinLongitude(Deg) =- 75.0
  • MaxLongitude(Deg) = -74.765625
0x54
00 00 00 00
0
0x58
03 00 00 00
Number of entities = 3
0x5C
14 00 00 00
Number of bytes in the signatures buffer = 20
0x60
03 00 00 00
Maximum allowed number of signature offsets = 3
0x64
1C 00 00 00
Maximum number of pair values per segment = 28
0x68
00 00 00 00
0
0x6C Signatures buffer containing 20 bytes.
F7 44 0C EA DE 01 10 4D 97 EB FB 55 10 EB 7B 72 00 00 00 00

That is GUID {EA0C44F7-01DE-4D10-97EB-FB5510EB7B72} = Water Polygons (GPS), followed by 4 zeroes.

Then follows 3 entities.
The first entity (at offset 0x80 of the file) is

Offset Value Description
0x80
01 00 00 00
Number of segments in the entity = 1
0x84
03 00 00 00
Segment type = 3 - Polygons
0x88
01 00
Number of signatures offsets = 1
0x8A
00 00 00 00
Signatures offset = 0: points to the only one GUID

This entity is followed by only one segment, at offset 0x8E of the file

Offset Value Description
0x8E
0E 00 00 00
Number of points = 14
0x92
00
AddFlag = 0
0x93
02
2 – Method2
0x94
0F
Root Mask = 0x0F
So N = (15 * 14* 2 + 7) >> 3 = 53
53 bytes to follow.
0x95 53 bytes:
84 3E 56 37
65 10 74 1D
3C 44 5B 9F
11 C5 D0 05
C9 2C F3 D5
B2 3A 8D 8A
8D 9D 9B 67
8B ED 52 59
B3 BC 0B 36
35 5F 54 25
B5 33 30 6D
E2 64 4B EB
36 A1 8F D5
0D

The next entity starts at file offset 0xCA.

So the algorithm for method 2 goes like this:

Mask = 0x7FFF ; 

NbBytesLeftToRead = 53 ; shiftValue = 0; PairIndex = 0 ; PositionInPair = 0
   valueFromFile = read (4 bytes) = 0x37563E84
   result = (valueFromFile >> shiftValue) & mask = 0x3E84
   listOfValues[0] = 0x3E84

NbBytesLeftToRead = 49 ; shiftValue = 0x0F; PairIndex = 0 ; PositionInPair = 1
   result = (valueFromFile >> shiftValue) & mask = 0x6EAC
   listOfValues[1] = 0x6EAC

NbBytesLeftToRead = 49 ; shiftValue = 0x1E; PairIndex = 1 ; PositionInPair = 0
   result = (valueFromFile >> shiftValue) & mask = 0x000
   listOfValues[2] = 0x0000
   rootMask + shiftValue = 0x2D > 0x20 so
	valueFromFile = read (4 bytes) = 0x1D741065
	shiftValue = 0xFFFFFFFE
	
NbBytesLeftToRead = 45 ; shiftValue = 0xFFFFFFFE; PairIndex = 1 ; PositionInPair = 0	
   result = (valueFromFile << (-shiftValue)) & mask = 0x4194
   result += listOfValues[2] = 0x4194 + 0x0000 = 0x4194
   listOfValues[2] = 0x4194

NbBytesLeftToRead = 45 ; shiftValue = 0x0D; PairIndex = 1 ; PositionInPair = 1	
   result = (valueFromFile >> shiftValue) & mask = 0x6BA0
   listOfValues[3] = 0x6BA0

NbBytesLeftToRead = 45 ; shiftValue = 0x1C; PairIndex = 2 ; PositionInPair = 0
   result = (valueFromFile >> shiftValue) & mask = 0x0001
   listOfValues[4] = 0x0001
   rootMask + shiftValue = 0x2BD > 0x20 so
	valueFromFile = read (4 bytes) = 0x9F5B443C
	shiftValue = 0xFFFFFFFC

NbBytesLeftToRead = 41 ; shiftValue = 0xFFFFFFFC; PairIndex = 2 ; PositionInPair = 0	
   result = (valueFromFile << (-shiftValue)) & mask = 0x43C0
   result += listOfValues[4] = 0x0x43C0 + 0x0001 = 0x43C1
   listOfValues[4] = 0x43C1

etc

Once the 53 bytes have been processed, the list contains the following 28 values, making up a list of 14 pairs.

3E84
6EAC
4194
6BA0
43C1
6B68
4467
6862
4905
6659
4B57
69D5
58A8
73B1
59E6
76C5
5952
7966
582E
79A9
5545
76A4
4C0C
7136
4B64
6DD6
3E84
6EAC

To get the coordinates of the first point, we take the first pair {0x3E84, 0x6EAC} and use the convertToCoordinates method described at Subsection for section of type Terrain_Vector_DB.
The first value in the pair is longitude-related. The second value in the pair is latitude-related.


doubledeltaLongFactor = ((-74.765625) - (- 75.0)) * 0.000030517578125;  // = 0.000007152557373046875
double deltaLatFactor  = (47.8125 - 47.63671875) * 0.000030517578125;    // = 0.0000053644180297851563

double LongitudeDeg = (- 75.0) + (0x3E84 * deltaLongFactor);    // = -74.885530471801758
double LatitudeDeg = 47.63671875 + (0x6EAC * deltaLatFactor);   // = 47.788703441619873

Annexe A

Computing the bounding coordinates from a DWORD value


public static List<double> GetBoundingCoordinates(uint boundingValue)
{
    var list = new List<double>();
    var shiftValue = 15;
    var work = boundingValue;
    var latitudeData = (uint)0;
    var longitudeData = (uint)0;

    while (work < 0x80000000 && shiftValue >= 0)
    {
        shiftValue--;
        work *= 4;
    }
    work &= 0x7FFFFFFF;    // Remove negative flag, if any
    var powerOfTwo = shiftValue;

    while (shiftValue >= 0)
    {
        if (work >= 0x80000000)
        {
            latitudeData += (uint)(1 << shiftValue);
        }

        if ((work & 0x40000000) != 0)
        {
            longitudeData += (uint)(1 << shiftValue);
        }
        work *= 4;
        shiftValue--;
    }

    // factor = 1.0 / (2^i)
    var factor = 1.0 / (1 << powerOfTwo);

    // Calc bounding coordinates
    var minLatitudeDeg = 90.0 - ((latitudeData + 1.0) * factor * 360.0);
    var maxLatitudeDeg = 90.0 - (latitudeData * factor * 360.0);
    var minLongitude = (longitudeData * factor * 480.0) - 180.0;
    var maxLongitude = ((longitudeData + 1.0) * factor * 480.0) - 180.0;
    
    list.Add(minLatitudeDeg);
    list.Add(maxLatitudeDeg);
    list.Add(minLongitude);
    list.Add(maxLongitude);
    return list;
}

Computing Longitude and Latitude from a DWORD value

Latitude and longitude are no longer represented as before. Each location on the earth is fixed in the LOD grid. Longitude and latitude are each represented by a 4 byte value (DWORD). The formula for obtaining the decimal values is as follows:

(double) Lon = (DWORD) Lon * (360.0 / (3 * 0x10000000) – 180.0)
(double) Lat = 90.0 - (DWORD) Lat * (180.0 / (2 * 0x10000000))