# 
# PERL mapgen module by gary68
#
# This module contains a lot of useful graphic functions for working with osm files and data. This enables you (in conjunction with osm.pm)
# to easily draw custom maps.
# Have a look at the last (commented) function below. It is useful for your main program!
#
#
#
#
# Copyright (C) 2010, Gerhard Schwanz
#
# This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the 
# Free Software Foundation; either version 3 of the License, or (at your option) any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with this program; if not, see <http://www.gnu.org/licenses/>

#
# INFO
#
# graph top left coordinates: (0,0)
# size for lines = pixel width / thickness
#
# 1.051 l0 calculation adapted


package OSM::mapgen ; #  

use strict ;
use warnings ;

use Math::Trig;
use File::stat;
use Time::localtime;
use List::Util qw[min max] ;
use Encode ;
use OSM::osm ;
use OSM::QuadTree ;
use GD ;
use Geo::Proj4 ;

use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);

$VERSION = '1.19' ;

require Exporter ;

@ISA = qw ( Exporter AutoLoader ) ;

@EXPORT = qw ( 		addAreaIcon
			addOnewayArrows
			center
			convert
			createLabel
			createWayLabels
			declutterStat
			drawArea 
			drawAreaMP
			drawAreaOcean
			drawAreaPix 
			drawCircle 
			drawCircleRadius 
			drawCircleRadiusText 
			drawCoords
			drawHead 
			drawFoot 
			drawGrid
			drawLegend 
			drawNodeDot 
			drawNodeDotRouteStops 
			drawNodeDotPix 
			drawNodeCircle 
			drawNodeCirclePix 
			drawPageNumber
			drawPageNumberTop
			drawPageNumberBottom
			drawPageNumberLeft
			drawPageNumberRight
			drawRuler 
			drawTextPix 
			drawTextPix2 
			drawTextPixGrid
			drawWay 
			drawWayBridge 
			drawWayPix 
			drawWayRoute
			fitsPaper
			getDimensions
			getScale
			getValue
			gridSquare
			initGraph 
			initOneways
			labelWay 
			placeLabelAndIcon
			printScale
			scalePoints
			scaleBase
			setdpi 
			setBaseDpi
			simplifiedPercent
			sizePNG 
			sizeSVG
			writeSVG ) ;

#
# constants
#

my %dashStyle = () ;
my %dashDefinition = () ; # for 300 dpi
@{$dashDefinition{1}} = (60,20,"round") ; #grid
@{$dashDefinition{11}} = (16,16,"butt") ; # tunnel

my $wayIndexLabelColor = 9 ;
my $wayIndexLabelSize = 10 ;
my $wayIndexLabelFont = 11 ;
my $wayIndexLabelOffset = 12 ;
my $wayIndexLegendLabel = 14 ;

my $lineCap = "round" ;
my $lineJoin = "round" ;

my @occupiedAreas = () ;
my $labelPathId = 0 ;

my $qtWayLabels ;
my $qtPoiLabels ;

#
# variables
#
my $proj ;
my $projSizeX ;
my $projSizeY ;
my ($projLeft, $projRight, $projBottom, $projTop) ;


my ($top, $bottom, $left, $right) ; # min and max real world coordinates
my ($sizeX, $sizeY) ; # pic size in pixels

my %svgOutputWays ;
my %svgOutputNodes ;
my @svgOutputAreas = () ;
my @svgOutputText = () ;
my @svgOutputPixel = () ;
my @svgOutputPixelGrid = () ;
my @svgOutputDef = () ;
my @svgOutputPathText = () ;
my @svgOutputIcons = () ;
my @svgOutputRouteStops = () ;
my $pathNumber = 0 ;
my $svgBaseFontSize = 10 ;
my @svgOutputRoutes = () ;

my %areaDef = () ;
my $areaNum = 1 ;

my $numIcons = 0 ;
my $numIconsMoved = 0 ;
my $numIconsOmitted = 0 ;
my $numLabels = 0 ;
my $numLabelsMoved = 0 ;
my $numLabelsOmitted = 0 ;
my $numWayLabelsOmitted = 0 ;

my $dpi = 0 ;
my $baseDpi ;

# clutter information
my %clutter = () ;
my %clutterIcon = () ;
my @lines ;

my $simplified = 0 ;
my $simplifyTotal = 0 ;

my $shieldPathId = 0 ;
my %createdShields = () ; # key = name; value = id of path
my %shieldXSize = () ;
my %shieldYSize = () ;


sub setdpi {
	$dpi = shift ;
}

sub setBaseDpi {
	$baseDpi = shift ;
}


sub initGraph {
#
# function initializes the picture, the colors and the background (white)
#
	my ($x, $l, $b, $r, $t, $color, $projection, $ellipsoid) = @_ ;	

	# my $l0 = int($l) - 1 ;
	my $l0 = int(($r+$l) / 2 ) ;

	$proj = Geo::Proj4->new(
		proj => $projection, 
		ellps => $ellipsoid, 
		lon_0 => $l0 
		) or die "parameter error: ".Geo::Proj4->error. "\n"; 


	($projLeft, $projBottom) = $proj->forward($b, $l) ; # lat/lon!!!
	($projRight, $projTop) = $proj->forward($t, $r) ; # lat/lon!!!

	# print "PROJ: bounds: $projLeft $projRight $projBottom $projTop\n" ;

	$projSizeX = $projRight - $projLeft ;
	$projSizeY = $projTop - $projBottom ;

	my $factor = $projSizeY / $projSizeX ;

	# print "PROJ: $projSizeX x $projSizeY units, factor = $factor\n" ;
	
	$sizeX = int ($x) ;
	$sizeY = int ($x * $factor) ;

	# print "PROJ: $sizeX x $sizeY pixels\n" ;
	# print "PROJ: t b l r $t $b $l $r\n" ;
	# print "PROJ: pt pb pl pr $projTop $projBottom $projLeft $projRight\n" ;
	# print "PROJ: factor $factor\n" ;
	# print "PROJ: l0 $l0\n" ;

	$top = $t ;
	$left = $l ;
	$right = $r ;
	$bottom = $b ;

	drawArea ($color, "", $l, $t, $r, $t, $r, $b, $l, $b, $l, $t) ;

	$qtWayLabels = OSM::QuadTree->new(  -xmin  => 0,
                                      -xmax  => $sizeX+100,
                                      -ymin  => 0,
                                      -ymax  => $sizeY+40,
                                      -depth => 5);
	$qtPoiLabels = OSM::QuadTree->new(  -xmin  => 0,
                                      -xmax  => $sizeX+100,
                                      -ymin  => 0,
                                      -ymax  => $sizeY+40,
                                      -depth => 5);
	initDashes() ;
}

sub initDashes {
#
# sub creates internal dash styles according to base definition
#
	foreach my $style (keys %dashDefinition) {
		my @array = @{$dashDefinition{$style}} ;
		my $lc = pop @array ;
		my $dashString = "" ;
		foreach my $entry (@array) {
			my $entryScaled = scalePoints ( scaleBase ($entry) ) ;
			$dashString .= "$entryScaled," ;
		}
		$dashString .= $lc ;
		$dashStyle{$style} = $dashString ;
	}
}



sub convert {
#
# converts real world coordinates to system graph pixel coordinates
#
	my ($x, $y) = @_ ;

	my ($x1, $y1) = $proj->forward($y, $x) ; # lat/lon!!!

	my $x2 = int ( ($x1 - $projLeft) / ($projRight - $projLeft) * $sizeX ) ;
	my $y2 = $sizeY - int ( ($y1 - $projBottom) / ($projTop - $projBottom) * $sizeY ) ;

	return ($x2, $y2) ;
}

sub gridSquare {
#
# returns grid square of given coordinates for directories
#
	my ($lon, $lat, $parts) = @_ ;
	my ($x, $y) = convert ($lon, $lat) ;
	# my $partsY = $sizeY / ($sizeX / $parts) ;
	my $xi = int ($x / ($sizeX / $parts)) + 1 ;
	my $yi = int ($y / ($sizeX / $parts)) + 1 ;
	if ( ($x >= 0) and ($x <= $sizeX) and ($y >= 0) and ($y <= $sizeY) ) {
		return (chr($xi+64) . $yi) ;
	}
	else {
		return undef ;
	}
}



