933 lines
29 KiB
Perl
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;
|
|
|
|
|