#!/usr/bin/perl -w 
# Лицензия "Делайте что хотите", а если очень нужно, то GPL :) 
# Сообщения на английском, чтобы не связываться с локалями и gettext :( .
# Кроме стандартных расширений perl необходим модуль perl::Magick.
#
# Эта программа предназначена для преобразования больших карт OziExplorer в формат, 
# который был бы пригоден для средств GPS-навигации на Sharp Zaurus: qpegps и gpsdrive.
# Файлы .ozf* нужно преобразовать в изображение (.png). Входные данные для запуска
# конвертера (координаты двух точек) можно получить из .map файла. 

use POSIX;
use File::Basename;
use Image::Magick;
use encoding 'cp1251';

sub truncatecoord ($) { # для qpegps достаточно 6 цифр после десятичной точки
    my ($num) = @_; 
    $num = unpack("x0 a9", $num);   
    return $num
}

sub testarg ($) { #FIXME
    if (not defined $_[0]) {die "Bad argument"};
}


if ( not $#ARGV + 1) { 
    print "Large raster maps cutting and resizing tool v.0.1\n";
    print "\ngpsmapper FILENAME map_X map_Y fragment_X fragment_Y overlap_X overlap_Y scale \\\n          point1_X point1_Y point1_lon point1_lat point2_X point2_Y point2_lon \\\n	  point2_lat [-simulate]\n";
    print "\nThis script will split and resize a large raster map image to produce usable maps\nfor Zaurus GPS moving map solutions (qpegps and gpsdrive).\n";	
    print "It uses ImageMagick internally to do the job. This script was made for conversion \nof city maps, so the  Earth surface is considered to be flat.\n"; 
    print "\nRequired parameters are:\n";
    print "map_X, map_Y           - map image dimensions If they differ from the real size,\n                         of the map, it will be scaled to specified size.\n";
    print "fragment_X, fragment_Y - size of the fragments. Gpsdrive requires 1280x1024.\n";
    print "overlap_X, overlap_Y   - amount of overlap between neighbour fragments.\n";
    print "scale                  - scale of the map (not used in calculonions)\n";
    print "point1_X point1_Y point1_lon point1_lat\n";
    print "                       - XY position and GPS coordinates of a point on the map.\n                         lonitude and latgitude must be given in decimal degrees.\n                         the script needs two diagonally placed points to\n                         calculone geolocation data for both qpegps and gpsdrive.\n";
    print "\nAn optional parameter -simulate allows to skip all image operations.\n";
} else { 

my $suffixes = "\.[G|g][I|i][F|f]|\.[J|j][P|p][G|g]|\.[P|p][N|n][G|g]|\.[B|b][M|m][P|p]";
my ($inmap_x, $inmap_y, $inmapclass);
my $inmapname = $ARGV[0];
my $basemapname = fileparse($inmapname, $suffixes);
   $basemapname = "map_".$basemapname; # для gpsdrive
my $base_w = $ARGV[1]; testarg($ARGV[1]);
my $base_h = $ARGV[2]; testarg($ARGV[2]);
my $frag_w = $ARGV[3]; testarg($ARGV[3]);
my $frag_h = $ARGV[4]; testarg($ARGV[4]);
my $over_x = $ARGV[5]; testarg($ARGV[5]);
my $over_y = $ARGV[6]; testarg($ARGV[6]);
my $scale = $ARGV[7]; testarg($ARGV[7]);
my $first_lon = $ARGV[8]; testarg($ARGV[8]);
my $first_lat = $ARGV[9]; testarg($ARGV[9]);
my $first_x = $ARGV[10]; testarg($ARGV[10]);
my $first_y = $ARGV[11]; testarg($ARGV[11]);
my $secnd_lon = $ARGV[12]; testarg($ARGV[12]);
my $secnd_lat = $ARGV[13]; testarg($ARGV[13]);
my $secnd_x = $ARGV[14]; testarg($ARGV[14]);
my $secnd_y = $ARGV[15]; testarg($ARGV[15]);
my $simulate; if (defined $ARGV[16] and $ARGV[16] eq "-simulate") {$simulate = 1} else {$simulate = 0};
my ($baselon, $baselat);


    $lonperpix = ($secnd_lon - $first_lon)/($secnd_x - $first_x); # Шаг координат в 1 пикселе, две точки должны быть расположены диагонально, в углах картинки
    $latperpix = ($secnd_lat - $first_lat)/($secnd_y - $first_y); # это всегда отрицательное число

    $baselon = $first_lon - ($first_x * $lonperpix); # Поправка, если координаты точки1 не 0х0 
    $baselat = $first_lat - ($first_y * $latperpix);

    $fragnum_x = floor($base_w/($frag_w - $over_x)); # Максимальное количество целых фрагментов
    $fragnum_y = floor($base_h/($frag_h - $over_y));
    $cut_x = ($fragnum_x * ($frag_w - $over_x)) + $over_x;
    $cut_y = ($fragnum_y * ($frag_h - $over_y)) + $over_y;
    if ($cut_x > $base_w) {--($fragnum_x); $cut_x = ($fragnum_x * ($frag_w - $over_x)) + $over_x;}; # Если последний фрагмент выходит за рамки изображения
    if ($cut_y > $base_h) {--($fragnum_y); $cut_y = ($fragnum_y * ($frag_h - $over_y)) + $over_y;};
    $startcut_x = floor(($base_w - $cut_x)/2); # Координаты начала разрезаемого на фрагменты прямоугольника 
    $startcut_y = floor(($base_h - $cut_y)/2);

    print "$basemapname\n\n";
    # Показать координаты крайних угловых точек
    print "0 X 0 = ".$baselon." ".$baselat."\n".$base_w." X ".$base_h." = ".(($base_w * $lonperpix) + $baselon)." ".(($base_h * $latperpix) + $baselat)."\n\n";
    print "$fragnum_x Х $fragnum_y fragment(s)\nI shall crop an area of $cut_x X $cut_y pixels starting from $startcut_x X $startcut_y.\n";
    print "Recommended ".$fragnum_x." X ".$fragnum_y." fragment size is ".((($base_w - $over_x) / $fragnum_x) + $over_x)." X ".((($base_h - $over_y) / $fragnum_y) + $over_y)." pixels.\n\n";

    if (not $simulate) {
	print "Reading $inmapname...\n";
        $bigmap=Image::Magick->new(); # Чтение картинки (долго)
        $x=$bigmap->ReadImage($inmapname);
	warn "$x" if "$x";
	($inmap_x, $inmap_y, $inmapclass)=$bigmap->Get('width', 'height', 'class'); # Получение свойств картинки
	if (not $inmap_x == $base_w or not $inmap_y == $base_h) { # Если запрошенные размеры отличаются - растянуть или сжать
	    print "Scaling $inmap_x X $inmap_y map to $base_w X $base_h...\n";
	    $bigmap->Resize('width'=>$base_w, 'height'=>$base_h, 'filter'=>'cubic', 'blur'=>0);
	}; 
    };

    open (QGPS, ">> maps.txt");
    open (GPSD, ">> map_koord.txt");
    
    for($b=0; $b<$fragnum_y; ++$b) { # Перебор фрагментов по y 
	for($a=0; $a<$fragnum_x; ++$a) { # Перебор по x
	    $x=($startcut_x + ($a * ($frag_w - $over_x)));
	    $y=($startcut_y + ($b * ($frag_h - $over_y)));
	    if (not $simulate) {
		print "Creating fragment $basemapname-$a-$b.png - $x X $y\n";
		$fragment=$bigmap->Clone();
		$fragment->Mogrify('Crop', $frag_w."x".$frag_h."+".$x."+".$y); # Вырезание фрагмента
		$fragment->Set('page'=>$frag_w."x".$frag_h."+0+0");
		$fragment->Write("$basemapname-$a-$b.jpg");
		if ($inmapclass ne 'PseudoClass') { # Бооольшой тормоз
		    print "Converting fragment to 256 colors to save Zaurus memory space...\n"; #Для экономии памяти на Заурусе лучше преобразовать карту в 256 цветов  
		    $fragment->Quantize('colors'=>256,'dither'=>0,'treedepth'=>3);
		};
		$fragment->Write("$basemapname-$a-$b.png");
    		undef $fragment;
	    };
	    $tlcoord_x = ($x * $lonperpix) + $baselon; # Расчет координат привязки для qpegps
	    $tlcoord_y = ($y * $latperpix) + $baselat;
	    $drcoord_x = (($x + $frag_w) * $lonperpix) + $baselon;
	    $drcoord_y = (($y + $frag_h) * $latperpix) + $baselat;
	    
	    print QGPS "LINEAR $basemapname-$a-$b.png $scale $frag_w $frag_h ".truncatecoord($tlcoord_x)." ".truncatecoord($tlcoord_y)." 0 0 ".truncatecoord($drcoord_x)." ".truncatecoord($drcoord_y)." $frag_w $frag_h\n"; # qpegps

	    $coord_x = ($tlcoord_x + $drcoord_x)/2; # Расчет координат центра для gpsdrive
	    $coord_y = ($tlcoord_y + $drcoord_y)/2;

	    print GPSD "$basemapname-$a-$b.png $coord_y $coord_x $scale\n"; # gpsdrive

	};
    }; 

    close (QGPS);
    close (GPSD);
    print "Finished.\n\n"; 
};