sub occupyArea {
#
# occupy area and make entry in quad tree for later use
#
	my ($x1, $x2, $y1, $y2) = @_ ;
	# left, right, bottom, top (bottom > top!)
	push @occupiedAreas, [$x1, $x2, $y1, $y2] ;
	$qtPoiLabels->add ($#occupiedAreas, $x1, $y1, $x2, $y2) ;
}

sub areaOccupied {
#
# look up possible interfering objects in quad tree and check for collision
#
	my ($x1, $x2, $y1, $y2) = @_ ;
	# left, right, bottom, top (bottom > top!)
	my $occupied = 0 ;

	my $ref2 = $qtPoiLabels->getEnclosedObjects ($x1, $y2, $x2, $y1) ;
	my @index = @$ref2 ;
	my @occupiedAreasTemp = () ;
	foreach my $nr (@index) {
		push @occupiedAreasTemp, $occupiedAreas[$nr] ;
	} 

	LAB1: foreach my $area (@occupiedAreasTemp) {
		my $intersection = 1 ;
		if ($x1 > $area->[1]) { $intersection = 0 ; } ;
		if ($x2 < $area->[0]) { $intersection = 0 ; } ;
		if ($y1 < $area->[3]) { $intersection = 0 ; } ;
		if ($y2 > $area->[2]) { $intersection = 0 ; } ;
		if ($intersection == 1) { 
			$occupied = 1 ; 
			last LAB1 ;	
		}
	}
	return ($occupied) ;
}

sub splitLabel {
#
# split label text at space locations and then merge new parts if new part will be smaller than 21 chars
#
	my $text = shift ;
	my @lines = split / /, $text ;
	my $merged = 1 ;
	while ($merged) {
		$merged = 0 ;
		LAB2: for (my $i=0; $i<$#lines; $i++) {
			if (length ($lines[$i] . " " . $lines[$i+1]) <= 20) {
				$lines[$i] = $lines[$i] . " " . $lines[$i+1] ;
				splice (@lines, $i+1, 1) ;
				$merged = 1 ;
				last LAB2 ;
			}
		}
	}
	return (\@lines) ;
}


sub svgElementIcon {
#
# create SVG text for icons
#
	my ($x, $y, $icon, $sizeX, $sizeY) = @_ ;
	my ($out) = "<image x=\"" . $x . "\"" ;
	$out .= " y=\"" . $y . "\"" ;
	if ($sizeX > 0) { $out .= " width=\"" . $sizeX . "\"" ; }
	if ($sizeY > 0) { $out .= " height=\"" . $sizeY . "\"" ; }
	$out .= " xlink:href=\"" . $icon . "\" />" ;

	return ($out) ;	
}

sub drawHead {
#
# draws text on top left corner of the picture
#
	my ($text, $col, $size, $font) = @_ ;
	push @svgOutputText, svgElementText (20, 20, $text, $size, $font, $col) ;
}

sub drawFoot {
#
# draws text on bottom left corner of the picture
#
	my ($text, $col, $size, $font) = @_ ;
	my $posX = 80 ;
	my $posY = 40 ;
	push @svgOutputText, svgElementText (
		scalePoints ( scaleBase ($posX) ), 
		$sizeY - ( scalePoints ( scaleBase ($posY) ) ), 
		$text, 
		scalePoints ( scaleBase ($size) ) , 
		$font, 
		$col
	) ;
}



sub drawTextPix {
#
# draws text at pixel position
# with small offset direction bottom
#
	my ($x1, $y1, $text, $col, $size, $font) = @_ ;

	push @svgOutputPixel, svgElementText ($x1, $y1, $text, $size, $font, $col) ;
}

sub drawTextPixGrid {
#
# draws text at pixel position. code goes to grid
#
	my ($x1, $y1, $text, $col, $size) = @_ ;

	push @svgOutputPixelGrid, svgElementText ($x1, $y1+9, $text, $size, "sans-serif", $col) ;
}

sub drawNodeDot {
#
# draws node as a dot at given real world coordinates
#
	my ($lon, $lat, $col, $size) = @_ ;
	my ($x1, $y1) = convert ($lon, $lat) ;
	push @{$svgOutputNodes{0}}, svgElementCircleFilled ($x1, $y1, $size, $col) ;
}

sub drawNodeDotRouteStops {
#
# draws node as a dot at given real world coordinates
#
	my ($lon, $lat, $col, $size) = @_ ;
	my ($x1, $y1) = convert ($lon, $lat) ;
	push @svgOutputRouteStops, svgElementCircleFilled ($x1, $y1, $size, $col) ;
}

sub drawNodeDotPix {
#
# draws node as a dot at given pixels
#
	my ($x1, $y1, $col, $size) = @_ ;
	push @svgOutputPixel, svgElementCircleFilled ($x1, $y1, $size, $col) ;
}


sub drawCircle {
	my ($lon, $lat, $radius, $color, $thickness) = @_ ;
	# radius in meters

	my ($x, $y) = convert ($lon, $lat) ;
	my $thickness2 = scalePoints ($thickness) ;

	my $radiusPixel = $radius / (1000 * distance ($left, $bottom, $right, $bottom) ) * $sizeX ;
	push @svgOutputPixelGrid, svgElementCircle ($x, $y, $radiusPixel, $thickness2, $color) ;
}

sub drawWay {
#
# draws way as a line at given real world coordinates. nodes have to be passed as array ($lon, $lat, $lon, $lat...)
# $size = thickness
#
	my ($layer, $col, $size, $dash, @nodes) = @_ ;
	my $i ;
	my @points = () ;

	for ($i=0; $i<$#nodes; $i+=2) {
		my ($x, $y) = convert ($nodes[$i], $nodes[$i+1]) ;
		push @points, $x ; push @points, $y ; 
	}
	push @{$svgOutputWays{$layer+$size/100}}, svgElementPolyline ($col, $size, $dash, @points) ;
}

sub drawWayBridge {
#
# draws way as a line at given real world coordinates. nodes have to be passed as array ($lon, $lat, $lon, $lat...)
# $size = thickness
#
	my ($layer, $col, $size, $dash, @nodes) = @_ ;
	my $i ;
	my @points = () ;

	if ($dash eq "11") { $dash = $dashStyle{11} ; }

	for ($i=0; $i<$#nodes; $i+=2) {
		my ($x, $y) = convert ($nodes[$i], $nodes[$i+1]) ;
		push @points, $x ; push @points, $y ; 
	}
	push @{$svgOutputWays{$layer+$size/100}}, svgElementPolylineBridge ($col, $size, $dash, @points) ;
}

sub drawWayPix {
#
# draws way as a line at given pixels. nodes have to be passed as array ($x, $y, $x, $y...)
# $size = thickness
#
	my ($col, $size, $dash, @nodes) = @_ ;
	my $i ;
	my @points = () ;

	for ($i=0; $i<$#nodes; $i+=2) {
		my ($x, $y) = ($nodes[$i], $nodes[$i+1]) ;
		push @points, $x ; push @points, $y ; 
	}
	push @svgOutputPixel, svgElementPolyline ($col, $size, $dash, @points) ;
}

sub drawWayPixGrid {
#
# draws way as a line at given pixels. nodes have to be passed as array ($x, $y, $x, $y...)
# $size = thickness
#
	my ($col, $size, $dash, @nodes) = @_ ;
	my $i ;
	my @points = () ;

	for ($i=0; $i<$#nodes; $i+=2) {
		my ($x, $y) = ($nodes[$i], $nodes[$i+1]) ;
		push @points, $x ; push @points, $y ; 
	}
	push @svgOutputPixelGrid, svgElementPolyline ($col, $size, $dash, @points) ;
}


sub labelWay {
#
# labels a way
#
	my ($col, $size, $font, $text, $tSpan, @nodes) = @_ ;
	my $i ;
	my @points = () ;

	for ($i=0; $i<$#nodes; $i+=2) {
		my ($x, $y) = convert ($nodes[$i], $nodes[$i+1]) ;
		push @points, $x ; push @points, $y ; 
	}
	my $pathName = "Path" . $pathNumber ; $pathNumber++ ;
	push @svgOutputDef, svgElementPath ($pathName, @points) ;
	push @svgOutputPathText, svgElementPathTextAdvanced ($col, $size, $font, $text, $pathName, $tSpan, "middle", 50, 0) ;
}


sub createWayLabels {
#
# finally take all way label candidates and try to label them
#
	my ($ref, $ruleRef, $declutter, $halo, $svgName) = @_ ;
	my @labelCandidates = @$ref ;
	my @wayRules = @$ruleRef ;
	my %notDrawnLabels = () ;
	my %drawnLabels = () ;

	# calc ratio to label ways first where label just fits
	# these will be drawn first
	foreach my $candidate (@labelCandidates) {
		my $wLen = $candidate->[2] ;
		my $lLen = $candidate->[3] ;
		if ($wLen == 0) { $wLen = 1 ; }
		if ($lLen == 0) { $lLen = 1 ; }
		$candidate->[5] = $lLen / $wLen ;
	}
	@labelCandidates = sort { $b->[5] <=> $a->[5] } @labelCandidates ;

	foreach my $candidate (@labelCandidates) {
		my $rule = $candidate->[0] ; # integer
		my @ruleData = @{$wayRules[$rule]} ;
		my $name = $candidate->[1] ;
		my $wLen = $candidate->[2] ;
		my $lLen = $candidate->[3] ;
		my @points = @{$candidate->[4]} ;

		my $toLabel = 1 ;
		if ( ($declutter eq "1") and ($points[0] > $points[-2]) and ( ($ruleData[1] eq "motorway") or ($ruleData[1] eq "trunk") ) ) {
			$toLabel = 0 ;
		}

		if ($lLen > $wLen*0.95) {
			$notDrawnLabels { $name } = 1 ;
		}

		if ( ($lLen > $wLen*0.95) or ($toLabel == 0) ) {
			# label too long
			$numWayLabelsOmitted++ ;
		}
		else {

			if (grep /shield/i, $name) {
				# create shield if necessary
				if ( ! defined $createdShields{ $name }) {
					createShield ($name, $ruleData[$wayIndexLabelSize]) ;
				}

				# @points = (x1, y1, x2, y2 ... ) 
				# $wLen in pixels
				# $lLen in pixels
				# <use xlink:href="#a661" x="40" y="40" />

				my $shieldMaxSize = $shieldXSize{ $name } ;
				if ($shieldYSize{ $name } > $shieldMaxSize) { $shieldMaxSize = $shieldYSize{ $name } ; } 

				my $numShields = int ($wLen / ($shieldMaxSize * 12) ) ;
				# if ($numShields > 4) { $numShields = 4 ; } 

				if ($numShields > 0) {
					my $step = $wLen / ($numShields + 1) ;
					my $position = $step ; 
					while ($position < $wLen) {
						my ($x, $y) = getPointOfWay (\@points, $position) ;
						# print "XY: $x, $y\n" ;

						# place shield if not occupied
			
						my $x2 = int ($x - $shieldXSize{ $name } / 2) ;
						my $y2 = int ($y - $shieldYSize{ $name } / 2) ;

						# print "AREA: $x2, $y2, $x2+$lLen, $y2+$lLen\n" ;

						if ( ! areaOccupied ($x2, $x2+$shieldXSize{ $name }, $y2+$shieldYSize{ $name }, $y2) ) {

							my $id = $createdShields{$name};
							push @svgOutputIcons, "<use xlink:href=\"#$id\" x=\"$x2\" y=\"$y2\" />" ;

							occupyArea ($x2, $x2+$shieldXSize{ $name }, $y2+$shieldYSize{ $name }, $y2) ;
						}

						$position += $step ;
					}
				}

			}

			else {

				# print "$wLen - $name - $lLen\n" ;
				my $numLabels = int ($wLen / (4 * $lLen)) ;
				if ($numLabels < 1) { $numLabels = 1 ; }
				if ($numLabels > 4) { $numLabels = 4 ; }

				if ($numLabels == 1) {
					my $spare = 0.95 * $wLen - $lLen ;
					my $sparePercentHalf = $spare / ($wLen*0.95) *100 / 2 ;
					my $startOffset = 50 - $sparePercentHalf ;
					my $endOffset = 50 + $sparePercentHalf ;
					# five possible positions per way
					my $step = ($endOffset - $startOffset) / 5 ;
					my @positions = () ;
					my $actual = $startOffset ;
					while ($actual <= $endOffset) {
						my ($ref, $angle) = subWay (\@points, $lLen, "middle", $actual) ;
						my @way = @$ref ;
						my ($col) = lineCrossings (\@way) ;
						# calc quality of position. distance from middle and bend angles
						my $quality = $angle + abs (50 - $actual) ;
						if ($col == 0) { push @positions, ["middle", $actual, $quality] ; }
						$actual += $step ;
					}
					if (scalar @positions > 0) {
						$drawnLabels { $name } = 1 ;
						# sort by quality and take best one
						@positions = sort {$a->[2] <=> $b->[2]} @positions ;
						my ($pos) = shift @positions ;
						my ($ref, $angle) = subWay (\@points, $lLen, $pos->[0], $pos->[1]) ;
						my @finalWay = @$ref ;
						my $pathName = "Path" . $pathNumber ; $pathNumber++ ;
						push @svgOutputDef, svgElementPath ($pathName, @points) ;
						push @svgOutputPathText, svgElementPathTextAdvanced ($ruleData[$wayIndexLabelColor], $ruleData[$wayIndexLabelSize], 
							$ruleData[$wayIndexLabelFont], $name, $pathName, $ruleData[$wayIndexLabelOffset], $pos->[0], $pos->[1], $halo) ;
						occupyLines (\@finalWay) ;
					}
					else {
						$numWayLabelsOmitted++ ;
					}
				}
				else { # more than one label
					my $labelDrawn = 0 ;
					my $interval = int (100 / ($numLabels + 1)) ;
					my @positions = () ;
					for (my $i=1; $i<=$numLabels; $i++) {
						push @positions, $i * $interval ;
					}
			
					foreach my $position (@positions) {
						my ($refFinal, $angle) = subWay (\@points, $lLen, "middle", $position) ;
						my (@finalWay) = @$refFinal ;
						my ($collision) = lineCrossings (\@finalWay) ;
						if ($collision == 0) {
							$labelDrawn = 1 ;
							$drawnLabels { $name } = 1 ;
							my $pathName = "Path" . $pathNumber ; $pathNumber++ ;
							push @svgOutputDef, svgElementPath ($pathName, @finalWay) ;
							push @svgOutputPathText, svgElementPathTextAdvanced ($ruleData[$wayIndexLabelColor], $ruleData[$wayIndexLabelSize], 
								$ruleData[$wayIndexLabelFont], $name, $pathName, $ruleData[$wayIndexLabelOffset], "middle", 50, $halo) ;
							occupyLines (\@finalWay) ;
						}
						else {
							# print "INFO: $name labeled less often than desired.\n" ;
						}
					}
					if ($labelDrawn == 0) {
						$notDrawnLabels { $name } = 1 ;
					}
				}
			}
		}
	}
	my $labelFileName = $svgName ;
	$labelFileName =~ s/\.svg/_NotDrawnLabels.txt/ ;
	my $labelFile ;
	open ($labelFile, ">", $labelFileName) or die ("couldn't open label file $labelFileName") ;
	print $labelFile "Not drawn labels\n\n" ;
	foreach my $labelName (sort keys %notDrawnLabels) {
		if (!defined $drawnLabels { $labelName } ) {
			print $labelFile "$labelName\n" ;
		}
	}
	close ($labelFile) ;

}


sub occupyLines {
#
# store drawn lines and make quad tree entries
# accepts multiple coordinates that form a way
#
	my ($ref) = shift ;
	my @coordinates = @$ref ;

	for (my $i=0; $i<$#coordinates-2; $i+=2) {
		push @lines, [$coordinates[$i], $coordinates[$i+1], $coordinates[$i+2], $coordinates[$i+3]] ;
		# print "PUSHED $coordinates[$i], $coordinates[$i+1], $coordinates[$i+2], $coordinates[$i+3]\n" ;
		# drawWayPix ("black", 1, 0, @coordinates)

		$qtWayLabels->add ($#lines, $coordinates[$i], $coordinates[$i+1], $coordinates[$i+2], $coordinates[$i+3]) ;

	}
}


sub lineCrossings {
#
# checks for line collisions
# accepts multiple lines in form of multiple coordinates
#
	my ($ref) = shift ;
	my @coordinates = @$ref ;
	my @testLines = () ;

	for (my $i=0; $i<$#coordinates-2; $i+=2) {
		push @testLines, [$coordinates[$i], $coordinates[$i+1], $coordinates[$i+2], $coordinates[$i+3]] ;
	}

	# find area of way
	my ($found) = 0 ;
	my $xMin = 999999 ; my $xMax = 0 ;
	my $yMin = 999999 ; my $yMax = 0 ;
	foreach my $l1 (@testLines) {
		if ($l1->[0] > $xMax) { $xMax = $l1->[0] ; }
		if ($l1->[0] < $xMin) { $xMin = $l1->[0] ; }
		if ($l1->[1] > $yMax) { $yMax = $l1->[1] ; }
		if ($l1->[1] < $yMin) { $yMin = $l1->[1] ; }
	}
	
	# get indexes from quad tree
	my $ref2 = $qtWayLabels->getEnclosedObjects ($xMin, $yMin, $xMax, $yMax) ;
	# create array linesInArea
	my @linesInAreaIndex = @$ref2 ;
	my @linesInArea = () ;
	foreach my $lineNr (@linesInAreaIndex) {
		push @linesInArea, $lines[$lineNr] ;
	} 

	LABCR: foreach my $l1 (@testLines) {
		foreach my $l2 (@linesInArea) {
			my ($x, $y) = intersection (@$l1, @$l2) ;
			if (($x !=0) and ($y != 0)) {
				$found = 1 ;
				last LABCR ;
			}
		}
	}
	if ($found == 0) {
		return 0 ;
	}
	else {
		return 1 ;
	}	
}

sub triangleNode {
#
# get segment of segment as coordinates
# from start or from end of segment
#
	# 0 = start
	# 1 = end
	my ($x1, $y1, $x2, $y2, $len, $startEnd) = @_ ;
	my ($c) = sqrt ( ($x2-$x1)**2 + ($y2-$y1)**2) ;
	my $percent = $len / $c ;

	my ($x, $y) ;
	if ($startEnd == 0 ) {	
		$x = $x1 + ($x2-$x1)*$percent ;
		$y = $y1 + ($y2-$y1)*$percent ;
	}
	else {
		$x = $x2 - ($x2-$x1)*$percent ;
		$y = $y2 - ($y2-$y1)*$percent ;
	}
	return ($x, $y) ;
}


sub subWay {
#
# takes coordinates and label information and creates new way/path
# also calculates total angles / bends
#
	my ($ref, $labLen, $alignment, $position) = @_ ;
	my @coordinates = @$ref ;
	my @points ;
	my @dists ;
	my @angles = () ;

	for (my $i=0; $i < $#coordinates; $i+=2) {
		push @points, [$coordinates[$i],$coordinates[$i+1]] ;
	}

	$dists[0] = 0 ;
	my $dist = 0 ;
	if (scalar @points > 1) {
		for (my $i=1;$i<=$#points; $i++) {
			$dist = $dist + sqrt ( ($points[$i-1]->[0]-$points[$i]->[0])**2 + ($points[$i-1]->[1]-$points[$i]->[1])**2 ) ;
			$dists[$i] = $dist ;
		}			
	}

	# calc angles at nodes
	if (scalar @points > 2) {
		for (my $i=1;$i<$#points; $i++) {
			$angles[$i] = angleMapgen ($points[$i-1]->[0], $points[$i-1]->[1], $points[$i]->[0], $points[$i]->[1], $points[$i]->[0], $points[$i]->[1], $points[$i+1]->[0], $points[$i+1]->[1]) ;
		}			
	}

	my $wayLength = $dist ;
	my $refPoint = $wayLength / 100 * $position ;
	my $labelStart ; my $labelEnd ;
	if ($alignment eq "start") { # left
		$labelStart = $refPoint ;
		$labelEnd = $labelStart + $labLen ;
	}
	if ($alignment eq "end") { # right
		$labelEnd = $refPoint ;
		$labelStart = $labelEnd - $labLen ;
	}
	if ($alignment eq "middle") { # center
		$labelEnd = $refPoint + $labLen / 2 ;
		$labelStart = $refPoint - $labLen / 2 ;
	}

	# find start and end segments
	my $startSeg ; my $endSeg ;
	for (my $i=0; $i<$#points; $i++) {
		if ( ($dists[$i]<=$labelStart) and ($dists[$i+1]>=$labelStart) ) { $startSeg = $i ; }
		if ( ($dists[$i]<=$labelEnd) and ($dists[$i+1]>=$labelEnd) ) { $endSeg = $i ; }
	}

	my @finalWay = () ;
	my $finalAngle = 0 ;
	my ($sx, $sy) = triangleNode ($coordinates[$startSeg*2], $coordinates[$startSeg*2+1], $coordinates[$startSeg*2+2], $coordinates[$startSeg*2+3], $labelStart-$dists[$startSeg], 0) ;
	push @finalWay, $sx, $sy ;

	if ($startSeg != $endSeg) {
		for (my $i=$startSeg+1; $i<=$endSeg; $i++) { 
			push @finalWay, $coordinates[$i*2], $coordinates[$i*2+1] ; 
			$finalAngle += abs ($angles[$i]) ;
		}
	}

	my ($ex, $ey) = triangleNode ($coordinates[$endSeg*2], $coordinates[$endSeg*2+1], $coordinates[$endSeg*2+2], $coordinates[$endSeg*2+3], $labelEnd-$dists[$endSeg], 0) ;
	push @finalWay, $ex, $ey ;
	
	return (\@finalWay, $finalAngle) ;	
}

sub intersection {
#
# returns intersection point of two lines, else (0,0)
#
	my ($g1x1) = shift ;
	my ($g1y1) = shift ;
	my ($g1x2) = shift ;
	my ($g1y2) = shift ;
	
	my ($g2x1) = shift ;
	my ($g2y1) = shift ;
	my ($g2x2) = shift ;
	my ($g2y2) = shift ;

	if (($g1x1 == $g2x1) and ($g1y1 == $g2y1)) { # p1 = p1 ?
		return ($g1x1, $g1y1) ;
	}
	if (($g1x1 == $g2x2) and ($g1y1 == $g2y2)) { # p1 = p2 ?
		return ($g1x1, $g1y1) ;
	}
	if (($g1x2 == $g2x1) and ($g1y2 == $g2y1)) { # p2 = p1 ?
		return ($g1x2, $g1y2) ;
	}

	if (($g1x2 == $g2x2) and ($g1y2 == $g2y2)) { # p2 = p1 ?
		return ($g1x2, $g1y2) ;
	}

	my $g1m ;
	if ( ($g1x2-$g1x1) != 0 )  {
		$g1m = ($g1y2-$g1y1)/($g1x2-$g1x1) ; # steigungen
	}
	else {
		$g1m = 999999 ;
	}

	my $g2m ;
	if ( ($g2x2-$g2x1) != 0 ) {
		$g2m = ($g2y2-$g2y1)/($g2x2-$g2x1) ;
	}
	else {
		$g2m = 999999 ;
	}

	if ($g1m == $g2m) {   # parallel
		return (0, 0) ;
	}

	my ($g1b) = $g1y1 - $g1m * $g1x1 ; # abschnitte
	my ($g2b) = $g2y1 - $g2m * $g2x1 ;

	my ($sx) = ($g2b-$g1b) / ($g1m-$g2m) ;             # schnittpunkt
	my ($sy) = ($g1m*$g2b - $g2m*$g1b) / ($g1m-$g2m);

	my ($g1xmax) = max ($g1x1, $g1x2) ;
	my ($g1xmin) = min ($g1x1, $g1x2) ;	
	my ($g1ymax) = max ($g1y1, $g1y2) ;	
	my ($g1ymin) = min ($g1y1, $g1y2) ;	

	my ($g2xmax) = max ($g2x1, $g2x2) ;
	my ($g2xmin) = min ($g2x1, $g2x2) ;	
	my ($g2ymax) = max ($g2y1, $g2y2) ;	
	my ($g2ymin) = min ($g2y1, $g2y2) ;	

	if 	(($sx >= $g1xmin) and
		($sx >= $g2xmin) and
		($sx <= $g1xmax) and
		($sx <= $g2xmax) and
		($sy >= $g1ymin) and
		($sy >= $g2ymin) and
		($sy <= $g1ymax) and
		($sy <= $g2ymax)) {
		return ($sx, $sy) ;
	}
	else {
		return (0, 0) ;
	}
} 

sub angleMapgen {
#
# angle between lines/segments
#
	my ($g1x1) = shift ;
	my ($g1y1) = shift ;
	my ($g1x2) = shift ;
	my ($g1y2) = shift ;
	my ($g2x1) = shift ;
	my ($g2y1) = shift ;
	my ($g2x2) = shift ;
	my ($g2y2) = shift ;

	my $g1m ;
	if ( ($g1x2-$g1x1) != 0 )  {
		$g1m = ($g1y2-$g1y1)/($g1x2-$g1x1) ; # steigungen
	}
	else {
		$g1m = 999999999 ;
	}

	my $g2m ;
	if ( ($g2x2-$g2x1) != 0 ) {
		$g2m = ($g2y2-$g2y1)/($g2x2-$g2x1) ;
	}
	else {
		$g2m = 999999999 ;
	}

	if ($g1m == $g2m) {   # parallel
		return (0) ;
	}
	else {
		my $t1 = $g1m -$g2m ;
		my $t2 = 1 + $g1m * $g2m ;
		if ($t2 == 0) {
			return 90 ;
		}
		else {
			my $a = atan (abs ($t1/$t2)) / 3.141592654 * 180 ;
			return $a ;
		}
	}
} 


#------------------------------------------------------------------------------------------------------------


sub drawArea {
#
# draws an area like waterway=riverbank or landuse=forest. 
# pass color as string and nodes as list (x1, y1, x2, y2...) - real world coordinates
#
	my ($col, $icon, @nodes) = @_ ;
	my $i ;
	my @points = () ;
	
	for ($i=0; $i<$#nodes; $i+=2) {
		my ($x1, $y1) = convert ($nodes[$i], $nodes[$i+1]) ;
		push @points, $x1 ; push @points, $y1 ; 
	}
	push @svgOutputAreas, svgElementPolygonFilled ($col, $icon, @points) ;
}

sub drawAreaPix {
#
# draws an area like waterway=riverbank or landuse=forest. 
# pass color as string and nodes as list (x1, y1, x2, y2...) - pixels
# used for legend
#
	my ($col, $icon, @nodes) = @_ ;
	my $i ;
	my @points = () ;
	for ($i=0; $i<$#nodes; $i+=2) {
		my ($x1, $y1) = ($nodes[$i], $nodes[$i+1]) ;
		push @points, $x1 ; push @points, $y1 ; 
	}
	push @svgOutputPixel, svgElementPolygonFilled ($col, $icon, @points) ;
}

sub drawAreaMP {
#
# draws an area like waterway=riverbank or landuse=forest. 
# pass color as string and nodes as list (x1, y1, x2, y2...) - real world coordinates
#
# receives ARRAY of ARRAY of NODES LIST! NOT coordinates list like other functions
#
	my ($col, $icon, $ref, $refLon, $refLat) = @_ ;
	# my %lon = %$refLon ;
	# my %lat = %$refLat ;
	my @ways = @$ref ;
	my $i ;
	my @array = () ;

	foreach my $way (@ways) {	
		my @actual = @$way ;
		# print "drawAreaMP - actual ring/way: @actual\n" ; 
			my @points = () ;
		for ($i=0; $i<$#actual; $i++) { # without last node! SVG command 'z'!
			my ($x1, $y1) = convert ( $$refLon{$actual[$i]}, $$refLat{$actual[$i]} ) ;
			push @points, $x1 ; push @points, $y1 ; 
		}
		push @array, [@points] ;
		# print "drawAreaMP - array pushed: @points\n" ; 
	}

	push @svgOutputAreas, svgElementMultiPolygonFilled ($col, $icon, \@array) ;
}



sub drawRuler {
#
# draws ruler in top right corner, size is automatic
#
	my $col = shift ;

	my $B ; my $B2 ;
	my $L ; my $Lpix ;
	my $x ;
	my $text ;
	my $rx = $sizeX - scalePoints (scaleBase (80)) ;
	my $ry = scalePoints (scaleBase (60)) ; #v1.17
	# my $ry = scalePoints (scaleBase (80)) ;
	my $lineThickness = 8 ; # at 300dpi
	my $textSize = 40 ; # at 300 dpi
	my $textDist = 60 ; # at 300 dpi
	my $lineLen = 40 ; # at 300 dpi
		
	$B = $right - $left ; 				# in degrees
	$B2 = $B * cos ($top/360*3.14*2) * 111.1 ;	# in km
	$text = "50m" ; $x = 0.05 ;			# default length ruler

	if ($B2 > 0.5) {$text = "100m" ; $x = 0.1 ; }	# enlarge ruler
	if ($B2 > 1) {$text = "500m" ; $x = 0.5 ; }	# enlarge ruler
	if ($B2 > 5) {$text = "1km" ; $x = 1 ; }
	if ($B2 > 10) {$text = "5km" ; $x = 5 ; }
	if ($B2 > 50) {$text = "10km" ; $x = 10 ; }
	$L = $x / (cos ($top/360*3.14*2) * 111.1 ) ;	# length ruler in km
	$Lpix = $L / $B * $sizeX ;			# length ruler in pixels

	push @svgOutputText, svgElementLine ($rx-$Lpix,$ry,$rx,$ry, $col, scalePoints( scaleBase ($lineThickness) ) ) ;
	push @svgOutputText, svgElementLine ($rx-$Lpix,$ry,$rx-$Lpix,$ry+scalePoints(scaleBase($lineLen)), $col, scalePoints( scaleBase ($lineThickness) ) ) ;
	push @svgOutputText, svgElementLine ($rx,$ry,$rx,$ry+scalePoints(scaleBase($lineLen)), $col, scalePoints( scaleBase ($lineThickness) )) ;
	push @svgOutputText, svgElementLine ($rx-$Lpix/2,$ry,$rx-$Lpix/2,$ry+scalePoints(scaleBase($lineLen/2)), $col, scalePoints( scaleBase ($lineThickness) ) ) ;
	push @svgOutputText, svgElementText ($rx-$Lpix, $ry+scalePoints(scaleBase($textDist)), $text, scalePoints(scaleBase($textSize)), "sans-serif", $col) ;
}

sub drawGrid {
#
# draw grid on top of map. receives number of parts in x/lon direction
#
	my ($number, $color) = @_ ;
	my $part = $sizeX / $number ;
	my $numY = $sizeY / $part ;
	# vertical lines
	for (my $i = 1; $i <= $number; $i++) {
		drawWayPixGrid ($color, 1, $dashStyle{1}, $i*$part, 0, $i*$part, $sizeY) ;
		drawTextPixGrid (($i-1)*$part+$part/2, scalePoints(scaleBase(160)), chr($i+64), $color, scalePoints(scaleBase(60))) ;
	}
	# hor. lines
	for (my $i = 1; $i <= $numY; $i++) {
		drawWayPixGrid ($color, 1, $dashStyle{1}, 0, $i*$part, $sizeX, $i*$part) ;
		drawTextPixGrid (scalePoints(scaleBase(20)), ($i-1)*$part+$part/2, $i, $color, scalePoints(scaleBase(60))) ;
	}
}



#####
# SVG
#####


sub writeSVG {
#
# writes svg elemets collected so far to file
#
	my ($fileName) = shift ;
	my $file ;
	my ($paper, $w, $h) = fitsPaper ($dpi) ;

	open ($file, ">", $fileName) || die "can't open svg output file";
	print $file "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"no\"?>\n" ;
	print $file "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\" >\n" ;

	my ($svg) = "<svg version=\"1.1\" baseProfile=\"full\" xmlns=\"http://www.w3.org/2000/svg\" " ;
	$svg .= "xmlns:xlink=\"http://www.w3.org/1999/xlink\" xmlns:ev=\"http://www.w3.org/2001/xml-events\" " ;
	$svg .= "width=\"$w" . "cm\" height=\"$h" . "cm\" viewBox=\"0 0 $sizeX $sizeY\">\n" ;
	print $file $svg ;

	print $file "<rect width=\"$sizeX\" height=\"$sizeY\" y=\"0\" x=\"0\" fill=\"#ffffff\" />\n" ;

	print $file "<defs>\n" ;
	foreach (@svgOutputDef) { print $file $_, "\n" ; }
	print $file "</defs>\n" ;

	print $file "<g id=\"Areas\">\n" ;
	foreach (@svgOutputAreas) { print $file $_, "\n" ; }
	print $file "</g>\n" ;

	print $file "<g id=\"Ways\">\n" ;
	foreach my $layer (sort {$a <=> $b} (keys %svgOutputWays)) {
		foreach (@{$svgOutputWays{$layer}}) { print $file $_, "\n" ; }
	}
	print $file "</g>\n" ;

	print $file "<g id=\"Nodes\">\n" ;
	foreach my $layer (sort {$a <=> $b} (keys %svgOutputNodes)) {
		foreach (@{$svgOutputNodes{$layer}}) { print $file $_, "\n" ; }
	}
	print $file "</g>\n" ;


	print $file "<g id=\"Routes\">\n" ;
	foreach (@svgOutputRoutes) { print $file $_, "\n" ; }
	print $file "</g>\n" ;

	print $file "<g id=\"RouteStops\">\n" ;
	foreach (@svgOutputRouteStops) { print $file $_, "\n" ; }
	print $file "</g>\n" ;

	print $file "<g id=\"Text\">\n" ;
	foreach (@svgOutputText) { print $file $_, "\n" ; }
	print $file "</g>\n" ;

	print $file "<g id=\"Icons\">\n" ;
	foreach (@svgOutputIcons) { print $file $_, "\n" ; }
	print $file "</g>\n" ;

	print $file "<g id=\"Labels\">\n" ;
	foreach (@svgOutputPathText) { print $file $_, "\n" ; }
	print $file "</g>\n" ;

	print $file "<g id=\"Grid\">\n" ;
	foreach (@svgOutputPixelGrid) { print $file $_, "\n" ; }
	print $file "</g>\n" ;

	print $file "<g id=\"Pixels\">\n" ;
	foreach (@svgOutputPixel) { print $file $_, "\n" ; }
	print $file "</g>\n" ;

	print $file "</svg>\n" ;
	close ($file) ;
}

sub svgElementText {
#
# creates string with svg element incl utf-8 encoding
#
	my ($x, $y, $text, $size, $font, $col) = @_ ; 
	my $svg = "<text x=\"" . $x . "\" y=\"" . $y . 
		"\" font-size=\"" . $size . 
		"\" font-family=\"" . $font . 
		"\" fill=\"" . $col . 
		"\">" . $text . "</text>" ;
	return $svg ;
}

sub svgElementCircleFilled {
#
# draws circle filled
#
	my ($x, $y, $size, $col) = @_ ;
	my $svg = "<circle cx=\"" . $x . "\" cy=\"" . $y . "\" r=\"" . $size . "\" fill=\"" . $col  . "\" />" ;
	return $svg ;
}

sub svgElementCircle {
#
# draws not filled circle / dot
#
	my ($x, $y, $radius, $size, $col) = @_ ;
	my $svg = "<circle cx=\"" . $x . "\" cy=\"" . $y . "\" r=\"" . $radius . "\" fill=\"none\" stroke=\"" . $col  . "\" stroke-width=\"$size\" />" ;
	return $svg ;
}

sub svgElementLine {
#
# draws line between two points
#
	my ($x1, $y1, $x2, $y2, $col, $size) = @_ ;
	my $svg = "<polyline points=\"" . $x1 . "," . $y1 . " " . $x2 . "," . $y2 . "\" stroke=\"" . $col . "\" stroke-width=\"" . $size . "\"/>" ;
	return $svg ;
}




sub svgElementPolyline {
#
# draws way to svg
#
	my ($col, $size, $dash, @points) = @_ ;

	my $refp = simplifyPoints (\@points) ;
	@points = @$refp ;


	my $svg = "<polyline points=\"" ;
	my $i ;
	for ($i=0; $i<scalar(@points)-1; $i+=2) {
		$svg = $svg . $points[$i] . "," . $points[$i+1] . " " ;
	}
	if ($dash eq "none") { 
		my $lc = "round" ;
		$svg = $svg . "\" stroke=\"" . $col . "\" stroke-width=\"" . $size . "\" stroke-linecap=\"" . $lc . "\" stroke-linejoin=\"" . $lineJoin . "\" fill=\"none\" />" ;
	}
	else {
		my $lc = "" ; my $ds = "" ;
		($lc, $ds) = getDashElements ($dash) ;
		$svg = $svg . "\" stroke=\"" . $col . "\" stroke-width=\"" . $size . "\" stroke-linecap=\"" . $lc . "\" stroke-linejoin=\"" . $lineJoin . "\" stroke-dasharray=\"" . $ds . "\" fill=\"none\" />" ;
	}
	return $svg ;
}


sub svgElementPolylineBridge {
#
# draws way to svg
#
	my ($col, $size, $dash, @points) = @_ ;

	my $refp = simplifyPoints (\@points) ;
	@points = @$refp ;

	my $svg = "<polyline points=\"" ;
	my $i ;
	for ($i=0; $i<scalar(@points)-1; $i+=2) {
		$svg = $svg . $points[$i] . "," . $points[$i+1] . " " ;
	}
	if ($dash eq "none") { 
		$svg = $svg . "\" stroke=\"" . $col . "\" stroke-width=\"" . $size . "\" fill=\"none\" />" ;
	}
	else {
		my $lc = "" ; my $ds ;
		($lc, $ds) = getDashElements ($dash) ;
		$svg = $svg . "\" stroke=\"" . $col . "\" stroke-width=\"" . $size . "\" stroke-linecap=\"" . $lc . "\" stroke-dasharray=\"" . $ds . "\" fill=\"none\" />" ;
	}
	return $svg ;
}



sub getDashElements {
	my $string = shift ;
	my @a = split /,/, $string ;
	my $cap = pop @a ;
	my $ds = "" ; my $first = 1 ;
	foreach my $v (@a) {
		if ($first) {
			$first = 0 ;
		}
		else {
			$ds .= "," ;
		}
		$ds .= $v ;
	}
	# print "GETDE $cap, $ds\n" ;
	return ($cap, $ds) ;
}



sub svgElementPath {
#
# creates path element for later use with textPath
#
	my ($pathName, @points) = @_ ;

	my $refp = simplifyPoints (\@points) ;
	@points = @$refp ;

	my $svg = "<path id=\"" . $pathName . "\" d=\"M " ;
	my $i ;
	my $first = 1 ;
	for ($i=0; $i<scalar(@points); $i+=2) {
		if ($first) {
			$svg = $svg . $points[$i] . "," . $points[$i+1] . " " ;
			$first = 0 ;
		}
		else {
			$svg = $svg . "L " . $points[$i] . "," . $points[$i+1] . " " ;
		}
	}
	$svg = $svg . "\" />\n" ;
}


sub svgElementPathTextAdvanced {
#
# draws text to path element; anchors: start, middle, end
#
	my ($col, $size, $font, $text, $pathName, $tSpan, $alignment, $offset, $halo) = @_ ;

	my $svg = "<text font-family=\"" . $font . "\" " ;
	$svg = $svg . "font-size=\"" . $size . "\" " ;

	if ($halo > 0) {
		$svg = $svg . "font-weight=\"bold\" " ;
		$svg = $svg . "stroke=\"white\" " ;
		$svg = $svg . "stroke-width=\"" . $halo . "\" " ;
		$svg = $svg . "opacity=\"90\%\" " ;
	}

	$svg = $svg . "fill=\"" . $col . "\" >\n" ;
	$svg = $svg . "<textPath xlink:href=\"#" . $pathName . "\" text-anchor=\"" . $alignment . "\" startOffset=\"" . $offset . "%\" >\n" ;
	$svg = $svg . "<tspan dy=\"" . $tSpan . "\" >" . $text . " </tspan>\n" ;
	$svg = $svg . "</textPath>\n</text>\n" ;
	return $svg ;
}


sub svgElementPolygonFilled {
#
# draws areas in svg, filled with color 
#
	my ($col, $icon, @points) = @_ ;

	my $refp = simplifyPoints (\@points) ;
	@points = @$refp ;

	my $i ;
	my $svg ;
	if (defined $areaDef{$icon}) {
		$svg = "<path fill-rule=\"evenodd\" style=\"fill:url(" . $areaDef{$icon} . ")\" d=\"" ;
		# print "AREA POLYGON with icon $icon drawn\n" ;
	}
	else {
		$svg = "<path fill-rule=\"evenodd\" fill=\"" . $col . "\" d=\"" ;
	}


	for ($i=0; $i<scalar(@points); $i+=2) {
		if ($i == 0) { $svg .= " M " ; } else { $svg .= " L " ; }
		$svg = $svg . $points[$i] . " " . $points[$i+1] ;
	}
	$svg .= " z" ;




#	for ($i=0; $i<scalar(@points); $i+=2) {
#		$svg = $svg . $points[$i] . "," . $points[$i+1] . " " ;
#	}
	$svg = $svg . "\" />" ;
	return $svg ;
}

sub svgElementMultiPolygonFilled {
#
# draws mp in svg, filled with color. accepts holes. receives ARRAY of ARRAY of coordinates
#
	my ($col, $icon, $ref) = @_ ;

	my @ways = @$ref ;
	my $i ;
	my $svg ;
	if (defined $areaDef{$icon}) {
		$svg = "<path fill-rule=\"evenodd\" style=\"fill:url(" . $areaDef{$icon} . ")\" d=\"" ;
		# print "AREA PATH with icon $icon drawn\n" ;
	}
	else {
		$svg = "<path fill-rule=\"evenodd\" fill=\"" . $col . "\" d=\"" ;
	}
	
	foreach my $way (@ways) {
		my @actual = @$way ;
		# print "svg - actual: @actual\n" ;
		for ($i=0; $i<scalar(@actual); $i+=2) {
			if ($i == 0) { $svg .= " M " ; } else { $svg .= " L " ; }
			$svg = $svg . $actual[$i] . " " . $actual[$i+1] ;
		}
		$svg .= " z" ;
		# print "svg - text = $svg\n" ; 
	}

	$svg = $svg . "\" />" ;
	# print "svg - text = $svg\n" ; 
	return $svg ;
}

sub createLabel {
#
# takes @tags and labelKey(s) from style file and creates labelTextTotal and array of labels for directory
# takes more keys in one string - using a separator. 
#
# § all listed keys will be searched for and values be concatenated
# # first of found keys will be used to select value
# "name§ref" will return all values if given
# "name#ref" will return name, if given. if no name is given, ref will be used. none given, no text
#
	my ($ref1, $styleLabelText, $lon, $lat) = @_ ;
	my @tags = @$ref1 ;
	my @keys ;
	my @labels = () ;
	my $labelTextTotal = "" ; 

	if (grep /!/, $styleLabelText) { # AND
		@keys = split ( /!/, $styleLabelText) ;
		# print "par found: $styleLabelText; @keys\n" ;
		for (my $i=0; $i<=$#keys; $i++) {
			if ($keys[$i] eq "_lat") { push @labels, $lat ; } 
			if ($keys[$i] eq "_lon") { push @labels, $lon ; } 
			foreach my $tag (@tags) {
				if ($tag->[0] eq $keys[$i]) {
					push @labels, $tag->[1] ;
				}
			}
		}
		$labelTextTotal = "" ;
		foreach my $label (@labels) { $labelTextTotal .= $label . " " ; }
	}
	else { # PRIO
		@keys = split ( /#/, $styleLabelText) ;
		my $i = 0 ; my $found = 0 ;
		while ( ($i<=$#keys) and ($found == 0) ) {
			if ($keys[$i] eq "_lat") { push @labels, $lat ; $found = 1 ; $labelTextTotal = $lat ; } 
			if ($keys[$i] eq "_lon") { push @labels, $lon ; $found = 1 ; $labelTextTotal = $lon ; } 
			foreach my $tag (@tags) {
				if ($tag->[0] eq $keys[$i]) {
					push @labels, $tag->[1] ;
					$labelTextTotal = $tag->[1] ;
					$found = 1 ;
				}
			}
			$i++ ;
		}		
	}
	return ( $labelTextTotal, \@labels) ;
}

sub center {
#
# calculate center of area by averageing lons/lats. could be smarter because result could be outside of area! TODO
#
	my @nodes = @_ ;
	my $x = 0 ;
	my $y = 0 ;
	my $num = 0 ;

	while (scalar @nodes > 0) { 
		my $y1 = pop @nodes ;
		my $x1 = pop @nodes ;
		$x += $x1 ;
		$y += $y1 ;
		$num++ ;
	}
	$x = $x / $num ;
	$y = $y / $num ;
	return ($x, $y) ;
}

sub printScale {
#
# print scale based on dpi and global variables left, right etc.
#
	my ($dpi, $color) = @_ ;

	my $dist = distance ($left, $bottom, $right, $bottom) ;
	my $inches = $sizeX / $dpi ;
	my $cm = $inches * 2.54 ;
	my $scale = int ( $dist / ($cm/100/1000)  ) ;
	$scale = int ($scale / 100) * 100 ;
	my $text = "1 : $scale" ;
	# sizes for 300 dpi
	my $posX = 350 ;
	my $posY = 50 ;
	my $size = 56 ;
	drawTextPix (
		$sizeX-scalePoints( scaleBase($posX) ), 
		scalePoints( scaleBase($posY) ), 
		$text, $color, 
		scalePoints( scaleBase ($size) ), "sans-serif"
	) ;
}


sub getScale {
#
# calcs scale of map
#
	my ($dpi) = shift ;

	my $dist = distance ($left, $bottom, $right, $bottom) ;
	my $inches = $sizeX / $dpi ;
	my $cm = $inches * 2.54 ;
	my $scale = int ( $dist / ($cm/100/1000)  ) ;
	$scale = int ($scale / 100) * 100 ;

	return ($scale) ;
}

sub fitsPaper {
#
# takes dpi and calculates on what paper size the map will fit. sizes are taken from global variables
#
	my ($dpi) = shift ;



	my @sizes = () ;
	my $width = $sizeX / $dpi * 2.54 ;
	my $height = $sizeY / $dpi * 2.54 ;
	my $paper = "" ;
	push @sizes, ["4A0", 168.2, 237.8] ;
	push @sizes, ["2A0", 118.9, 168.2] ;
	push @sizes, ["A0", 84.1, 118.9] ;
	push @sizes, ["A1", 59.4, 84.1] ;
	push @sizes, ["A2", 42, 59.4] ;
	push @sizes, ["A3", 29.7, 42] ;
	push @sizes, ["A4", 21, 29.7] ;
	push @sizes, ["A5", 14.8, 21] ;
	push @sizes, ["A6", 10.5, 14.8] ;
	push @sizes, ["A7", 7.4, 10.5] ;
	push @sizes, ["none", 0, 0] ;

	foreach my $size (@sizes) {
		if ( ( ($width<=$size->[1]) and ($height<=$size->[2]) ) or ( ($width<=$size->[2]) and ($height<=$size->[1]) ) ) {
			$paper = $size->[0] ;
		}
	}

	return ($paper, $width, $height) ;
}




sub drawCoords {
#
# draws coordinates grid on map
#
	my ($exp, $color) = @_ ;
	my $step = 10 ** $exp ;

	# vert. lines
	my $start = int ($left / $step) + 1 ;
	my $actual = $start * $step ;
	while ($actual < $right) {
		# print "actualX: $actual\n" ;
		my ($x1, $y1) = convert ($actual, 0) ;
		drawTextPixGrid ($x1+scalePoints(scaleBase(10)), $sizeY-scalePoints(scaleBase(50)), $actual, $color, scalePoints(scaleBase(40))) ;
		drawWayPixGrid ($color, 1, "none", ($x1, 0, $x1, $sizeY) ) ;
		$actual += $step ;
	}

	# hor lines
	$start = int ($bottom / $step) + 1 ;
	$actual = $start * $step ;
	while ($actual < $top) {
		# print "actualY: $actual\n" ;
		my ($x1, $y1) = convert (0, $actual) ;
		drawTextPixGrid ($sizeX-scalePoints(scaleBase(180)), $y1+scalePoints(scaleBase(30)), $actual, $color, scalePoints(scaleBase(40))) ;
		drawWayPixGrid ($color, 1, "none", (0, $y1, $sizeX, $y1) ) ;
		$actual += $step ;
	}
}


sub getValue {
#
# gets value of a certain tag
#
	my ($key, $ref) = @_ ;
	my @relationTags = @$ref ;

	my $value = "" ;
	foreach my $tag (@relationTags) {
		if ($tag->[0] eq $key) { $value = $tag->[1] ; }
	}
	return ($value) ;
}


sub drawWayRoute {
#
# draws way as a line at given real world coordinates. nodes have to be passed as array ($lon, $lat, $lon, $lat...)
# $size = thickness
#
	my ($col, $size, $dash, $opacity, @nodes) = @_ ;
	my $i ;
	my @points = () ;

	for ($i=0; $i<$#nodes; $i+=2) {
		my ($x, $y) = convert ($nodes[$i], $nodes[$i+1]) ;
		push @points, $x ; push @points, $y ; 
	}
	push @svgOutputRoutes, svgElementPolylineOpacity ($col, $size, $dash, $opacity, @points) ;
}


sub svgElementPolylineOpacity {
#
# draws way to svg with opacity; for routes
#
	my ($col, $size, $dash, $opacity, @points) = @_ ;

	my $refp = simplifyPoints (\@points) ;
	@points = @$refp ;


	my $svg = "<polyline points=\"" ;
	my $i ;
	for ($i=0; $i<scalar(@points)-1; $i+=2) {
		$svg = $svg . $points[$i] . "," . $points[$i+1] . " " ;
	}
	if ($dash eq "none") { 
		my $lc = "round" ;
		$svg = $svg . "\" stroke=\"" . $col . 
			"\" stroke-width=\"" . $size . 
			"\" stroke-opacity=\"" . $opacity . 
			"\" stroke-linecap=\"" . $lc . 
			"\" stroke-linejoin=\"" . $lineJoin . "\" fill=\"none\" />" ;
	}
	else {
		my $lc = "" ; my $ds = "" ;
		($lc, $ds) = getDashElements ($dash) ;
		$svg = $svg . "\" stroke=\"" . $col . 
			"\" stroke-width=\"" . $size . 
			"\" stroke-opacity=\"" . $opacity . 
			"\" stroke-linecap=\"" . $lc . 
			"\" stroke-linejoin=\"" . $lineJoin . 
			"\" stroke-dasharray=\"" . $ds . 
			"\" fill=\"none\" />" ;
	}
	return $svg ;
}


sub addAreaIcon {
#
# initial collection of area icons 
#
	my $fileNameOriginal = shift ;
	# print "AREA: $fileNameOriginal\n" ;
	my $result = open (my $file, "<", $fileNameOriginal) ;
	close ($file) ;
	if ($result) {
		my ($x, $y) ;
		if (grep /.svg/, $fileNameOriginal) {
			($x, $y) = sizeSVG ($fileNameOriginal) ;
			if ( ($x == 0) or ($y == 0) ) { 
				$x = 32 ; $y = 32 ; 
				print "WARNING: size of file $fileNameOriginal could not be determined. Set to 32px x 32px\n" ;
			} 
		}

		if (grep /.png/, $fileNameOriginal) {
			($x, $y) = sizePNG ($fileNameOriginal) ;
		}

		if (!defined $areaDef{$fileNameOriginal}) {

			my $x1 = scalePoints( $x ) ; # scale area icons 
			my $y1 = scalePoints( $y ) ;
			my $fx = $x1 / $x ;
			my $fy = $y1 / $y ;
			
			# add defs to svg output
			my $defName = "A" . $areaNum ;
			# print "INFO area icon $fileNameOriginal, $defName, $x, $y --- $x1, $y1 --- $fx, $fy --- processed.\n" ;
			$areaNum++ ;

			my $svgElement = "<pattern id=\"" . $defName . "\" width=\"" . $x . "\" height=\"" . $y . "\" " ;
			$svgElement .= "patternTransform=\"translate(0,0) scale(" . $fx . "," . $fy . ")\" \n" ;
			$svgElement .= "patternUnits=\"userSpaceOnUse\">\n" ;
			$svgElement .= "  <image xlink:href=\"" . $fileNameOriginal . "\"/>\n" ;
			$svgElement .= "</pattern>\n" ;
			push @svgOutputDef, $svgElement ;
			$defName = "#" . $defName ;
			$areaDef{$fileNameOriginal} = $defName ;
		}
	}
	else {
		print "WARNING: area icon $fileNameOriginal not found!\n" ;
	}
}




sub svgEle {
#
# creates svg element string
#
	my ($a, $b) = @_ ;
	my $out = $a . "=\"" . $b . "\" " ;
	return ($out)
}



sub initOneways {
#
# write marker defs to svg 
#
	my $color = shift ;
	my $markerSize = scalePoints (scaleBase (20)) ;

	push @svgOutputDef, "<marker id=\"Arrow1\"" ;
	push @svgOutputDef, "viewBox=\"0 0 10 10\" refX=\"5\" refY=\"5\"" ;
	push @svgOutputDef, "markerUnits=\"strokeWidth\"" ;
	push @svgOutputDef, "markerWidth=\"" . $markerSize . "\" markerHeight=\"" . $markerSize . "\"" ;
	push @svgOutputDef, "orient=\"auto\">" ;
	push @svgOutputDef, "<path d=\"M 0 4 L 6 4 L 6 2 L 10 5 L 6 8 L 6 6 L 0 6 Z\" fill=\"" . $color .  "\" />" ;
	push @svgOutputDef, "</marker>" ;
}


sub addOnewayArrows {
#
# adds oneway arrows to new pathes
#
	my ($wayNodesRef, $lonRef, $latRef, $direction, $thickness, $color, $layer) = @_ ;
	my @wayNodes = @$wayNodesRef ;
	my $minDist = scalePoints(scaleBase(25)) ;
	# print "OW: mindist = $minDist\n" ;

	if ($direction == -1) { @wayNodes = reverse @wayNodes ; }

	# create new pathes with new nodes
	for (my $i=0; $i<scalar(@wayNodes)-1;$i++) {
		my ($x1, $y1) = convert ($$lonRef{$wayNodes[$i]}, $$latRef{$wayNodes[$i]}) ;
		my ($x2, $y2) = convert ($$lonRef{$wayNodes[$i+1]}, $$latRef{$wayNodes[$i+1]}) ;
		my $xn = ($x2+$x1) / 2 ;
		my $yn = ($y2+$y1) / 2 ;
		if (sqrt (($x2-$x1)**2+($y2-$y1)**2) > $minDist) {
			# create path
			# use path
			my $svg = "<path d=\"M $x1 $y1 L $xn $yn L $x2 $y2\" fill=\"none\" marker-mid=\"url(#Arrow1)\" />" ;
			
			push @{$svgOutputWays{$layer+$thickness/100}}, $svg ;
		}
	}
}

sub declutterStat {
#
# creates print string with clutter/declutter information
#
	my $perc1 ;
	my $perc2 ;
	my $perc3 ;
	my $perc4 ;
	if ($numIcons != 0) {
		$perc1 = int ($numIconsMoved / $numIcons * 100) ;
		$perc2 = int ($numIconsOmitted / $numIcons * 100) ;
	}
	else {
		$perc1 = 0 ;
		$perc2 = 0 ;
	}
	if ($numLabels != 0) {
		$perc3 = int ($numLabelsMoved / $numLabels * 100) ;
		$perc4 = int ($numLabelsOmitted / $numLabels * 100) ;
	}
	else {
		$perc3 = 0 ;
		$perc4 = 0 ;
	}

	my $out = "$numIcons icons drawn.\n" ; 
	$out .= "  $numIconsMoved moved. ($perc1 %)\n" ;
	$out .= "  $numIconsOmitted omitted (possibly with label!). ($perc2 %)\n" ;

	$out .= "$numLabels labels drawn.\n" ; 
	$out .= "  $numLabelsMoved moved. ($perc3 %)\n" ;
	$out .= "  $numLabelsOmitted omitted. ($perc4 %)\n\n" ;
	$out .= "$numWayLabelsOmitted way labels omitted because way was too short, collision or declutter.\n" ;


}

sub placeLabelAndIcon {
#
# intelligent icon and label placement alg.
#
	my ($lon, $lat, $offset, $thickness, $text, $color, $textSize, $font, $ppc, $icon, $iconSizeX, $iconSizeY, $allowIconMove, $halo) = @_ ;

	my ($x, $y) = convert ($lon, $lat) ; # center !
	$y = $y + $offset ;

	my ($ref) = splitLabel ($text) ;
	my (@lines) = @$ref ;
	my $numLines = scalar @lines ;
	my $maxTextLenPix = 0 ;
	my $orientation = "" ;
	my $lineDist = 2 ;
	my $tries = 0 ;

	foreach my $line (@lines) {
		my $len = length ($line) * $ppc / 10 * $textSize ; # in pixels
		if ($len > $maxTextLenPix) { $maxTextLenPix = $len ; }
	}
	my $spaceTextX = $maxTextLenPix ;
	my $spaceTextY = $numLines * ($lineDist+$textSize) ;


	if ($icon ne "none") {
		$numIcons++ ;
		# space for icon?
			my $sizeX1 = $iconSizeX ; if ($sizeX1 == 0) { $sizeX1 = 20 ; }
			my $sizeY1 = $iconSizeY ; if ($sizeY1 == 0) { $sizeY1 = 20 ; }
			my $iconX = $x - $sizeX1/2 ; # top left corner
			my $iconY = $y - $sizeY1/2 ; 

			my @shifts = (0) ;
			if ($allowIconMove eq "1") {
				@shifts = ( 0, scalePoints(scaleBase(-15)), scalePoints(scaleBase(15)) ) ;
			}
			my $posFound = 0 ; my $posCount = 0 ;
			LABAB: foreach my $xShift (@shifts) {
				foreach my $yShift (@shifts) {
					$posCount++ ;
					if ( ! areaOccupied ($iconX+$xShift, $iconX+$sizeX1+$xShift, $iconY+$sizeY1+$yShift, $iconY+$yShift) ) {
						push @svgOutputIcons, svgElementIcon ($iconX+$xShift, $iconY+$yShift, $icon, $sizeX1, $sizeY1) ;
						occupyArea ($iconX+$xShift, $iconX+$sizeX1+$xShift, $iconY+$sizeY1+$yShift, $iconY+$yShift) ;
						$posFound = 1 ;
						if ($posCount > 1) { $numIconsMoved++ ; }
						$iconX = $iconX + $xShift ; # for later use with label
						$iconY = $iconY + $yShift ;
						last LABAB ;
					}
				}
			}
			if ($posFound == 1) {

				# label text?
				if ($text ne "") {
					$numLabels++ ;


					$sizeX1 += 1 ; $sizeY1 += 1 ;

					my ($x1, $x2, $y1, $y2) ;
					# $x, $y centered 
					# yes, check if space for label, choose position, draw
					# no, count omitted text

					my @positions = () ; my $positionFound = 0 ;
					# pos 1 centered below
					$x1 = $x - $spaceTextX/2 ; $x2 = $x + $spaceTextX/2 ; $y1 = $y + $sizeY1/2 + $spaceTextY ; $y2 = $y + $sizeY1/2 ; $orientation = "centered" ; 
					push @positions, [$x1, $x2, $y1, $y2, $orientation] ;

					# pos 2/3 to the right, bottom, top
					$x1 = $x + $sizeX1/2 ; $x2 = $x + $sizeX1/2 + $spaceTextX ; $y1 = $y + $sizeY1/2 ; $y2 = $y1 - $spaceTextY ; $orientation = "left" ; 
					push @positions, [$x1, $x2, $y1, $y2, $orientation] ;
					$x1 = $x + $sizeX1/2 ; $x2 = $x + $sizeX1/2 + $spaceTextX ; $y2 = $y - $sizeY1/2 ; $y1 = $y2 + $spaceTextY ; $orientation = "left" ; 
					push @positions, [$x1, $x2, $y1, $y2, $orientation] ;

					# pos 4 centered upon
					$x1 = $x - $spaceTextX/2 ; $x2 = $x + $spaceTextX/2 ; $y1 = $y - $sizeY1/2 ; $y2 = $y - $sizeY1/2 - $spaceTextY ; $orientation = "centered" ; 
					push @positions, [$x1, $x2, $y1, $y2, $orientation] ;

					# pos 5/6 to the right, below and upon
					$x1 = $x + $sizeX1/2 ; $x2 = $x + $sizeX1/2 + $spaceTextX ; $y2 = $y + $sizeY1/2 ; $y1 = $y2 + $spaceTextY ; $orientation = "left" ; 
					push @positions, [$x1, $x2, $y1, $y2, $orientation] ;
					$x1 = $x + $sizeX1/2 ; $x2 = $x + $sizeX1/2 + $spaceTextX ; $y1 = $y - $sizeY1/2 ; $y2 = $y1 - $spaceTextY ; $orientation = "left" ; 
					push @positions, [$x1, $x2, $y1, $y2, $orientation] ;

					# left normal, bottom, top
					$x1 = $x - $sizeX1/2 - $spaceTextX ; $x2 = $x - $sizeX1/2 ; $y1 = $y + $sizeY1/2 ; $y2 = $y1 - $spaceTextY ; $orientation = "right" ; 
					push @positions, [$x1, $x2, $y1, $y2, $orientation] ;
					$x1 = $x - $sizeX1/2 - $spaceTextX ; $x2 = $x - $sizeX1/2 ; $y2 = $y - $sizeY1/2 ; $y1 = $y2 + $spaceTextY ; $orientation = "right" ; 
					push @positions, [$x1, $x2, $y1, $y2, $orientation] ;

					# left corners, bottom, top
					$x1 = $x - $sizeX1/2 - $spaceTextX ; $x2 = $x - $sizeX1/2 ; $y2 = $y + $sizeY1/2 ; $y1 = $y2 + $spaceTextY ; $orientation = "right" ; 
					push @positions, [$x1, $x2, $y1, $y2, $orientation] ;
					$x1 = $x - $sizeX1/2 - $spaceTextX ; $x2 = $x - $sizeX1/2 ; $y1 = $y - $sizeY1/2 ; $y2 = $y1 - $spaceTextY ; $orientation = "right" ; 
					push @positions, [$x1, $x2, $y1, $y2, $orientation] ;


					$tries = 0 ;
					LABB: foreach my $pos (@positions) {
						$tries++ ;
						$positionFound = checkAndDrawText ($pos->[0], $pos->[1], $pos->[2], $pos->[3], $pos->[4], $numLines, \@lines, $color, $textSize, $font, $lineDist, $halo) ;
						if ($positionFound == 1) {
							last LABB ;
						}
					}
					if ($positionFound == 0) { $numLabelsOmitted++ ; }
					if ($tries > 1) { $numLabelsMoved++ ; }
				}
			}
			else {
				# no, count omitted
				$numIconsOmitted++ ;
			}
	}
	else { # only text
		my ($x1, $x2, $y1, $y2) ;
		# x1, x2, y1, y2
		# left, right, bottom, top		
		# choose space for text, draw
		# count omitted

		$numLabels++ ;
		my @positions = () ;
		$x1 = $x + $thickness ; $x2 = $x + $thickness + $spaceTextX ; $y1 = $y ; $y2 = $y - $spaceTextY ; $orientation = "left" ; 
		push @positions, [$x1, $x2, $y1, $y2, $orientation] ;
		$x1 = $x + $thickness ; $x2 = $x + $thickness + $spaceTextX ; $y1 = $y + $spaceTextY ; $y2 = $y ; $orientation = "left" ; 
		push @positions, [$x1, $x2, $y1, $y2, $orientation] ;

		$x1 = $x - ($thickness + $spaceTextX) ; $x2 = $x - $thickness ; $y1 = $y ; $y2 = $y - $spaceTextY ; $orientation = "right" ; 
		push @positions, [$x1, $x2, $y1, $y2, $orientation] ;
		$x1 = $x - ($thickness + $spaceTextX) ; $x2 = $x - $thickness ; $y1 = $y ; $y2 = $y - $spaceTextY ; $orientation = "right" ; 
		push @positions, [$x1, $x2, $y1, $y2, $orientation] ;

		$x1 = $x - $spaceTextX/2 ; $x2 = $x + $spaceTextX/2 ; $y1 = $y - $thickness ; $y2 = $y - ($thickness + $spaceTextY) ; $orientation = "centered" ; 
		push @positions, [$x1, $x2, $y1, $y2, $orientation] ;
		$x1 = $x - $spaceTextX/2 ; $x2 = $x + $spaceTextX/2 ; $y1 = $y + $thickness + $spaceTextY ; $y2 = $y + $thickness ; $orientation = "centered" ; 
		push @positions, [$x1, $x2, $y1, $y2, $orientation] ;

		my $positionFound = 0 ;
		$tries = 0 ;
		LABA: foreach my $pos (@positions) {
			$tries++ ;
			# print "$lines[0]   $pos->[0], $pos->[1], $pos->[2], $pos->[3], $pos->[4], $numLines\n" ;
			$positionFound = checkAndDrawText ($pos->[0], $pos->[1], $pos->[2], $pos->[3], $pos->[4], $numLines, \@lines, $color, $textSize, $font, $lineDist, $halo) ;
			if ($positionFound == 1) {
				last LABA ;
			}
		}
		if ($positionFound == 0) { $numLabelsOmitted++ ; }
		if ($tries > 1) { $numLabelsMoved++ ; }
	}
}


sub checkAndDrawText {
#
# checks if area available and if so draws text
#
	my ($x1, $x2, $y1, $y2, $orientation, $numLines, $ref, $col, $size, $font, $lineDist, $halo) = @_ ;
	my @lines = @$ref ;

	if (!areaOccupied ($x1, $x2, $y1, $y2)) {

		for (my $i=0; $i<=$#lines; $i++) {
			my @points = ($x1, $y2+($i+1)*($size+$lineDist), $x2, $y2+($i+1)*($size+$lineDist)) ;
			my $pathName = "LabelPath" . $labelPathId ; 
			$labelPathId++ ;
			push @svgOutputDef, svgElementPath ($pathName, @points) ;
			if ($orientation eq "centered") {
				push @svgOutputPathText, svgElementPathTextAdvanced ($col, $size, $font, $lines[$i], $pathName, 0, "middle", 50, $halo) ;
			}
			if ($orientation eq "left") {
				push @svgOutputPathText, svgElementPathTextAdvanced ($col, $size, $font, $lines[$i], $pathName, 0, "start", 0, $halo) ;
			}
			if ($orientation eq "right") {
				push @svgOutputPathText, svgElementPathTextAdvanced ($col, $size, $font, $lines[$i], $pathName, 0, "end", 100, $halo) ;
			}
		}

		occupyArea ($x1, $x2, $y1, $y2) ;
		
		return (1) ;
	}
	else {
		return 0 ;
	}
}

sub getDimensions {
#
# returns dimensions of map
#
	return ($sizeX, $sizeY) ;
}



sub drawAreaOcean {
	my ($col, $ref) = @_ ;
	push @svgOutputAreas, svgElementMultiPolygonFilled ($col, "none", $ref) ;
}

sub sizePNG {
#
# evaluates size of png graphics
#
	my $fileName = shift ;

	my ($x, $y) ;
	my $file ;
	my $result = open ($file, "<", $fileName) ;
	if ($result) {
		my $pic = newFromPng GD::Image($file) ;
		($x, $y) = $pic->getBounds ;
		close ($file) ;
	}
	else {
		($x, $y) = (0, 0) ;
	}
	return ($x, $y) ;
}

sub sizeSVG {
#
# evaluates size of svg graphics
#
	my $fileName = shift ;
	my $file ;
	my ($x, $y) ; undef $x ; undef $y ;

	my $result = open ($file, "<", $fileName) ;
	if ($result) {
		my $line ;
		while ($line = <$file>) {
			my ($x1) = ( $line =~ /^.*width=\"([\d]+)px\"/ ) ; 
			my ($y1) = ( $line =~ /^.*height=\"([\d]+)px\"/ ) ;
			if (!defined $x1) {
				($x1) = ( $line =~ /^\s*width=\"([\d]+)\"/ ) ; 

			} 
			if (!defined $y1) {
				($y1) = ( $line =~ /^\s*height=\"([\d]+)\"/ ) ; 
			} 
			if (defined $x1) { $x = $x1 ; }
			if (defined $y1) { $y = $y1 ; }
		}
		close ($file) ;
	}

	if ( (!defined $x) or (!defined $y) ) { 
		$x = 0 ; $y = 0 ; 
		print "WARNING: size of file $fileName could not be determined.\n" ;
	} 
	return ($x, $y) ;
}

sub scalePoints {
	my $a = shift ;
	# my $b = $a ;
	my $b = $a / $baseDpi * $dpi ;

	return (int ($b*10)) / 10 ;
}


sub scaleBase {
#
# function scales sizes given in 300dpi to base dpi given in rules so texts in legend, ruler etc. will appear in same size
#
	my $a = shift ;
	my $b = $a / 300 * $baseDpi ;
	return $b ;
}

#-----------------------------------------------------------------------------

sub simplifyPoints {
	my $ref = shift ;
	my @points = @$ref ;
	my @newPoints ;
	my $maxIndex = $#points ;

	if (scalar @points > 4) {
		# push first
		push @newPoints, $points[0], $points[1] ;

		# push other
		for (my $i=2; $i <= $maxIndex; $i+=2) {
			$simplifyTotal++ ;
			if ( ($points[$i]==$points[$i-2]) and ($points[$i+1]==$points[$i-1]) ) {
				# same
				$simplified++ ;
			}
			else {
				push @newPoints, $points[$i], $points[$i+1] ;
			}
		}
		return (\@newPoints) ;
	}
	else {
		return ($ref) ;
	}

}

sub simplifiedPercent {
	return ( int ($simplified / $simplifyTotal * 100) ) ;
}

sub drawPageNumber {
	my ($size, $col, $num) = @_ ;
	my $x = $sizeX - scalePoints (scaleBase (80)) ;
	my $y = $sizeY - scalePoints (scaleBase (80)) ;
	drawTextPixGrid ($x, $y, $num, $col, scalePoints ( scaleBase ($size) ) ) ;
}

sub drawPageNumberLeft {
	my ($size, $col, $num) = @_ ;
	my $x = scalePoints (scaleBase (80)) ;
	my $y = $sizeY / 2 ;
	drawTextPixGrid ($x, $y, $num, $col, scalePoints ( scaleBase ($size) ) ) ;

}

sub drawPageNumberBottom {
	my ($size, $col, $num) = @_ ;
	my $x = $sizeX / 2 ;
	my $y = $sizeY - scalePoints (scaleBase (80)) ;
	drawTextPixGrid ($x, $y, $num, $col, scalePoints ( scaleBase ($size) ) ) ;

}

sub drawPageNumberRight {
	my ($size, $col, $num) = @_ ;
	my $x = $sizeX - scalePoints (scaleBase (80)) ;
	my $y = $sizeY / 2 ;
	drawTextPixGrid ($x, $y, $num, $col, scalePoints ( scaleBase ($size) ) ) ;

}

sub drawPageNumberTop {
	my ($size, $col, $num) = @_ ;
	my $x = $sizeX / 2 ;
	my $y = scalePoints (scaleBase (80)) ;
	drawTextPixGrid ($x, $y, $num, $col, scalePoints ( scaleBase ($size) ) ) ;

}


sub createShield {
	my ($name, $targetSize) = @_ ;
	my @a = split /:/, $name ;
	my $shieldFileName = $a[1] ;
	my $shieldText = $a[2] ;

	if (! defined $createdShields{$name}) {
		open (my $file, "<", $shieldFileName) or die ("ERROR: shield definition $shieldFileName not found.\n") ;
		my @defText = <$file> ;
		close ($file) ;

		# get size
		# calc scaling
		my $sizeX = 0 ;
		my $sizeY = 0 ;
		foreach my $line (@defText) {
			if (grep /<svg/, $line) {
				($sizeY) = ( $line =~ /height=\"(\d+)px\"/ ) ;
				($sizeX) = ( $line =~ /width=\"(\d+)px\"/ ) ;
				if ( (!defined $sizeX) or (!defined $sizeY) ) {
					die "ERROR: size of shield in $shieldFileName could not be determined.\n" ;
				}
			}
		}
		if ( ($sizeX == 0) or ($sizeY == 0) ) {
			die "ERROR: initial size of shield $shieldFileName could not be determined.\n" ;
		}

		my $scaleFactor = $targetSize / $sizeY ;
		# print "factor: $scaleFactor\n" ;

		$shieldXSize{ $name } = int ($sizeX * $scaleFactor) ;
		$shieldYSize{ $name } = int ($sizeY * $scaleFactor) ;

		$shieldPathId++ ;
		my $shieldPathName = "ShieldPath" . $shieldPathId ;
		my $shieldGroupName = "ShieldGroup" . $shieldPathId ;

		foreach my $line (@defText) {
			$line =~ s/REPLACEID/$shieldGroupName/ ;
			$line =~ s/REPLACESCALE/$scaleFactor/g ;
			$line =~ s/REPLACEPATH/$shieldPathName/ ;
			$line =~ s/REPLACELABEL/$shieldText/ ;
		}

		foreach my $line (@defText) {
			push @svgOutputDef, $line ;
			# print "DEF: $line" ;
		}
		# print "\n" ; 

		$createdShields{$name} = $shieldGroupName ;
	}
}



sub getPointOfWay {
	#
	# returns point of way at distance/position
	#

	my ($ref, $position) = @_ ;
	my @points = @$ref ;

	my @double = () ;
	while (scalar @points > 0) {
		my $x = shift @points ;
		my $y = shift @points ;
		push @double, [$x, $y] ;
	}

	my $i = 0 ; my $actLen = 0 ;
	while ($actLen < $position) {
		$actLen += sqrt ( ($double[$i]->[0]-$double[$i+1]->[0])**2 + ($double[$i]->[1]-$double[$i+1]->[1])**2 ) ;
		$i++ ;
	}

	my $x = int (($double[$i]->[0] +  $double[$i-1]->[0]) / 2) ;
	my $y = int (($double[$i]->[1] +  $double[$i-1]->[1]) / 2) ;

	# print "POW: $x, $y\n" ;

	return ($x, $y) ;
}






1 ;


