source: subversion/applications/utils/gary68/mwMulti.pm @ 34714

Last change on this file since 34714 was 29705, checked in by lucasvr, 6 years ago

New command, -ignorelabels, to indicate that labels are not to be rendered at all.

File size: 13.0 KB
Line 
1#
2# PERL mapweaver module by gary68
3#
4#
5#
6#
7# Copyright (C) 2011, Gerhard Schwanz
8#
9# 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
10# Free Software Foundation; either version 3 of the License, or (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License along with this program; if not, see <http://www.gnu.org/licenses/>
16#
17
18
19package mwMulti ; 
20
21use strict ;
22use warnings ;
23
24use mwMap ;
25use mwMisc ;
26use mwFile ;
27use mwLabel ;
28use mwConfig ;
29use mwRules ;
30
31use Math::Polygon ;
32
33
34
35use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
36
37require Exporter ;
38
39@ISA = qw ( Exporter AutoLoader ) ;
40
41@EXPORT = qw (  processMultipolygons
42
43                 ) ;
44
45my $newId = 0 ;
46
47my %multiNodes = () ;
48my %multiTags = () ;
49my %multiPaths = () ;
50
51my %wayUsed = () ;
52
53# -------------------------------------------------------------------------
54
55sub processMultipolygons {
56        my $notDrawnMP = 0 ;
57        my $mp = 0 ;
58        my $mpLabels = 0 ;
59        my $mpNotDrawnLabels = 0 ;
60        print "draw multipolygons...\n" ;
61
62        preprocessMultipolygons() ;
63
64        foreach my $multiId (keys %multiTags) {
65
66                my $ruleRef = getAreaRule ( \@{$multiTags{$multiId}} ) ;
67
68                if (defined $ruleRef) {
69
70                        my $svgText = "" ;
71                        my $icon = "" ;
72                        if ($$ruleRef{'icon'} ne "none") {
73                                $icon = $$ruleRef{'icon'} ;
74                        }
75                        else {
76                                my $col = $$ruleRef{'color'} ;
77                                $svgText = "fill=\"$col\" " ;
78                        }
79
80                        my $ref = $multiPaths{$multiId}[0] ; # first, outer way
81                        my $size = areaSize ( $ref ) ;
82
83                        if ($size >= cv('minareasize') ) {
84                                drawArea ($svgText, $icon, $multiPaths{$multiId}, 1, "multi") ;
85                                $mp++ ;
86
87
88                                # LABELS
89                                my $name = "" ; my $ref1 ;
90                                if ( cv('ignorelabels') eq "0" ) {
91                                ($name, $ref1) = createLabel ( $multiTags{$multiId}, $$ruleRef{'label'}, 0, 0) ;
92
93                                if ( ( $$ruleRef{'label'} ne "none") and 
94                                        ( cv('nolabel') eq "1" ) and 
95                                        ($name eq "") ) 
96                                    { 
97                                            $name = "NO LABEL" ; 
98                                }
99                }
100
101                                if ($name ne "") {
102                                        if ($size >= cv('minarealabelsize') ) {
103                                                $mpLabels++ ;
104                                                if (cv('debug') eq "1") { print "MP LABEL: $name, size: $$ruleRef{'labelsize'}, color: $$ruleRef{'labelcolor'}\n" ; }
105
106                                                my ($x, $y) = areaCenter ( $multiPaths{$multiId}[0] ) ;
107
108
109                                                my $labelFont = $$ruleRef{'labelfont'} ;
110                                                my $labelFontFamily = $$ruleRef{'labelfontfamily'} ;
111                                                my $labelSize = $$ruleRef{'labelsize'} ;
112                                                my $color = $$ruleRef{'labelcolor'} ;
113                                                my $labelBold = $$ruleRef{'labelbold'} ;
114                                                my $labelItalic = $$ruleRef{'labelitalic'} ;
115                                                my $labelHalo = $$ruleRef{'labelhalo'} ;
116                                                my $labelHaloColor = $$ruleRef{'labelhalocolor'} ;
117
118                                                my $svgText = createTextSVG ( $labelFontFamily, $labelFont, $labelBold, $labelItalic, $labelSize, $color, $labelHalo, $labelHaloColor) ; 
119
120
121
122
123
124
125                                                # $svgText = createTextSVG ( undef, undef, $$ruleRef{'labelsize'}, $$ruleRef{'labelcolor'}, undef, undef ) ;
126                                                if (cv('debug') eq "1") { print "MP LABEL: svg: \"$svgText\"\n" ; }
127                                                placeLabelAndIcon ($x, $y, 1, 0, $name, $svgText, "none", 0, 0, "arealabels") ;
128                                        } # if size
129                                        else {
130                                                $mpNotDrawnLabels++ ;
131                                        }
132                                }
133                                else {
134
135                                }
136                        }
137                        else {
138                                $notDrawnMP++ ;
139                        }
140
141                } # if rule
142        } # foreach multi
143        print "$mp multipolygon areas drawn, $notDrawnMP not drawn because they were too small.\n" ;
144        print "$mpLabels multipolygon labels drawn, $mpNotDrawnLabels not drawn because belonging areas were too small.\n" ;
145}
146
147# ------------------------------------------------------------------------------------------
148
149sub preprocessMultipolygons {
150#
151# preprecess all multipolygons
152#
153
154        my ($wayNodesRef, $wayTagsRef) = getWayPointers() ;
155        my ($relationMembersRef, $relationTagsRef) = getRelationPointers() ;
156
157        foreach my $relId (keys %$relationMembersRef) {
158                my $isMulti = 0 ;
159                foreach my $tag (@{$$relationTagsRef{$relId}}) {
160                        if ( ($tag->[0] eq "type") and ($tag->[1] eq "multipolygon") ) { $isMulti = 1 ; }
161                }
162
163                if ($isMulti) {
164                        if (cv('debug') eq "1") { print "\n---------------------------------------------------\n" ; }
165                        if (cv('debug') eq "1") { print "\nRelation $relId is multipolygon!\n" ; }
166                       
167                        # get inner and outer ways
168                        my (@innerWays) = () ; my (@outerWays) = () ;
169                        foreach my $member ( @{$$relationMembersRef{$relId}} ) {
170                                if ( ($member->[0] eq "way") and ($member->[2] eq "outer") and (defined @{$$wayNodesRef{$member->[1]}} ) ) { push @outerWays, $member->[1] ; }
171                                if ( ($member->[0] eq "way") and ($member->[2] eq "inner") and (defined @{$$wayNodesRef{$member->[1]}} )) { push @innerWays, $member->[1] ; }
172                        }
173                        if (cv('debug') eq "1") { print "OUTER WAYS: @outerWays\n" ; }
174                        if (cv('debug') eq "1") { print "INNER WAYS: @innerWays\n" ; }
175
176                        my ($ringsWaysRef, $ringsNodesRef) ;
177                        my @ringWaysInner = () ; my @ringNodesInner = () ; my @ringTagsInner = () ;
178                        # build rings inner
179                        if (scalar @innerWays > 0) {
180                                ($ringsWaysRef, $ringsNodesRef) = buildRings (\@innerWays, 1) ;
181                                @ringWaysInner = @$ringsWaysRef ; 
182                                @ringNodesInner = @$ringsNodesRef ;
183                                for (my $ring=0; $ring<=$#ringWaysInner; $ring++) {
184                                        if (cv('debug') eq "1") { print "INNER RING $ring: @{$ringWaysInner[$ring]}\n" ; }
185                                        my $firstWay = $ringWaysInner[$ring]->[0] ;
186                                        if (scalar @{$ringWaysInner[$ring]} == 1) {$wayUsed{$firstWay} = 1 ; } # way will be marked as used/drawn by multipolygon
187
188                                        @{$ringTagsInner[$ring]} = @{$$wayTagsRef{$firstWay}} ; # ring will be tagged like first contained way
189                                        if (cv('debug') eq "1") {
190                                                print "tags from first way...\n" ;
191                                                foreach my $tag (@{$$wayTagsRef{$firstWay}}) {
192                                                        print "  $tag->[0] - $tag->[1]\n" ;
193                                                }
194                                        }
195                                        if ( (scalar @{$$wayTagsRef{$firstWay}}) == 0 ) {
196                                                if (cv('debug') eq "1") { print "tags set to hole in mp.\n" ; }
197                                                push @{$ringTagsInner[$ring]}, ["multihole", "yes"] ;
198                                        }
199                                }
200                        }
201
202                        # build rings outer
203                        my @ringWaysOuter = () ; my @ringNodesOuter = () ; my @ringTagsOuter = () ;
204                        if (scalar @outerWays > 0) {
205                                ($ringsWaysRef, $ringsNodesRef) = buildRings (\@outerWays, 1) ;
206                                @ringWaysOuter = @$ringsWaysRef ; # not necessary for outer
207                                @ringNodesOuter = @$ringsNodesRef ;
208                                for (my $ring=0; $ring<=$#ringWaysOuter; $ring++) {
209                                        if (cv('debug') eq "1") { print "OUTER RING $ring: @{$ringWaysOuter[$ring]}\n" ; }
210                                        my $firstWay = $ringWaysOuter[$ring]->[0] ;
211                                        if (scalar @{$ringWaysOuter[$ring]} == 1) {$wayUsed{$firstWay} = 1 ; }
212                                        @{$ringTagsOuter[$ring]} = @{$$relationTagsRef{$relId}} ; # tags from relation
213                                        if (cv('debug') eq "1") {
214                                                print "tags from relation...\n" ;
215                                                foreach my $tag (@{$$relationTagsRef{$relId}}) {
216                                                        print "  $tag->[0] - $tag->[1]\n" ;
217                                                }
218                                        }
219                                        if (scalar @{$$relationTagsRef{$relId}} == 1) {
220                                                @{$ringTagsOuter[$ring]} = @{$$wayTagsRef{$firstWay}} ; # ring will be tagged like first way
221                                        }
222                                }
223                        } # outer
224                       
225                        my @ringNodesTotal = (@ringNodesInner, @ringNodesOuter) ;
226                        my @ringWaysTotal = (@ringWaysInner, @ringWaysOuter) ;
227                        my @ringTagsTotal = (@ringTagsInner, @ringTagsOuter) ;
228
229                        processRings (\@ringNodesTotal, \@ringWaysTotal, \@ringTagsTotal) ;
230                } # multi
231
232        } # relIds
233}
234
235# -----------------------------------------------------------------------------------------
236
237sub processRings {
238#
239# process rings of multipolygons and create path data for svg
240#
241        my ($ref1, $ref2, $ref3) = @_ ;
242        my @ringNodes = @$ref1 ;
243        my @ringWays = @$ref2 ;
244        my @ringTags = @$ref3 ;
245        my @polygon = () ;
246        my @polygonSize = () ;
247        my @ringIsIn = () ;
248        my @stack = () ; # all created stacks
249        my %selectedStacks = () ; # stacks selected for processing
250        my $actualLayer = 0 ; # for new tags
251        # rings referenced by array index
252
253        my ($lonRef, $latRef) = getNodePointers() ;
254        my ($wayNodesRef, $wayTagsRef) = getWayPointers() ;
255
256        # create polygons
257        if (cv('debug') eq "1") { print "CREATING POLYGONS\n" ; }
258        for (my $ring = 0 ; $ring <= $#ringWays; $ring++) {
259                my @poly = () ;
260                foreach my $node ( @{$ringNodes[$ring]} ) {
261                        push @poly, [$$lonRef{$node}, $$latRef{$node}] ;
262                }
263                my ($p) = Math::Polygon->new(@poly) ;
264                $polygon[$ring] = $p ;
265                $polygonSize[$ring] = $p->area ;
266                if (cv('debug') eq "1") { 
267                        print "  POLYGON $ring - created, size = $polygonSize[$ring] \n" ; 
268                        foreach my $tag (@{$ringTags[$ring]}) {
269                                print "    $tag->[0] - $tag->[1]\n" ;
270                        }
271                }
272        }
273
274
275        # create is_in list (unsorted) for each ring
276        if (cv('debug') eq "1") { print "CALC isIn\n" ; }
277        for (my $ring1=0 ; $ring1<=$#polygon; $ring1++) {
278                my $res = 0 ;
279                for (my $ring2=0 ; $ring2<=$#polygon; $ring2++) {
280                        if ($ring1 < $ring2) {
281                                $res = isIn ($polygon[$ring1], $polygon[$ring2]) ;
282                                if ($res == 1) { 
283                                        push @{$ringIsIn[$ring1]}, $ring2 ; 
284                                        if (cv('debug') eq "1") { print "  $ring1 isIn $ring2\n" ; }
285                                } 
286                                if ($res == 2) { 
287                                        push @{$ringIsIn[$ring2]}, $ring1 ; 
288                                        if (cv('debug') eq "1") { print "  $ring2 isIn $ring1\n" ; }
289                                } 
290                        }
291                }
292        }
293        if (cv('debug') eq "1") {
294                print "IS IN LIST\n" ;
295                for (my $ring1=0 ; $ring1<=$#ringNodes; $ring1++) {
296                        if (defined @{$ringIsIn[$ring1]}) {
297                                print "  ring $ring1 isIn - @{$ringIsIn[$ring1]}\n" ;
298                        }
299                }
300                print "\n" ;
301        }
302
303        # sort is_in list, biggest first
304        if (cv('debug') eq "1") { print "SORTING isIn\n" ; }
305        for (my $ring=0 ; $ring<=$#ringIsIn; $ring++) {
306                my @isIn = () ;
307                foreach my $ring2 (@{$ringIsIn[$ring]}) {
308                        push @isIn, [$ring2, $polygonSize[$ring2]] ;
309                }
310                @isIn = sort { $a->[1] <=> $b->[1] } (@isIn) ; # sorted array
311
312                my @isIn2 = () ; # only ring numbers
313                foreach my $temp (@isIn) {
314                        push @isIn2, $temp->[0] ;
315                }
316                @{$stack[$ring]} = reverse (@isIn2) ; 
317                push @{$stack[$ring]}, $ring ; # sorted descending and ring self appended
318                if (cv('debug') eq "1") { print "  stack ring $ring sorted: @{$stack[$ring]}\n" ; }
319        }
320
321        # find tops and select stacks
322        if (cv('debug') eq "1") { print "SELECTING STACKS\n" ; }
323        my $actualStack = 0 ;
324        for (my $stackNumber=0 ; $stackNumber<=$#stack; $stackNumber++) {
325                # look for top element
326                my $topElement = $stack[$stackNumber]->[(scalar @{$stack[$stackNumber]} - 1)] ;
327                my $found = 0 ;
328                for (my $stackNumber2=0 ; $stackNumber2<=$#stack; $stackNumber2++) {
329                        if ($stackNumber != $stackNumber2) {
330                                foreach my $ring (@{$stack[$stackNumber2]}) {
331                                        if ($ring == $topElement) { 
332                                                $found = 1 ;
333                                                if (cv('debug') eq "1") { print "      element also found in stack $stackNumber2\n" ; }
334                                        }
335                                }
336                        }
337                }
338
339                if ($found == 0) {
340                        @{$selectedStacks{$actualStack}} = @{$stack[$stackNumber]} ;
341                        $actualStack++ ;
342                        if (cv('debug') eq "1") { print "    stack $stackNumber has been selected.\n" ; }
343                }
344       
345        }
346       
347        # process selected stacks
348
349        if (cv('debug') eq "1") { print "PROCESS SELECTED STACKS\n" ; }
350        # while stacks left
351        while (scalar (keys %selectedStacks) > 0) {
352                my (@k) = keys %selectedStacks ;
353                if (cv('debug') eq "1") { print "  stacks available: @k\n" ; }
354                my @nodes = () ;
355                my @nodesOld ;
356                my @processedStacks = () ;
357
358                # select one bottom element
359                my $key = $k[0] ; # key of first stack
360                if (cv('debug') eq "1") { print "  stack nr $key selected\n" ; }
361                my $ringToDraw = $selectedStacks{$key}[0] ;
362                if (cv('debug') eq "1") { print "  ring to draw: $ringToDraw\n" ; }
363
364                push @nodesOld, @{$ringNodes[$ringToDraw]} ; # outer polygon
365                push @nodes, [@{$ringNodes[$ringToDraw]}] ; # outer polygon as array
366
367                # and remove ring from stacks; store processed stacks
368                foreach my $k2 (keys %selectedStacks) {
369                        if ($selectedStacks{$k2}[0] == $ringToDraw) { 
370                                shift (@{$selectedStacks{$k2}}) ; 
371                                push @processedStacks, $k2 ;
372                                if (scalar @{$selectedStacks{$k2}} == 0) { delete $selectedStacks{$k2} ; }
373                                if (cv('debug') eq "1") { print "  removed $ringToDraw from stack $k2\n" ; }
374                        } 
375                }
376
377                # foreach stack in processed stacks
378                foreach my $k (@processedStacks) {
379                        # if now bottom of a stack is hole, then add this polygon to points
380                        if (defined $selectedStacks{$k}) {
381                                my $tempRing = $selectedStacks{$k}[0] ;
382                                my $temp = $ringTags[$tempRing]->[0]->[0] ;
383                                if (cv('debug') eq "1") { print "           testing for hole: stack $k, ring $tempRing, tag $temp\n" ; }
384                                if ($ringTags[$tempRing]->[0]->[0] eq "multihole") {
385                                        push @nodesOld, @{$ringNodes[$tempRing]} ;
386                                        push @nodes, [@{$ringNodes[$tempRing]}] ;
387                                        # print "      nodes so far: @nodes\n" ;
388                                        # and remove this element from stack
389                                        shift @{$selectedStacks{$k}} ;
390                                        if (scalar @{$selectedStacks{$k}} == 0) { delete $selectedStacks{$k} ; }
391                                        if (cv('debug') eq "1") { print "  ring $tempRing identified as hole\n" ; }
392                                }
393                        }
394                }
395
396                # add way
397
398                @{$multiNodes{$newId}} = @nodesOld ;
399                @{$multiTags{$newId}} = @{$ringTags[$ringToDraw]} ;
400                @{$multiPaths{$newId}} = @nodes ;
401
402                push @{$$wayTagsRef{$newId}}, ["layer", $actualLayer] ;
403                $actualLayer++ ;
404
405                if (cv('debug') eq "1") { 
406                        print "  DRAWN: $ringToDraw, wayId $newId\n" ; 
407                        foreach my $tag (@{$ringTags[$ringToDraw]}) {
408                                print "    k/v $tag->[0] - $tag->[1]\n" ;
409                        }
410                }
411
412                $newId++ ;
413
414        } # (while)
415}
416
417
4181 ;
419
420
Note: See TracBrowser for help on using the repository browser.