Tile Index Scheme: Difference between revisions

From FlightGear wiki
Jump to navigation Jump to search
(corrections)
 
Line 2: Line 2:
The tiles are cut so that none of them is excessively large or small compared to other tiles regarding their actual size on the earth surface.
The tiles are cut so that none of them is excessively large or small compared to other tiles regarding their actual size on the earth surface.


Each tile is defined by the geodetic coordinates of its northwest corner and its extends in degrees of geodetic latitude and longitude.
Each tile is defined by the geodetic coordinates of its southwest corner and it extends in degrees of geodetic latitude and longitude.
We will refer to the latitude resp. longitude of the northwest corner as lat resp. lon.
We will refer to the latitude, longitude of the southwest corner as lat, lon.
However, the index calculations should also work with any other position inside the tile.
However, the index calculations should also work with any other position inside the tile.


Line 17: Line 17:
* latitude offset (0...7).
* latitude offset (0...7).


Each degree of latitude is split up into 8 rows of tiles.
Each degree of latitude is split up into 8 rows of tiles.  Each tile spans 1/8th degree (0.125 degrees of latitude).
Therefore the base latitude index and offset are directly derived from lat using the <code>floor</code> and <code>trunc</code> functions:
Therefore the base latitude index and offset are directly derived from lat using the <code>floor</code> and <code>trunc</code> functions:


   base_y=floor(lat)
   base_y = floor(lat)
   y=trunc((lat-base_y)*8)
   y = trunc((lat - base_y) * 8)


floor & trunc explained: http://www.cplusplus.com/reference/cmath/trunc/
floor & trunc explained: http://www.cplusplus.com/reference/cmath/trunc/
Line 33: Line 33:
! Tile Width (deg)
! Tile Width (deg)
|-
|-
| 89 ≤ abs(lat) ≤ 90
| 89 ≤ lat < 90
| 360
| 12
|-
|-
| 88 abs(lat) <  89
| 86 ≤ lat <  89
| 8
|-
| 86 ≤ abs(lat) <  88
| 4
| 4
|-
|-
| 83 ≤ abs(lat) <  86
| 83 ≤ lat <  86
| 2
| 2
|-
|-
| 76 ≤ abs(lat) <  83
| 76 ≤ lat <  83
| 1
| 1
|-
|-
| 62 ≤ abs(lat) <  76
| 62 ≤ lat <  76
| 0.5
| 0.5
|-
|-
| 22 ≤ abs(lat) <  62
| 22 ≤ lat <  62
| 0.25
| 0.25
|-
|-
| 0 abs(lat) <  22
| -22 ≤ lat <  22
| 0.125
| 0.125
|-
| -62  ≤ lat <  -22
| 0.25
|-
| -76 ≤ lat <  -62
| 0.5
|-
| -83 ≤ lat <  -76
| 1
|-
| -86 ≤ lat <  -83
| 2
|-
| -89 ≤ lat <  -86
| 4
|-
| -90 ≤ lat < -89
| 12
|}
|}


Line 61: Line 76:
The naive code for determining the base longitude index is therefore
The naive code for determining the base longitude index is therefore


   base_x=floor(floor(lon/tile_width)*tile_width)
   base_x=floor(floor(lon / tile_width) * tile_width)
 
   x=floor((lon - base_x) / tile_width)
However, in case of latitudes north of N89 or south of S89, the whole row of latitude is a single tile.
For western longitudes <code>base_x</code> would be calculated as -360, which is out of range.
Therefore, we need to cap <code>base_x</code> to the range -180...179.
 
  base_x=floor(floor(lon/tile_width)*tile_width)
  if base_x<-180
    base_x=-180
   x=floor((lon-base_x)/tile_width)


The final tile index is found by composing the base offsets and indices into a bit-field:
The final tile index is found by composing the base offsets and indices into a bit-field:


   index=((lon+180)<<14) + ((lat + 90) << 6) + (y << 3) + x
   index=((lon + 180) << 14) + ((lat + 90) << 6) + (y << 3) + x


