#!/usr/bin/perl -w ######################################################################## # calc-tile.pl # # Synopsis: Calculate a FlightGear tile base on longitude and latitude. # Usage: perl calc-tile.pl ######################################################################## use strict; use warnings; use POSIX; ######################################################################## # Constants. ######################################################################## my $EPSILON = 0.0000001; my $DIRSEP = '/'; my $pgmname = $0; if ($pgmname =~ /(\\|\/)/) { my @tmpsp = split(/(\\|\/)/,$pgmname); $pgmname = $tmpsp[-1]; } ######################################################################## # Globals. ######################################################################## my $download = ''; my ($lon, $lat); # user variables my $VERS = "0.0.6 2016-10-08"; my $load_log = 0; my $in_file = ''; my $verbosity = 0; my $out_file = ''; # ### DEBUG ### my $debug_on = 0; my $def_file = 'def_file'; ### program variables my @warnings = (); ######################################################################## # Functions. ######################################################################## sub prt($) { print shift } sub VERB1() { return $verbosity >= 1; } sub VERB2() { return $verbosity >= 2; } sub VERB5() { return $verbosity >= 5; } sub VERB9() { return $verbosity >= 9; } sub pgm_exit($$) { my ($val,$msg) = @_; if (length($msg)) { $msg .= "\n" if (!($msg =~ /\n$/)); prt($msg); } ### show_warnings($val); ### close_log($outfile,$load_log); exit($val); } # # Calculate the number of columns of tiles in a degree of longitude. # sub bucket_span { my ($lat) = (@_); if ($lat>= 89.0 ) { return 360.0; } elsif ($lat>= 88.0 ) { return 8.0; } elsif ($lat>= 86.0 ) { return 4.0; } elsif ($lat>= 83.0 ) { return 2.0; } elsif ($lat>= 76.0 ) { return 1.0; } elsif ($lat>= 62.0 ) { return 0.5; } elsif ($lat>= 22.0 ) { return 0.25; } elsif ($lat>= -22.0 ) { return 0.125; } elsif ($lat>= -62.0 ) { return 0.25; } elsif ($lat>= -76.0 ) { return 0.5; } elsif ($lat>= -83.0 ) { return 1.0; } elsif ($lat>= -86.0 ) { return 2.0; } elsif ($lat>= -88.0 ) { return 4.0; } elsif ($lat>= -89.0 ) { return 8.0; } else { return 360.0; } } # # Format longitude as e/w. # sub format_lon { my ($lon) = (@_); if ($lon < 0) { return sprintf("w%03d", int(0-$lon)); } else { return sprintf("e%03d", int($lon)); } } # # Format latitude as n/s. # sub format_lat { my ($lat) = (@_); if ($lat < 0) { return sprintf("s%02d", int(0-$lat)); } else { return sprintf("n%02d", int($lat)); } } # # Generate the directory name for a location. # sub directory_name { my ($lon, $lat) = (@_); my $lon_floor = POSIX::floor($lon); my $lat_floor = POSIX::floor($lat); my $lon_chunk = POSIX::floor($lon/10.0) * 10; my $lat_chunk = POSIX::floor($lat/10.0) * 10; $download = format_lon($lon_chunk) . format_lat($lat_chunk) . '.tgz'; return format_lon($lon_chunk) . format_lat($lat_chunk) . $DIRSEP . format_lon($lon_floor) . format_lat($lat_floor); } # # Generate the tile index for a location. # sub tile_index { my ($lon, $lat) = (@_); my $lon_floor = POSIX::floor($lon); my $lat_floor = POSIX::floor($lat); my $span = bucket_span($lat); my $x; if ($span < $EPSILON) { $lon = 0; $x = 0; } elsif ($span <= 1.0) { $x = int(($lon - $lon_floor) / $span); } else { if ($lon >= 0) { $lon = int(int($lon/$span) * $span); } else { $lon = int(int(($lon+1)/$span) * $span - $span); if ($lon < -180) { $lon = -180; } } $x = 0; } my $y; $y = int(($lat - $lat_floor) * 8); my $index = 0; $index += ($lon_floor + 180) << 14; $index += ($lat_floor + 90) << 6; $index += $y << 3; $index += $x; return $index; } my $PI = 3.1415926535897932384626433832795029; my $D2R = $PI / 180; my $R2D = 180 / $PI; # function lon2tile( lon, zoom ) { sub lon2tile($$) { my ($lon, $zoom) = @_; ##return Math.floor( ( lon + 180 ) / 360 * Math.pow( 2, zoom ) ); return int( ( $lon + 180 ) / 360 * ($zoom * $zoom) ); } # function lat2tile( lat, zoom ) { sub lat2tile($$) { my ($lat, $zoom) = @_; my $pi = $PI # return Math.floor(( 1 - Math.log( Math.tan( lat * pi / 180) + 1 / Math.cos( lat * pi / 180)) / pi )/2 * Math.pow(2, zoom) ); #return int(( 1 - Math.log( Math.tan( lat * pi / 180) + 1 / Math.cos( lat * pi / 180)) / pi )/2 * Math.pow(2, zoom) ); } #function tile2lon( x, zoom ) { # return ( x / Math.pow( 2, zoom ) * 360 - 180 ); #} #function tile2lat( y, zoom ) { #var pi = Math.PI #var n = pi - 2 * pi * y / Math.pow( 2, zoom ); #return 180 / pi * Math.atan( 0.5 * ( Math.exp( n ) - Math.exp( -n ) )); #} ### if ((scalar @ARGV) < 2) { ### print "Usage: perl calc-tile.pl lon lat\n"; ### exit(-1); ### } ######################################################################## # Main program. ######################################################################## parse_args(@ARGV); my $dir = directory_name($lon, $lat); my $index = tile_index($lon, $lat); my $path = "$dir$DIRSEP$index.stg"; prt("Longitude: $lon\n"); prt("Latitude: $lat\n"); prt("Tile: $index\n"); prt("Path: \"$path\"\n"); prt("Download: $download\n"); exit 0; ######################################## sub usage { prt("Usage: $pgmname [options] lon lat\n"); } sub need_arg { my ($arg,@av) = @_; pgm_exit(1,"ERROR: [$arg] must have a following argument!\n") if (!@av); } sub parse_args { my (@av) = @_; my ($arg,$sarg); my $verb = VERB2(); while (@av) { $arg = $av[0]; if ($arg =~ /^-/) { $sarg = substr($arg,1); $sarg = substr($sarg,1) while ($sarg =~ /^-/); if (($sarg =~ /^h/i)||($sarg eq '?')) { give_help(); pgm_exit(0,"Help exit(0)"); } elsif ($sarg =~ /^v/) { if ($sarg =~ /^v.*(\d+)$/) { $verbosity = $1; } else { while ($sarg =~ /^v/) { $verbosity++; $sarg = substr($sarg,1); } } $verb = VERB2(); prt("Verbosity = $verbosity\n") if ($verb); #} elsif ($sarg =~ /^l/) { # if ($sarg =~ /^ll/) { # $load_log = 2; # } else { # $load_log = 1; # } # prt("Set to load log at end. ($load_log)\n") if ($verb); } elsif ($sarg =~ /^o/) { need_arg(@av); shift @av; $sarg = $av[0]; $out_file = $sarg; prt("Set out file to [$out_file].\n") if ($verb); } else { pgm_exit(1,"ERROR: Invalid argument [$arg]! Try -?\n"); } } else { if ( ! defined $lon ) { $lon = $arg; } elsif ( ! defined $lat ) { $lat = $arg; } else { # todo: maybe list... usage(); pgm_exit(1,"Error: Aleady have lon $lon and lat $lat!\nWhat is this $arg? At present only 2 bare args allowed.\n"); } ### $in_file = $arg; ### prt("Set input to [$in_file]\n") if ($verb); } shift @av; } if ($debug_on) { prtw("WARNING: DEBUG is ON!\n"); if (length($in_file) == 0) { $in_file = $def_file; prt("Set DEFAULT input to [$in_file]\n"); } } if ( !defined $lat || !defined $lon ) { # length($in_file) == 0) { pgm_exit(1,"ERROR: No lon lat found in command!\n"); } #if (! -f $in_file) { # pgm_exit(1,"ERROR: Unable to find in file [$in_file]! Check name, location...\n"); #} } sub give_help { prt("$pgmname: version $VERS\n"); usage(); # prt("Usage: $pgmname [options] lon lat\n"); prt("Options:\n"); prt(" --help (-h or -?) = This help, and exit 0.\n"); prt(" --verb[n] (-v) = Bump [or set] verbosity. def=$verbosity\n"); ### prt(" --load (-l) = Load LOG at end. ($outfile)\n"); prt(" --out (-o) = Write output to this file.\n"); } # eof 1;