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

Last change on this file since 26357 was 26357, checked in by gary68, 8 years ago

mwMulti.pm added for mapweaver

File size: 11.7 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
57        print "draw multipolygons...\n" ;
58
59        preprocessMultipolygons() ;
60
61        foreach my $multiId (keys %multiTags) {
62
63                my $ruleRef = getAreaRule ( \@{$multiTags{$multiId}} ) ;
64
65                if (defined $ruleRef) {
66
67                        # TODO test size first!
68
69                        my $svgText = "" ;
70                        my $icon = "" ;
71                        if ($$ruleRef{'icon'} ne "none") {
72                                $icon = $$ruleRef{'icon'} ;
73                        }
74                        else {
75                                my $col = $$ruleRef{'color'} ;
76                                $svgText = "fill=\"$col\" " ;
77                        }
78
79                        drawArea ($svgText, $icon, $multiPaths{$multiId}, 1, "multi") ;
80
81                        # LABELS
82                        my $name = "" ; my $ref1 ;
83                        ($name, $ref1) = createLabel ( $multiTags{$multiId}, $$ruleRef{'label'}, 0, 0) ;
84
85                        if ( ( $$ruleRef{'label'} ne "none") and 
86                                ( cv('nolabel') eq "1" ) and 
87                                ($name eq "") ) 
88                        { 
89                                $name = "NO LABEL" ; 
90                        }
91
92                        if ($name ne "") {
93                                my ($x, $y) = center (nodes2Coordinates( @{$multiNodes{$multiId}} ) ) ;
94
95                                # placeLabelAndIcon ($x,$y, 0, 0, $name, $test->[$wayIndexLabelColor], $test->[$wayIndexLabelSize], $test->[$wayIndexLabelFont], $ppc, "none", 0, 0, $allowIconMoveOpt, $halo) ;
96
97                                $svgText = "" ;
98                                my $iSize = $$ruleRef{'iconsize'} ;
99                                placeLabelAndIcon ($x, $y, 0, 0, $name, $svgText, $icon, $iSize, $iSize, "multi") ;
100
101                        } # if
102                } # if rule
103        } # foreach multi
104}
105
106# ------------------------------------------------------------------------------------------
107
108sub preprocessMultipolygons {
109#
110# preprecess all multipolygons
111#
112
113        my ($wayNodesRef, $wayTagsRef) = getWayPointers() ;
114        my ($relationMembersRef, $relationTagsRef) = getRelationPointers() ;
115
116        foreach my $relId (keys %$relationMembersRef) {
117                my $isMulti = 0 ;
118                foreach my $tag (@{$$relationTagsRef{$relId}}) {
119                        if ( ($tag->[0] eq "type") and ($tag->[1] eq "multipolygon") ) { $isMulti = 1 ; }
120                }
121
122                if ($isMulti) {
123                        if (cv('debug') eq "1") { print "\n---------------------------------------------------\n" ; }
124                        if (cv('debug') eq "1") { print "\nRelation $relId is multipolygon!\n" ; }
125                       
126                        # get inner and outer ways
127                        my (@innerWays) = () ; my (@outerWays) = () ;
128                        foreach my $member ( @{$$relationMembersRef{$relId}} ) {
129                                if ( ($member->[0] eq "way") and ($member->[2] eq "outer") and (defined @{$$wayNodesRef{$member->[1]}} ) ) { push @outerWays, $member->[1] ; }
130                                if ( ($member->[0] eq "way") and ($member->[2] eq "inner") and (defined @{$$wayNodesRef{$member->[1]}} )) { push @innerWays, $member->[1] ; }
131                        }
132                        if (cv('debug') eq "1") { print "OUTER WAYS: @outerWays\n" ; }
133                        if (cv('debug') eq "1") { print "INNER WAYS: @innerWays\n" ; }
134
135                        my ($ringsWaysRef, $ringsNodesRef) ;
136                        my @ringWaysInner = () ; my @ringNodesInner = () ; my @ringTagsInner = () ;
137                        # build rings inner
138                        if (scalar @innerWays > 0) {
139                                ($ringsWaysRef, $ringsNodesRef) = buildRings (\@innerWays, 1) ;
140                                @ringWaysInner = @$ringsWaysRef ; 
141                                @ringNodesInner = @$ringsNodesRef ;
142                                for (my $ring=0; $ring<=$#ringWaysInner; $ring++) {
143                                        if (cv('debug') eq "1") { print "INNER RING $ring: @{$ringWaysInner[$ring]}\n" ; }
144                                        my $firstWay = $ringWaysInner[$ring]->[0] ;
145                                        if (scalar @{$ringWaysInner[$ring]} == 1) {$wayUsed{$firstWay} = 1 ; } # way will be marked as used/drawn by multipolygon
146
147                                        @{$ringTagsInner[$ring]} = @{$$wayTagsRef{$firstWay}} ; # ring will be tagged like first contained way
148                                        if (cv('debug') eq "1") {
149                                                print "tags from first way...\n" ;
150                                                foreach my $tag (@{$$wayTagsRef{$firstWay}}) {
151                                                        print "  $tag->[0] - $tag->[1]\n" ;
152                                                }
153                                        }
154                                        if ( (scalar @{$$wayTagsRef{$firstWay}}) == 0 ) {
155                                                if (cv('debug') eq "1") { print "tags set to hole in mp.\n" ; }
156                                                push @{$ringTagsInner[$ring]}, ["multihole", "yes"] ;
157                                        }
158                                }
159                        }
160
161                        # build rings outer
162                        my @ringWaysOuter = () ; my @ringNodesOuter = () ; my @ringTagsOuter = () ;
163                        if (scalar @outerWays > 0) {
164                                ($ringsWaysRef, $ringsNodesRef) = buildRings (\@outerWays, 1) ;
165                                @ringWaysOuter = @$ringsWaysRef ; # not necessary for outer
166                                @ringNodesOuter = @$ringsNodesRef ;
167                                for (my $ring=0; $ring<=$#ringWaysOuter; $ring++) {
168                                        if (cv('debug') eq "1") { print "OUTER RING $ring: @{$ringWaysOuter[$ring]}\n" ; }
169                                        my $firstWay = $ringWaysOuter[$ring]->[0] ;
170                                        if (scalar @{$ringWaysOuter[$ring]} == 1) {$wayUsed{$firstWay} = 1 ; }
171                                        @{$ringTagsOuter[$ring]} = @{$$relationTagsRef{$relId}} ; # tags from relation
172                                        if (cv('debug') eq "1") {
173                                                print "tags from relation...\n" ;
174                                                foreach my $tag (@{$$relationTagsRef{$relId}}) {
175                                                        print "  $tag->[0] - $tag->[1]\n" ;
176                                                }
177                                        }
178                                        if (scalar @{$$relationTagsRef{$relId}} == 1) {
179                                                @{$ringTagsOuter[$ring]} = @{$$wayTagsRef{$firstWay}} ; # ring will be tagged like first way
180                                        }
181                                }
182                        } # outer
183                       
184                        my @ringNodesTotal = (@ringNodesInner, @ringNodesOuter) ;
185                        my @ringWaysTotal = (@ringWaysInner, @ringWaysOuter) ;
186                        my @ringTagsTotal = (@ringTagsInner, @ringTagsOuter) ;
187
188                        processRings (\@ringNodesTotal, \@ringWaysTotal, \@ringTagsTotal) ;
189                } # multi
190
191        } # relIds
192}
193
194# -----------------------------------------------------------------------------------------
195
196sub processRings {
197#
198# process rings of multipolygons and create path data for svg
199#
200        my ($ref1, $ref2, $ref3) = @_ ;
201        my @ringNodes = @$ref1 ;
202        my @ringWays = @$ref2 ;
203        my @ringTags = @$ref3 ;
204        my @polygon = () ;
205        my @polygonSize = () ;
206        my @ringIsIn = () ;
207        my @stack = () ; # all created stacks
208        my %selectedStacks = () ; # stacks selected for processing
209        my $actualLayer = 0 ; # for new tags
210        # rings referenced by array index
211
212        my ($lonRef, $latRef) = getNodePointers() ;
213        my ($wayNodesRef, $wayTagsRef) = getWayPointers() ;
214
215        # create polygons
216        if (cv('debug') eq "1") { print "CREATING POLYGONS\n" ; }
217        for (my $ring = 0 ; $ring <= $#ringWays; $ring++) {
218                my @poly = () ;
219                foreach my $node ( @{$ringNodes[$ring]} ) {
220                        push @poly, [$$lonRef{$node}, $$latRef{$node}] ;
221                }
222                my ($p) = Math::Polygon->new(@poly) ;
223                $polygon[$ring] = $p ;
224                $polygonSize[$ring] = $p->area ;
225                if (cv('debug') eq "1") { 
226                        print "  POLYGON $ring - created, size = $polygonSize[$ring] \n" ; 
227                        foreach my $tag (@{$ringTags[$ring]}) {
228                                print "    $tag->[0] - $tag->[1]\n" ;
229                        }
230                }
231        }
232
233
234        # create is_in list (unsorted) for each ring
235        if (cv('debug') eq "1") { print "CALC isIn\n" ; }
236        for (my $ring1=0 ; $ring1<=$#polygon; $ring1++) {
237                my $res = 0 ;
238                for (my $ring2=0 ; $ring2<=$#polygon; $ring2++) {
239                        if ($ring1 < $ring2) {
240                                $res = isIn ($polygon[$ring1], $polygon[$ring2]) ;
241                                if ($res == 1) { 
242                                        push @{$ringIsIn[$ring1]}, $ring2 ; 
243                                        if (cv('debug') eq "1") { print "  $ring1 isIn $ring2\n" ; }
244                                } 
245                                if ($res == 2) { 
246                                        push @{$ringIsIn[$ring2]}, $ring1 ; 
247                                        if (cv('debug') eq "1") { print "  $ring2 isIn $ring1\n" ; }
248                                } 
249                        }
250                }
251        }
252        if (cv('debug') eq "1") {
253                print "IS IN LIST\n" ;
254                for (my $ring1=0 ; $ring1<=$#ringNodes; $ring1++) {
255                        if (defined @{$ringIsIn[$ring1]}) {
256                                print "  ring $ring1 isIn - @{$ringIsIn[$ring1]}\n" ;
257                        }
258                }
259                print "\n" ;
260        }
261
262        # sort is_in list, biggest first
263        if (cv('debug') eq "1") { print "SORTING isIn\n" ; }
264        for (my $ring=0 ; $ring<=$#ringIsIn; $ring++) {
265                my @isIn = () ;
266                foreach my $ring2 (@{$ringIsIn[$ring]}) {
267                        push @isIn, [$ring2, $polygonSize[$ring2]] ;
268                }
269                @isIn = sort { $a->[1] <=> $b->[1] } (@isIn) ; # sorted array
270
271                my @isIn2 = () ; # only ring numbers
272                foreach my $temp (@isIn) {
273                        push @isIn2, $temp->[0] ;
274                }
275                @{$stack[$ring]} = reverse (@isIn2) ; 
276                push @{$stack[$ring]}, $ring ; # sorted descending and ring self appended
277                if (cv('debug') eq "1") { print "  stack ring $ring sorted: @{$stack[$ring]}\n" ; }
278        }
279
280        # find tops and select stacks
281        if (cv('debug') eq "1") { print "SELECTING STACKS\n" ; }
282        my $actualStack = 0 ;
283        for (my $stackNumber=0 ; $stackNumber<=$#stack; $stackNumber++) {
284                # look for top element
285                my $topElement = $stack[$stackNumber]->[(scalar @{$stack[$stackNumber]} - 1)] ;
286                my $found = 0 ;
287                for (my $stackNumber2=0 ; $stackNumber2<=$#stack; $stackNumber2++) {
288                        if ($stackNumber != $stackNumber2) {
289                                foreach my $ring (@{$stack[$stackNumber2]}) {
290                                        if ($ring == $topElement) { 
291                                                $found = 1 ;
292                                                if (cv('debug') eq "1") { print "      element also found in stack $stackNumber2\n" ; }
293                                        }
294                                }
295                        }
296                }
297
298                if ($found == 0) {
299                        @{$selectedStacks{$actualStack}} = @{$stack[$stackNumber]} ;
300                        $actualStack++ ;
301                        if (cv('debug') eq "1") { print "    stack $stackNumber has been selected.\n" ; }
302                }
303       
304        }
305       
306        # process selected stacks
307
308        if (cv('debug') eq "1") { print "PROCESS SELECTED STACKS\n" ; }
309        # while stacks left
310        while (scalar (keys %selectedStacks) > 0) {
311                my (@k) = keys %selectedStacks ;
312                if (cv('debug') eq "1") { print "  stacks available: @k\n" ; }
313                my @nodes = () ;
314                my @nodesOld ;
315                my @processedStacks = () ;
316
317                # select one bottom element
318                my $key = $k[0] ; # key of first stack
319                if (cv('debug') eq "1") { print "  stack nr $key selected\n" ; }
320                my $ringToDraw = $selectedStacks{$key}[0] ;
321                if (cv('debug') eq "1") { print "  ring to draw: $ringToDraw\n" ; }
322
323                push @nodesOld, @{$ringNodes[$ringToDraw]} ; # outer polygon
324                push @nodes, [@{$ringNodes[$ringToDraw]}] ; # outer polygon as array
325
326                # and remove ring from stacks; store processed stacks
327                foreach my $k2 (keys %selectedStacks) {
328                        if ($selectedStacks{$k2}[0] == $ringToDraw) { 
329                                shift (@{$selectedStacks{$k2}}) ; 
330                                push @processedStacks, $k2 ;
331                                if (scalar @{$selectedStacks{$k2}} == 0) { delete $selectedStacks{$k2} ; }
332                                if (cv('debug') eq "1") { print "  removed $ringToDraw from stack $k2\n" ; }
333                        } 
334                }
335
336                # foreach stack in processed stacks
337                foreach my $k (@processedStacks) {
338                        # if now bottom of a stack is hole, then add this polygon to points
339                        if (defined $selectedStacks{$k}) {
340                                my $tempRing = $selectedStacks{$k}[0] ;
341                                my $temp = $ringTags[$tempRing]->[0]->[0] ;
342                                if (cv('debug') eq "1") { print "           testing for hole: stack $k, ring $tempRing, tag $temp\n" ; }
343                                if ($ringTags[$tempRing]->[0]->[0] eq "multihole") {
344                                        push @nodesOld, @{$ringNodes[$tempRing]} ;
345                                        push @nodes, [@{$ringNodes[$tempRing]}] ;
346                                        # print "      nodes so far: @nodes\n" ;
347                                        # and remove this element from stack
348                                        shift @{$selectedStacks{$k}} ;
349                                        if (scalar @{$selectedStacks{$k}} == 0) { delete $selectedStacks{$k} ; }
350                                        if (cv('debug') eq "1") { print "  ring $tempRing identified as hole\n" ; }
351                                }
352                        }
353                }
354
355                # add way
356
357                @{$multiNodes{$newId}} = @nodesOld ;
358                @{$multiTags{$newId}} = @{$ringTags[$ringToDraw]} ;
359                @{$multiPaths{$newId}} = @nodes ;
360
361                push @{$$wayTagsRef{$newId}}, ["layer", $actualLayer] ;
362                $actualLayer++ ;
363
364                if (cv('debug') eq "1") { 
365                        print "  DRAWN: $ringToDraw, wayId $newId\n" ; 
366                        foreach my $tag (@{$ringTags[$ringToDraw]}) {
367                                print "    k/v $tag->[0] - $tag->[1]\n" ;
368                        }
369                }
370
371                $newId++ ;
372
373        } # (while)
374}
375
376
3771 ;
378
379
Note: See TracBrowser for help on using the repository browser.