So here is the final code for calculating the index (using the width from the table above for <code>tile_width</code>)
So here is the final code for calculating the index (using the width from the table above for <code>tile_width</code>)


   base_y=floor(lat)
   base_y = floor(lat)
   y=trunc((lat-base_y)*8)
   y = trunc((lat - base_y) * 8)
   base_x=floor(floor(lon/tile_width)*tile_width)
   base_x = floor(floor(lon / tile_width) * tile_width)
  if base_x<-180
   x = floor((lon - base_x) / tile_width)
    base_x=-180
   x=floor((lon-base_x)/tile_width)
 
== Inconsistency Issues ==
The scheme explained above is the same that is currently implemented in SimGear and as such used by TerraGear and FlightGear.
However, for latitudes north of N88 and south of S88 this scheme is inconsistent.
 
First of all, for latitudes north of N89 and south of S89 inconsistent base longitude offsets are generated for western and eastern longitudes, even though this area is covered by a single tile.
For western longitudes, the longitude offset is calculated as -180, while for eastern longitudes it is calculated as 0, leading to different tile indices for the same tile.
 
Secondly, in the range between 88 and 89 degrees of latitude tiles have a width of 8 degrees of longitude.
As the Greenwhich Meridian effectively is a tile border, longitudes west of W176 will lead to a longitude offset of -184, which is then corrected to -180 by the currently implemented code.
 
This means that the points between W176 and W172 are associated with two tiles, the one at base index -180 and the one at base index -176.
 
The latter problem arises from the fact that 8, the width of the tiles in this area, is not a factor of 180.
 
One solution would be to define the meridian at W180 to be a tile border.
As 360 is an integral multiple of 8 (and all other tile widths as defined in the table above), E180 would also be a tile border.
 
The calculation would be simplified to
 
  base_x=trunc(trunc((lon+180)/width))*width-180
  x=trunc((x-base_x)/width)


{{Terra}}
{{Terra}}


[[Category:Scenery enhancement]]
[[Category:Scenery enhancement]]

Latest revision as of 08:12, 28 April 2019

Scenery in FlightGear is composed of tiles, each associated with a tile index. The tiles are cut so that none of them is excessively large or small compared to other tiles regarding their actual size on the earth surface.

Each tile is defined by the geodetic coordinates of its southwest corner and it extends in degrees of geodetic latitude and longitude. We will refer to the latitude, longitude of the southwest corner as lat, lon. However, the index calculations should also work with any other position inside the tile.

The authoritative source for the tile index scheme is the definition of the class SGBucket in SimGear (simgear/bucket/newbucket.{hxx, cxx}).

Calculating the Tile Index

The tile index is composed of four parts

  • base longitude index (-180...179)
  • base latitude index (-90...89),
  • longitude offset (0...7), and
  • latitude offset (0...7).

Each degree of latitude is split up into 8 rows of tiles. Each tile spans 1/8th degree (0.125 degrees of latitude). Therefore the base latitude index and offset are directly derived from lat using the floor and trunc functions:

 base_y = floor(lat)
 y = trunc((lat - base_y) * 8)

floor & trunc explained: http://www.cplusplus.com/reference/cmath/trunc/

The tiling in east-west direction is more complex. As the meridians converge towards the pole, the width of tile columns must be adapted based on latitude, as the following table shows (taken from sg_bucket_span in simgear/bucket/newbucket.hxx):

Latitude Range Tile Width (deg)
89 ≤ lat < 90 12
86 ≤ lat < 89 4
83 ≤ lat < 86 2
76 ≤ lat < 83 1
62 ≤ lat < 76 0.5
22 ≤ lat < 62 0.25
-22 ≤ lat < 22 0.125
-62 ≤ lat < -22 0.25
-76 ≤ lat < -62 0.5
-83 ≤ lat < -76 1
-86 ≤ lat < -83 2
-89 ≤ lat < -86 4
-90 ≤ lat < -89 12

Note that in latitudes north of N83 or south of S83 tiles span more than a single degree of latitude. The naive code for determining the base longitude index is therefore

 base_x=floor(floor(lon / tile_width) * tile_width)
 x=floor((lon - base_x) / tile_width)

The final tile index is found by composing the base offsets and indices into a bit-field:

 index=((lon + 180) << 14) + ((lat + 90) << 6) + (y << 3) + x

So here is the final code for calculating the index (using the width from the table above for tile_width)

 base_y = floor(lat)
 y = trunc((lat - base_y) * 8)
 base_x = floor(floor(lon / tile_width) * tile_width)
 x = floor((lon - base_x) / tile_width)