1#!/usr/bin/perl -w
2########################################################################
3# calc-tile.pl
4#
5# Synopsis: Calculate a FlightGear tile base on longitude and latitude.
6# Usage: perl calc-tile.pl <lon> <lat>
7########################################################################
8
9use strict;
10use POSIX;
11
12
13
14########################################################################
15# Constants.
16########################################################################
17
18my $EPSILON = 0.0000001;
19my $DIRSEP = '/';
20
21
22
23########################################################################
24# Functions.
25########################################################################
26
27#
28# Calculate the number of columns of tiles in a degree of longitude.
29#
30sub bucket_span {
31  my ($lat) = (@_);
32  if ($lat>= 89.0 ) {
33    return 360.0;
34  } elsif ($lat>= 88.0 ) {
35    return 8.0;
36  } elsif ($lat>= 86.0 ) {
37    return 4.0;
38  } elsif ($lat>= 83.0 ) {
39    return 2.0;
40  } elsif ($lat>= 76.0 ) {
41    return 1.0;
42  } elsif ($lat>= 62.0 ) {
43    return 0.5;
44  } elsif ($lat>= 22.0 ) {
45    return 0.25;
46  } elsif ($lat>= -22.0 ) {
47    return 0.125;
48  } elsif ($lat>= -62.0 ) {
49    return 0.25;
50  } elsif ($lat>= -76.0 ) {
51    return 0.5;
52  } elsif ($lat>= -83.0 ) {
53    return 1.0;
54  } elsif ($lat>= -86.0 ) {
55    return 2.0;
56  } elsif ($lat>= -88.0 ) {
57    return 4.0;
58  } elsif ($lat>= -89.0 ) {
59    return 8.0;
60  } else {
61    return 360.0;
62  }
63}
64
65#
66# Format longitude as e/w.
67#
68sub format_lon {
69  my ($lon) = (@_);
70  if ($lon < 0) {
71    return sprintf("w%03d", int(0-$lon));
72  } else {
73    return sprintf("e%03d", int($lon));
74  }
75}
76
77#
78# Format latitude as n/s.
79#
80sub format_lat {
81  my ($lat) = (@_);
82  if ($lat < 0) {
83    return sprintf("s%02d", int(0-$lat));
84  } else {
85    return sprintf("n%02d", int($lat));
86  }
87}
88
89#
90# Generate the directory name for a location.
91#
92sub directory_name {
93  my ($lon, $lat) = (@_);
94  my $lon_floor = POSIX::floor($lon);
95  my $lat_floor = POSIX::floor($lat);
96  my $lon_chunk = POSIX::floor($lon/10.0) * 10;
97  my $lat_chunk = POSIX::floor($lat/10.0) * 10;
98  return format_lon($lon_chunk) . format_lat($lat_chunk) . $DIRSEP
99    . format_lon($lon_floor) . format_lat($lat_floor);
100}
101
102#
103# Generate the tile index for a location.
104#
105sub tile_index {
106  my ($lon, $lat) = (@_);
107  my $lon_floor = POSIX::floor($lon);
108  my $lat_floor = POSIX::floor($lat);
109  my $span = bucket_span($lat);
110
111  my $x;
112  if ($span < $EPSILON) {
113    $lon = 0;
114    $x = 0;
115  } elsif ($span <= 1.0) {
116    $x = int(($lon - $lon_floor) / $span);
117  } else {
118    if ($lon >= 0) {
119      $lon = int(int($lon/$span) * $span);
120    } else {
121      $lon = int(int(($lon+1)/$span) * $span - $span);
122      if ($lon < -180) {
123        $lon = -180;
124      }
125    }
126    $x = 0;
127  }
128
129  my $y;
130  $y = int(($lat - $lat_floor) * 8);
131
132
133  my $index = 0;
134  $index += ($lon_floor + 180) << 14;
135  $index += ($lat_floor + 90) << 6;
136  $index += $y << 3;
137  $index += $x;
138
139  return $index;
140}
141
142
143
144########################################################################
145# Main program.
146########################################################################
147
148my ($lon, $lat) = (@ARGV);
149
150my $dir = directory_name($lon, $lat);
151my $index = tile_index($lon, $lat);
152my $path = "$dir$DIRSEP$index.stg";
153
154print "Longitude: $lon\n";
155print "Latitude:  $lat\n";
156print "Tile:      $index\n";
157print "Path:      \"$path\"\n";
158
1591;
160