mapweaver/mwMulti.pm

289 lines
12 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 mwMulti;
use strict;
use warnings;
use mwMap;
use mwMisc;
use mwFile;
use mwLabel;
use mwConfig;
use mwRules;
use Math::Polygon;
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
require Exporter;
@ISA = qw(Exporter AutoLoader);
@EXPORT = qw(processMultipolygons
);
my $newId = 0;
my %multiNodes =();
my %multiTags =();
my %multiPaths =();
my %wayUsed =();
# -------------------------------------------------------------------------
sub processMultipolygons{ my $notDrawnMP = 0;
my $mp = 0;
my $mpLabels = 0;
my $mpNotDrawnLabels = 0;
print "draw multipolygons...\n";
preprocessMultipolygons();
foreach my $multiId(keys %multiTags){
my $ruleRef = getAreaRule(\@{$multiTags{$multiId}});
if(defined $ruleRef){
my $svgText = "";
my $icon = "";
if($$ruleRef{'icon'}ne "none"){ $icon = $$ruleRef{'icon'};
} else{ my $col = $$ruleRef{'color'};
$svgText = "fill=\"$col\" ";
}
my $ref = $multiPaths{$multiId}[0]; # first, outer way
my $size = areaSize($ref);
if($size >= cv('minareasize')){ drawArea($svgText, $icon, $multiPaths{$multiId}, 1, "multi");
$mp++;
# LABELS
my $name = ""; my $ref1;
if(cv('ignorelabels')eq "0"){ ($name, $ref1)= createLabel($multiTags{$multiId}, $$ruleRef{'label'}, 0, 0);
if(($$ruleRef{'label'}ne "none")and
(cv('nolabel')eq "1")and
($name eq "")) {
$name = "NO LABEL";
} }
if($name ne ""){ if($size >= cv('minarealabelsize')){ $mpLabels++;
if(cv('debug')eq "1"){print "MP LABEL: $name, size: $$ruleRef{'labelsize'}, color: $$ruleRef{'labelcolor'}\n";}
my($x, $y)= areaCenter($multiPaths{$multiId}[0]);
my $labelFont = $$ruleRef{'labelfont'};
my $labelFontFamily = $$ruleRef{'labelfontfamily'};
my $labelSize = $$ruleRef{'labelsize'};
my $color = $$ruleRef{'labelcolor'};
my $labelBold = $$ruleRef{'labelbold'};
my $labelItalic = $$ruleRef{'labelitalic'};
my $labelHalo = $$ruleRef{'labelhalo'};
my $labelHaloColor = $$ruleRef{'labelhalocolor'};
my $svgText = createTextSVG($labelFontFamily, $labelFont, $labelBold, $labelItalic, $labelSize, $color, $labelHalo, $labelHaloColor);
# $svgText = createTextSVG(undef, undef, $$ruleRef{'labelsize'}, $$ruleRef{'labelcolor'}, undef, undef);
if(cv('debug')eq "1"){print "MP LABEL: svg: \"$svgText\"\n";} placeLabelAndIcon($x, $y, 1, 0, $name, $svgText, "none", 0, 0, "arealabels");
}# if size
else{ $mpNotDrawnLabels++;
} } else{
} } else{ $notDrawnMP++;
}
}# if rule
}# foreach multi
print "$mp multipolygon areas drawn, $notDrawnMP not drawn because they were too small.\n";
print "$mpLabels multipolygon labels drawn, $mpNotDrawnLabels not drawn because belonging areas were too small.\n";
}
# ------------------------------------------------------------------------------------------
sub preprocessMultipolygons{#
# preprecess all multipolygons
#
my($wayNodesRef, $wayTagsRef)= getWayPointers();
my($relationMembersRef, $relationTagsRef)= getRelationPointers();
foreach my $relId(keys %$relationMembersRef){ my $isMulti = 0;
foreach my $tag(@{$$relationTagsRef{$relId}}){ if(($tag->[0] eq "type")and($tag->[1] eq "multipolygon")){$isMulti = 1;} }
if($isMulti){ if(cv('debug')eq "1"){print "\n---------------------------------------------------\n";} if(cv('debug')eq "1"){print "\nRelation $relId is multipolygon!\n";}
# get inner and outer ways
my(@innerWays)=(); my(@outerWays)=();
foreach my $member(@{$$relationMembersRef{$relId}}){ if(($member->[0] eq "way")and($member->[2] eq "outer")and(defined @{$$wayNodesRef{$member->[1]}})){push @outerWays, $member->[1];} if(($member->[0] eq "way")and($member->[2] eq "inner")and(defined @{$$wayNodesRef{$member->[1]}})){push @innerWays, $member->[1];} } if(cv('debug')eq "1"){print "OUTER WAYS: @outerWays\n";} if(cv('debug')eq "1"){print "INNER WAYS: @innerWays\n";}
my($ringsWaysRef, $ringsNodesRef);
my @ringWaysInner =(); my @ringNodesInner =(); my @ringTagsInner =();
# build rings inner
if(scalar @innerWays > 0){ ($ringsWaysRef, $ringsNodesRef)= buildRings(\@innerWays, 1);
@ringWaysInner = @$ringsWaysRef;
@ringNodesInner = @$ringsNodesRef;
for(my $ring=0; $ring<=$#ringWaysInner; $ring++){ if(cv('debug')eq "1"){print "INNER RING $ring: @{$ringWaysInner[$ring]}\n";} my $firstWay = $ringWaysInner[$ring]->[0];
if(scalar @{$ringWaysInner[$ring]}== 1){$wayUsed{$firstWay}= 1;}# way will be marked as used/drawn by multipolygon
@{$ringTagsInner[$ring]}= @{$$wayTagsRef{$firstWay}}; # ring will be tagged like first contained way
if(cv('debug')eq "1"){ print "tags from first way...\n";
foreach my $tag(@{$$wayTagsRef{$firstWay}}){ print " $tag->[0] - $tag->[1]\n";
} } if((scalar @{$$wayTagsRef{$firstWay}})== 0){ if(cv('debug')eq "1"){print "tags set to hole in mp.\n";} push @{$ringTagsInner[$ring]}, ["multihole", "yes"];
} } }
# build rings outer
my @ringWaysOuter =(); my @ringNodesOuter =(); my @ringTagsOuter =();
if(scalar @outerWays > 0){ ($ringsWaysRef, $ringsNodesRef)= buildRings(\@outerWays, 1);
@ringWaysOuter = @$ringsWaysRef; # not necessary for outer
@ringNodesOuter = @$ringsNodesRef;
for(my $ring=0; $ring<=$#ringWaysOuter; $ring++){ if(cv('debug')eq "1"){print "OUTER RING $ring: @{$ringWaysOuter[$ring]}\n";} my $firstWay = $ringWaysOuter[$ring]->[0];
if(scalar @{$ringWaysOuter[$ring]}== 1){$wayUsed{$firstWay}= 1;} @{$ringTagsOuter[$ring]}= @{$$relationTagsRef{$relId}}; # tags from relation
if(cv('debug')eq "1"){ print "tags from relation...\n";
foreach my $tag(@{$$relationTagsRef{$relId}}){ print " $tag->[0] - $tag->[1]\n";
} } if(scalar @{$$relationTagsRef{$relId}}== 1){ @{$ringTagsOuter[$ring]}= @{$$wayTagsRef{$firstWay}}; # ring will be tagged like first way
} } }# outer
my @ringNodesTotal =(@ringNodesInner, @ringNodesOuter);
my @ringWaysTotal =(@ringWaysInner, @ringWaysOuter);
my @ringTagsTotal =(@ringTagsInner, @ringTagsOuter);
processRings(\@ringNodesTotal, \@ringWaysTotal, \@ringTagsTotal);
}# multi
}# relIds
}
# -----------------------------------------------------------------------------------------
sub processRings{#
# process rings of multipolygons and create path data for svg
#
my($ref1, $ref2, $ref3)= @_;
my @ringNodes = @$ref1;
my @ringWays = @$ref2;
my @ringTags = @$ref3;
my @polygon =();
my @polygonSize =();
my @ringIsIn =();
my @stack =(); # all created stacks
my %selectedStacks =(); # stacks selected for processing
my $actualLayer = 0; # for new tags
# rings referenced by array index
my($lonRef, $latRef)= getNodePointers();
my($wayNodesRef, $wayTagsRef)= getWayPointers();
# create polygons
if(cv('debug')eq "1"){print "CREATING POLYGONS\n";} for(my $ring = 0; $ring <= $#ringWays; $ring++){ my @poly =();
foreach my $node(@{$ringNodes[$ring]}){ push @poly, [$$lonRef{$node}, $$latRef{$node}];
} my($p)= Math::Polygon->new(@poly);
$polygon[$ring] = $p;
$polygonSize[$ring] = $p->area;
if(cv('debug')eq "1"){
print " POLYGON $ring - created, size = $polygonSize[$ring] \n";
foreach my $tag(@{$ringTags[$ring]}){ print " $tag->[0] - $tag->[1]\n";
} }}
# create is_in list(unsorted)for each ring
if(cv('debug')eq "1"){print "CALC isIn\n";} for(my $ring1=0; $ring1<=$#polygon; $ring1++){ my $res = 0;
for(my $ring2=0; $ring2<=$#polygon; $ring2++){ if($ring1 < $ring2){ $res = isIn($polygon[$ring1], $polygon[$ring2]);
if($res == 1){
push @{$ringIsIn[$ring1]}, $ring2;
if(cv('debug')eq "1"){print " $ring1 isIn $ring2\n";} }
if($res == 2){
push @{$ringIsIn[$ring2]}, $ring1;
if(cv('debug')eq "1"){print " $ring2 isIn $ring1\n";} }
} }} if(cv('debug')eq "1"){ print "IS IN LIST\n";
for(my $ring1=0; $ring1<=$#ringNodes; $ring1++){ if(defined @{$ringIsIn[$ring1]}){ print " ring $ring1 isIn - @{$ringIsIn[$ring1]}\n";
} } print "\n";
}
# sort is_in list, biggest first
if(cv('debug')eq "1"){print "SORTING isIn\n";} for(my $ring=0; $ring<=$#ringIsIn; $ring++){ my @isIn =();
foreach my $ring2(@{$ringIsIn[$ring]}){ push @isIn, [$ring2, $polygonSize[$ring2]];
} @isIn = sort{$a->[1] <=> $b->[1]}(@isIn); # sorted array
my @isIn2 =(); # only ring numbers
foreach my $temp(@isIn){ push @isIn2, $temp->[0];
} @{$stack[$ring]}= reverse(@isIn2);
push @{$stack[$ring]}, $ring; # sorted descending and ring self appended
if(cv('debug')eq "1"){print " stack ring $ring sorted: @{$stack[$ring]}\n";}}
# find tops and select stacks
if(cv('debug')eq "1"){print "SELECTING STACKS\n";} my $actualStack = 0;
for(my $stackNumber=0; $stackNumber<=$#stack; $stackNumber++){ # look for top element
my $topElement = $stack[$stackNumber]->[(scalar @{$stack[$stackNumber]}- 1)];
my $found = 0;
for(my $stackNumber2=0; $stackNumber2<=$#stack; $stackNumber2++){ if($stackNumber != $stackNumber2){ foreach my $ring(@{$stack[$stackNumber2]}){ if($ring == $topElement){
$found = 1;
if(cv('debug')eq "1"){print " element also found in stack $stackNumber2\n";} } } } }
if($found == 0){ @{$selectedStacks{$actualStack}}= @{$stack[$stackNumber]};
$actualStack++;
if(cv('debug')eq "1"){print " stack $stackNumber has been selected.\n";} }
}
# process selected stacks
if(cv('debug')eq "1"){print "PROCESS SELECTED STACKS\n";} # while stacks left
while(scalar(keys %selectedStacks)> 0){ my(@k)= keys %selectedStacks;
if(cv('debug')eq "1"){print " stacks available: @k\n";} my @nodes =();
my @nodesOld;
my @processedStacks =();
# select one bottom element
my $key = $k[0]; # key of first stack
if(cv('debug')eq "1"){print " stack nr $key selected\n";} my $ringToDraw = $selectedStacks{$key}[0];
if(cv('debug')eq "1"){print " ring to draw: $ringToDraw\n";}
push @nodesOld, @{$ringNodes[$ringToDraw]}; # outer polygon
push @nodes, [@{$ringNodes[$ringToDraw]}]; # outer polygon as array
# and remove ring from stacks; store processed stacks
foreach my $k2(keys %selectedStacks){ if($selectedStacks{$k2}[0] == $ringToDraw){
shift(@{$selectedStacks{$k2}});
push @processedStacks, $k2;
if(scalar @{$selectedStacks{$k2}}== 0){delete $selectedStacks{$k2};} if(cv('debug')eq "1"){print " removed $ringToDraw from stack $k2\n";} }
}
# foreach stack in processed stacks
foreach my $k(@processedStacks){ # if now bottom of a stack is hole, then add this polygon to points
if(defined $selectedStacks{$k}){ my $tempRing = $selectedStacks{$k}[0];
my $temp = $ringTags[$tempRing]->[0]->[0];
if(cv('debug')eq "1"){print " testing for hole: stack $k, ring $tempRing, tag $temp\n";} if($ringTags[$tempRing]->[0]->[0] eq "multihole"){ push @nodesOld, @{$ringNodes[$tempRing]};
push @nodes, [@{$ringNodes[$tempRing]}];
# print " nodes so far: @nodes\n";
# and remove this element from stack
shift @{$selectedStacks{$k}};
if(scalar @{$selectedStacks{$k}}== 0){delete $selectedStacks{$k};} if(cv('debug')eq "1"){print " ring $tempRing identified as hole\n";} } } }
# add way
@{$multiNodes{$newId}}= @nodesOld;
@{$multiTags{$newId}}= @{$ringTags[$ringToDraw]};
@{$multiPaths{$newId}}= @nodes;
push @{$$wayTagsRef{$newId}}, ["layer", $actualLayer];
$actualLayer++;
if(cv('debug')eq "1"){
print " DRAWN: $ringToDraw, wayId $newId\n";
foreach my $tag(@{$ringTags[$ringToDraw]}){ print " k/v $tag->[0] - $tag->[1]\n";
} }
$newId++;
}#(while)}
1;