mapweaver/mwMap.pm

933 lines
29 KiB
Perl

#
# PERL mapweaver module by gary68
#
#
#
#
# Copyright(C)2011, 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/>
#
package mwMap;
use strict;
use warnings;
use mwConfig;
# use mwMisc;
# use mwFile;
# use mwLabel;
use OSM::osm;
use Geo::Proj4;
my $areaNum = 0;
my %areaDef =();
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
require Exporter;
@ISA = qw(Exporter AutoLoader);
@EXPORT = qw(initGraph
coordsOut
drawCircle
drawSquare
drawTriangle
drawDiamond
drawRect
drawText
writeMap
drawWay
drawArea
fitsPaper
getScale
createPath
pathText
placeIcon
convert
gridSquare
getDimensions
initOneways
addOnewayArrows
addAreaIcon
createShield
getMaxShieldSize
getShieldSizes
getShieldId
addToLayer
createLegendFile
);
my @belowWays =("background", "base", "area", "multi");
my @aboveWays =("arealabels", "wayLabels", "routes", "routeStops", "shields", "nodes", "icons", "text", "additional", "occupied");
my @elements =("scale", "ruler", "legend", "wns", "header", "footer", "rectangles", "title", "gpx");
my %svgLayer =();
my %wayLayer =();
my $shieldPathId = 0;
my %createdShields =();
my %shieldXSize =();
my %shieldYSize =();
my $proj;
my($bottom, $left, $right, $top);
my($sizeX, $sizeY);
my($projLeft, $projBottom, $projRight, $projTop);
my($projSizeX, $projSizeY);
sub initGraph{
# function initializes the picture and projection
my($x, $l, $b, $r, $t)= @_;
# my $l0 = int($l)- 1;
my $l0 = int(($r+$l)/ 2);
$proj = Geo::Proj4->new( proj => cv('projection'),
ellps => cv('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!!!
$projSizeX = $projRight - $projLeft;
$projSizeY = $projTop - $projBottom;
my $factor = $projSizeY / $projSizeX;
$sizeX = int($x);
$sizeY = int($x * $factor);
$top = $t;
$left = $l;
$right = $r;
$bottom = $b;
if((cv('bgcolor')ne "none")and(cv('bgcolor')ne "")){ my $col = cv('bgcolor');
my $svgText = "fill=\"$col\" ";
drawRect(0, 0, $sizeX, $sizeY, 0, $svgText, "background");
}
if(cv('ruler')ne "0"){ drawRuler();
}
if(cv('scale')ne "0"){ drawScale();
}
if(cv('grid')!= 0){ drawGrid();
} if(cv('coords')eq "1"){ drawCoords();
} if(length cv('foot')> 0){ drawFoot();
} if(length cv('head')> 0){ drawHead();
}
}
sub addToLayer{ my($layer, $text)= @_;
if($layer =~ /^[\d\-\.]+$/){ push @{$wayLayer{$layer}}, $text;
# print "adding NUMERIC: $text\n";
} else{ push @{$svgLayer{$layer}}, $text;
# print "adding TEXTUAL: $text\n";
}}
sub drawWay{
# accepts list of nodes(plus convert=1)or list of x,y,x,y(convert=0)and draws way/polygon to layerNr if defined or to layerName
my($nodesRef, $convert, $svgString, $layerName, $layerNumber)= @_;
my @points =();
my $valid = 0;
# convert? and expand.
my($lonRef, $latRef, $tagRef)= mwFile::getNodePointers();
if($convert){ foreach my $node(@$nodesRef){ my($x, $y)= convert($$lonRef{$node}, $$latRef{$node});
push @points, $x, $y;
if(! coordsOut($x, $y)){ $valid = 1;
} }} else{ @points = @$nodesRef;
foreach my $node(@$nodesRef){ my($x, $y)=($$lonRef{$node}, $$latRef{$node});
if(! coordsOut($x, $y)){ $valid = 1;
} }}
my $refp = simplifyPoints(\@points);
@points = @$refp;
if($valid){
my $svg = "<polyline points=\"";
for(my$i=0; $i<scalar(@points)-1; $i+=2){ $svg = $svg . $points[$i] . "," . $points[$i+1] . " ";
}
$svg = $svg . "\" $svgString />";
if(defined $layerNumber){ push @{$wayLayer{$layerNumber}}, $svg;
} else{ push @{$svgLayer{$layerName}}, $svg;
}} else{ # if(){print "way not drawn, outside\n";}}}
sub drawText{
my($x, $y, $convert, $text, $svgString, $layerName)= @_;
if($convert){($x, $y)= convert($x, $y);
}
if(! coordsOut($x, $y)){
my $svg = "<text x=\"$x\" y=\"$y\" $svgString>" . $text . "</text>";
push @{$svgLayer{$layerName}}, $svg;
}}
sub drawCircle{
# draws circle element to svgLayer given; if convertCoords then lon / lat is converted to x / y
# circleradius either in pixel or in meters(convert=1) my($x, $y, $convertCoords, $radius, $convertRadius, $format, $layerName)= @_;
if($convertCoords){($x, $y)= convert($x, $y);
} if($convertRadius){ $radius = $radius /(1000 * distance($left, $bottom, $right, $bottom))* $sizeX;
}
if(! coordsOut($x, $y)){
my $svg = "<circle cx=\"$x\" cy=\"$y\" r=\"$radius\" ";
$svg .= $format . " />";
push @{$svgLayer{$layerName}}, $svg;
}}
sub drawSquare{
# draws square element to svgLayer given; if convertCoords then lon / lat is converted to x / y
# square size either in pixel or in meters(convert=1) my($x, $y, $convertCoords, $size, $convertSize, $format, $layerName)= @_;
if($convertCoords){($x, $y)= convert($x, $y);
} if($convertSize){ $size = $size /(1000 * distance($left, $bottom, $right, $bottom))* $sizeX;
}
my $x1 = $x - $size;
my $y1 = $y - $size;
my $dSize = 2 * $size;
if(! coordsOut($x, $y)){
my $svg = "<rect x=\"$x1\" y=\"$y1\" width=\"$dSize\" height=\"$dSize\" ";
$svg .= $format . " />";
push @{$svgLayer{$layerName}}, $svg;
}}
sub drawTriangle{
# draws triangle element to svgLayer given; if convertCoords then lon / lat is converted to x / y
# square size either in pixel or in meters(convert=1) my($x, $y, $convertCoords, $size, $convertSize, $format, $layerName)= @_;
if($convertCoords){($x, $y)= convert($x, $y);
} if($convertSize){ $size = $size /(1000 * distance($left, $bottom, $right, $bottom))* $sizeX;
}
my $h = int(sqrt($size * $size / 2));
my $x1 = $x;
# my $y1 = $y - $size;
my $y1 = $y - $h;
my $x2 = $x - $h;
my $y2 = $y + $h;
my $x3 = $x + $h;
my $y3 = $y + $h;
if(! coordsOut($x1, $y1, $x2, $y2, $x3, $y3)){
my $svg = "<polyline points=\"$x1,$y1 $x2,$y2 $x3,$y3 $x1,$y1\" ";
$svg .= $format . " />";
push @{$svgLayer{$layerName}}, $svg;
}}
sub drawDiamond{
# draws diamond element to svgLayer given; if convertCoords then lon / lat is converted to x / y
# square size either in pixel or in meters(convert=1) my($x, $y, $convertCoords, $size, $convertSize, $format, $layerName)= @_;
if($convertCoords){($x, $y)= convert($x, $y);
} if($convertSize){ $size = $size /(1000 * distance($left, $bottom, $right, $bottom))* $sizeX;
}
my $x1 = $x - $size; # left
my $y1 = $y;
my $x2 = $x; # top
my $y2 = $y - $size;
my $x3 = $x + $size; #right
my $y3 = $y;
my $x4 = $x; # bottom
my $y4 = $y + $size;
if(! coordsOut($x1, $y1, $x2, $y2, $x3, $y3, $x4, $y4)){ my $svg = "<polyline points=\"$x1,$y1 $x2,$y2 $x3,$y3 $x4,$y4 $x1,$y1\" ";
$svg .= $format . " />";
push @{$svgLayer{$layerName}}, $svg;
}}
sub drawRect{
# draws square element to svgLayer given; if convertCoords then lon / lat is converted to x / y
# square size either in pixel or in meters(convert=1) my($x1, $y1, $x2, $y2, $convertCoords, $format, $layerName)= @_;
if($convertCoords){($x1, $y1)= convert($x1, $y1);
($x2, $y2)= convert($x2, $y2);
}
my $sizeX = $x2 - $x1;
my $sizeY = $y2 - $y1;
if(! coordsOut($x1, $y1, $x2, $y2)){ my $svg = "<rect x=\"$x1\" y=\"$y1\" width=\"$sizeX\" height=\"$sizeY\" ";
$svg .= $format . " />";
push @{$svgLayer{$layerName}}, $svg;
}}
sub createPath{#
# creates path element for later use with textPath
#
my($pathName, $refp, $layer)= @_;
my $refp2 = simplifyPoints($refp);
my @points = @$refp2;
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";
push @{$svgLayer{$layer}}, $svg;
}
sub pathText{#
# draws text to path element; alignment: start, middle, end
#
my($svgText, $text, $pathName, $tSpan, $alignment, $offset, $layer)= @_;
my $svg = "<text $svgText >\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";
push @{$svgLayer{$layer}}, $svg;
}
sub placeIcon{#
# create SVG text for icons
#
my($x, $y, $icon, $sizeX, $sizeY, $layer)= @_;
if(! coordsOut($x, $y)){
my($out)= "<image x=\"" . $x . "\"";
$out .= " y=\"" . $y . "\"";
if($sizeX > 0){$out .= " width=\"" . $sizeX . "\"";} if($sizeY > 0){$out .= " height=\"" . $sizeY . "\"";} $out .= " xlink:href=\"" . $icon . "\" />";
push @{$svgLayer{$layer}}, $out;
}}
sub drawArea{#
# draws mp in svg ARRAY of ARRAY of nodes/coordinates
#
my($svgText, $icon, $ref, $convert, $layer)= @_;
my @ways = @$ref;
my $i;
my $svg = "";
my $valid = 1;
my @newArray =();
# TODO loop converts original data !!!
if($convert){ my($lonRef, $latRef, $tagRef)= mwFile::getNodePointers();
foreach my $aRef(@ways){ my @way = @$aRef;
my @newCoords =();
foreach my $n(@way){ my($x, $y)= convert($$lonRef{$n}, $$latRef{$n});
push @newCoords, $x, $y;
if(coordsOut($x, $y)){ $valid = 0;
} } push @newArray , [@newCoords];
} @ways = @newArray;
}
if(defined $areaDef{$icon}){ $svg = "<path $svgText fill-rule=\"evenodd\" style=\"fill:url(" . $areaDef{$icon}. ")\" d=\"";
} else{ $svg = "<path $svgText fill-rule=\"evenodd\" d=\"";
}
foreach my $way(@ways){ my @actual = @$way;
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";
}
$svg = $svg . "\" />";
if($valid){ push @{$svgLayer{$layer}}, $svg;
}}
# ---------------------------------------------------------------------
sub writeMap{
my $fileName = cv('out');
open(my $file, ">", $fileName)|| die "can't open svg output file $fileName\n";
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 $w = $sizeX / 300 * 2.54; # cm
my $h = $sizeY / 300 * 2.54;
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;
# definitions
if(defined @{$svgLayer{'definitions'}}){ print $file "<defs>\n";
foreach(@{$svgLayer{'definitions'}}){print $file $_, "\n";} print $file "</defs>\n";
}
# below ways
foreach my $layer(@belowWays){ if(defined @{$svgLayer{$layer}}){ print $file "<g id=\"$layer\">\n";
foreach(@{$svgLayer{$layer}}){print $file $_, "\n";} print $file "</g>\n";
}}
# ways
foreach my $layer(sort{$a <=> $b}keys %wayLayer){ if(defined @{$wayLayer{$layer}}){ print $file "<g id=\"way$layer\">\n";
foreach(@{$wayLayer{$layer}}){print $file $_, "\n";} print $file "</g>\n";
}}
# above of ways
foreach my $layer(@aboveWays){ if(defined @{$svgLayer{$layer}}){ print $file "<g id=\"$layer\">\n";
foreach(@{$svgLayer{$layer}}){print $file $_, "\n";} print $file "</g>\n";
}}
foreach my $layer(@elements){ if(defined @{$svgLayer{$layer}}){ print $file "<g id=\"$layer\">\n";
foreach(@{$svgLayer{$layer}}){print $file $_, "\n";} print $file "</g>\n";
}}
print $file "</svg>\n";
close($file);
if(cv('pdf')eq "1"){ my($pdfName)= $fileName;
$pdfName =~ s/\.svg/\.pdf/;
print "creating pdf file $pdfName ...\n";
`inkscape -A $pdfName $fileName`;
}
if(cv('png')eq "1"){ my($pngName)= $fileName;
$pngName =~ s/\.svg/\.png/;
my $dpi = cv('pngdpi');
print "creating png file $pngName($dpi dpi)...\n";
`inkscape --export-dpi=$dpi -e $pngName $fileName`;
}
}
# -----------------------------------------------------------------------------------
sub drawGrid{#
# draw grid on top of map. receives number of parts in x/lon direction
#
my $number = cv('grid');
my $color = cv('gridcolor');
my $part = $sizeX / $number;
my $numY = $sizeY / $part;
my $svgStringLine="stroke=\"$color\" stroke-width=\"5\" stroke-dasharray=\"30,30\"";
my $svgStringText = mwMisc::createTextSVG(cv('elementFontFamily'), cv('elementFont'), undef, undef, 60, $color, undef, undef);
# vertical lines
for(my $i = 1; $i <= $number; $i++){ my @coords =($i*$part, 0, $i*$part, $sizeY);
drawWay(\@coords, 0, $svgStringLine, "additional", undef);
drawText(($i-1)*$part+$part/2, 160, 0, chr($i+64), $svgStringText, "additional");
}
# hor. lines
for(my $i = 1; $i <= $numY; $i++){ my @coords =(0, $i*$part, $sizeX, $i*$part);
drawWay(\@coords, 0, $svgStringLine, "additional", undef);
drawText(20,($i-1)*$part+$part/2, 0, $i, $svgStringText, "additional");
}}
sub drawCoords{#
# draws coordinates grid on map
#
my $exp = cv('coordsexp');
my $color = cv('coordscolor');
my $step = 10 ** $exp;
# vert. lines
my $start = int($left / $step)+ 1;
my $actual = $start * $step;
my $svgStringLine="stroke=\"$color\" stroke-width=\"3\"";
my $svgStringText = mwMisc::createTextSVG(cv('elementFontFamily'), cv('elementFont'), undef, undef, 30, $color, undef, undef);
while($actual < $right){ my($x1, $y1)= convert($actual, 0);
drawText($x1+10, $sizeY-50, 0, $actual, $svgStringText, "additional");
my @coords =($x1, 0, $x1, $sizeY);
drawWay(\@coords, 0, $svgStringLine, "additional", undef);
$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);
drawText($sizeX-180, $y1+30, 0, $actual, $svgStringText, "additional");
my @coords =(0, $y1, $sizeX, $y1);
drawWay(\@coords, 0, $svgStringLine, "additional", undef);
$actual += $step;
}}
# -----------------------------------------------------------------------------------
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 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 drawRuler{#
# draws ruler
#
my $col = cv('rulercolor');
my $B; my $B2;
my $L; my $Lpix;
my $x;
my $text;
my $lineThickness = 8; # at 300dpi
my $textSize = 40; # at 300 dpi
my $textDist = 60; # at 300 dpi
my $lineLen = 40; # at 300 dpi
my $xOffset = 2 * $lineThickness;
my $yOffset = 2 * $lineThickness;
$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 = "100 m"; $x = 0.1;}# enlarge ruler
if($B2 > 1){$text = "500 m"; $x = 0.5;}# enlarge ruler
if($B2 > 5){$text = "1 km"; $x = 1;} if($B2 > 10){$text = "5 km"; $x = 5;} if($B2 > 50){$text = "10 km"; $x = 10;} $L = $x /(cos($top/360*3.14*2)* 111.1); # length ruler in km
$Lpix = $L / $B * $sizeX; # length ruler in pixels
my $rSizeX = int($Lpix + 2 * $xOffset);
my $rSizeY = int($lineLen + $textSize + 3 * $yOffset);
addToLayer("definitions", "<g id=\"rulerdef\" width=\"$rSizeX\" height=\"$rSizeY\" >");
if(cv('rulerbackground')ne "none"){ my $color = cv('rulerbackground');
my $svgString = "fill=\"$color\"";
drawRect(0, 0, $rSizeX, $rSizeY, 0, $svgString, "definitions");
}
my $svgString = "stroke=\"$col\" stroke-width=\"$lineThickness\" stroke-linecap=\"round\" ";
my @coords =($xOffset, $yOffset, $xOffset+$Lpix, $yOffset);
drawWay(\@coords, 0, $svgString, "definitions", undef);
@coords =($xOffset, $yOffset, $xOffset, $yOffset+$lineLen);
drawWay(\@coords, 0, $svgString, "definitions", undef);
@coords =($xOffset+$Lpix, $yOffset, $xOffset+$Lpix, $yOffset+$lineLen);
drawWay(\@coords, 0, $svgString, "definitions", undef);
@coords =($xOffset+$Lpix/2, $yOffset, $xOffset+$Lpix/2, $yOffset+$lineLen/2);
drawWay(\@coords, 0, $svgString, "definitions", undef);
my $scale= getScale();
$text .= "(1:$scale)";
$svgString = mwMisc::createTextSVG(cv('elementFontFamily'), cv('elementFont'), undef, undef, 35, $col, undef, undef);
drawText($xOffset, $yOffset+$textDist+30, 0, $text, $svgString, "definitions");
addToLayer("definitions", "</g>");
my $posX = 40; my $posY = 40;
if(cv('ruler')eq "2"){ $posX = $sizeX - 40 - $rSizeX;
$posY = 40;
}
if(cv('ruler')eq "3"){ $posX = 40;
$posY = $sizeY - 40 - $rSizeY;
}
if(cv('ruler')eq "4"){ $posX = $sizeX - 40 - $rSizeX;
$posY = $sizeY - 40 - $rSizeY;
}
addToLayer("ruler", "<use x=\"$posX\" y=\"$posY\" xlink:href=\"#rulerdef\" />");
}
sub drawScale{#
# draws scale value
#
my $col = cv('scalecolor');
my $xOffset = 20;
my $yOffset = 20;
my $fontSize = 70;
my $borderDist = 60;
my $rSizeX = int(350 + 2 * $xOffset);
my $rSizeY = int($fontSize + 2 * $yOffset);
addToLayer("definitions", "<g id=\"scaledef\" width=\"$rSizeX\" height=\"$rSizeY\" >");
if(cv('scalebackground')ne "none"){ my $color = cv('scalebackground');
my $svgString = "fill=\"$color\"";
drawRect(0, 0, $rSizeX, $rSizeY, 0, $svgString, "definitions");
}
my $scale= getScale();
my $svgString = mwMisc::createTextSVG(cv('elementFontFamily'), cv('elementFont'), undef, undef, $fontSize, $col, undef, undef);
drawText($xOffset, $fontSize + $yOffset, 0, "1:$scale", $svgString, "definitions");
addToLayer("definitions", "</g>");
my $posX = $borderDist; my $posY = $borderDist;
if(cv('scale')eq "2"){ $posX = $sizeX - $borderDist - $rSizeX;
$posY = $borderDist;
}
if(cv('scale')eq "3"){ $posX = $borderDist;
$posY = $sizeY - $borderDist - $rSizeY;
}
if(cv('scale')eq "4"){ $posX = $sizeX - $borderDist - $rSizeX;
$posY = $sizeY - $borderDist - $rSizeY;
}
addToLayer("scale", "<use x=\"$posX\" y=\"$posY\" xlink:href=\"#scaledef\" />");
}
sub drawFoot{#
# draws footer
#
my $col = cv('footcolor');
my $text = cv('foot');
my $len = length $text;
my $xOffset = 20;
my $yOffset = 20;
my $fontSize = cv('footsize');
my $borderDistX = 60;
my $borderDistY = $fontSize + 50;
my $rSizeX = int($len*cv('ppc')/10*$fontSize + 2 * $xOffset);
my $rSizeY = int($fontSize + 2 * $yOffset);
addToLayer("definitions", "<g id=\"footdef\" width=\"$rSizeX\" height=\"$rSizeY\" >");
if(cv('footbackground')ne "none"){ my $color = cv('footbackground');
my $svgString = "fill=\"$color\"";
drawRect(0, 0, $rSizeX, $rSizeY, 0, $svgString, "definitions");
}
my $svgString = mwMisc::createTextSVG(cv('elementFontFamily'), cv('elementFont'), undef, undef, $fontSize, $col, undef, undef);
drawText($xOffset, $fontSize + $yOffset, 0, $text, $svgString, "definitions");
addToLayer("definitions", "</g>");
my $posX = $borderDistX; my $posY = $sizeY - $borderDistY;
addToLayer("footer", "<use x=\"$posX\" y=\"$posY\" xlink:href=\"#footdef\" />");
}
sub drawHead{#
# draws header
#
my $col = cv('headcolor');
my $text = cv('head');
my $len = length $text;
my $xOffset = 20;
my $yOffset = 20;
my $fontSize = cv('headsize');
my $borderDistX = 60;
my $borderDistY = 60;
my $rSizeX = int($len*cv('ppc')/10*$fontSize + 2 * $xOffset);
my $rSizeY = int($fontSize + 2 * $yOffset);
addToLayer("definitions", "<g id=\"headdef\" width=\"$rSizeX\" height=\"$rSizeY\" >");
if(cv('headbackground')ne "none"){ my $color = cv('headbackground');
my $svgString = "fill=\"$color\"";
drawRect(0, 0, $rSizeX, $rSizeY, 0, $svgString, "definitions");
}
my $svgString = mwMisc::createTextSVG(cv('elementFontFamily'), cv('elementFont'), undef, undef, $fontSize, $col, undef, undef);
drawText($xOffset, $fontSize + $yOffset, 0, $text, $svgString, "definitions");
addToLayer("definitions", "</g>");
my $posX = $borderDistX; my $posY = $borderDistY;
addToLayer("header", "<use x=\"$posX\" y=\"$posY\" xlink:href=\"#headdef\" />");
}
sub fitsPaper{#
# calculates on what paper size the map will fit. sizes are taken from global variables
#
my $width = $sizeX / 300 * 2.54;
my $height = $sizeY / 300 * 2.54;
my $paper = "";
my @sizes =();
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 getScale{#
# calcs scale of map
#
my($dpi)= 300;
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 gridSquare{#
# returns grid square of given coordinates for directories
#
my($lon, $lat)= @_;
my $parts = cv('grid');
my($x, $y)= convert($lon, $lat);
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 getDimensions{ return($sizeX, $sizeY);
}
# ----------------------------------------------------------------------
sub initOneways{#
# write marker defs to svg
#
my $color = cv('onewaycolor');
my @svgOutputDef =();
for(my $markerSize = 5; $markerSize <= 40; $markerSize++){ push @svgOutputDef, "<marker id=\"Arrow$markerSize\"";
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>";
}
foreach my $line(@svgOutputDef){ addToLayer("definitions", $line);
}}
sub addOnewayArrows{#
# adds oneway arrows to new pathes
#
my($wayNodesRef, $direction, $thickness, $layer)= @_;
my @wayNodes = @$wayNodesRef;
my($lonRef, $latRef)= mwFile::getNodePointers();
if($direction == -1){@wayNodes = reverse @wayNodes;}
my $minDist = cv('onewaysize')* 1.5;
my $markerSize = cv('onewaySize');
if(cv('onewayAutoSize')!= 0){ $markerSize = int($thickness / 100 * cv('onewayAutoSize'));
if($markerSize < 5){$markerSize = 5;} if($markerSize > 40){$markerSize = 40;} $minDist = $markerSize * 1.5;
}
# 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(#Arrow$markerSize)\" />";
addToLayer($layer+$thickness/100, $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)= mwMisc::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)= mwMisc::sizePNG($fileNameOriginal);
}
if(!defined $areaDef{$fileNameOriginal}){
my $x1 = $x; # scale area icons
my $y1 = $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";
addToLayer("definitions", $svgElement);
$defName = "#" . $defName;
$areaDef{$fileNameOriginal}= $defName;
}} else{ print "WARNING: area icon $fileNameOriginal not found!\n";
}}
# ----------------------------------------------------------------------------
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;
$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){ addToLayer("definitions", $line);
}
$createdShields{$name}= $shieldGroupName;
}}
sub getMaxShieldSize{ my $name = shift;
my $max = $shieldXSize{$name};
if($shieldYSize{$name}> $max){$max = $shieldYSize{$name};} return $max;
}
sub getShieldSizes{ my $name = shift;
my $x = $shieldXSize{$name};
my $y = $shieldYSize{$name};
return $x, $y;
}
sub getShieldId{ my $name = shift;
return $createdShields{$name};
}
# --------------------------------------------------------------------
sub createLegendFile{ my($x, $y, $extension, $group)= @_;
my $svgName = cv('out');
$svgName =~ s/\.svg/$extension\.svg/i;
my $pngName = $svgName;
$pngName =~ s/\.svg/\.png/i;
my $pdfName = $svgName;
$pdfName =~ s/\.svg/\.pdf/i;
open(my $file, ">", $svgName)|| die "can't open legend svg output file $svgName\n";
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 $w = $x / 300 * 2.54; # cm
my $h = $y / 300 * 2.54;
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 $x $y\">\n";
print $file $svg;
print $file "<defs>\n";
foreach(@{$svgLayer{'definitions'}}){print $file $_, "\n";} print $file "</defs>\n";
print $file "<use x=\"0\" y=\"0\" xlink:href=\"$group\" />\n";
print $file "</svg>\n";
close $file;
if(cv('pdf')eq "1"){ print "creating pdf file $pdfName ...\n";
`inkscape -A $pdfName $svgName`;
}
if(cv('png')eq "1"){ my $dpi = cv('pngdpi');
print "creating png file $pngName($dpi dpi)...\n";
`inkscape --export-dpi=$dpi -e $pngName $svgName`;
}}
sub coordsOut{ my @points = @_;
my $allOut = 0;
my $outLeft = 1;
my $outRight = 1;
my $outTop = 1;
my $outBottom = 1;
for(my $i=0; $i < scalar(@points)- 1; $i += 2){ my $x = $points[$i];
my $y = $points[$i+1];
if($x >= 0){$outLeft = 0;} if($x <= $sizeX){$outRight = 0;} if($y >= 0){$outBottom = 0;} if($y <= $sizeY){$outTop = 0;}}
if(($outLeft)or($outRight)or($outTop)or($outBottom)){$allOut = 1;}
return $allOut;
}
1;