mapweaver/mwLabel.pm

253 lines
9.8 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 mwLabel;
use strict;
use warnings;
use mwConfig;
use mwMap;
# use mwMisc;
use mwOccupy;
my $labelPathId = 0;
my @lines =();
my $numIconsMoved = 0;
my $numLabels = 0;
my $numIcons = 0;
my $numLabelsOmitted = 0;
my $numLabelsMoved = 0;
my $numIconsOmitted = 0;
my %poiHash =();
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
require Exporter;
@ISA = qw(Exporter AutoLoader);
@EXPORT = qw( placeLabelAndIcon
addToPoiHash
getPoiHash
);
sub placeLabelAndIcon{#
# intelligent icon and label placement alg.
#
my($lon, $lat, $offset, $thickness, $text, $svgText, $icon, $iconSizeX, $iconSizeY, $layer)= @_;
if(cv('debug')eq "1"){print "PLAI: $lon, $lat, $offset, $thickness, $text, $svgText, $icon, $iconSizeX, $iconSizeY, $layer\n";}
my($x, $y)= mwMap::convert($lon, $lat); # center !
if(! coordsOut($x, $y)){
$y = $y + $offset;
my($ref)= splitLabel($text);
my(@lines)= @$ref;
my $numLines = scalar @lines;
my $maxTextLenPix = 0;
my $orientation = "";
my $lineDist = cv('linedist');;
my $tries = 0;
my $allowIconMove = cv('allowiconmove');
my($textSize)=($svgText =~ /font-size=\"(\d+)\"/);
if(! defined $textSize){die("ERROR: font size could not be determined from svg format string \"$svgText\"\n");}
foreach my $line(@lines){ my $len = length($line)* cv('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, -15, 15);
} my $posFound = 0; my $posCount = 0;
my($iconAreaX1, $iconAreaY1, $iconAreaX2, $iconAreaY2);
LABAB: foreach my $xShift(@shifts){ foreach my $yShift(@shifts){ $posCount++;
if((! boxAreaOccupied($iconX+$xShift, $iconY+$sizeY1+$yShift, $iconX+$sizeX1+$xShift, $iconY+$yShift))or(cv('forcenodes')eq "1")){ placeIcon($iconX+$xShift, $iconY+$yShift, $icon, $sizeX1, $sizeY1, "nodes");
$iconAreaX1 = $iconX+$xShift;
$iconAreaY1 = $iconY+$sizeY1+$yShift;
$iconAreaX2 = $iconX+$sizeX1+$xShift;
$iconAreaY2 = $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], \@lines, $svgText, $layer);
if($positionFound == 1){ last LABB;
} } if($positionFound == 0){$numLabelsOmitted++;} if($tries > 1){$numLabelsMoved++;} }# label
boxOccupyArea($iconAreaX1, $iconAreaY1, $iconAreaX2, $iconAreaY2, 0, 2);
}# pos found
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], \@lines, $svgText, $layer);
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, $refLines, $svgText, $layer)= @_;
if(cv('debug')eq "1"){print "CADT: $x1, $x2, $y1, $y2, $orientation, $refLines, $svgText, $layer\n";}
my @lines = @$refLines;
my $numLines = scalar @lines;
my $lineDist = cv('linedist');
my($size)=($svgText =~ /font-size=\"(\d+)\"/);
if(! defined $size){die("ERROR: font size could not be determined from svg format string \"$svgText\"\n");}
# WATCH for variable sequence!
if((! boxAreaOccupied($x1, $y1, $x2, $y2))or
(cv('forcenodes')eq "1")){
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++;
createPath($pathName, \@points, "definitions");
if($orientation eq "centered"){ pathText($svgText, $lines[$i], $pathName, 0, "middle", 50, $layer) } if($orientation eq "left"){ pathText($svgText, $lines[$i], $pathName, 0, "start", 0, $layer) } if($orientation eq "right"){ pathText($svgText, $lines[$i], $pathName, 0, "end", 100, $layer) } }
boxOccupyArea($x1, $y1, $x2, $y2, 0, 2);
return(1);
} else{ return 0;
}}
sub splitLabel{#
# split label text at space locations and then merge new parts if new part will be smaller than XX 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])<= cv('maxcharperline')){
$lines[$i] = $lines[$i] . " " . $lines[$i+1];
splice(@lines, $i+1, 1);
$merged = 1;
last LAB2;
} }} return(\@lines);
}
# ------------------------------------------------------------
sub addToPoiHash{ my($name, $sq)= @_;
if(defined $sq){ $poiHash{$name}{$sq}= 1;
} else{ $poiHash{$name}= 1;
}}
sub getPoiHash{ return \%poiHash;
}
1;