#!/usr/bin/perl

# get maps from map.search.ch (for Switzerland only)
# Copyright (C) 2007  Roger Britt <fetchsearchmap@primat.ch>
#
# This script is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or any later version.
# 
# This script is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
# 
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

use strict;
use Getopt::Long;
use Pod::Usage;
use POSIX qw(ceil floor);
use Fcntl qw(:DEFAULT :flock);

my $TRUE            = 1;
my $FALSE           = 0;

my $INFO            = "fetchsearchmap (c) 2007 Roger Britt <fetchsearchmap\@primat.ch>";
my $VERSION         = 0.1;
my $USAGE           = "Usage: fetchsearchmap.pl <start Latitude> <start Longitude> <end Latitude> <end Longitude>\n
<start Latitude>  = Latitude from left upper corner in format:   DD.MMMMM
<start Longitude> = Longitude from left upper corner in format:  DD.MMMMM
<end Latitude>    = Latitude from right lower corner in format:  DD.MMMMM
<end Longitude>   = Longitude from right lower corner in format: DD.MMMMM

Example: fetchsearchmap.pl 47.05992 8.30378 47.05992 8.31000";
my $USAGENOTE       = "*** Don't missuse this service by downloading more maps as 
    you need! You will risc that you will be blocked by this server.";

my $MAP_DIR         = "$ENV{'HOME'}/.gpsdrive/";
my $KOORD_FILE      = 'map_koord.txt';
my $FILEPREFIX      = 'map_file';
my $FILESUFFIX      = '.jpg';
my $TMPFILENAME     = 'map_new.jpg';
my $mapserver       = 'http://map.search.ch/chmap.jpg?layer=bg,fg,copy';
my $poingOfInterest = '&poi=bergbahn,parkhaus,haltestelle,zug,stau,mobilit
y,bar,cafe,hotel,restaurant,kino,museum,theater,kirche,polizei,schule,spital,apo
theke,geldautomat,post,shop,tankstelle,berg,pass,wasserfall';
my $mapwidth        = 1280;
my $mapheight       = 1024;
my $zoomlevel       = 0.5;
my $scale           = 2250;
my $niceSleep       = 3;

#command line arguments
my $numberOfArgs = $#ARGV + 1;
my $argnum;

print $INFO."\n\n";
print $USAGENOTE."\n\n";

if ($numberOfArgs < 4){
	print $USAGE."\n\n";
	exit(1);
}

my $startLat        = $ARGV[0];
my $startLon        = $ARGV[1];
my $endLat          = $ARGV[2];
my $endLon          = $ARGV[3];

my $startx          = roundUp(calculateCHX($startLat, $startLon));
my $starty          = roundDown(calculateCHY($startLat, $startLon));
my $endx            = roundDown(calculateCHX($endLat, $endLon));
my $endy            = roundUp(calculateCHY($endLat, $endLon));

print "\nDownloading maps from point (".$startLat."/".$startLon.") to (".$endLat."/".$endLon.")\n";
print "You want to download";
my $numberOfDownloads = tile($startx, $starty, $endx, $endy, $TRUE);
print " ".$numberOfDownloads." maps.\n";

my $continue        = &promptUser("Would you like to continue? ", "yes/no");

if ($continue eq "yes" || $continue eq "y"){
	print "\nstart downloading ".$numberOfDownloads." maps:\n";
	tile($startx, $starty, $endx, $endy, $FALSE);
} else{
	print "\nabort without downloading!\n";
}



