Difference between revisions of "BGL File Format"
(→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.
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 |
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. |
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:
|
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:
|
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 | |
WORD | Id (0x003C) |
0x02 | |
DWORD | Size of the airport record |
0x06 | |
BYTE | Number of RUNWAY subrecords |
0x07 | |
BYTE | Number of COM subrecords |
0x08 | |
BYTE | Number of START subrecords |
0x09 | |
BYTE | Number of APPROACH subrecords (?) |
0x0A | |
BYTE | Bit 0-6 : Number of aprons (?) Bit 7: flag for deleteAirport record |
0x0B | |
BYTE | Number of HELIPAD subrecords |
0x0C | |
DWORD | Longitude (See Computing Longitude and Latitude from a DWORD value) |
0x10 | |
DWORD | Latitude(See Computing Longitude and Latitude from a DWORD value) |
0x14 | |
DWORD | Altitude (in meters) |
0x18 | |
DWORD | Tower Longitude (if present) |
0x1C | |
DWORD | Tower Latitude (if present) |
0x20 | |
DWORD | Tower Altitude (in meters) |
0x24 | |
Float | Magnetic Variation (Deg) |
0x28 | |
DWORD | ICAO Ident (Special Format) |
0x30 | |
DWORD | Region Ident (Special Format) |
0x34 | |
BYTE | Unknown |
0x35 | |
BYTE | Traffic Scalar |
0x36 | |
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 | |
WORD | Id (0x0019 ) |
0x02 | |
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 | |
WORD | Id (0x0066 ) |
0x02 | |
DWORD | Size of the subrecord |
0x06 | |
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 | |
WORD | Id (0x0004 ) |
0x02 | |
DWORD | Size of the runway subrecord |
0x06 | |
WORD | Type of surface:
|
0x08 | |
BYTE | Primary runway number (01 - 36) |
0x09 | |
BYTE | Primary runway designator:
|
0x0A | |
BYTE | Secondary runway number |
0x0B | |
BYTE | Secondary runway designator |
0x0C | |
DWORD | ICAO Ident. for primary ILS (special format), 0x0000 if none. |
0x10 | |
DWORD | ICAO Ident. for seconday ILS (special format) |
0x14 | |
DWORD | Longitude (See Computing Longitude and Latitude from a DWORD value) |
0x18 | |
DWORD | Latitude(See Computing Longitude and Latitude from a DWORD value) |
0x1C | |
LONG | Elevation (in meters) |
0x20 | |
FLOAT | Length (in meters) |
0x24 | |
FLOAT | Width (in meters) |
0x28 | |
FLOAT | Heading (Deg) |
0x2C | |
FLOAT | Pattern Altitude |
0x30 | |
WORD | Marking flags: BIT 0: edges |
0x32 | |
BYTE | Light flags: BIT 0-1: edge (00 none, 01 low, 10 medium, 11 high) |
0x33 | |
BYTE | Pattern flags: BIT 0: primaryTakeoff (0 = YES) |
The following sub-subreports can be present within a runway subrecord:
OffsetThreshold
Offset | Number of bytes |
Format | Description |
---|---|---|---|
0x00 | |
WORD | Id Primary (0x0005 ) Id Secondary (0x0006 ) |
0x02 | |
DWORD | Size of subrecord (0x10) |
0x06 | |
WORD | Surface (same as in runway) |
0x08 | |
FLOAT | Length in meters |
0x0C | |
FLOAT | Width in meters |
BlastPad
Offset | Number of bytes |
Format | Description |
---|---|---|---|
0x00 | |
WORD | Id Primary (0x0007 ) Id Secondary (0x0008 ) |
0x02 | |
DWORD | Size of subrecord (0x10) |
0x06 | |
WORD | Surface (same as in runway) |
0x08 | |
FLOAT | Length in meters |
0x0C | |
FLOAT | Width in meters |
Overrun
Offset | Number of bytes |
Format | Description |
---|---|---|---|
0x00 | |
WORD | Id Primary (0x0009 ) Id Secondary (0x000A ) |
0x02 | |
DWORD | Size of subrecord (0x10) |
0x06 | |
WORD | Surface (same as in runway) |
0x08 | |
FLOAT | Length in meters |
0x0C | |
FLOAT | Width in meters |
Vasi
Offset | Number of bytes |
Format | Description |
---|---|---|---|
0x00 | |
WORD | Id Primary Left (0x000B ) Id Primary Right (0x000C ) |
0x02 | |
DWORD | Size of subrecord (0x18) |
0x06 | |
WORD | Type:
|
0x08 | |
FLOAT | BiasX |
0x0C | |
FLOAT | BiasZ |
0x10 | |
FLOAT | Spacing |
0x14 | |
FLOAT | Pitch |
Approach Lights
Offset | Number of bytes |
Format | Description |
---|---|---|---|
0x00 | |
WORD | Id Primary Left (0x000B ) Id Primary Right (0x000C ) |
0x02 | |
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.
{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:
|
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
|
0x06 | Data buffer – Size may vary depending on the method used. See below. | |
- | N | N depends on the AddFlag defined at relative offset 0x04.
|
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
|
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:
|
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))