# tile($startx, $starty, $endx, $endy, $calculate)
sub tile{
	my $startx = $_[0];
	my $starty = $_[1];
	my $endx = $_[2];
	my $endy = $_[3];
	my $calculate = $_[4];
	
	my $cycles = 0;
	my $countx = $startx;
	my $county = 0;
	
	for ($countx; $countx>=$endx; $countx=$countx-500) {
		$county = $starty;
		for ($county; $county<=$endy; $county=$county+500) {
			my $url;
			my $lat;
			my $lon;
			$cycles++;
			$url = getURL($zoomlevel, $mapwidth, $mapheight, $countx, $county, $mapserver);
			if ($calculate == $FALSE){
				if (addKoordinateRecord(calculateWGSLat($countx, $county), calculateWGSLon($countx, $county), $scale, $MAP_DIR, $KOORD_FILE, $FILEPREFIX, $FILESUFFIX, $TMPFILENAME, $TRUE) == $TRUE){
					sleep($niceSleep);
					if (downloadMap($countx, $county, $url, $TMPFILENAME) == $TRUE){
						# add koordinate record
						addKoordinateRecord(calculateWGSLat($countx, $county), calculateWGSLon($countx, $county), $scale, $MAP_DIR, $KOORD_FILE, $FILEPREFIX, $FILESUFFIX, $TMPFILENAME, $FALSE);
					}
				} else {
					print "map with coordinates (".$countx."/".$county.") already exists\n";
				}
			} else {
				print ".";
			}
		}
	}
	if ($calculate == $TRUE){
		return $cycles;
	}
}

# addKoordinateRecord($lat, $lon, $scale, $path, $file, $FILEPREFIX, $FILESUFFIX, $tempFile, $check);
sub addKoordinateRecord{
	my $lat = $_[0];
	my $lon = $_[1];
	my $scale = $_[2];
	my $path = $_[3];
	my $file = $_[4];
	my $fileprefix = $_[5];
	my $filesuffix = $_[6];
	my $tempFile = $_[7];
	my $check = $_[8];
	my $lastFilename;
	my $lastNumber;
	my $newNumber;
	my $newFilename;
	
	if ($check == $TRUE){
		if (system("egrep 'map_file.....jpg ".$lat." ".$lon." ".$scale."' ".$path.$file." >/dev/null 2>&1") == 0){
			#map already exists
			return $FALSE;
		} else{
			return $TRUE;
		}
	} else{
		open(FH, $path.$file) or die "Couldn't open koordinate file: $!\n";
		while (<FH>) {
			chomp;
			s/^\s+//;
			s/\s+$//;
			next unless length;
			($lastFilename) = split(/ /, $_);
		}
		close (FH);
		
		$lastNumber = substr($lastFilename,8,4);
		$newNumber = sprintf("%04.0f", $lastNumber+1);
		$newFilename = $fileprefix.$newNumber.$filesuffix;
		
		system("echo '".$newFilename." ".$lat." ".$lon." ".$scale."' >> ".$path.$file);
		rename($path.$tempFile, $path.$newFilename);
	}
}


# downloadMap($x, $y, $url, $filename)
sub downloadMap{
	print "downloading Map with coordinate: ($_[1]/$_[0]) ";
	
	if (system("wget -q -O".$_[3]." '".$_[2]."'") == 0) {
	  print "OK\n";
	  return $TRUE;
	} else {
	  print "Failure\n";
	  return $FALSE;
	}
}

# getURL($zoomlevel, $mapwidth, $mapheight, $x, $y, $mapserver);
sub getURL{
	my $parKoord      = '&zd='.$_[0].'&x='.(-1 * ($_[1] / 2)).'&y='.(-1 * ($_[2] / 2));
	my $parDet        = '&w='.$_[1].'&h='.$_[2].'&base='.$_[4].','.$_[3];
	return ($_[5].$parKoord.$poingOfInterest.$parDet);
}

sub calculateCHX{
	my $Latss     = $_[0] * 3600;
	my $Lonss     = $_[1] * 3600;
	
	my $Lathg     = ($Latss - 169028.66) / 10000;
	my $Lonhg     = ($Lonss - 26782.5) / 10000;
	
	my $x         = 200147.07 + 308807.95 * $Lathg + 3745.25 * ($Lonhg * $Lonhg) + 76.63 * ($Lathg * $Lathg) + 119.79 * ($Lathg * $Lathg * $Lathg) - 194.56 * ($Lonhg * $Lonhg) * $Lathg;
	my $xRounded  = sprintf ("%.0f", $x);
	
	return ($xRounded);
}


sub calculateCHY{
	my $Latss     = $_[0] * 3600;
	my $Lonss     = $_[1] * 3600;
	
	my $Lathg     = ($Latss - 169028.66) / 10000;
	my $Lonhg     = ($Lonss - 26782.5) / 10000;
	
	my $y         = 600072.37 + 211455.93 * $Lonhg - 10938.51 * $Lonhg * $Lathg - 0.36 * $Lonhg * ($Lathg * $Lathg) - 44.54 * ($Lonhg * $Lonhg * $Lonhg);
	my $yRounded  = sprintf ("%.0f", $y);

	return ($yRounded);
}

# calculateWGSLon($x, $y)
sub calculateWGSLon{
	my $xtmp = ($_[0] - 200000)/1000000;
	my $ytmp = ($_[1] - 600000)/1000000;
	my $lontmp = 2.6779094 + 4.728982 * $ytmp + 0.791484 * $ytmp *$xtmp + 0.1306 * $ytmp * $xtmp * $xtmp - 0.0436 * $ytmp * $ytmp * $ytmp;
	
	return ($lontmp*100/36);
}

# calculateWGSLat()
sub calculateWGSLat{
	my $xtmp = ($_[0] - 200000)/1000000;
	my $ytmp = ($_[1] - 600000)/1000000;
	my $lattmp = 16.9023892 + 3.238272 * $xtmp - 0.270978 * $ytmp * $ytmp - 0.002528 * $xtmp * $xtmp - 0.0447 * $ytmp * $ytmp * $xtmp - 0.0140 * $xtmp * $xtmp * $xtmp;
	
	return ($lattmp*100/36);
}

sub roundUp {
	return (ceil($_[0] * 0.002) / 0.002);
}

sub roundDown {
	return (floor($_[0] * 0.002) / 0.002);
}

#----------------------------(  promptUser  )-----------------------------#
#                                                                         #
#  FUNCTION:	promptUser                                                #
#                                                                         #
#  PURPOSE:	Prompt the user for some type of input, and return the    #
#		input back to the calling program.                        #
#                                                                         #
#  ARGS:	$promptString - what you want to prompt the user with     #
#		$defaultValue - (optional) a default value for the prompt #
#                                                                         #
#-------------------------------------------------------------------------#
sub promptUser {

   #-------------------------------------------------------------------#
   #  two possible input arguments - $promptString, and $defaultValue  #
   #  make the input arguments local variables.                        #
   #-------------------------------------------------------------------#

   my $promptString = $_[0];
   my $defaultValue = $_[1];

   #-------------------------------------------------------------------#
   #  if there is a default value, use the first print statement; if   #
   #  no default is provided, print the second string.                 #
   #-------------------------------------------------------------------#

   if ($defaultValue) {
      print $promptString, "[", $defaultValue, "]: ";
   } else {
      print $promptString, ": ";
   }

   $| = 1;               # force a flush after our print
   $_ = <STDIN>;         # get the input from STDIN (presumably the keyboard)


   #------------------------------------------------------------------#
   # remove the newline character from the end of the input the user  #
   # gave us.                                                         #
   #------------------------------------------------------------------#

   chomp;

   #-----------------------------------------------------------------#
   #  if we had a $default value, and the user gave us input, then   #
   #  return the input; if we had a default, and they gave us no     #
   #  no input, return the $defaultValue.                            #
   #                                                                 # 
   #  if we did not have a default value, then just return whatever  #
   #  the user gave us.  if they just hit the <enter> key,           #
   #  the calling routine will have to deal with that.               #
   #-----------------------------------------------------------------#

   if ("$defaultValue") {
      return $_ ? $_ : $defaultValue;    # return $_ if it has a value
   } else {
      return $_;
   }
}
